dijit-all.js.uncompressed.js 984 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. /*
  7. This is an optimized version of Dojo, built for deployment and not for
  8. development. To get sources and documentation, please visit:
  9. http://dojotoolkit.org
  10. */
  11. if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12. dojo._hasResource["dojo.colors"] = true;
  13. dojo.provide("dojo.colors");
  14. dojo.getObject("colors", true, dojo);
  15. //TODO: this module appears to break naming conventions
  16. /*=====
  17. dojo.colors = {
  18. // summary: Color utilities
  19. }
  20. =====*/
  21. (function(){
  22. // this is a standard conversion prescribed by the CSS3 Color Module
  23. var hue2rgb = function(m1, m2, h){
  24. if(h < 0){ ++h; }
  25. if(h > 1){ --h; }
  26. var h6 = 6 * h;
  27. if(h6 < 1){ return m1 + (m2 - m1) * h6; }
  28. if(2 * h < 1){ return m2; }
  29. if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
  30. return m1;
  31. };
  32. dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
  33. // summary:
  34. // get rgb(a) array from css-style color declarations
  35. // description:
  36. // this function can handle all 4 CSS3 Color Module formats: rgb,
  37. // rgba, hsl, hsla, including rgb(a) with percentage values.
  38. var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
  39. if(m){
  40. var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
  41. if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
  42. var r = c[0];
  43. if(r.charAt(r.length - 1) == "%"){
  44. // 3 rgb percentage values
  45. a = dojo.map(c, function(x){
  46. return parseFloat(x) * 2.56;
  47. });
  48. if(l == 4){ a[3] = c[3]; }
  49. return dojo.colorFromArray(a, obj); // dojo.Color
  50. }
  51. return dojo.colorFromArray(c, obj); // dojo.Color
  52. }
  53. if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
  54. // normalize hsl values
  55. var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
  56. S = parseFloat(c[1]) / 100,
  57. L = parseFloat(c[2]) / 100,
  58. // calculate rgb according to the algorithm
  59. // recommended by the CSS3 Color Module
  60. m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
  61. m1 = 2 * L - m2;
  62. a = [
  63. hue2rgb(m1, m2, H + 1 / 3) * 256,
  64. hue2rgb(m1, m2, H) * 256,
  65. hue2rgb(m1, m2, H - 1 / 3) * 256,
  66. 1
  67. ];
  68. if(l == 4){ a[3] = c[3]; }
  69. return dojo.colorFromArray(a, obj); // dojo.Color
  70. }
  71. }
  72. return null; // dojo.Color
  73. };
  74. var confine = function(c, low, high){
  75. // summary:
  76. // sanitize a color component by making sure it is a number,
  77. // and clamping it to valid values
  78. c = Number(c);
  79. return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
  80. };
  81. dojo.Color.prototype.sanitize = function(){
  82. // summary: makes sure that the object has correct attributes
  83. var t = this;
  84. t.r = Math.round(confine(t.r, 0, 255));
  85. t.g = Math.round(confine(t.g, 0, 255));
  86. t.b = Math.round(confine(t.b, 0, 255));
  87. t.a = confine(t.a, 0, 1);
  88. return this; // dojo.Color
  89. };
  90. })();
  91. dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
  92. // summary: creates a greyscale color with an optional alpha
  93. return dojo.colorFromArray([g, g, g, a]);
  94. };
  95. // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
  96. dojo.mixin(dojo.Color.named, {
  97. aliceblue: [240,248,255],
  98. antiquewhite: [250,235,215],
  99. aquamarine: [127,255,212],
  100. azure: [240,255,255],
  101. beige: [245,245,220],
  102. bisque: [255,228,196],
  103. blanchedalmond: [255,235,205],
  104. blueviolet: [138,43,226],
  105. brown: [165,42,42],
  106. burlywood: [222,184,135],
  107. cadetblue: [95,158,160],
  108. chartreuse: [127,255,0],
  109. chocolate: [210,105,30],
  110. coral: [255,127,80],
  111. cornflowerblue: [100,149,237],
  112. cornsilk: [255,248,220],
  113. crimson: [220,20,60],
  114. cyan: [0,255,255],
  115. darkblue: [0,0,139],
  116. darkcyan: [0,139,139],
  117. darkgoldenrod: [184,134,11],
  118. darkgray: [169,169,169],
  119. darkgreen: [0,100,0],
  120. darkgrey: [169,169,169],
  121. darkkhaki: [189,183,107],
  122. darkmagenta: [139,0,139],
  123. darkolivegreen: [85,107,47],
  124. darkorange: [255,140,0],
  125. darkorchid: [153,50,204],
  126. darkred: [139,0,0],
  127. darksalmon: [233,150,122],
  128. darkseagreen: [143,188,143],
  129. darkslateblue: [72,61,139],
  130. darkslategray: [47,79,79],
  131. darkslategrey: [47,79,79],
  132. darkturquoise: [0,206,209],
  133. darkviolet: [148,0,211],
  134. deeppink: [255,20,147],
  135. deepskyblue: [0,191,255],
  136. dimgray: [105,105,105],
  137. dimgrey: [105,105,105],
  138. dodgerblue: [30,144,255],
  139. firebrick: [178,34,34],
  140. floralwhite: [255,250,240],
  141. forestgreen: [34,139,34],
  142. gainsboro: [220,220,220],
  143. ghostwhite: [248,248,255],
  144. gold: [255,215,0],
  145. goldenrod: [218,165,32],
  146. greenyellow: [173,255,47],
  147. grey: [128,128,128],
  148. honeydew: [240,255,240],
  149. hotpink: [255,105,180],
  150. indianred: [205,92,92],
  151. indigo: [75,0,130],
  152. ivory: [255,255,240],
  153. khaki: [240,230,140],
  154. lavender: [230,230,250],
  155. lavenderblush: [255,240,245],
  156. lawngreen: [124,252,0],
  157. lemonchiffon: [255,250,205],
  158. lightblue: [173,216,230],
  159. lightcoral: [240,128,128],
  160. lightcyan: [224,255,255],
  161. lightgoldenrodyellow: [250,250,210],
  162. lightgray: [211,211,211],
  163. lightgreen: [144,238,144],
  164. lightgrey: [211,211,211],
  165. lightpink: [255,182,193],
  166. lightsalmon: [255,160,122],
  167. lightseagreen: [32,178,170],
  168. lightskyblue: [135,206,250],
  169. lightslategray: [119,136,153],
  170. lightslategrey: [119,136,153],
  171. lightsteelblue: [176,196,222],
  172. lightyellow: [255,255,224],
  173. limegreen: [50,205,50],
  174. linen: [250,240,230],
  175. magenta: [255,0,255],
  176. mediumaquamarine: [102,205,170],
  177. mediumblue: [0,0,205],
  178. mediumorchid: [186,85,211],
  179. mediumpurple: [147,112,219],
  180. mediumseagreen: [60,179,113],
  181. mediumslateblue: [123,104,238],
  182. mediumspringgreen: [0,250,154],
  183. mediumturquoise: [72,209,204],
  184. mediumvioletred: [199,21,133],
  185. midnightblue: [25,25,112],
  186. mintcream: [245,255,250],
  187. mistyrose: [255,228,225],
  188. moccasin: [255,228,181],
  189. navajowhite: [255,222,173],
  190. oldlace: [253,245,230],
  191. olivedrab: [107,142,35],
  192. orange: [255,165,0],
  193. orangered: [255,69,0],
  194. orchid: [218,112,214],
  195. palegoldenrod: [238,232,170],
  196. palegreen: [152,251,152],
  197. paleturquoise: [175,238,238],
  198. palevioletred: [219,112,147],
  199. papayawhip: [255,239,213],
  200. peachpuff: [255,218,185],
  201. peru: [205,133,63],
  202. pink: [255,192,203],
  203. plum: [221,160,221],
  204. powderblue: [176,224,230],
  205. rosybrown: [188,143,143],
  206. royalblue: [65,105,225],
  207. saddlebrown: [139,69,19],
  208. salmon: [250,128,114],
  209. sandybrown: [244,164,96],
  210. seagreen: [46,139,87],
  211. seashell: [255,245,238],
  212. sienna: [160,82,45],
  213. skyblue: [135,206,235],
  214. slateblue: [106,90,205],
  215. slategray: [112,128,144],
  216. slategrey: [112,128,144],
  217. snow: [255,250,250],
  218. springgreen: [0,255,127],
  219. steelblue: [70,130,180],
  220. tan: [210,180,140],
  221. thistle: [216,191,216],
  222. tomato: [255,99,71],
  223. transparent: [0, 0, 0, 0],
  224. turquoise: [64,224,208],
  225. violet: [238,130,238],
  226. wheat: [245,222,179],
  227. whitesmoke: [245,245,245],
  228. yellowgreen: [154,205,50]
  229. });
  230. }
  231. if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  232. dojo._hasResource["dojo.i18n"] = true;
  233. dojo.provide("dojo.i18n");
  234. dojo.getObject("i18n", true, dojo);
  235. /*=====
  236. dojo.i18n = {
  237. // summary: Utility classes to enable loading of resources for internationalization (i18n)
  238. };
  239. =====*/
  240. // when using a real AMD loader, dojo.i18n.getLocalization is already defined by dojo/lib/backCompat
  241. dojo.i18n.getLocalization = dojo.i18n.getLocalization || function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
  242. // summary:
  243. // Returns an Object containing the localization for a given resource
  244. // bundle in a package, matching the specified locale.
  245. // description:
  246. // Returns a hash containing name/value pairs in its prototypesuch
  247. // that values can be easily overridden. Throws an exception if the
  248. // bundle is not found. Bundle must have already been loaded by
  249. // `dojo.requireLocalization()` or by a build optimization step. NOTE:
  250. // try not to call this method as part of an object property
  251. // definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In
  252. // some loading situations, the bundle may not be available in time
  253. // for the object definition. Instead, call this method inside a
  254. // function that is run after all modules load or the page loads (like
  255. // in `dojo.addOnLoad()`), or in a widget lifecycle method.
  256. // packageName:
  257. // package which is associated with this resource
  258. // bundleName:
  259. // the base filename of the resource bundle (without the ".js" suffix)
  260. // locale:
  261. // the variant to load (optional). By default, the locale defined by
  262. // the host environment: dojo.locale
  263. locale = dojo.i18n.normalizeLocale(locale);
  264. // look for nearest locale match
  265. var elements = locale.split('-');
  266. var module = [packageName,"nls",bundleName].join('.');
  267. var bundle = dojo._loadedModules[module];
  268. if(bundle){
  269. var localization;
  270. for(var i = elements.length; i > 0; i--){
  271. var loc = elements.slice(0, i).join('_');
  272. if(bundle[loc]){
  273. localization = bundle[loc];
  274. break;
  275. }
  276. }
  277. if(!localization){
  278. localization = bundle.ROOT;
  279. }
  280. // make a singleton prototype so that the caller won't accidentally change the values globally
  281. if(localization){
  282. var clazz = function(){};
  283. clazz.prototype = localization;
  284. return new clazz(); // Object
  285. }
  286. }
  287. throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
  288. };
  289. dojo.i18n.normalizeLocale = function(/*String?*/locale){
  290. // summary:
  291. // Returns canonical form of locale, as used by Dojo.
  292. //
  293. // description:
  294. // All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
  295. // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by
  296. // the user agent's locale unless overridden by djConfig.
  297. var result = locale ? locale.toLowerCase() : dojo.locale;
  298. if(result == "root"){
  299. result = "ROOT";
  300. }
  301. return result; // String
  302. };
  303. dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
  304. // summary:
  305. // See dojo.requireLocalization()
  306. // description:
  307. // Called by the bootstrap, but factored out so that it is only
  308. // included in the build when needed.
  309. var targetLocale = dojo.i18n.normalizeLocale(locale);
  310. var bundlePackage = [moduleName, "nls", bundleName].join(".");
  311. // NOTE:
  312. // When loading these resources, the packaging does not match what is
  313. // on disk. This is an implementation detail, as this is just a
  314. // private data structure to hold the loaded resources. e.g.
  315. // `tests/hello/nls/en-us/salutations.js` is loaded as the object
  316. // `tests.hello.nls.salutations.en_us={...}` The structure on disk is
  317. // intended to be most convenient for developers and translators, but
  318. // in memory it is more logical and efficient to store in a different
  319. // order. Locales cannot use dashes, since the resulting path will
  320. // not evaluate as valid JS, so we translate them to underscores.
  321. //Find the best-match locale to load if we have available flat locales.
  322. var bestLocale = "";
  323. if(availableFlatLocales){
  324. var flatLocales = availableFlatLocales.split(",");
  325. for(var i = 0; i < flatLocales.length; i++){
  326. //Locale must match from start of string.
  327. //Using ["indexOf"] so customBase builds do not see
  328. //this as a dojo._base.array dependency.
  329. if(targetLocale["indexOf"](flatLocales[i]) == 0){
  330. if(flatLocales[i].length > bestLocale.length){
  331. bestLocale = flatLocales[i];
  332. }
  333. }
  334. }
  335. if(!bestLocale){
  336. bestLocale = "ROOT";
  337. }
  338. }
  339. //See if the desired locale is already loaded.
  340. var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
  341. var bundle = dojo._loadedModules[bundlePackage];
  342. var localizedBundle = null;
  343. if(bundle){
  344. if(dojo.config.localizationComplete && bundle._built){return;}
  345. var jsLoc = tempLocale.replace(/-/g, '_');
  346. var translationPackage = bundlePackage+"."+jsLoc;
  347. localizedBundle = dojo._loadedModules[translationPackage];
  348. }
  349. if(!localizedBundle){
  350. bundle = dojo["provide"](bundlePackage);
  351. var syms = dojo._getModuleSymbols(moduleName);
  352. var modpath = syms.concat("nls").join("/");
  353. var parent;
  354. dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
  355. var jsLoc = loc.replace(/-/g, '_');
  356. var translationPackage = bundlePackage + "." + jsLoc;
  357. var loaded = false;
  358. if(!dojo._loadedModules[translationPackage]){
  359. // Mark loaded whether it's found or not, so that further load attempts will not be made
  360. dojo["provide"](translationPackage);
  361. var module = [modpath];
  362. if(loc != "ROOT"){module.push(loc);}
  363. module.push(bundleName);
  364. var filespec = module.join("/") + '.js';
  365. loaded = dojo._loadPath(filespec, null, function(hash){
  366. hash = hash.root || hash;
  367. // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
  368. var clazz = function(){};
  369. clazz.prototype = parent;
  370. bundle[jsLoc] = new clazz();
  371. for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
  372. });
  373. }else{
  374. loaded = true;
  375. }
  376. if(loaded && bundle[jsLoc]){
  377. parent = bundle[jsLoc];
  378. }else{
  379. bundle[jsLoc] = parent;
  380. }
  381. if(availableFlatLocales){
  382. //Stop the locale path searching if we know the availableFlatLocales, since
  383. //the first call to this function will load the only bundle that is needed.
  384. return true;
  385. }
  386. });
  387. }
  388. //Save the best locale bundle as the target locale bundle when we know the
  389. //the available bundles.
  390. if(availableFlatLocales && targetLocale != bestLocale){
  391. bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
  392. }
  393. };
  394. (function(){
  395. // If other locales are used, dojo.requireLocalization should load them as
  396. // well, by default.
  397. //
  398. // Override dojo.requireLocalization to do load the default bundle, then
  399. // iterate through the extraLocale list and load those translations as
  400. // well, unless a particular locale was requested.
  401. var extra = dojo.config.extraLocale;
  402. if(extra){
  403. if(!extra instanceof Array){
  404. extra = [extra];
  405. }
  406. var req = dojo.i18n._requireLocalization;
  407. dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
  408. req(m,b,locale, availableFlatLocales);
  409. if(locale){return;}
  410. for(var i=0; i<extra.length; i++){
  411. req(m,b,extra[i], availableFlatLocales);
  412. }
  413. };
  414. }
  415. })();
  416. dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
  417. // summary:
  418. // A helper method to assist in searching for locale-based resources.
  419. // Will iterate through the variants of a particular locale, either up
  420. // or down, executing a callback function. For example, "en-us" and
  421. // true will try "en-us" followed by "en" and finally "ROOT".
  422. locale = dojo.i18n.normalizeLocale(locale);
  423. var elements = locale.split('-');
  424. var searchlist = [];
  425. for(var i = elements.length; i > 0; i--){
  426. searchlist.push(elements.slice(0, i).join('-'));
  427. }
  428. searchlist.push(false);
  429. if(down){searchlist.reverse();}
  430. for(var j = searchlist.length - 1; j >= 0; j--){
  431. var loc = searchlist[j] || "ROOT";
  432. var stop = searchFunc(loc);
  433. if(stop){ break; }
  434. }
  435. };
  436. dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
  437. // summary:
  438. // Load built, flattened resource bundles, if available for all
  439. // locales used in the page. Only called by built layer files.
  440. function preload(locale){
  441. locale = dojo.i18n.normalizeLocale(locale);
  442. dojo.i18n._searchLocalePath(locale, true, function(loc){
  443. for(var i=0; i<localesGenerated.length;i++){
  444. if(localesGenerated[i] == loc){
  445. dojo["require"](bundlePrefix+"_"+loc);
  446. return true; // Boolean
  447. }
  448. }
  449. return false; // Boolean
  450. });
  451. }
  452. preload();
  453. var extra = dojo.config.extraLocale||[];
  454. for(var i=0; i<extra.length; i++){
  455. preload(extra[i]);
  456. }
  457. };
  458. }
  459. if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  460. dojo._hasResource["dijit._PaletteMixin"] = true;
  461. dojo.provide("dijit._PaletteMixin");
  462. dojo.declare("dijit._PaletteMixin",
  463. [dijit._CssStateMixin],
  464. {
  465. // summary:
  466. // A keyboard accessible palette, for picking a color/emoticon/etc.
  467. // description:
  468. // A mixin for a grid showing various entities, so the user can pick a certain entity.
  469. // defaultTimeout: Number
  470. // Number of milliseconds before a held key or button becomes typematic
  471. defaultTimeout: 500,
  472. // timeoutChangeRate: Number
  473. // Fraction of time used to change the typematic timer between events
  474. // 1.0 means that each typematic event fires at defaultTimeout intervals
  475. // < 1.0 means that each typematic event fires at an increasing faster rate
  476. timeoutChangeRate: 0.90,
  477. // value: String
  478. // Currently selected color/emoticon/etc.
  479. value: null,
  480. // _selectedCell: [private] Integer
  481. // Index of the currently selected cell. Initially, none selected
  482. _selectedCell: -1,
  483. /*=====
  484. // _currentFocus: [private] DomNode
  485. // The currently focused cell (if the palette itself has focus), or otherwise
  486. // the cell to be focused when the palette itself gets focus.
  487. // Different from value, which represents the selected (i.e. clicked) cell.
  488. _currentFocus: null,
  489. =====*/
  490. /*=====
  491. // _xDim: [protected] Integer
  492. // This is the number of cells horizontally across.
  493. _xDim: null,
  494. =====*/
  495. /*=====
  496. // _yDim: [protected] Integer
  497. // This is the number of cells vertically down.
  498. _yDim: null,
  499. =====*/
  500. // tabIndex: String
  501. // Widget tab index.
  502. tabIndex: "0",
  503. // cellClass: [protected] String
  504. // CSS class applied to each cell in the palette
  505. cellClass: "dijitPaletteCell",
  506. // dyeClass: [protected] String
  507. // Name of javascript class for Object created for each cell of the palette.
  508. // dyeClass should implements dijit.Dye interface
  509. dyeClass: '',
  510. _preparePalette: function(choices, titles, dyeClassObj) {
  511. // summary:
  512. // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
  513. // for each cell
  514. // choices: String[][]
  515. // id's for each cell of the palette, used to create Dye JS object for each cell
  516. // titles: String[]
  517. // Localized tooltip for each cell
  518. // dyeClassObj: Constructor?
  519. // If specified, use this constructor rather than this.dyeClass
  520. this._cells = [];
  521. var url = this._blankGif;
  522. dyeClassObj = dyeClassObj || dojo.getObject(this.dyeClass);
  523. for(var row=0; row < choices.length; row++){
  524. var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
  525. for(var col=0; col < choices[row].length; col++){
  526. var value = choices[row][col];
  527. if(value){
  528. var cellObject = new dyeClassObj(value, row, col);
  529. var cellNode = dojo.create("td", {
  530. "class": this.cellClass,
  531. tabIndex: "-1",
  532. title: titles[value],
  533. role: "presentation"
  534. });
  535. // prepare cell inner structure
  536. cellObject.fillCell(cellNode, url);
  537. this.connect(cellNode, "ondijitclick", "_onCellClick");
  538. this._trackMouseState(cellNode, this.cellClass);
  539. dojo.place(cellNode, rowNode);
  540. cellNode.index = this._cells.length;
  541. // save cell info into _cells
  542. this._cells.push({node:cellNode, dye:cellObject});
  543. }
  544. }
  545. }
  546. this._xDim = choices[0].length;
  547. this._yDim = choices.length;
  548. // Now set all events
  549. // The palette itself is navigated to with the tab key on the keyboard
  550. // Keyboard navigation within the Palette is with the arrow keys
  551. // Spacebar selects the cell.
  552. // For the up key the index is changed by negative the x dimension.
  553. var keyIncrementMap = {
  554. UP_ARROW: -this._xDim,
  555. // The down key the index is increase by the x dimension.
  556. DOWN_ARROW: this._xDim,
  557. // Right and left move the index by 1.
  558. RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
  559. LEFT_ARROW: this.isLeftToRight() ? -1 : 1
  560. };
  561. for(var key in keyIncrementMap){
  562. this._connects.push(
  563. dijit.typematic.addKeyListener(
  564. this.domNode,
  565. {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
  566. this,
  567. function(){
  568. var increment = keyIncrementMap[key];
  569. return function(count){ this._navigateByKey(increment, count); };
  570. }(),
  571. this.timeoutChangeRate,
  572. this.defaultTimeout
  573. )
  574. );
  575. }
  576. },
  577. postCreate: function(){
  578. this.inherited(arguments);
  579. // Set initial navigable node.
  580. this._setCurrent(this._cells[0].node);
  581. },
  582. focus: function(){
  583. // summary:
  584. // Focus this widget. Puts focus on the most recently focused cell.
  585. // The cell already has tabIndex set, just need to set CSS and focus it
  586. dijit.focus(this._currentFocus);
  587. },
  588. _onCellClick: function(/*Event*/ evt){
  589. // summary:
  590. // Handler for click, enter key & space key. Selects the cell.
  591. // evt:
  592. // The event.
  593. // tags:
  594. // private
  595. var target = evt.currentTarget,
  596. value = this._getDye(target).getValue();
  597. // First focus the clicked cell, and then send onChange() notification.
  598. // onChange() (via _setValueAttr) must be after the focus call, because
  599. // it may trigger a refocus to somewhere else (like the Editor content area), and that
  600. // second focus should win.
  601. // Use setTimeout because IE doesn't like changing focus inside of an event handler.
  602. this._setCurrent(target);
  603. setTimeout(dojo.hitch(this, function(){
  604. dijit.focus(target);
  605. this._setValueAttr(value, true);
  606. }));
  607. // workaround bug where hover class is not removed on popup because the popup is
  608. // closed and then there's no onblur event on the cell
  609. dojo.removeClass(target, "dijitPaletteCellHover");
  610. dojo.stopEvent(evt);
  611. },
  612. _setCurrent: function(/*DomNode*/ node){
  613. // summary:
  614. // Sets which node is the focused cell.
  615. // description:
  616. // At any point in time there's exactly one
  617. // cell with tabIndex != -1. If focus is inside the palette then
  618. // focus is on that cell.
  619. //
  620. // After calling this method, arrow key handlers and mouse click handlers
  621. // should focus the cell in a setTimeout().
  622. // tags:
  623. // protected
  624. if("_currentFocus" in this){
  625. // Remove tabIndex on old cell
  626. dojo.attr(this._currentFocus, "tabIndex", "-1");
  627. }
  628. // Set tabIndex of new cell
  629. this._currentFocus = node;
  630. if(node){
  631. dojo.attr(node, "tabIndex", this.tabIndex);
  632. }
  633. },
  634. _setValueAttr: function(value, priorityChange){
  635. // summary:
  636. // This selects a cell. It triggers the onChange event.
  637. // value: String value of the cell to select
  638. // tags:
  639. // protected
  640. // priorityChange:
  641. // Optional parameter used to tell the select whether or not to fire
  642. // onChange event.
  643. // clear old selected cell
  644. if(this._selectedCell >= 0){
  645. dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
  646. }
  647. this._selectedCell = -1;
  648. // search for cell matching specified value
  649. if(value){
  650. for(var i = 0; i < this._cells.length; i++){
  651. if(value == this._cells[i].dye.getValue()){
  652. this._selectedCell = i;
  653. dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
  654. break;
  655. }
  656. }
  657. }
  658. // record new value, or null if no matching cell
  659. this._set("value", this._selectedCell >= 0 ? value : null);
  660. if(priorityChange || priorityChange === undefined){
  661. this.onChange(value);
  662. }
  663. },
  664. onChange: function(value){
  665. // summary:
  666. // Callback when a cell is selected.
  667. // value: String
  668. // Value corresponding to cell.
  669. },
  670. _navigateByKey: function(increment, typeCount){
  671. // summary:
  672. // This is the callback for typematic.
  673. // It changes the focus and the highlighed cell.
  674. // increment:
  675. // How much the key is navigated.
  676. // typeCount:
  677. // How many times typematic has fired.
  678. // tags:
  679. // private
  680. // typecount == -1 means the key is released.
  681. if(typeCount == -1){ return; }
  682. var newFocusIndex = this._currentFocus.index + increment;
  683. if(newFocusIndex < this._cells.length && newFocusIndex > -1){
  684. var focusNode = this._cells[newFocusIndex].node;
  685. this._setCurrent(focusNode);
  686. // Actually focus the node, for the benefit of screen readers.
  687. // Use setTimeout because IE doesn't like changing focus inside of an event handler
  688. setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
  689. }
  690. },
  691. _getDye: function(/*DomNode*/ cell){
  692. // summary:
  693. // Get JS object for given cell DOMNode
  694. return this._cells[cell.index].dye;
  695. }
  696. });
  697. /*=====
  698. dojo.declare("dijit.Dye",
  699. null,
  700. {
  701. // summary:
  702. // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
  703. constructor: function(alias, row, col){
  704. // summary:
  705. // Initialize according to value or alias like "white"
  706. // alias: String
  707. },
  708. getValue: function(){
  709. // summary:
  710. // Return "value" of cell; meaning of "value" varies by subclass.
  711. // description:
  712. // For example color hex value, emoticon ascii value etc, entity hex value.
  713. },
  714. fillCell: function(cell, blankGif){
  715. // summary:
  716. // Add cell DOMNode inner structure
  717. // cell: DomNode
  718. // The surrounding cell
  719. // blankGif: String
  720. // URL for blank cell image
  721. }
  722. }
  723. );
  724. =====*/
  725. }
  726. if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  727. dojo._hasResource["dijit.ColorPalette"] = true;
  728. dojo.provide("dijit.ColorPalette");
  729. dojo.declare("dijit.ColorPalette",
  730. [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
  731. {
  732. // summary:
  733. // A keyboard accessible color-picking widget
  734. // description:
  735. // Grid showing various colors, so the user can pick a certain color.
  736. // Can be used standalone, or as a popup.
  737. //
  738. // example:
  739. // | <div dojoType="dijit.ColorPalette"></div>
  740. //
  741. // example:
  742. // | var picker = new dijit.ColorPalette({ },srcNode);
  743. // | picker.startup();
  744. // palette: [const] String
  745. // Size of grid, either "7x10" or "3x4".
  746. palette: "7x10",
  747. // _palettes: [protected] Map
  748. // This represents the value of the colors.
  749. // The first level is a hashmap of the different palettes available.
  750. // The next two dimensions represent the columns and rows of colors.
  751. _palettes: {
  752. "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
  753. ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
  754. ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
  755. ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
  756. ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
  757. ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
  758. ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
  759. "3x4": [["white", "lime", "green", "blue"],
  760. ["silver", "yellow", "fuchsia", "navy"],
  761. ["gray", "red", "purple", "black"]]
  762. },
  763. // templateString: String
  764. // The template of this widget.
  765. templateString: dojo.cache("dijit", "templates/ColorPalette.html", "<div class=\"dijitInline dijitColorPalette\">\n\t<table class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\">\n\t\t<tbody dojoAttachPoint=\"gridNode\"></tbody>\n\t</table>\n</div>\n"),
  766. baseClass: "dijitColorPalette",
  767. buildRendering: function(){
  768. // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
  769. // <img> nodes
  770. this.inherited(arguments);
  771. // Creates <img> nodes in each cell of the template.
  772. // Pass in "customized" dijit._Color constructor for specified palette and high-contrast vs. normal mode
  773. this._preparePalette(
  774. this._palettes[this.palette],
  775. dojo.i18n.getLocalization("dojo", "colors", this.lang),
  776. dojo.declare(dijit._Color, {
  777. hc: dojo.hasClass(dojo.body(), "dijit_a11y"),
  778. palette: this.palette
  779. })
  780. );
  781. }
  782. });
  783. dojo.declare("dijit._Color", dojo.Color, {
  784. // summary:
  785. // Object associated with each cell in a ColorPalette palette.
  786. // Implements dijit.Dye.
  787. // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
  788. // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
  789. // for showing the color.
  790. template:
  791. "<span class='dijitInline dijitPaletteImg'>" +
  792. "<img src='${blankGif}' alt='${alt}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
  793. "</span>",
  794. // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
  795. // but scrolled and clipped to show the correct color only
  796. hcTemplate:
  797. "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
  798. "<img src='${image}' alt='${alt}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
  799. "</span>",
  800. // _imagePaths: [protected] Map
  801. // This is stores the path to the palette images used for high-contrast mode display
  802. _imagePaths: {
  803. "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
  804. "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png")
  805. },
  806. constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
  807. this._alias = alias;
  808. this._row = row;
  809. this._col = col;
  810. this.setColor(dojo.Color.named[alias]);
  811. },
  812. getValue: function(){
  813. // summary:
  814. // Note that although dijit._Color is initialized with a value like "white" getValue() always
  815. // returns a hex value
  816. return this.toHex();
  817. },
  818. fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
  819. var html = dojo.string.substitute(this.hc ? this.hcTemplate : this.template, {
  820. // substitution variables for normal mode
  821. color: this.toHex(),
  822. blankGif: blankGif,
  823. alt: this._alias,
  824. // variables used for high contrast mode
  825. image: this._imagePaths[this.palette].toString(),
  826. left: this._col * -20 - 5,
  827. top: this._row * -20 - 5,
  828. size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
  829. });
  830. dojo.place(html, cell);
  831. }
  832. });
  833. }
  834. if(!dojo._hasResource["dijit.Declaration"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  835. dojo._hasResource["dijit.Declaration"] = true;
  836. dojo.provide("dijit.Declaration");
  837. dojo.declare(
  838. "dijit.Declaration",
  839. dijit._Widget,
  840. {
  841. // summary:
  842. // The Declaration widget allows a developer to declare new widget
  843. // classes directly from a snippet of markup.
  844. // _noScript: [private] Boolean
  845. // Flag to parser to leave alone the script tags contained inside of me
  846. _noScript: true,
  847. // stopParser: [private] Boolean
  848. // Flag to parser to not try and parse widgets declared inside of me
  849. stopParser: true,
  850. // widgetClass: [const] String
  851. // Name of class being declared, ex: "acme.myWidget"
  852. widgetClass: "",
  853. // propList: [const] Object
  854. // Set of attributes for this widget along with default values, ex:
  855. // {delay: 100, title: "hello world"}
  856. defaults: null,
  857. // mixins: [const] String[]
  858. // List containing the prototype for this widget, and also any mixins,
  859. // ex: ["dijit._Widget", "dijit._Container"]
  860. mixins: [],
  861. buildRendering: function(){
  862. var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),
  863. methods = dojo.query("> script[type^='dojo/method']", src).orphan(),
  864. connects = dojo.query("> script[type^='dojo/connect']", src).orphan(),
  865. srcType = src.nodeName;
  866. var propList = this.defaults || {};
  867. // For all methods defined like <script type="dojo/method" data-dojo-event="foo">,
  868. // add that method to prototype.
  869. // If there's no "event" specified then it's code to run on instantiation,
  870. // so it becomes a connection to "postscript" (handled below).
  871. dojo.forEach(methods, function(s){
  872. var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"),
  873. func = dojo.parser._functionFromScript(s);
  874. if(evt){
  875. propList[evt] = func;
  876. }else{
  877. connects.push(s);
  878. }
  879. });
  880. // map array of strings like [ "dijit.form.Button" ] to array of mixin objects
  881. // (note that dojo.map(this.mixins, dojo.getObject) doesn't work because it passes
  882. // a bogus third argument to getObject(), confusing it)
  883. this.mixins = this.mixins.length ?
  884. dojo.map(this.mixins, function(name){ return dojo.getObject(name); } ) :
  885. [ dijit._Widget, dijit._Templated ];
  886. propList.widgetsInTemplate = true;
  887. propList._skipNodeCache = true;
  888. propList.templateString = "<"+srcType+" class='"+src.className+"' dojoAttachPoint='"+(src.getAttribute("dojoAttachPoint") || '')+"' dojoAttachEvent='"+(src.getAttribute("dojoAttachEvent") || '')+"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
  889. // strip things so we don't create stuff under us in the initial setup phase
  890. dojo.query("[dojoType]", src).forEach(function(node){
  891. node.removeAttribute("dojoType");
  892. });
  893. // create the new widget class
  894. var wc = dojo.declare(
  895. this.widgetClass,
  896. this.mixins,
  897. propList
  898. );
  899. // Handle <script> blocks of form:
  900. // <script type="dojo/connect" data-dojo-event="foo">
  901. // and
  902. // <script type="dojo/method">
  903. // (Note that the second one is just shorthand for a dojo/connect to postscript)
  904. // Since this is a connect in the declaration, we are actually connection to the method
  905. // in the _prototype_.
  906. dojo.forEach(connects, function(s){
  907. var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event") || "postscript",
  908. func = dojo.parser._functionFromScript(s);
  909. dojo.connect(wc.prototype, evt, func);
  910. });
  911. }
  912. }
  913. );
  914. }
  915. if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  916. dojo._hasResource["dojo.dnd.common"] = true;
  917. dojo.provide("dojo.dnd.common");
  918. dojo.getObject("dnd", true, dojo);
  919. dojo.dnd.getCopyKeyState = dojo.isCopyKey;
  920. dojo.dnd._uniqueId = 0;
  921. dojo.dnd.getUniqueId = function(){
  922. // summary:
  923. // returns a unique string for use with any DOM element
  924. var id;
  925. do{
  926. id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
  927. }while(dojo.byId(id));
  928. return id;
  929. };
  930. dojo.dnd._empty = {};
  931. dojo.dnd.isFormElement = function(/*Event*/ e){
  932. // summary:
  933. // returns true if user clicked on a form element
  934. var t = e.target;
  935. if(t.nodeType == 3 /*TEXT_NODE*/){
  936. t = t.parentNode;
  937. }
  938. return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
  939. };
  940. }
  941. if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  942. dojo._hasResource["dojo.dnd.autoscroll"] = true;
  943. dojo.provide("dojo.dnd.autoscroll");
  944. dojo.getObject("dnd", true, dojo);
  945. dojo.dnd.getViewport = dojo.window.getBox;
  946. dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
  947. dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
  948. dojo.dnd.V_AUTOSCROLL_VALUE = 16;
  949. dojo.dnd.H_AUTOSCROLL_VALUE = 16;
  950. dojo.dnd.autoScroll = function(e){
  951. // summary:
  952. // a handler for onmousemove event, which scrolls the window, if
  953. // necesary
  954. // e: Event
  955. // onmousemove event
  956. // FIXME: needs more docs!
  957. var v = dojo.window.getBox(), dx = 0, dy = 0;
  958. if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
  959. dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
  960. }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
  961. dx = dojo.dnd.H_AUTOSCROLL_VALUE;
  962. }
  963. if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
  964. dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
  965. }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
  966. dy = dojo.dnd.V_AUTOSCROLL_VALUE;
  967. }
  968. window.scrollBy(dx, dy);
  969. };
  970. dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
  971. dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
  972. dojo.dnd.autoScrollNodes = function(e){
  973. // summary:
  974. // a handler for onmousemove event, which scrolls the first avaialble
  975. // Dom element, it falls back to dojo.dnd.autoScroll()
  976. // e: Event
  977. // onmousemove event
  978. // FIXME: needs more docs!
  979. for(var n = e.target; n;){
  980. if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
  981. var s = dojo.getComputedStyle(n);
  982. if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
  983. var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
  984. //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
  985. var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
  986. h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
  987. rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
  988. if(dojo.isWebKit || dojo.isOpera){
  989. // FIXME: this code should not be here, it should be taken into account
  990. // either by the event fixing code, or the dojo.position()
  991. // FIXME: this code doesn't work on Opera 9.5 Beta
  992. rx += dojo.body().scrollLeft;
  993. ry += dojo.body().scrollTop;
  994. }
  995. if(rx > 0 && rx < b.w){
  996. if(rx < w){
  997. dx = -w;
  998. }else if(rx > b.w - w){
  999. dx = w;
  1000. }
  1001. }
  1002. //console.log("ry =", ry, "b.h =", b.h, "h =", h);
  1003. if(ry > 0 && ry < b.h){
  1004. if(ry < h){
  1005. dy = -h;
  1006. }else if(ry > b.h - h){
  1007. dy = h;
  1008. }
  1009. }
  1010. var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
  1011. n.scrollLeft = n.scrollLeft + dx;
  1012. n.scrollTop = n.scrollTop + dy;
  1013. if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
  1014. }
  1015. }
  1016. try{
  1017. n = n.parentNode;
  1018. }catch(x){
  1019. n = null;
  1020. }
  1021. }
  1022. dojo.dnd.autoScroll(e);
  1023. };
  1024. }
  1025. if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1026. dojo._hasResource["dojo.dnd.Mover"] = true;
  1027. dojo.provide("dojo.dnd.Mover");
  1028. dojo.declare("dojo.dnd.Mover", null, {
  1029. constructor: function(node, e, host){
  1030. // summary:
  1031. // an object which makes a node follow the mouse, or touch-drag on touch devices.
  1032. // Used as a default mover, and as a base class for custom movers.
  1033. // node: Node
  1034. // a node (or node's id) to be moved
  1035. // e: Event
  1036. // a mouse event, which started the move;
  1037. // only pageX and pageY properties are used
  1038. // host: Object?
  1039. // object which implements the functionality of the move,
  1040. // and defines proper events (onMoveStart and onMoveStop)
  1041. this.node = dojo.byId(node);
  1042. var pos = e.touches ? e.touches[0] : e;
  1043. this.marginBox = {l: pos.pageX, t: pos.pageY};
  1044. this.mouseButton = e.button;
  1045. var h = (this.host = host), d = node.ownerDocument;
  1046. this.events = [
  1047. // At the start of a drag, onFirstMove is called, and then the following two
  1048. // connects are disconnected
  1049. dojo.connect(d, "onmousemove", this, "onFirstMove"),
  1050. dojo.connect(d, "ontouchmove", this, "onFirstMove"),
  1051. // These are called continually during the drag
  1052. dojo.connect(d, "onmousemove", this, "onMouseMove"),
  1053. dojo.connect(d, "ontouchmove", this, "onMouseMove"),
  1054. // And these are called at the end of the drag
  1055. dojo.connect(d, "onmouseup", this, "onMouseUp"),
  1056. dojo.connect(d, "ontouchend", this, "onMouseUp"),
  1057. // cancel text selection and text dragging
  1058. dojo.connect(d, "ondragstart", dojo.stopEvent),
  1059. dojo.connect(d.body, "onselectstart", dojo.stopEvent)
  1060. ];
  1061. // notify that the move has started
  1062. if(h && h.onMoveStart){
  1063. h.onMoveStart(this);
  1064. }
  1065. },
  1066. // mouse event processors
  1067. onMouseMove: function(e){
  1068. // summary:
  1069. // event processor for onmousemove/ontouchmove
  1070. // e: Event
  1071. // mouse/touch event
  1072. dojo.dnd.autoScroll(e);
  1073. var m = this.marginBox,
  1074. pos = e.touches ? e.touches[0] : e;
  1075. this.host.onMove(this, {l: m.l + pos.pageX, t: m.t + pos.pageY}, e);
  1076. dojo.stopEvent(e);
  1077. },
  1078. onMouseUp: function(e){
  1079. if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
  1080. e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
  1081. this.destroy();
  1082. }
  1083. dojo.stopEvent(e);
  1084. },
  1085. // utilities
  1086. onFirstMove: function(e){
  1087. // summary:
  1088. // makes the node absolute; it is meant to be called only once.
  1089. // relative and absolutely positioned nodes are assumed to use pixel units
  1090. var s = this.node.style, l, t, h = this.host;
  1091. switch(s.position){
  1092. case "relative":
  1093. case "absolute":
  1094. // assume that left and top values are in pixels already
  1095. l = Math.round(parseFloat(s.left)) || 0;
  1096. t = Math.round(parseFloat(s.top)) || 0;
  1097. break;
  1098. default:
  1099. s.position = "absolute"; // enforcing the absolute mode
  1100. var m = dojo.marginBox(this.node);
  1101. // event.pageX/pageY (which we used to generate the initial
  1102. // margin box) includes padding and margin set on the body.
  1103. // However, setting the node's position to absolute and then
  1104. // doing dojo.marginBox on it *doesn't* take that additional
  1105. // space into account - so we need to subtract the combined
  1106. // padding and margin. We use getComputedStyle and
  1107. // _getMarginBox/_getContentBox to avoid the extra lookup of
  1108. // the computed style.
  1109. var b = dojo.doc.body;
  1110. var bs = dojo.getComputedStyle(b);
  1111. var bm = dojo._getMarginBox(b, bs);
  1112. var bc = dojo._getContentBox(b, bs);
  1113. l = m.l - (bc.l - bm.l);
  1114. t = m.t - (bc.t - bm.t);
  1115. break;
  1116. }
  1117. this.marginBox.l = l - this.marginBox.l;
  1118. this.marginBox.t = t - this.marginBox.t;
  1119. if(h && h.onFirstMove){
  1120. h.onFirstMove(this, e);
  1121. }
  1122. // Disconnect onmousemove and ontouchmove events that call this function
  1123. dojo.disconnect(this.events.shift());
  1124. dojo.disconnect(this.events.shift());
  1125. },
  1126. destroy: function(){
  1127. // summary:
  1128. // stops the move, deletes all references, so the object can be garbage-collected
  1129. dojo.forEach(this.events, dojo.disconnect);
  1130. // undo global settings
  1131. var h = this.host;
  1132. if(h && h.onMoveStop){
  1133. h.onMoveStop(this);
  1134. }
  1135. // destroy objects
  1136. this.events = this.node = this.host = null;
  1137. }
  1138. });
  1139. }
  1140. if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1141. dojo._hasResource["dojo.dnd.Moveable"] = true;
  1142. dojo.provide("dojo.dnd.Moveable");
  1143. /*=====
  1144. dojo.declare("dojo.dnd.__MoveableArgs", [], {
  1145. // handle: Node||String
  1146. // A node (or node's id), which is used as a mouse handle.
  1147. // If omitted, the node itself is used as a handle.
  1148. handle: null,
  1149. // delay: Number
  1150. // delay move by this number of pixels
  1151. delay: 0,
  1152. // skip: Boolean
  1153. // skip move of form elements
  1154. skip: false,
  1155. // mover: Object
  1156. // a constructor of custom Mover
  1157. mover: dojo.dnd.Mover
  1158. });
  1159. =====*/
  1160. dojo.declare("dojo.dnd.Moveable", null, {
  1161. // object attributes (for markup)
  1162. handle: "",
  1163. delay: 0,
  1164. skip: false,
  1165. constructor: function(node, params){
  1166. // summary:
  1167. // an object, which makes a node moveable
  1168. // node: Node
  1169. // a node (or node's id) to be moved
  1170. // params: dojo.dnd.__MoveableArgs?
  1171. // optional parameters
  1172. this.node = dojo.byId(node);
  1173. if(!params){ params = {}; }
  1174. this.handle = params.handle ? dojo.byId(params.handle) : null;
  1175. if(!this.handle){ this.handle = this.node; }
  1176. this.delay = params.delay > 0 ? params.delay : 0;
  1177. this.skip = params.skip;
  1178. this.mover = params.mover ? params.mover : dojo.dnd.Mover;
  1179. this.events = [
  1180. dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
  1181. dojo.connect(this.handle, "ontouchstart", this, "onMouseDown"),
  1182. // cancel text selection and text dragging
  1183. dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
  1184. dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
  1185. ];
  1186. },
  1187. // markup methods
  1188. markupFactory: function(params, node){
  1189. return new dojo.dnd.Moveable(node, params);
  1190. },
  1191. // methods
  1192. destroy: function(){
  1193. // summary:
  1194. // stops watching for possible move, deletes all references, so the object can be garbage-collected
  1195. dojo.forEach(this.events, dojo.disconnect);
  1196. this.events = this.node = this.handle = null;
  1197. },
  1198. // mouse event processors
  1199. onMouseDown: function(e){
  1200. // summary:
  1201. // event processor for onmousedown/ontouchstart, creates a Mover for the node
  1202. // e: Event
  1203. // mouse/touch event
  1204. if(this.skip && dojo.dnd.isFormElement(e)){ return; }
  1205. if(this.delay){
  1206. this.events.push(
  1207. dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
  1208. dojo.connect(this.handle, "ontouchmove", this, "onMouseMove"),
  1209. dojo.connect(this.handle, "onmouseup", this, "onMouseUp"),
  1210. dojo.connect(this.handle, "ontouchend", this, "onMouseUp")
  1211. );
  1212. var pos = e.touches ? e.touches[0] : e;
  1213. this._lastX = pos.pageX;
  1214. this._lastY = pos.pageY;
  1215. }else{
  1216. this.onDragDetected(e);
  1217. }
  1218. dojo.stopEvent(e);
  1219. },
  1220. onMouseMove: function(e){
  1221. // summary:
  1222. // event processor for onmousemove/ontouchmove, used only for delayed drags
  1223. // e: Event
  1224. // mouse/touch event
  1225. var pos = e.touches ? e.touches[0] : e;
  1226. if(Math.abs(pos.pageX - this._lastX) > this.delay || Math.abs(pos.pageY - this._lastY) > this.delay){
  1227. this.onMouseUp(e);
  1228. this.onDragDetected(e);
  1229. }
  1230. dojo.stopEvent(e);
  1231. },
  1232. onMouseUp: function(e){
  1233. // summary:
  1234. // event processor for onmouseup, used only for delayed drags
  1235. // e: Event
  1236. // mouse event
  1237. for(var i = 0; i < 2; ++i){
  1238. dojo.disconnect(this.events.pop());
  1239. }
  1240. dojo.stopEvent(e);
  1241. },
  1242. onSelectStart: function(e){
  1243. // summary:
  1244. // event processor for onselectevent and ondragevent
  1245. // e: Event
  1246. // mouse event
  1247. if(!this.skip || !dojo.dnd.isFormElement(e)){
  1248. dojo.stopEvent(e);
  1249. }
  1250. },
  1251. // local events
  1252. onDragDetected: function(/* Event */ e){
  1253. // summary:
  1254. // called when the drag is detected;
  1255. // responsible for creation of the mover
  1256. new this.mover(this.node, e, this);
  1257. },
  1258. onMoveStart: function(/* dojo.dnd.Mover */ mover){
  1259. // summary:
  1260. // called before every move operation
  1261. dojo.publish("/dnd/move/start", [mover]);
  1262. dojo.addClass(dojo.body(), "dojoMove");
  1263. dojo.addClass(this.node, "dojoMoveItem");
  1264. },
  1265. onMoveStop: function(/* dojo.dnd.Mover */ mover){
  1266. // summary:
  1267. // called after every move operation
  1268. dojo.publish("/dnd/move/stop", [mover]);
  1269. dojo.removeClass(dojo.body(), "dojoMove");
  1270. dojo.removeClass(this.node, "dojoMoveItem");
  1271. },
  1272. onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
  1273. // summary:
  1274. // called during the very first move notification;
  1275. // can be used to initialize coordinates, can be overwritten.
  1276. // default implementation does nothing
  1277. },
  1278. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
  1279. // summary:
  1280. // called during every move notification;
  1281. // should actually move the node; can be overwritten.
  1282. this.onMoving(mover, leftTop);
  1283. var s = mover.node.style;
  1284. s.left = leftTop.l + "px";
  1285. s.top = leftTop.t + "px";
  1286. this.onMoved(mover, leftTop);
  1287. },
  1288. onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  1289. // summary:
  1290. // called before every incremental move; can be overwritten.
  1291. // default implementation does nothing
  1292. },
  1293. onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  1294. // summary:
  1295. // called after every incremental move; can be overwritten.
  1296. // default implementation does nothing
  1297. }
  1298. });
  1299. }
  1300. if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1301. dojo._hasResource["dojo.dnd.move"] = true;
  1302. dojo.provide("dojo.dnd.move");
  1303. /*=====
  1304. dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
  1305. // constraints: Function
  1306. // Calculates a constraint box.
  1307. // It is called in a context of the moveable object.
  1308. constraints: function(){},
  1309. // within: Boolean
  1310. // restrict move within boundaries.
  1311. within: false
  1312. });
  1313. =====*/
  1314. dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
  1315. // object attributes (for markup)
  1316. constraints: function(){},
  1317. within: false,
  1318. // markup methods
  1319. markupFactory: function(params, node){
  1320. return new dojo.dnd.move.constrainedMoveable(node, params);
  1321. },
  1322. constructor: function(node, params){
  1323. // summary:
  1324. // an object that makes a node moveable
  1325. // node: Node
  1326. // a node (or node's id) to be moved
  1327. // params: dojo.dnd.move.__constrainedMoveableArgs?
  1328. // an optional object with additional parameters;
  1329. // the rest is passed to the base class
  1330. if(!params){ params = {}; }
  1331. this.constraints = params.constraints;
  1332. this.within = params.within;
  1333. },
  1334. onFirstMove: function(/* dojo.dnd.Mover */ mover){
  1335. // summary:
  1336. // called during the very first move notification;
  1337. // can be used to initialize coordinates, can be overwritten.
  1338. var c = this.constraintBox = this.constraints.call(this, mover);
  1339. c.r = c.l + c.w;
  1340. c.b = c.t + c.h;
  1341. if(this.within){
  1342. var mb = dojo._getMarginSize(mover.node);
  1343. c.r -= mb.w;
  1344. c.b -= mb.h;
  1345. }
  1346. },
  1347. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  1348. // summary:
  1349. // called during every move notification;
  1350. // should actually move the node; can be overwritten.
  1351. var c = this.constraintBox, s = mover.node.style;
  1352. this.onMoving(mover, leftTop);
  1353. leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
  1354. leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
  1355. s.left = leftTop.l + "px";
  1356. s.top = leftTop.t + "px";
  1357. this.onMoved(mover, leftTop);
  1358. }
  1359. });
  1360. /*=====
  1361. dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
  1362. // box: Object
  1363. // a constraint box
  1364. box: {}
  1365. });
  1366. =====*/
  1367. dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
  1368. // box:
  1369. // object attributes (for markup)
  1370. box: {},
  1371. // markup methods
  1372. markupFactory: function(params, node){
  1373. return new dojo.dnd.move.boxConstrainedMoveable(node, params);
  1374. },
  1375. constructor: function(node, params){
  1376. // summary:
  1377. // an object, which makes a node moveable
  1378. // node: Node
  1379. // a node (or node's id) to be moved
  1380. // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
  1381. // an optional object with parameters
  1382. var box = params && params.box;
  1383. this.constraints = function(){ return box; };
  1384. }
  1385. });
  1386. /*=====
  1387. dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
  1388. // area: String
  1389. // A parent's area to restrict the move.
  1390. // Can be "margin", "border", "padding", or "content".
  1391. area: ""
  1392. });
  1393. =====*/
  1394. dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
  1395. // area:
  1396. // object attributes (for markup)
  1397. area: "content",
  1398. // markup methods
  1399. markupFactory: function(params, node){
  1400. return new dojo.dnd.move.parentConstrainedMoveable(node, params);
  1401. },
  1402. constructor: function(node, params){
  1403. // summary:
  1404. // an object, which makes a node moveable
  1405. // node: Node
  1406. // a node (or node's id) to be moved
  1407. // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
  1408. // an optional object with parameters
  1409. var area = params && params.area;
  1410. this.constraints = function(){
  1411. var n = this.node.parentNode,
  1412. s = dojo.getComputedStyle(n),
  1413. mb = dojo._getMarginBox(n, s);
  1414. if(area == "margin"){
  1415. return mb; // Object
  1416. }
  1417. var t = dojo._getMarginExtents(n, s);
  1418. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  1419. if(area == "border"){
  1420. return mb; // Object
  1421. }
  1422. t = dojo._getBorderExtents(n, s);
  1423. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  1424. if(area == "padding"){
  1425. return mb; // Object
  1426. }
  1427. t = dojo._getPadExtents(n, s);
  1428. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  1429. return mb; // Object
  1430. };
  1431. }
  1432. });
  1433. // patching functions one level up for compatibility
  1434. dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
  1435. dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
  1436. dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
  1437. }
  1438. if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1439. dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
  1440. dojo.provide("dojo.dnd.TimedMoveable");
  1441. /*=====
  1442. dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
  1443. // timeout: Number
  1444. // delay move by this number of ms,
  1445. // accumulating position changes during the timeout
  1446. timeout: 0
  1447. });
  1448. =====*/
  1449. (function(){
  1450. // precalculate long expressions
  1451. var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
  1452. dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
  1453. // summary:
  1454. // A specialized version of Moveable to support an FPS throttling.
  1455. // This class puts an upper restriction on FPS, which may reduce
  1456. // the CPU load. The additional parameter "timeout" regulates
  1457. // the delay before actually moving the moveable object.
  1458. // object attributes (for markup)
  1459. timeout: 40, // in ms, 40ms corresponds to 25 fps
  1460. constructor: function(node, params){
  1461. // summary:
  1462. // an object that makes a node moveable with a timer
  1463. // node: Node||String
  1464. // a node (or node's id) to be moved
  1465. // params: dojo.dnd.__TimedMoveableArgs
  1466. // object with additional parameters.
  1467. // sanitize parameters
  1468. if(!params){ params = {}; }
  1469. if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
  1470. this.timeout = params.timeout;
  1471. }
  1472. },
  1473. // markup methods
  1474. markupFactory: function(params, node){
  1475. return new dojo.dnd.TimedMoveable(node, params);
  1476. },
  1477. onMoveStop: function(/* dojo.dnd.Mover */ mover){
  1478. if(mover._timer){
  1479. // stop timer
  1480. clearTimeout(mover._timer)
  1481. // reflect the last received position
  1482. oldOnMove.call(this, mover, mover._leftTop)
  1483. }
  1484. dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
  1485. },
  1486. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  1487. mover._leftTop = leftTop;
  1488. if(!mover._timer){
  1489. var _t = this; // to avoid using dojo.hitch()
  1490. mover._timer = setTimeout(function(){
  1491. // we don't have any pending requests
  1492. mover._timer = null;
  1493. // reflect the last received position
  1494. oldOnMove.call(_t, mover, mover._leftTop);
  1495. }, this.timeout);
  1496. }
  1497. }
  1498. });
  1499. })();
  1500. }
  1501. if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1502. dojo._hasResource["dojo.fx.Toggler"] = true;
  1503. dojo.provide("dojo.fx.Toggler");
  1504. dojo.declare("dojo.fx.Toggler", null, {
  1505. // summary:
  1506. // A simple `dojo.Animation` toggler API.
  1507. //
  1508. // description:
  1509. // class constructor for an animation toggler. It accepts a packed
  1510. // set of arguments about what type of animation to use in each
  1511. // direction, duration, etc. All available members are mixed into
  1512. // these animations from the constructor (for example, `node`,
  1513. // `showDuration`, `hideDuration`).
  1514. //
  1515. // example:
  1516. // | var t = new dojo.fx.Toggler({
  1517. // | node: "nodeId",
  1518. // | showDuration: 500,
  1519. // | // hideDuration will default to "200"
  1520. // | showFunc: dojo.fx.wipeIn,
  1521. // | // hideFunc will default to "fadeOut"
  1522. // | });
  1523. // | t.show(100); // delay showing for 100ms
  1524. // | // ...time passes...
  1525. // | t.hide();
  1526. // node: DomNode
  1527. // the node to target for the showing and hiding animations
  1528. node: null,
  1529. // showFunc: Function
  1530. // The function that returns the `dojo.Animation` to show the node
  1531. showFunc: dojo.fadeIn,
  1532. // hideFunc: Function
  1533. // The function that returns the `dojo.Animation` to hide the node
  1534. hideFunc: dojo.fadeOut,
  1535. // showDuration:
  1536. // Time in milliseconds to run the show Animation
  1537. showDuration: 200,
  1538. // hideDuration:
  1539. // Time in milliseconds to run the hide Animation
  1540. hideDuration: 200,
  1541. // FIXME: need a policy for where the toggler should "be" the next
  1542. // time show/hide are called if we're stopped somewhere in the
  1543. // middle.
  1544. // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
  1545. // each animation individually.
  1546. // FIXME: also would be nice to have events from the animations exposed/bridged
  1547. /*=====
  1548. _showArgs: null,
  1549. _showAnim: null,
  1550. _hideArgs: null,
  1551. _hideAnim: null,
  1552. _isShowing: false,
  1553. _isHiding: false,
  1554. =====*/
  1555. constructor: function(args){
  1556. var _t = this;
  1557. dojo.mixin(_t, args);
  1558. _t.node = args.node;
  1559. _t._showArgs = dojo.mixin({}, args);
  1560. _t._showArgs.node = _t.node;
  1561. _t._showArgs.duration = _t.showDuration;
  1562. _t.showAnim = _t.showFunc(_t._showArgs);
  1563. _t._hideArgs = dojo.mixin({}, args);
  1564. _t._hideArgs.node = _t.node;
  1565. _t._hideArgs.duration = _t.hideDuration;
  1566. _t.hideAnim = _t.hideFunc(_t._hideArgs);
  1567. dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
  1568. dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
  1569. },
  1570. show: function(delay){
  1571. // summary: Toggle the node to showing
  1572. // delay: Integer?
  1573. // Ammount of time to stall playing the show animation
  1574. return this.showAnim.play(delay || 0);
  1575. },
  1576. hide: function(delay){
  1577. // summary: Toggle the node to hidden
  1578. // delay: Integer?
  1579. // Ammount of time to stall playing the hide animation
  1580. return this.hideAnim.play(delay || 0);
  1581. }
  1582. });
  1583. }
  1584. if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1585. dojo._hasResource["dojo.fx"] = true;
  1586. dojo.provide("dojo.fx");
  1587. /*=====
  1588. dojo.fx = {
  1589. // summary: Effects library on top of Base animations
  1590. };
  1591. =====*/
  1592. (function(){
  1593. var d = dojo,
  1594. _baseObj = {
  1595. _fire: function(evt, args){
  1596. if(this[evt]){
  1597. this[evt].apply(this, args||[]);
  1598. }
  1599. return this;
  1600. }
  1601. };
  1602. var _chain = function(animations){
  1603. this._index = -1;
  1604. this._animations = animations||[];
  1605. this._current = this._onAnimateCtx = this._onEndCtx = null;
  1606. this.duration = 0;
  1607. d.forEach(this._animations, function(a){
  1608. this.duration += a.duration;
  1609. if(a.delay){ this.duration += a.delay; }
  1610. }, this);
  1611. };
  1612. d.extend(_chain, {
  1613. _onAnimate: function(){
  1614. this._fire("onAnimate", arguments);
  1615. },
  1616. _onEnd: function(){
  1617. d.disconnect(this._onAnimateCtx);
  1618. d.disconnect(this._onEndCtx);
  1619. this._onAnimateCtx = this._onEndCtx = null;
  1620. if(this._index + 1 == this._animations.length){
  1621. this._fire("onEnd");
  1622. }else{
  1623. // switch animations
  1624. this._current = this._animations[++this._index];
  1625. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  1626. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  1627. this._current.play(0, true);
  1628. }
  1629. },
  1630. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  1631. if(!this._current){ this._current = this._animations[this._index = 0]; }
  1632. if(!gotoStart && this._current.status() == "playing"){ return this; }
  1633. var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
  1634. this._fire("beforeBegin");
  1635. }),
  1636. onBegin = d.connect(this._current, "onBegin", this, function(arg){
  1637. this._fire("onBegin", arguments);
  1638. }),
  1639. onPlay = d.connect(this._current, "onPlay", this, function(arg){
  1640. this._fire("onPlay", arguments);
  1641. d.disconnect(beforeBegin);
  1642. d.disconnect(onBegin);
  1643. d.disconnect(onPlay);
  1644. });
  1645. if(this._onAnimateCtx){
  1646. d.disconnect(this._onAnimateCtx);
  1647. }
  1648. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  1649. if(this._onEndCtx){
  1650. d.disconnect(this._onEndCtx);
  1651. }
  1652. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  1653. this._current.play.apply(this._current, arguments);
  1654. return this;
  1655. },
  1656. pause: function(){
  1657. if(this._current){
  1658. var e = d.connect(this._current, "onPause", this, function(arg){
  1659. this._fire("onPause", arguments);
  1660. d.disconnect(e);
  1661. });
  1662. this._current.pause();
  1663. }
  1664. return this;
  1665. },
  1666. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  1667. this.pause();
  1668. var offset = this.duration * percent;
  1669. this._current = null;
  1670. d.some(this._animations, function(a){
  1671. if(a.duration <= offset){
  1672. this._current = a;
  1673. return true;
  1674. }
  1675. offset -= a.duration;
  1676. return false;
  1677. });
  1678. if(this._current){
  1679. this._current.gotoPercent(offset / this._current.duration, andPlay);
  1680. }
  1681. return this;
  1682. },
  1683. stop: function(/*boolean?*/ gotoEnd){
  1684. if(this._current){
  1685. if(gotoEnd){
  1686. for(; this._index + 1 < this._animations.length; ++this._index){
  1687. this._animations[this._index].stop(true);
  1688. }
  1689. this._current = this._animations[this._index];
  1690. }
  1691. var e = d.connect(this._current, "onStop", this, function(arg){
  1692. this._fire("onStop", arguments);
  1693. d.disconnect(e);
  1694. });
  1695. this._current.stop();
  1696. }
  1697. return this;
  1698. },
  1699. status: function(){
  1700. return this._current ? this._current.status() : "stopped";
  1701. },
  1702. destroy: function(){
  1703. if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
  1704. if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
  1705. }
  1706. });
  1707. d.extend(_chain, _baseObj);
  1708. dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
  1709. // summary:
  1710. // Chain a list of `dojo.Animation`s to run in sequence
  1711. //
  1712. // description:
  1713. // Return a `dojo.Animation` which will play all passed
  1714. // `dojo.Animation` instances in sequence, firing its own
  1715. // synthesized events simulating a single animation. (eg:
  1716. // onEnd of this animation means the end of the chain,
  1717. // not the individual animations within)
  1718. //
  1719. // example:
  1720. // Once `node` is faded out, fade in `otherNode`
  1721. // | dojo.fx.chain([
  1722. // | dojo.fadeIn({ node:node }),
  1723. // | dojo.fadeOut({ node:otherNode })
  1724. // | ]).play();
  1725. //
  1726. return new _chain(animations) // dojo.Animation
  1727. };
  1728. var _combine = function(animations){
  1729. this._animations = animations||[];
  1730. this._connects = [];
  1731. this._finished = 0;
  1732. this.duration = 0;
  1733. d.forEach(animations, function(a){
  1734. var duration = a.duration;
  1735. if(a.delay){ duration += a.delay; }
  1736. if(this.duration < duration){ this.duration = duration; }
  1737. this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
  1738. }, this);
  1739. this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
  1740. var self = this;
  1741. d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
  1742. function(evt){
  1743. self._connects.push(d.connect(self._pseudoAnimation, evt,
  1744. function(){ self._fire(evt, arguments); }
  1745. ));
  1746. }
  1747. );
  1748. };
  1749. d.extend(_combine, {
  1750. _doAction: function(action, args){
  1751. d.forEach(this._animations, function(a){
  1752. a[action].apply(a, args);
  1753. });
  1754. return this;
  1755. },
  1756. _onEnd: function(){
  1757. if(++this._finished > this._animations.length){
  1758. this._fire("onEnd");
  1759. }
  1760. },
  1761. _call: function(action, args){
  1762. var t = this._pseudoAnimation;
  1763. t[action].apply(t, args);
  1764. },
  1765. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  1766. this._finished = 0;
  1767. this._doAction("play", arguments);
  1768. this._call("play", arguments);
  1769. return this;
  1770. },
  1771. pause: function(){
  1772. this._doAction("pause", arguments);
  1773. this._call("pause", arguments);
  1774. return this;
  1775. },
  1776. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  1777. var ms = this.duration * percent;
  1778. d.forEach(this._animations, function(a){
  1779. a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
  1780. });
  1781. this._call("gotoPercent", arguments);
  1782. return this;
  1783. },
  1784. stop: function(/*boolean?*/ gotoEnd){
  1785. this._doAction("stop", arguments);
  1786. this._call("stop", arguments);
  1787. return this;
  1788. },
  1789. status: function(){
  1790. return this._pseudoAnimation.status();
  1791. },
  1792. destroy: function(){
  1793. d.forEach(this._connects, dojo.disconnect);
  1794. }
  1795. });
  1796. d.extend(_combine, _baseObj);
  1797. dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
  1798. // summary:
  1799. // Combine a list of `dojo.Animation`s to run in parallel
  1800. //
  1801. // description:
  1802. // Combine an array of `dojo.Animation`s to run in parallel,
  1803. // providing a new `dojo.Animation` instance encompasing each
  1804. // animation, firing standard animation events.
  1805. //
  1806. // example:
  1807. // Fade out `node` while fading in `otherNode` simultaneously
  1808. // | dojo.fx.combine([
  1809. // | dojo.fadeIn({ node:node }),
  1810. // | dojo.fadeOut({ node:otherNode })
  1811. // | ]).play();
  1812. //
  1813. // example:
  1814. // When the longest animation ends, execute a function:
  1815. // | var anim = dojo.fx.combine([
  1816. // | dojo.fadeIn({ node: n, duration:700 }),
  1817. // | dojo.fadeOut({ node: otherNode, duration: 300 })
  1818. // | ]);
  1819. // | dojo.connect(anim, "onEnd", function(){
  1820. // | // overall animation is done.
  1821. // | });
  1822. // | anim.play(); // play the animation
  1823. //
  1824. return new _combine(animations); // dojo.Animation
  1825. };
  1826. dojo.fx.wipeIn = function(/*Object*/ args){
  1827. // summary:
  1828. // Expand a node to it's natural height.
  1829. //
  1830. // description:
  1831. // Returns an animation that will expand the
  1832. // node defined in 'args' object from it's current height to
  1833. // it's natural height (with no scrollbar).
  1834. // Node must have no margin/border/padding.
  1835. //
  1836. // args: Object
  1837. // A hash-map of standard `dojo.Animation` constructor properties
  1838. // (such as easing: node: duration: and so on)
  1839. //
  1840. // example:
  1841. // | dojo.fx.wipeIn({
  1842. // | node:"someId"
  1843. // | }).play()
  1844. var node = args.node = d.byId(args.node), s = node.style, o;
  1845. var anim = d.animateProperty(d.mixin({
  1846. properties: {
  1847. height: {
  1848. // wrapped in functions so we wait till the last second to query (in case value has changed)
  1849. start: function(){
  1850. // start at current [computed] height, but use 1px rather than 0
  1851. // because 0 causes IE to display the whole panel
  1852. o = s.overflow;
  1853. s.overflow = "hidden";
  1854. if(s.visibility == "hidden" || s.display == "none"){
  1855. s.height = "1px";
  1856. s.display = "";
  1857. s.visibility = "";
  1858. return 1;
  1859. }else{
  1860. var height = d.style(node, "height");
  1861. return Math.max(height, 1);
  1862. }
  1863. },
  1864. end: function(){
  1865. return node.scrollHeight;
  1866. }
  1867. }
  1868. }
  1869. }, args));
  1870. d.connect(anim, "onEnd", function(){
  1871. s.height = "auto";
  1872. s.overflow = o;
  1873. });
  1874. return anim; // dojo.Animation
  1875. };
  1876. dojo.fx.wipeOut = function(/*Object*/ args){
  1877. // summary:
  1878. // Shrink a node to nothing and hide it.
  1879. //
  1880. // description:
  1881. // Returns an animation that will shrink node defined in "args"
  1882. // from it's current height to 1px, and then hide it.
  1883. //
  1884. // args: Object
  1885. // A hash-map of standard `dojo.Animation` constructor properties
  1886. // (such as easing: node: duration: and so on)
  1887. //
  1888. // example:
  1889. // | dojo.fx.wipeOut({ node:"someId" }).play()
  1890. var node = args.node = d.byId(args.node), s = node.style, o;
  1891. var anim = d.animateProperty(d.mixin({
  1892. properties: {
  1893. height: {
  1894. end: 1 // 0 causes IE to display the whole panel
  1895. }
  1896. }
  1897. }, args));
  1898. d.connect(anim, "beforeBegin", function(){
  1899. o = s.overflow;
  1900. s.overflow = "hidden";
  1901. s.display = "";
  1902. });
  1903. d.connect(anim, "onEnd", function(){
  1904. s.overflow = o;
  1905. s.height = "auto";
  1906. s.display = "none";
  1907. });
  1908. return anim; // dojo.Animation
  1909. };
  1910. dojo.fx.slideTo = function(/*Object*/ args){
  1911. // summary:
  1912. // Slide a node to a new top/left position
  1913. //
  1914. // description:
  1915. // Returns an animation that will slide "node"
  1916. // defined in args Object from its current position to
  1917. // the position defined by (args.left, args.top).
  1918. //
  1919. // args: Object
  1920. // A hash-map of standard `dojo.Animation` constructor properties
  1921. // (such as easing: node: duration: and so on). Special args members
  1922. // are `top` and `left`, which indicate the new position to slide to.
  1923. //
  1924. // example:
  1925. // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
  1926. var node = args.node = d.byId(args.node),
  1927. top = null, left = null;
  1928. var init = (function(n){
  1929. return function(){
  1930. var cs = d.getComputedStyle(n);
  1931. var pos = cs.position;
  1932. top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
  1933. left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
  1934. if(pos != 'absolute' && pos != 'relative'){
  1935. var ret = d.position(n, true);
  1936. top = ret.y;
  1937. left = ret.x;
  1938. n.style.position="absolute";
  1939. n.style.top=top+"px";
  1940. n.style.left=left+"px";
  1941. }
  1942. };
  1943. })(node);
  1944. init();
  1945. var anim = d.animateProperty(d.mixin({
  1946. properties: {
  1947. top: args.top || 0,
  1948. left: args.left || 0
  1949. }
  1950. }, args));
  1951. d.connect(anim, "beforeBegin", anim, init);
  1952. return anim; // dojo.Animation
  1953. };
  1954. })();
  1955. }
  1956. if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1957. dojo._hasResource["dijit.form._FormMixin"] = true;
  1958. dojo.provide("dijit.form._FormMixin");
  1959. dojo.declare("dijit.form._FormMixin", null, {
  1960. // summary:
  1961. // Mixin for containers of form widgets (i.e. widgets that represent a single value
  1962. // and can be children of a <form> node or dijit.form.Form widget)
  1963. // description:
  1964. // Can extract all the form widgets
  1965. // values and combine them into a single javascript object, or alternately
  1966. // take such an object and set the values for all the contained
  1967. // form widgets
  1968. /*=====
  1969. // value: Object
  1970. // Name/value hash for each child widget with a name and value.
  1971. // Child widgets without names are not part of the hash.
  1972. //
  1973. // If there are multiple child widgets w/the same name, value is an array,
  1974. // unless they are radio buttons in which case value is a scalar (since only
  1975. // one radio button can be checked at a time).
  1976. //
  1977. // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
  1978. //
  1979. // Example:
  1980. // | { name: "John Smith", interests: ["sports", "movies"] }
  1981. =====*/
  1982. // state: [readonly] String
  1983. // Will be "Error" if one or more of the child widgets has an invalid value,
  1984. // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
  1985. // which indicates that the form is ready to be submitted.
  1986. state: "",
  1987. // TODO:
  1988. // * Repeater
  1989. // * better handling for arrays. Often form elements have names with [] like
  1990. // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
  1991. //
  1992. //
  1993. reset: function(){
  1994. dojo.forEach(this.getDescendants(), function(widget){
  1995. if(widget.reset){
  1996. widget.reset();
  1997. }
  1998. });
  1999. },
  2000. validate: function(){
  2001. // summary:
  2002. // returns if the form is valid - same as isValid - but
  2003. // provides a few additional (ui-specific) features.
  2004. // 1 - it will highlight any sub-widgets that are not
  2005. // valid
  2006. // 2 - it will call focus() on the first invalid
  2007. // sub-widget
  2008. var didFocus = false;
  2009. return dojo.every(dojo.map(this.getDescendants(), function(widget){
  2010. // Need to set this so that "required" widgets get their
  2011. // state set.
  2012. widget._hasBeenBlurred = true;
  2013. var valid = widget.disabled || !widget.validate || widget.validate();
  2014. if(!valid && !didFocus){
  2015. // Set focus of the first non-valid widget
  2016. dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
  2017. widget.focus();
  2018. didFocus = true;
  2019. }
  2020. return valid;
  2021. }), function(item){ return item; });
  2022. },
  2023. setValues: function(val){
  2024. dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
  2025. return this.set('value', val);
  2026. },
  2027. _setValueAttr: function(/*Object*/ obj){
  2028. // summary:
  2029. // Fill in form values from according to an Object (in the format returned by get('value'))
  2030. // generate map from name --> [list of widgets with that name]
  2031. var map = { };
  2032. dojo.forEach(this.getDescendants(), function(widget){
  2033. if(!widget.name){ return; }
  2034. var entry = map[widget.name] || (map[widget.name] = [] );
  2035. entry.push(widget);
  2036. });
  2037. for(var name in map){
  2038. if(!map.hasOwnProperty(name)){
  2039. continue;
  2040. }
  2041. var widgets = map[name], // array of widgets w/this name
  2042. values = dojo.getObject(name, false, obj); // list of values for those widgets
  2043. if(values === undefined){
  2044. continue;
  2045. }
  2046. if(!dojo.isArray(values)){
  2047. values = [ values ];
  2048. }
  2049. if(typeof widgets[0].checked == 'boolean'){
  2050. // for checkbox/radio, values is a list of which widgets should be checked
  2051. dojo.forEach(widgets, function(w, i){
  2052. w.set('value', dojo.indexOf(values, w.value) != -1);
  2053. });
  2054. }else if(widgets[0].multiple){
  2055. // it takes an array (e.g. multi-select)
  2056. widgets[0].set('value', values);
  2057. }else{
  2058. // otherwise, values is a list of values to be assigned sequentially to each widget
  2059. dojo.forEach(widgets, function(w, i){
  2060. w.set('value', values[i]);
  2061. });
  2062. }
  2063. }
  2064. /***
  2065. * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
  2066. dojo.forEach(this.containerNode.elements, function(element){
  2067. if(element.name == ''){return}; // like "continue"
  2068. var namePath = element.name.split(".");
  2069. var myObj=obj;
  2070. var name=namePath[namePath.length-1];
  2071. for(var j=1,len2=namePath.length;j<len2;++j){
  2072. var p=namePath[j - 1];
  2073. // repeater support block
  2074. var nameA=p.split("[");
  2075. if(nameA.length > 1){
  2076. if(typeof(myObj[nameA[0]]) == "undefined"){
  2077. myObj[nameA[0]]=[ ];
  2078. } // if
  2079. nameIndex=parseInt(nameA[1]);
  2080. if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
  2081. myObj[nameA[0]][nameIndex] = { };
  2082. }
  2083. myObj=myObj[nameA[0]][nameIndex];
  2084. continue;
  2085. } // repeater support ends
  2086. if(typeof(myObj[p]) == "undefined"){
  2087. myObj=undefined;
  2088. break;
  2089. };
  2090. myObj=myObj[p];
  2091. }
  2092. if(typeof(myObj) == "undefined"){
  2093. return; // like "continue"
  2094. }
  2095. if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
  2096. return; // like "continue"
  2097. }
  2098. // TODO: widget values (just call set('value', ...) on the widget)
  2099. // TODO: maybe should call dojo.getNodeProp() instead
  2100. switch(element.type){
  2101. case "checkbox":
  2102. element.checked = (name in myObj) &&
  2103. dojo.some(myObj[name], function(val){ return val == element.value; });
  2104. break;
  2105. case "radio":
  2106. element.checked = (name in myObj) && myObj[name] == element.value;
  2107. break;
  2108. case "select-multiple":
  2109. element.selectedIndex=-1;
  2110. dojo.forEach(element.options, function(option){
  2111. option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
  2112. });
  2113. break;
  2114. case "select-one":
  2115. element.selectedIndex="0";
  2116. dojo.forEach(element.options, function(option){
  2117. option.selected = option.value == myObj[name];
  2118. });
  2119. break;
  2120. case "hidden":
  2121. case "text":
  2122. case "textarea":
  2123. case "password":
  2124. element.value = myObj[name] || "";
  2125. break;
  2126. }
  2127. });
  2128. */
  2129. // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
  2130. // which I am monitoring.
  2131. },
  2132. getValues: function(){
  2133. dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
  2134. return this.get('value');
  2135. },
  2136. _getValueAttr: function(){
  2137. // summary:
  2138. // Returns Object representing form values. See description of `value` for details.
  2139. // description:
  2140. // The value is updated into this.value every time a child has an onChange event,
  2141. // so in the common case this function could just return this.value. However,
  2142. // that wouldn't work when:
  2143. //
  2144. // 1. User presses return key to submit a form. That doesn't fire an onchange event,
  2145. // and even if it did it would come too late due to the setTimout(..., 0) in _handleOnChange()
  2146. //
  2147. // 2. app for some reason calls this.get("value") while the user is typing into a
  2148. // form field. Not sure if that case needs to be supported or not.
  2149. // get widget values
  2150. var obj = { };
  2151. dojo.forEach(this.getDescendants(), function(widget){
  2152. var name = widget.name;
  2153. if(!name || widget.disabled){ return; }
  2154. // Single value widget (checkbox, radio, or plain <input> type widget)
  2155. var value = widget.get('value');
  2156. // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
  2157. if(typeof widget.checked == 'boolean'){
  2158. if(/Radio/.test(widget.declaredClass)){
  2159. // radio button
  2160. if(value !== false){
  2161. dojo.setObject(name, value, obj);
  2162. }else{
  2163. // give radio widgets a default of null
  2164. value = dojo.getObject(name, false, obj);
  2165. if(value === undefined){
  2166. dojo.setObject(name, null, obj);
  2167. }
  2168. }
  2169. }else{
  2170. // checkbox/toggle button
  2171. var ary=dojo.getObject(name, false, obj);
  2172. if(!ary){
  2173. ary=[];
  2174. dojo.setObject(name, ary, obj);
  2175. }
  2176. if(value !== false){
  2177. ary.push(value);
  2178. }
  2179. }
  2180. }else{
  2181. var prev=dojo.getObject(name, false, obj);
  2182. if(typeof prev != "undefined"){
  2183. if(dojo.isArray(prev)){
  2184. prev.push(value);
  2185. }else{
  2186. dojo.setObject(name, [prev, value], obj);
  2187. }
  2188. }else{
  2189. // unique name
  2190. dojo.setObject(name, value, obj);
  2191. }
  2192. }
  2193. });
  2194. /***
  2195. * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
  2196. * but it doesn't understand [] notation, presumably)
  2197. var obj = { };
  2198. dojo.forEach(this.containerNode.elements, function(elm){
  2199. if(!elm.name) {
  2200. return; // like "continue"
  2201. }
  2202. var namePath = elm.name.split(".");
  2203. var myObj=obj;
  2204. var name=namePath[namePath.length-1];
  2205. for(var j=1,len2=namePath.length;j<len2;++j){
  2206. var nameIndex = null;
  2207. var p=namePath[j - 1];
  2208. var nameA=p.split("[");
  2209. if(nameA.length > 1){
  2210. if(typeof(myObj[nameA[0]]) == "undefined"){
  2211. myObj[nameA[0]]=[ ];
  2212. } // if
  2213. nameIndex=parseInt(nameA[1]);
  2214. if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
  2215. myObj[nameA[0]][nameIndex] = { };
  2216. }
  2217. } else if(typeof(myObj[nameA[0]]) == "undefined"){
  2218. myObj[nameA[0]] = { }
  2219. } // if
  2220. if(nameA.length == 1){
  2221. myObj=myObj[nameA[0]];
  2222. } else{
  2223. myObj=myObj[nameA[0]][nameIndex];
  2224. } // if
  2225. } // for
  2226. if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
  2227. if(name == name.split("[")[0]){
  2228. myObj[name]=elm.value;
  2229. } else{
  2230. // can not set value when there is no name
  2231. }
  2232. } else if(elm.type == "checkbox" && elm.checked){
  2233. if(typeof(myObj[name]) == 'undefined'){
  2234. myObj[name]=[ ];
  2235. }
  2236. myObj[name].push(elm.value);
  2237. } else if(elm.type == "select-multiple"){
  2238. if(typeof(myObj[name]) == 'undefined'){
  2239. myObj[name]=[ ];
  2240. }
  2241. for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
  2242. if(elm.options[jdx].selected){
  2243. myObj[name].push(elm.options[jdx].value);
  2244. }
  2245. }
  2246. } // if
  2247. name=undefined;
  2248. }); // forEach
  2249. ***/
  2250. return obj;
  2251. },
  2252. isValid: function(){
  2253. // summary:
  2254. // Returns true if all of the widgets are valid.
  2255. // Deprecated, will be removed in 2.0. Use get("state") instead.
  2256. return this.state == "";
  2257. },
  2258. onValidStateChange: function(isValid){
  2259. // summary:
  2260. // Stub function to connect to if you want to do something
  2261. // (like disable/enable a submit button) when the valid
  2262. // state changes on the form as a whole.
  2263. //
  2264. // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
  2265. },
  2266. _getState: function(){
  2267. // summary:
  2268. // Compute what this.state should be based on state of children
  2269. var states = dojo.map(this._descendants, function(w){
  2270. return w.get("state") || "";
  2271. });
  2272. return dojo.indexOf(states, "Error") >= 0 ? "Error" :
  2273. dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
  2274. },
  2275. disconnectChildren: function(){
  2276. // summary:
  2277. // Remove connections to monitor changes to children's value, error state, and disabled state,
  2278. // in order to update Form.value and Form.state.
  2279. dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect"));
  2280. dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); });
  2281. },
  2282. connectChildren: function(/*Boolean*/ inStartup){
  2283. // summary:
  2284. // Setup connections to monitor changes to children's value, error state, and disabled state,
  2285. // in order to update Form.value and Form.state.
  2286. //
  2287. // You can call this function directly, ex. in the event that you
  2288. // programmatically add a widget to the form *after* the form has been
  2289. // initialized.
  2290. var _this = this;
  2291. // Remove old connections, if any
  2292. this.disconnectChildren();
  2293. this._descendants = this.getDescendants();
  2294. // (Re)set this.value and this.state. Send watch() notifications but not on startup.
  2295. var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set");
  2296. set("value", this.get("value"));
  2297. set("state", this._getState());
  2298. // Monitor changes to error state and disabled state in order to update
  2299. // Form.state
  2300. var conns = (this._childConnections = []),
  2301. watches = (this._childWatches = []);
  2302. dojo.forEach(dojo.filter(this._descendants,
  2303. function(item){ return item.validate; }
  2304. ),
  2305. function(widget){
  2306. // We are interested in whenever the widget changes validity state - or
  2307. // whenever the disabled attribute on that widget is changed.
  2308. dojo.forEach(["state", "disabled"], function(attr){
  2309. watches.push(widget.watch(attr, function(attr, oldVal, newVal){
  2310. _this.set("state", _this._getState());
  2311. }));
  2312. });
  2313. });
  2314. // And monitor calls to child.onChange so we can update this.value
  2315. var onChange = function(){
  2316. // summary:
  2317. // Called when child's value or disabled state changes
  2318. // Use setTimeout() to collapse value changes in multiple children into a single
  2319. // update to my value. Multiple updates will occur on:
  2320. // 1. Form.set()
  2321. // 2. Form.reset()
  2322. // 3. user selecting a radio button (which will de-select another radio button,
  2323. // causing two onChange events)
  2324. if(_this._onChangeDelayTimer){
  2325. clearTimeout(_this._onChangeDelayTimer);
  2326. }
  2327. _this._onChangeDelayTimer = setTimeout(function(){
  2328. delete _this._onChangeDelayTimer;
  2329. _this._set("value", _this.get("value"));
  2330. }, 10);
  2331. };
  2332. dojo.forEach(
  2333. dojo.filter(this._descendants, function(item){ return item.onChange; } ),
  2334. function(widget){
  2335. // When a child widget's value changes,
  2336. // the efficient thing to do is to just update that one attribute in this.value,
  2337. // but that gets a little complicated when a checkbox is checked/unchecked
  2338. // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
  2339. // Doing simple thing for now.
  2340. conns.push(_this.connect(widget, "onChange", onChange));
  2341. // Disabling/enabling a child widget should remove it's value from this.value.
  2342. // Again, this code could be more efficient, doing simple thing for now.
  2343. watches.push(widget.watch("disabled", onChange));
  2344. }
  2345. );
  2346. },
  2347. startup: function(){
  2348. this.inherited(arguments);
  2349. // Initialize value and valid/invalid state tracking. Needs to be done in startup()
  2350. // so that children are initialized.
  2351. this.connectChildren(true);
  2352. // Make state change call onValidStateChange(), will be removed in 2.0
  2353. this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
  2354. },
  2355. destroy: function(){
  2356. this.disconnectChildren();
  2357. this.inherited(arguments);
  2358. }
  2359. });
  2360. }
  2361. if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2362. dojo._hasResource["dijit._DialogMixin"] = true;
  2363. dojo.provide("dijit._DialogMixin");
  2364. dojo.declare("dijit._DialogMixin", null,
  2365. {
  2366. // summary:
  2367. // This provides functions useful to Dialog and TooltipDialog
  2368. attributeMap: dijit._Widget.prototype.attributeMap,
  2369. execute: function(/*Object*/ formContents){
  2370. // summary:
  2371. // Callback when the user hits the submit button.
  2372. // Override this method to handle Dialog execution.
  2373. // description:
  2374. // After the user has pressed the submit button, the Dialog
  2375. // first calls onExecute() to notify the container to hide the
  2376. // dialog and restore focus to wherever it used to be.
  2377. //
  2378. // *Then* this method is called.
  2379. // type:
  2380. // callback
  2381. },
  2382. onCancel: function(){
  2383. // summary:
  2384. // Called when user has pressed the Dialog's cancel button, to notify container.
  2385. // description:
  2386. // Developer shouldn't override or connect to this method;
  2387. // it's a private communication device between the TooltipDialog
  2388. // and the thing that opened it (ex: `dijit.form.DropDownButton`)
  2389. // type:
  2390. // protected
  2391. },
  2392. onExecute: function(){
  2393. // summary:
  2394. // Called when user has pressed the dialog's OK button, to notify container.
  2395. // description:
  2396. // Developer shouldn't override or connect to this method;
  2397. // it's a private communication device between the TooltipDialog
  2398. // and the thing that opened it (ex: `dijit.form.DropDownButton`)
  2399. // type:
  2400. // protected
  2401. },
  2402. _onSubmit: function(){
  2403. // summary:
  2404. // Callback when user hits submit button
  2405. // type:
  2406. // protected
  2407. this.onExecute(); // notify container that we are about to execute
  2408. this.execute(this.get('value'));
  2409. },
  2410. _getFocusItems: function(){
  2411. // summary:
  2412. // Finds focusable items in dialog,
  2413. // and sets this._firstFocusItem and this._lastFocusItem
  2414. // tags:
  2415. // protected
  2416. var elems = dijit._getTabNavigable(this.containerNode);
  2417. this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
  2418. this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
  2419. }
  2420. }
  2421. );
  2422. }
  2423. if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2424. dojo._hasResource["dijit.DialogUnderlay"] = true;
  2425. dojo.provide("dijit.DialogUnderlay");
  2426. dojo.declare(
  2427. "dijit.DialogUnderlay",
  2428. [dijit._Widget, dijit._Templated],
  2429. {
  2430. // summary:
  2431. // The component that blocks the screen behind a `dijit.Dialog`
  2432. //
  2433. // description:
  2434. // A component used to block input behind a `dijit.Dialog`. Only a single
  2435. // instance of this widget is created by `dijit.Dialog`, and saved as
  2436. // a reference to be shared between all Dialogs as `dijit._underlay`
  2437. //
  2438. // The underlay itself can be styled based on and id:
  2439. // | #myDialog_underlay { background-color:red; }
  2440. //
  2441. // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
  2442. // suffixed with _underlay.
  2443. // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
  2444. // Inner div has opacity specified in CSS file.
  2445. templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>",
  2446. // Parameters on creation or updatable later
  2447. // dialogId: String
  2448. // Id of the dialog.... DialogUnderlay's id is based on this id
  2449. dialogId: "",
  2450. // class: String
  2451. // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
  2452. "class": "",
  2453. attributeMap: { id: "domNode" },
  2454. _setDialogIdAttr: function(id){
  2455. dojo.attr(this.node, "id", id + "_underlay");
  2456. this._set("dialogId", id);
  2457. },
  2458. _setClassAttr: function(clazz){
  2459. this.node.className = "dijitDialogUnderlay " + clazz;
  2460. this._set("class", clazz);
  2461. },
  2462. postCreate: function(){
  2463. // summary:
  2464. // Append the underlay to the body
  2465. dojo.body().appendChild(this.domNode);
  2466. },
  2467. layout: function(){
  2468. // summary:
  2469. // Sets the background to the size of the viewport
  2470. //
  2471. // description:
  2472. // Sets the background to the size of the viewport (rather than the size
  2473. // of the document) since we need to cover the whole browser window, even
  2474. // if the document is only a few lines long.
  2475. // tags:
  2476. // private
  2477. var is = this.node.style,
  2478. os = this.domNode.style;
  2479. // hide the background temporarily, so that the background itself isn't
  2480. // causing scrollbars to appear (might happen when user shrinks browser
  2481. // window and then we are called to resize)
  2482. os.display = "none";
  2483. // then resize and show
  2484. var viewport = dojo.window.getBox();
  2485. os.top = viewport.t + "px";
  2486. os.left = viewport.l + "px";
  2487. is.width = viewport.w + "px";
  2488. is.height = viewport.h + "px";
  2489. os.display = "block";
  2490. },
  2491. show: function(){
  2492. // summary:
  2493. // Show the dialog underlay
  2494. this.domNode.style.display = "block";
  2495. this.layout();
  2496. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  2497. },
  2498. hide: function(){
  2499. // summary:
  2500. // Hides the dialog underlay
  2501. this.bgIframe.destroy();
  2502. delete this.bgIframe;
  2503. this.domNode.style.display = "none";
  2504. }
  2505. }
  2506. );
  2507. }
  2508. if(!dojo._hasResource["dijit.layout._ContentPaneResizeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2509. dojo._hasResource["dijit.layout._ContentPaneResizeMixin"] = true;
  2510. dojo.provide("dijit.layout._ContentPaneResizeMixin");
  2511. dojo.declare("dijit.layout._ContentPaneResizeMixin", null, {
  2512. // summary:
  2513. // Resize() functionality of ContentPane. If there's a single layout widget
  2514. // child then it will call resize() with the same dimensions as the ContentPane.
  2515. // Otherwise just calls resize on each child.
  2516. //
  2517. // Also implements basic startup() functionality, where starting the parent
  2518. // will start the children
  2519. // doLayout: Boolean
  2520. // - false - don't adjust size of children
  2521. // - true - if there is a single visible child widget, set it's size to
  2522. // however big the ContentPane is
  2523. doLayout: true,
  2524. // isContainer: [protected] Boolean
  2525. // Indicates that this widget acts as a "parent" to the descendant widgets.
  2526. // When the parent is started it will call startup() on the child widgets.
  2527. // See also `isLayoutContainer`.
  2528. isContainer: true,
  2529. // isLayoutContainer: [protected] Boolean
  2530. // Indicates that this widget will call resize() on it's child widgets
  2531. // when they become visible.
  2532. isLayoutContainer: true,
  2533. _startChildren: function(){
  2534. // summary:
  2535. // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
  2536. // This starts all the widgets
  2537. dojo.forEach(this.getChildren(), function(child){
  2538. child.startup();
  2539. child._started = true;
  2540. });
  2541. },
  2542. startup: function(){
  2543. // summary:
  2544. // See `dijit.layout._LayoutWidget.startup` for description.
  2545. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  2546. // the same API.
  2547. if(this._started){ return; }
  2548. var parent = dijit._Contained.prototype.getParent.call(this);
  2549. this._childOfLayoutWidget = parent && parent.isLayoutContainer;
  2550. // I need to call resize() on my child/children (when I become visible), unless
  2551. // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
  2552. this._needLayout = !this._childOfLayoutWidget;
  2553. this.inherited(arguments);
  2554. this._startChildren();
  2555. if(this._isShown()){
  2556. this._onShow();
  2557. }
  2558. if(!this._childOfLayoutWidget){
  2559. // If my parent isn't a layout container, since my style *may be* width=height=100%
  2560. // or something similar (either set directly or via a CSS class),
  2561. // monitor when my size changes so that I can re-layout.
  2562. // For browsers where I can't directly monitor when my size changes,
  2563. // monitor when the viewport changes size, which *may* indicate a size change for me.
  2564. this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
  2565. // Using function(){} closure to ensure no arguments to resize.
  2566. this._needLayout = !this._childOfLayoutWidget;
  2567. this.resize();
  2568. });
  2569. }
  2570. },
  2571. _checkIfSingleChild: function(){
  2572. // summary:
  2573. // Test if we have exactly one visible widget as a child,
  2574. // and if so assume that we are a container for that widget,
  2575. // and should propagate startup() and resize() calls to it.
  2576. // Skips over things like data stores since they aren't visible.
  2577. var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
  2578. return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
  2579. }),
  2580. childWidgetNodes = childNodes.filter(function(node){
  2581. return dojo.hasAttr(node, "data-dojo-type") || dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
  2582. }),
  2583. candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
  2584. return widget && widget.domNode && widget.resize;
  2585. });
  2586. if(
  2587. // all child nodes are widgets
  2588. childNodes.length == childWidgetNodes.length &&
  2589. // all but one are invisible (like dojo.data)
  2590. candidateWidgets.length == 1
  2591. ){
  2592. this._singleChild = candidateWidgets[0];
  2593. }else{
  2594. delete this._singleChild;
  2595. }
  2596. // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
  2597. dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
  2598. },
  2599. resize: function(changeSize, resultSize){
  2600. // summary:
  2601. // See `dijit.layout._LayoutWidget.resize` for description.
  2602. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  2603. // the same API.
  2604. // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
  2605. // never called, so resize() is our trigger to do the initial href download (see [20099]).
  2606. // However, don't load href for closed TitlePanes.
  2607. if(!this._wasShown && this.open !== false){
  2608. this._onShow();
  2609. }
  2610. this._resizeCalled = true;
  2611. this._scheduleLayout(changeSize, resultSize);
  2612. },
  2613. _scheduleLayout: function(changeSize, resultSize){
  2614. // summary:
  2615. // Resize myself, and call resize() on each of my child layout widgets, either now
  2616. // (if I'm currently visible) or when I become visible
  2617. if(this._isShown()){
  2618. this._layout(changeSize, resultSize);
  2619. }else{
  2620. this._needLayout = true;
  2621. this._changeSize = changeSize;
  2622. this._resultSize = resultSize;
  2623. }
  2624. },
  2625. _layout: function(changeSize, resultSize){
  2626. // summary:
  2627. // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
  2628. // Also, since I am a Container widget, each of my children expects me to
  2629. // call resize() or layout() on them.
  2630. //
  2631. // Should be called on initialization and also whenever we get new content
  2632. // (from an href, or from set('content', ...))... but deferred until
  2633. // the ContentPane is visible
  2634. // Set margin box size, unless it wasn't specified, in which case use current size.
  2635. if(changeSize){
  2636. dojo.marginBox(this.domNode, changeSize);
  2637. }
  2638. // Compute content box size of containerNode in case we [later] need to size our single child.
  2639. var cn = this.containerNode;
  2640. if(cn === this.domNode){
  2641. // If changeSize or resultSize was passed to this method and this.containerNode ==
  2642. // this.domNode then we can compute the content-box size without querying the node,
  2643. // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
  2644. var mb = resultSize || {};
  2645. dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
  2646. if(!("h" in mb) || !("w" in mb)){
  2647. mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
  2648. }
  2649. this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
  2650. }else{
  2651. this._contentBox = dojo.contentBox(cn);
  2652. }
  2653. this._layoutChildren();
  2654. delete this._needLayout;
  2655. },
  2656. _layoutChildren: function(){
  2657. // Call _checkIfSingleChild() again in case app has manually mucked w/the content
  2658. // of the ContentPane (rather than changing it through the set("content", ...) API.
  2659. if(this.doLayout){
  2660. this._checkIfSingleChild();
  2661. }
  2662. if(this._singleChild && this._singleChild.resize){
  2663. var cb = this._contentBox || dojo.contentBox(this.containerNode);
  2664. // note: if widget has padding this._contentBox will have l and t set,
  2665. // but don't pass them to resize() or it will doubly-offset the child
  2666. this._singleChild.resize({w: cb.w, h: cb.h});
  2667. }else{
  2668. // All my child widgets are independently sized (rather than matching my size),
  2669. // but I still need to call resize() on each child to make it layout.
  2670. dojo.forEach(this.getChildren(), function(widget){
  2671. if(widget.resize){
  2672. widget.resize();
  2673. }
  2674. });
  2675. }
  2676. },
  2677. _isShown: function(){
  2678. // summary:
  2679. // Returns true if the content is currently shown.
  2680. // description:
  2681. // If I am a child of a layout widget then it actually returns true if I've ever been visible,
  2682. // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
  2683. // tree every call, and at least solves the performance problem on page load by deferring loading
  2684. // hidden ContentPanes until they are first shown
  2685. if(this._childOfLayoutWidget){
  2686. // If we are TitlePane, etc - we return that only *IF* we've been resized
  2687. if(this._resizeCalled && "open" in this){
  2688. return this.open;
  2689. }
  2690. return this._resizeCalled;
  2691. }else if("open" in this){
  2692. return this.open; // for TitlePane, etc.
  2693. }else{
  2694. var node = this.domNode, parent = this.domNode.parentNode;
  2695. return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden") &&
  2696. parent && parent.style && (parent.style.display != 'none');
  2697. }
  2698. },
  2699. _onShow: function(){
  2700. // summary:
  2701. // Called when the ContentPane is made visible
  2702. // description:
  2703. // For a plain ContentPane, this is called on initialization, from startup().
  2704. // If the ContentPane is a hidden pane of a TabContainer etc., then it's
  2705. // called whenever the pane is made visible.
  2706. //
  2707. // Does layout/resize of child widget(s)
  2708. // Need to keep track of whether ContentPane has been shown (which is different than
  2709. // whether or not it's currently visible).
  2710. this._wasShown = true;
  2711. if(this._needLayout){
  2712. // If a layout has been scheduled for when we become visible, do it now
  2713. this._layout(this._changeSize, this._resultSize);
  2714. }
  2715. this.inherited(arguments);
  2716. }
  2717. });
  2718. }
  2719. if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2720. dojo._hasResource["dojo.html"] = true;
  2721. dojo.provide("dojo.html");
  2722. dojo.getObject("html", true, dojo);
  2723. // the parser might be needed..
  2724. (function(){ // private scope, sort of a namespace
  2725. // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
  2726. var idCounter = 0,
  2727. d = dojo;
  2728. dojo.html._secureForInnerHtml = function(/*String*/ cont){
  2729. // summary:
  2730. // removes !DOCTYPE and title elements from the html string.
  2731. //
  2732. // khtml is picky about dom faults, you can't attach a style or <title> node as child of body
  2733. // must go into head, so we need to cut out those tags
  2734. // cont:
  2735. // An html string for insertion into the dom
  2736. //
  2737. return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
  2738. };
  2739. /*====
  2740. dojo.html._emptyNode = function(node){
  2741. // summary:
  2742. // removes all child nodes from the given node
  2743. // node: DOMNode
  2744. // the parent element
  2745. };
  2746. =====*/
  2747. dojo.html._emptyNode = dojo.empty;
  2748. dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
  2749. // summary:
  2750. // inserts the given content into the given node
  2751. // node:
  2752. // the parent element
  2753. // content:
  2754. // the content to be set on the parent element.
  2755. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  2756. // always empty
  2757. d.empty(node);
  2758. if(cont) {
  2759. if(typeof cont == "string") {
  2760. cont = d._toDom(cont, node.ownerDocument);
  2761. }
  2762. if(!cont.nodeType && d.isArrayLike(cont)) {
  2763. // handle as enumerable, but it may shrink as we enumerate it
  2764. for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
  2765. d.place( cont[i], node, "last");
  2766. }
  2767. } else {
  2768. // pass nodes, documentFragments and unknowns through to dojo.place
  2769. d.place(cont, node, "last");
  2770. }
  2771. }
  2772. // return DomNode
  2773. return node;
  2774. };
  2775. // we wrap up the content-setting operation in a object
  2776. dojo.declare("dojo.html._ContentSetter", null,
  2777. {
  2778. // node: DomNode|String
  2779. // An node which will be the parent element that we set content into
  2780. node: "",
  2781. // content: String|DomNode|DomNode[]
  2782. // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
  2783. content: "",
  2784. // id: String?
  2785. // Usually only used internally, and auto-generated with each instance
  2786. id: "",
  2787. // cleanContent: Boolean
  2788. // Should the content be treated as a full html document,
  2789. // and the real content stripped of <html>, <body> wrapper before injection
  2790. cleanContent: false,
  2791. // extractContent: Boolean
  2792. // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
  2793. extractContent: false,
  2794. // parseContent: Boolean
  2795. // Should the node by passed to the parser after the new content is set
  2796. parseContent: false,
  2797. // parserScope: String
  2798. // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
  2799. // will search for data-dojo-type (or dojoType). For backwards compatibility
  2800. // reasons defaults to dojo._scopeName (which is "dojo" except when
  2801. // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  2802. parserScope: dojo._scopeName,
  2803. // startup: Boolean
  2804. // Start the child widgets after parsing them. Only obeyed if parseContent is true.
  2805. startup: true,
  2806. // lifecyle methods
  2807. constructor: function(/* Object */params, /* String|DomNode */node){
  2808. // summary:
  2809. // Provides a configurable, extensible object to wrap the setting on content on a node
  2810. // call the set() method to actually set the content..
  2811. // the original params are mixed directly into the instance "this"
  2812. dojo.mixin(this, params || {});
  2813. // give precedence to params.node vs. the node argument
  2814. // and ensure its a node, not an id string
  2815. node = this.node = dojo.byId( this.node || node );
  2816. if(!this.id){
  2817. this.id = [
  2818. "Setter",
  2819. (node) ? node.id || node.tagName : "",
  2820. idCounter++
  2821. ].join("_");
  2822. }
  2823. },
  2824. set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
  2825. // summary:
  2826. // front-end to the set-content sequence
  2827. // cont:
  2828. // An html string, node or enumerable list of nodes for insertion into the dom
  2829. // If not provided, the object's content property will be used
  2830. if(undefined !== cont){
  2831. this.content = cont;
  2832. }
  2833. // in the re-use scenario, set needs to be able to mixin new configuration
  2834. if(params){
  2835. this._mixin(params);
  2836. }
  2837. this.onBegin();
  2838. this.setContent();
  2839. this.onEnd();
  2840. return this.node;
  2841. },
  2842. setContent: function(){
  2843. // summary:
  2844. // sets the content on the node
  2845. var node = this.node;
  2846. if(!node) {
  2847. // can't proceed
  2848. throw new Error(this.declaredClass + ": setContent given no node");
  2849. }
  2850. try{
  2851. node = dojo.html._setNodeContent(node, this.content);
  2852. }catch(e){
  2853. // check if a domfault occurs when we are appending this.errorMessage
  2854. // like for instance if domNode is a UL and we try append a DIV
  2855. // FIXME: need to allow the user to provide a content error message string
  2856. var errMess = this.onContentError(e);
  2857. try{
  2858. node.innerHTML = errMess;
  2859. }catch(e){
  2860. console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
  2861. }
  2862. }
  2863. // always put back the node for the next method
  2864. this.node = node; // DomNode
  2865. },
  2866. empty: function() {
  2867. // summary
  2868. // cleanly empty out existing content
  2869. // destroy any widgets from a previous run
  2870. // NOTE: if you dont want this you'll need to empty
  2871. // the parseResults array property yourself to avoid bad things happenning
  2872. if(this.parseResults && this.parseResults.length) {
  2873. dojo.forEach(this.parseResults, function(w) {
  2874. if(w.destroy){
  2875. w.destroy();
  2876. }
  2877. });
  2878. delete this.parseResults;
  2879. }
  2880. // this is fast, but if you know its already empty or safe, you could
  2881. // override empty to skip this step
  2882. dojo.html._emptyNode(this.node);
  2883. },
  2884. onBegin: function(){
  2885. // summary
  2886. // Called after instantiation, but before set();
  2887. // It allows modification of any of the object properties
  2888. // - including the node and content provided - before the set operation actually takes place
  2889. // This default implementation checks for cleanContent and extractContent flags to
  2890. // optionally pre-process html string content
  2891. var cont = this.content;
  2892. if(dojo.isString(cont)){
  2893. if(this.cleanContent){
  2894. cont = dojo.html._secureForInnerHtml(cont);
  2895. }
  2896. if(this.extractContent){
  2897. var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
  2898. if(match){ cont = match[1]; }
  2899. }
  2900. }
  2901. // clean out the node and any cruft associated with it - like widgets
  2902. this.empty();
  2903. this.content = cont;
  2904. return this.node; /* DomNode */
  2905. },
  2906. onEnd: function(){
  2907. // summary
  2908. // Called after set(), when the new content has been pushed into the node
  2909. // It provides an opportunity for post-processing before handing back the node to the caller
  2910. // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
  2911. if(this.parseContent){
  2912. // populates this.parseResults if you need those..
  2913. this._parse();
  2914. }
  2915. return this.node; /* DomNode */
  2916. },
  2917. tearDown: function(){
  2918. // summary
  2919. // manually reset the Setter instance if its being re-used for example for another set()
  2920. // description
  2921. // tearDown() is not called automatically.
  2922. // In normal use, the Setter instance properties are simply allowed to fall out of scope
  2923. // but the tearDown method can be called to explicitly reset this instance.
  2924. delete this.parseResults;
  2925. delete this.node;
  2926. delete this.content;
  2927. },
  2928. onContentError: function(err){
  2929. return "Error occured setting content: " + err;
  2930. },
  2931. _mixin: function(params){
  2932. // mix properties/methods into the instance
  2933. // TODO: the intention with tearDown is to put the Setter's state
  2934. // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
  2935. // so we could do something here to move the original properties aside for later restoration
  2936. var empty = {}, key;
  2937. for(key in params){
  2938. if(key in empty){ continue; }
  2939. // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
  2940. // .. but history shows we'll almost always guess wrong
  2941. this[key] = params[key];
  2942. }
  2943. },
  2944. _parse: function(){
  2945. // summary:
  2946. // runs the dojo parser over the node contents, storing any results in this.parseResults
  2947. // Any errors resulting from parsing are passed to _onError for handling
  2948. var rootNode = this.node;
  2949. try{
  2950. // store the results (widgets, whatever) for potential retrieval
  2951. var inherited = {};
  2952. dojo.forEach(["dir", "lang", "textDir"], function(name){
  2953. if(this[name]){
  2954. inherited[name] = this[name];
  2955. }
  2956. }, this);
  2957. this.parseResults = dojo.parser.parse({
  2958. rootNode: rootNode,
  2959. noStart: !this.startup,
  2960. inherited: inherited,
  2961. scope: this.parserScope
  2962. });
  2963. }catch(e){
  2964. this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
  2965. }
  2966. },
  2967. _onError: function(type, err, consoleText){
  2968. // summary:
  2969. // shows user the string that is returned by on[type]Error
  2970. // overide/implement on[type]Error and return your own string to customize
  2971. var errText = this['on' + type + 'Error'].call(this, err);
  2972. if(consoleText){
  2973. console.error(consoleText, err);
  2974. }else if(errText){ // a empty string won't change current content
  2975. dojo.html._setNodeContent(this.node, errText, true);
  2976. }
  2977. }
  2978. }); // end dojo.declare()
  2979. dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
  2980. // summary:
  2981. // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
  2982. // may be a better choice for simple HTML insertion.
  2983. // description:
  2984. // Unless you need to use the params capabilities of this method, you should use
  2985. // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
  2986. // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
  2987. // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
  2988. // or the other capabilities as defined by the params object for this method.
  2989. // node:
  2990. // the parent element that will receive the content
  2991. // cont:
  2992. // the content to be set on the parent element.
  2993. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  2994. // params:
  2995. // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
  2996. // example:
  2997. // A safe string/node/nodelist content replacement/injection with hooks for extension
  2998. // Example Usage:
  2999. // dojo.html.set(node, "some string");
  3000. // dojo.html.set(node, contentNode, {options});
  3001. // dojo.html.set(node, myNode.childNodes, {options});
  3002. if(undefined == cont){
  3003. console.warn("dojo.html.set: no cont argument provided, using empty string");
  3004. cont = "";
  3005. }
  3006. if(!params){
  3007. // simple and fast
  3008. return dojo.html._setNodeContent(node, cont, true);
  3009. }else{
  3010. // more options but slower
  3011. // note the arguments are reversed in order, to match the convention for instantiation via the parser
  3012. var op = new dojo.html._ContentSetter(dojo.mixin(
  3013. params,
  3014. { content: cont, node: node }
  3015. ));
  3016. return op.set();
  3017. }
  3018. };
  3019. })();
  3020. }
  3021. if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  3022. dojo._hasResource["dijit.layout.ContentPane"] = true;
  3023. dojo.provide("dijit.layout.ContentPane");
  3024. dojo.declare(
  3025. "dijit.layout.ContentPane", [dijit._Widget, dijit.layout._ContentPaneResizeMixin],
  3026. {
  3027. // summary:
  3028. // A widget containing an HTML fragment, specified inline
  3029. // or by uri. Fragment may include widgets.
  3030. //
  3031. // description:
  3032. // This widget embeds a document fragment in the page, specified
  3033. // either by uri, javascript generated markup or DOM reference.
  3034. // Any widgets within this content are instantiated and managed,
  3035. // but laid out according to the HTML structure. Unlike IFRAME,
  3036. // ContentPane embeds a document fragment as would be found
  3037. // inside the BODY tag of a full HTML document. It should not
  3038. // contain the HTML, HEAD, or BODY tags.
  3039. // For more advanced functionality with scripts and
  3040. // stylesheets, see dojox.layout.ContentPane. This widget may be
  3041. // used stand alone or as a base class for other widgets.
  3042. // ContentPane is useful as a child of other layout containers
  3043. // such as BorderContainer or TabContainer, but note that those
  3044. // widgets can contain any widget as a child.
  3045. //
  3046. // example:
  3047. // Some quick samples:
  3048. // To change the innerHTML: cp.set('content', '<b>new content</b>')
  3049. //
  3050. // Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection))
  3051. //
  3052. // To do an ajax update: cp.set('href', url)
  3053. // href: String
  3054. // The href of the content that displays now.
  3055. // Set this at construction if you want to load data externally when the
  3056. // pane is shown. (Set preload=true to load it immediately.)
  3057. // Changing href after creation doesn't have any effect; Use set('href', ...);
  3058. href: "",
  3059. /*=====
  3060. // content: String || DomNode || NodeList || dijit._Widget
  3061. // The innerHTML of the ContentPane.
  3062. // Note that the initialization parameter / argument to set("content", ...)
  3063. // can be a String, DomNode, Nodelist, or _Widget.
  3064. content: "",
  3065. =====*/
  3066. // extractContent: Boolean
  3067. // Extract visible content from inside of <body> .... </body>.
  3068. // I.e., strip <html> and <head> (and it's contents) from the href
  3069. extractContent: false,
  3070. // parseOnLoad: Boolean
  3071. // Parse content and create the widgets, if any.
  3072. parseOnLoad: true,
  3073. // parserScope: String
  3074. // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
  3075. // will search for data-dojo-type (or dojoType). For backwards compatibility
  3076. // reasons defaults to dojo._scopeName (which is "dojo" except when
  3077. // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  3078. parserScope: dojo._scopeName,
  3079. // preventCache: Boolean
  3080. // Prevent caching of data from href's by appending a timestamp to the href.
  3081. preventCache: false,
  3082. // preload: Boolean
  3083. // Force load of data on initialization even if pane is hidden.
  3084. preload: false,
  3085. // refreshOnShow: Boolean
  3086. // Refresh (re-download) content when pane goes from hidden to shown
  3087. refreshOnShow: false,
  3088. // loadingMessage: String
  3089. // Message that shows while downloading
  3090. loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
  3091. // errorMessage: String
  3092. // Message that shows if an error occurs
  3093. errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
  3094. // isLoaded: [readonly] Boolean
  3095. // True if the ContentPane has data in it, either specified
  3096. // during initialization (via href or inline content), or set
  3097. // via set('content', ...) / set('href', ...)
  3098. //
  3099. // False if it doesn't have any content, or if ContentPane is
  3100. // still in the process of downloading href.
  3101. isLoaded: false,
  3102. baseClass: "dijitContentPane",
  3103. // ioArgs: Object
  3104. // Parameters to pass to xhrGet() request, for example:
  3105. // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
  3106. ioArgs: {},
  3107. // onLoadDeferred: [readonly] dojo.Deferred
  3108. // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
  3109. // Calling onLoadDeferred.addCallback() or addErrback() registers your
  3110. // callback to be called only once, when the prior set('href', ...) call or
  3111. // the initial href parameter to the constructor finishes loading.
  3112. //
  3113. // This is different than an onLoad() handler which gets called any time any href
  3114. // or content is loaded.
  3115. onLoadDeferred: null,
  3116. // Override _Widget's attributeMap because we don't want the title attribute (used to specify
  3117. // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
  3118. // entire pane.
  3119. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  3120. title: []
  3121. }),
  3122. // Flag to parser that I'll parse my contents, so it shouldn't.
  3123. stopParser: true,
  3124. // template: [private] Boolean
  3125. // Flag from the parser that this ContentPane is inside a template
  3126. // so the contents are pre-parsed.
  3127. // (TODO: this declaration can be commented out in 2.0)
  3128. template: false,
  3129. create: function(params, srcNodeRef){
  3130. // Convert a srcNodeRef argument into a content parameter, so that the original contents are
  3131. // processed in the same way as contents set via set("content", ...), calling the parser etc.
  3132. // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
  3133. if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
  3134. var df = dojo.doc.createDocumentFragment();
  3135. srcNodeRef = dojo.byId(srcNodeRef)
  3136. while(srcNodeRef.firstChild){
  3137. df.appendChild(srcNodeRef.firstChild);
  3138. }
  3139. params = dojo.delegate(params, {content: df});
  3140. }
  3141. this.inherited(arguments, [params, srcNodeRef]);
  3142. },
  3143. postMixInProperties: function(){
  3144. this.inherited(arguments);
  3145. var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
  3146. this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
  3147. this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
  3148. },
  3149. buildRendering: function(){
  3150. this.inherited(arguments);
  3151. // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
  3152. // For subclasses of ContentPane that do have a template, does nothing.
  3153. if(!this.containerNode){
  3154. this.containerNode = this.domNode;
  3155. }
  3156. // remove the title attribute so it doesn't show up when hovering
  3157. // over a node (TODO: remove in 2.0, no longer needed after #11490)
  3158. this.domNode.title = "";
  3159. if(!dojo.attr(this.domNode,"role")){
  3160. dijit.setWaiRole(this.domNode, "group");
  3161. }
  3162. },
  3163. _startChildren: function(){
  3164. // summary:
  3165. // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
  3166. // This starts all the widgets
  3167. this.inherited(arguments);
  3168. // And this catches stuff like dojo.dnd.Source
  3169. if(this._contentSetter){
  3170. dojo.forEach(this._contentSetter.parseResults, function(obj){
  3171. if(!obj._started && !obj._destroyed && dojo.isFunction(obj.startup)){
  3172. obj.startup();
  3173. obj._started = true;
  3174. }
  3175. }, this);
  3176. }
  3177. },
  3178. setHref: function(/*String|Uri*/ href){
  3179. // summary:
  3180. // Deprecated. Use set('href', ...) instead.
  3181. dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
  3182. return this.set("href", href);
  3183. },
  3184. _setHrefAttr: function(/*String|Uri*/ href){
  3185. // summary:
  3186. // Hook so set("href", ...) works.
  3187. // description:
  3188. // Reset the (external defined) content of this pane and replace with new url
  3189. // Note: It delays the download until widget is shown if preload is false.
  3190. // href:
  3191. // url to the page you want to get, must be within the same domain as your mainpage
  3192. // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
  3193. this.cancel();
  3194. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  3195. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  3196. this._set("href", href);
  3197. // _setHrefAttr() is called during creation and by the user, after creation.
  3198. // Assuming preload == false, only in the second case do we actually load the URL;
  3199. // otherwise it's done in startup(), and only if this widget is shown.
  3200. if(this.preload || (this._created && this._isShown())){
  3201. this._load();
  3202. }else{
  3203. // Set flag to indicate that href needs to be loaded the next time the
  3204. // ContentPane is made visible
  3205. this._hrefChanged = true;
  3206. }
  3207. return this.onLoadDeferred; // dojo.Deferred
  3208. },
  3209. setContent: function(/*String|DomNode|Nodelist*/data){
  3210. // summary:
  3211. // Deprecated. Use set('content', ...) instead.
  3212. dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
  3213. this.set("content", data);
  3214. },
  3215. _setContentAttr: function(/*String|DomNode|Nodelist*/data){
  3216. // summary:
  3217. // Hook to make set("content", ...) work.
  3218. // Replaces old content with data content, include style classes from old content
  3219. // data:
  3220. // the new Content may be String, DomNode or NodeList
  3221. //
  3222. // if data is a NodeList (or an array of nodes) nodes are copied
  3223. // so you can import nodes from another document implicitly
  3224. // clear href so we can't run refresh and clear content
  3225. // refresh should only work if we downloaded the content
  3226. this._set("href", "");
  3227. // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
  3228. this.cancel();
  3229. // Even though user is just setting content directly, still need to define an onLoadDeferred
  3230. // because the _onLoadHandler() handler is still getting called from setContent()
  3231. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  3232. if(this._created){
  3233. // For back-compat reasons, call onLoad() for set('content', ...)
  3234. // calls but not for content specified in srcNodeRef (ie: <div dojoType=ContentPane>...</div>)
  3235. // or as initialization parameter (ie: new ContentPane({content: ...})
  3236. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  3237. }
  3238. this._setContent(data || "");
  3239. this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
  3240. return this.onLoadDeferred; // dojo.Deferred
  3241. },
  3242. _getContentAttr: function(){
  3243. // summary:
  3244. // Hook to make get("content") work
  3245. return this.containerNode.innerHTML;
  3246. },
  3247. cancel: function(){
  3248. // summary:
  3249. // Cancels an in-flight download of content
  3250. if(this._xhrDfd && (this._xhrDfd.fired == -1)){
  3251. this._xhrDfd.cancel();
  3252. }
  3253. delete this._xhrDfd; // garbage collect
  3254. this.onLoadDeferred = null;
  3255. },
  3256. uninitialize: function(){
  3257. if(this._beingDestroyed){
  3258. this.cancel();
  3259. }
  3260. this.inherited(arguments);
  3261. },
  3262. destroyRecursive: function(/*Boolean*/ preserveDom){
  3263. // summary:
  3264. // Destroy the ContentPane and its contents
  3265. // if we have multiple controllers destroying us, bail after the first
  3266. if(this._beingDestroyed){
  3267. return;
  3268. }
  3269. this.inherited(arguments);
  3270. },
  3271. _onShow: function(){
  3272. // summary:
  3273. // Called when the ContentPane is made visible
  3274. // description:
  3275. // For a plain ContentPane, this is called on initialization, from startup().
  3276. // If the ContentPane is a hidden pane of a TabContainer etc., then it's
  3277. // called whenever the pane is made visible.
  3278. //
  3279. // Does necessary processing, including href download and layout/resize of
  3280. // child widget(s)
  3281. this.inherited(arguments);
  3282. if(this.href){
  3283. if(!this._xhrDfd && // if there's an href that isn't already being loaded
  3284. (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
  3285. ){
  3286. return this.refresh(); // If child has an href, promise that fires when the load is complete
  3287. }
  3288. }
  3289. },
  3290. refresh: function(){
  3291. // summary:
  3292. // [Re]download contents of href and display
  3293. // description:
  3294. // 1. cancels any currently in-flight requests
  3295. // 2. posts "loading..." message
  3296. // 3. sends XHR to download new data
  3297. // Cancel possible prior in-flight request
  3298. this.cancel();
  3299. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  3300. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  3301. this._load();
  3302. return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
  3303. },
  3304. _load: function(){
  3305. // summary:
  3306. // Load/reload the href specified in this.href
  3307. // display loading message
  3308. this._setContent(this.onDownloadStart(), true);
  3309. var self = this;
  3310. var getArgs = {
  3311. preventCache: (this.preventCache || this.refreshOnShow),
  3312. url: this.href,
  3313. handleAs: "text"
  3314. };
  3315. if(dojo.isObject(this.ioArgs)){
  3316. dojo.mixin(getArgs, this.ioArgs);
  3317. }
  3318. var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
  3319. hand.addCallback(function(html){
  3320. try{
  3321. self._isDownloaded = true;
  3322. self._setContent(html, false);
  3323. self.onDownloadEnd();
  3324. }catch(err){
  3325. self._onError('Content', err); // onContentError
  3326. }
  3327. delete self._xhrDfd;
  3328. return html;
  3329. });
  3330. hand.addErrback(function(err){
  3331. if(!hand.canceled){
  3332. // show error message in the pane
  3333. self._onError('Download', err); // onDownloadError
  3334. }
  3335. delete self._xhrDfd;
  3336. return err;
  3337. });
  3338. // Remove flag saying that a load is needed
  3339. delete this._hrefChanged;
  3340. },
  3341. _onLoadHandler: function(data){
  3342. // summary:
  3343. // This is called whenever new content is being loaded
  3344. this._set("isLoaded", true);
  3345. try{
  3346. this.onLoadDeferred.callback(data);
  3347. }catch(e){
  3348. console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
  3349. }
  3350. },
  3351. _onUnloadHandler: function(){
  3352. // summary:
  3353. // This is called whenever the content is being unloaded
  3354. this._set("isLoaded", false);
  3355. try{
  3356. this.onUnload();
  3357. }catch(e){
  3358. console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
  3359. }
  3360. },
  3361. destroyDescendants: function(){
  3362. // summary:
  3363. // Destroy all the widgets inside the ContentPane and empty containerNode
  3364. // Make sure we call onUnload (but only when the ContentPane has real content)
  3365. if(this.isLoaded){
  3366. this._onUnloadHandler();
  3367. }
  3368. // Even if this.isLoaded == false there might still be a "Loading..." message
  3369. // to erase, so continue...
  3370. // For historical reasons we need to delete all widgets under this.containerNode,
  3371. // even ones that the user has created manually.
  3372. var setter = this._contentSetter;
  3373. dojo.forEach(this.getChildren(), function(widget){
  3374. if(widget.destroyRecursive){
  3375. widget.destroyRecursive();
  3376. }
  3377. });
  3378. if(setter){
  3379. // Most of the widgets in setter.parseResults have already been destroyed, but
  3380. // things like Menu that have been moved to <body> haven't yet
  3381. dojo.forEach(setter.parseResults, function(widget){
  3382. if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
  3383. widget.destroyRecursive();
  3384. }
  3385. });
  3386. delete setter.parseResults;
  3387. }
  3388. // And then clear away all the DOM nodes
  3389. dojo.html._emptyNode(this.containerNode);
  3390. // Delete any state information we have about current contents
  3391. delete this._singleChild;
  3392. },
  3393. _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
  3394. // summary:
  3395. // Insert the content into the container node
  3396. // first get rid of child widgets
  3397. this.destroyDescendants();
  3398. // dojo.html.set will take care of the rest of the details
  3399. // we provide an override for the error handling to ensure the widget gets the errors
  3400. // configure the setter instance with only the relevant widget instance properties
  3401. // NOTE: unless we hook into attr, or provide property setters for each property,
  3402. // we need to re-configure the ContentSetter with each use
  3403. var setter = this._contentSetter;
  3404. if(! (setter && setter instanceof dojo.html._ContentSetter)){
  3405. setter = this._contentSetter = new dojo.html._ContentSetter({
  3406. node: this.containerNode,
  3407. _onError: dojo.hitch(this, this._onError),
  3408. onContentError: dojo.hitch(this, function(e){
  3409. // fires if a domfault occurs when we are appending this.errorMessage
  3410. // like for instance if domNode is a UL and we try append a DIV
  3411. var errMess = this.onContentError(e);
  3412. try{
  3413. this.containerNode.innerHTML = errMess;
  3414. }catch(e){
  3415. console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
  3416. }
  3417. })/*,
  3418. _onError */
  3419. });
  3420. };
  3421. var setterParams = dojo.mixin({
  3422. cleanContent: this.cleanContent,
  3423. extractContent: this.extractContent,
  3424. parseContent: this.parseOnLoad,
  3425. parserScope: this.parserScope,
  3426. startup: false,
  3427. dir: this.dir,
  3428. lang: this.lang
  3429. }, this._contentSetterParams || {});
  3430. setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
  3431. // setter params must be pulled afresh from the ContentPane each time
  3432. delete this._contentSetterParams;
  3433. if(this.doLayout){
  3434. this._checkIfSingleChild();
  3435. }
  3436. if(!isFakeContent){
  3437. if(this._started){
  3438. // Startup each top level child widget (and they will start their children, recursively)
  3439. this._startChildren();
  3440. // Call resize() on each of my child layout widgets,
  3441. // or resize() on my single child layout widget...
  3442. // either now (if I'm currently visible) or when I become visible
  3443. this._scheduleLayout();
  3444. }
  3445. this._onLoadHandler(cont);
  3446. }
  3447. },
  3448. _onError: function(type, err, consoleText){
  3449. this.onLoadDeferred.errback(err);
  3450. // shows user the string that is returned by on[type]Error
  3451. // override on[type]Error and return your own string to customize
  3452. var errText = this['on' + type + 'Error'].call(this, err);
  3453. if(consoleText){
  3454. console.error(consoleText, err);
  3455. }else if(errText){// a empty string won't change current content
  3456. this._setContent(errText, true);
  3457. }
  3458. },
  3459. // EVENT's, should be overide-able
  3460. onLoad: function(data){
  3461. // summary:
  3462. // Event hook, is called after everything is loaded and widgetified
  3463. // tags:
  3464. // callback
  3465. },
  3466. onUnload: function(){
  3467. // summary:
  3468. // Event hook, is called before old content is cleared
  3469. // tags:
  3470. // callback
  3471. },
  3472. onDownloadStart: function(){
  3473. // summary:
  3474. // Called before download starts.
  3475. // description:
  3476. // The string returned by this function will be the html
  3477. // that tells the user we are loading something.
  3478. // Override with your own function if you want to change text.
  3479. // tags:
  3480. // extension
  3481. return this.loadingMessage;
  3482. },
  3483. onContentError: function(/*Error*/ error){
  3484. // summary:
  3485. // Called on DOM faults, require faults etc. in content.
  3486. //
  3487. // In order to display an error message in the pane, return
  3488. // the error message from this method, as an HTML string.
  3489. //
  3490. // By default (if this method is not overriden), it returns
  3491. // nothing, so the error message is just printed to the console.
  3492. // tags:
  3493. // extension
  3494. },
  3495. onDownloadError: function(/*Error*/ error){
  3496. // summary:
  3497. // Called when download error occurs.
  3498. //
  3499. // In order to display an error message in the pane, return
  3500. // the error message from this method, as an HTML string.
  3501. //
  3502. // Default behavior (if this method is not overriden) is to display
  3503. // the error message inside the pane.
  3504. // tags:
  3505. // extension
  3506. return this.errorMessage;
  3507. },
  3508. onDownloadEnd: function(){
  3509. // summary:
  3510. // Called when download is finished.
  3511. // tags:
  3512. // callback
  3513. }
  3514. });
  3515. }
  3516. if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  3517. dojo._hasResource["dijit.TooltipDialog"] = true;
  3518. dojo.provide("dijit.TooltipDialog");
  3519. dojo.declare(
  3520. "dijit.TooltipDialog",
  3521. [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
  3522. {
  3523. // summary:
  3524. // Pops up a dialog that appears like a Tooltip
  3525. // title: String
  3526. // Description of tooltip dialog (required for a11y)
  3527. title: "",
  3528. // doLayout: [protected] Boolean
  3529. // Don't change this parameter from the default value.
  3530. // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
  3531. // is never a child of a layout container, nor can you specify the size of
  3532. // TooltipDialog in order to control the size of an inner widget.
  3533. doLayout: false,
  3534. // autofocus: Boolean
  3535. // A Toggle to modify the default focus behavior of a Dialog, which
  3536. // is to focus on the first dialog element after opening the dialog.
  3537. // False will disable autofocusing. Default: true
  3538. autofocus: true,
  3539. // baseClass: [protected] String
  3540. // The root className to use for the various states of this widget
  3541. baseClass: "dijitTooltipDialog",
  3542. // _firstFocusItem: [private] [readonly] DomNode
  3543. // The pointer to the first focusable node in the dialog.
  3544. // Set by `dijit._DialogMixin._getFocusItems`.
  3545. _firstFocusItem: null,
  3546. // _lastFocusItem: [private] [readonly] DomNode
  3547. // The pointer to which node has focus prior to our dialog.
  3548. // Set by `dijit._DialogMixin._getFocusItems`.
  3549. _lastFocusItem: null,
  3550. templateString: dojo.cache("dijit", "templates/TooltipDialog.html", "<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\"></div>\n</div>\n"),
  3551. _setTitleAttr: function(/*String*/ title){
  3552. this.containerNode.title = title;
  3553. this._set("title", title)
  3554. },
  3555. postCreate: function(){
  3556. this.inherited(arguments);
  3557. this.connect(this.containerNode, "onkeypress", "_onKey");
  3558. },
  3559. orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
  3560. // summary:
  3561. // Configure widget to be displayed in given position relative to the button.
  3562. // This is called from the dijit.popup code, and should not be called
  3563. // directly.
  3564. // tags:
  3565. // protected
  3566. var newC = "dijitTooltipAB" + (corner.charAt(1) == 'L' ? "Left" : "Right")
  3567. + " dijitTooltip"
  3568. + (corner.charAt(0) == 'T' ? "Below" : "Above");
  3569. dojo.replaceClass(this.domNode, newC, this._currentOrientClass || "");
  3570. this._currentOrientClass = newC;
  3571. },
  3572. focus: function(){
  3573. // summary:
  3574. // Focus on first field
  3575. this._getFocusItems(this.containerNode);
  3576. dijit.focus(this._firstFocusItem);
  3577. },
  3578. onOpen: function(/*Object*/ pos){
  3579. // summary:
  3580. // Called when dialog is displayed.
  3581. // This is called from the dijit.popup code, and should not be called directly.
  3582. // tags:
  3583. // protected
  3584. this.orient(this.domNode,pos.aroundCorner, pos.corner);
  3585. this._onShow(); // lazy load trigger
  3586. },
  3587. onClose: function(){
  3588. // summary:
  3589. // Called when dialog is hidden.
  3590. // This is called from the dijit.popup code, and should not be called directly.
  3591. // tags:
  3592. // protected
  3593. this.onHide();
  3594. },
  3595. _onKey: function(/*Event*/ evt){
  3596. // summary:
  3597. // Handler for keyboard events
  3598. // description:
  3599. // Keep keyboard focus in dialog; close dialog on escape key
  3600. // tags:
  3601. // private
  3602. var node = evt.target;
  3603. var dk = dojo.keys;
  3604. if(evt.charOrCode === dk.TAB){
  3605. this._getFocusItems(this.containerNode);
  3606. }
  3607. var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
  3608. if(evt.charOrCode == dk.ESCAPE){
  3609. // Use setTimeout to avoid crash on IE, see #10396.
  3610. setTimeout(dojo.hitch(this, "onCancel"), 0);
  3611. dojo.stopEvent(evt);
  3612. }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
  3613. if(!singleFocusItem){
  3614. dijit.focus(this._lastFocusItem); // send focus to last item in dialog
  3615. }
  3616. dojo.stopEvent(evt);
  3617. }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
  3618. if(!singleFocusItem){
  3619. dijit.focus(this._firstFocusItem); // send focus to first item in dialog
  3620. }
  3621. dojo.stopEvent(evt);
  3622. }else if(evt.charOrCode === dk.TAB){
  3623. // we want the browser's default tab handling to move focus
  3624. // but we don't want the tab to propagate upwards
  3625. evt.stopPropagation();
  3626. }
  3627. }
  3628. }
  3629. );
  3630. }
  3631. if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  3632. dojo._hasResource["dijit.Dialog"] = true;
  3633. dojo.provide("dijit.Dialog");
  3634. // dijit/TooltipDialog required for back-compat. TODO: remove in 2.0
  3635. /*=====
  3636. dijit._underlay = function(kwArgs){
  3637. // summary:
  3638. // A shared instance of a `dijit.DialogUnderlay`
  3639. //
  3640. // description:
  3641. // A shared instance of a `dijit.DialogUnderlay` created and
  3642. // used by `dijit.Dialog`, though never created until some Dialog
  3643. // or subclass thereof is shown.
  3644. };
  3645. =====*/
  3646. dojo.declare(
  3647. "dijit._DialogBase",
  3648. [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
  3649. {
  3650. // summary:
  3651. // A modal dialog Widget
  3652. //
  3653. // description:
  3654. // Pops up a modal dialog window, blocking access to the screen
  3655. // and also graying out the screen Dialog is extended from
  3656. // ContentPane so it supports all the same parameters (href, etc.)
  3657. //
  3658. // example:
  3659. // | <div dojoType="dijit.Dialog" href="test.html"></div>
  3660. //
  3661. // example:
  3662. // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
  3663. // | dojo.body().appendChild(foo.domNode);
  3664. // | foo.startup();
  3665. templateString: dojo.cache("dijit", "templates/Dialog.html", "<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div dojoAttachPoint=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t<span dojoAttachPoint=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"),
  3666. baseClass: "dijitDialog",
  3667. cssStateNodes: {
  3668. closeButtonNode: "dijitDialogCloseIcon"
  3669. },
  3670. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  3671. title: [
  3672. { node: "titleNode", type: "innerHTML" },
  3673. { node: "titleBar", type: "attribute" }
  3674. ],
  3675. "aria-describedby":""
  3676. }),
  3677. // open: [readonly] Boolean
  3678. // True if Dialog is currently displayed on screen.
  3679. open: false,
  3680. // duration: Integer
  3681. // The time in milliseconds it takes the dialog to fade in and out
  3682. duration: dijit.defaultDuration,
  3683. // refocus: Boolean
  3684. // A Toggle to modify the default focus behavior of a Dialog, which
  3685. // is to re-focus the element which had focus before being opened.
  3686. // False will disable refocusing. Default: true
  3687. refocus: true,
  3688. // autofocus: Boolean
  3689. // A Toggle to modify the default focus behavior of a Dialog, which
  3690. // is to focus on the first dialog element after opening the dialog.
  3691. // False will disable autofocusing. Default: true
  3692. autofocus: true,
  3693. // _firstFocusItem: [private readonly] DomNode
  3694. // The pointer to the first focusable node in the dialog.
  3695. // Set by `dijit._DialogMixin._getFocusItems`.
  3696. _firstFocusItem: null,
  3697. // _lastFocusItem: [private readonly] DomNode
  3698. // The pointer to which node has focus prior to our dialog.
  3699. // Set by `dijit._DialogMixin._getFocusItems`.
  3700. _lastFocusItem: null,
  3701. // doLayout: [protected] Boolean
  3702. // Don't change this parameter from the default value.
  3703. // This ContentPane parameter doesn't make sense for Dialog, since Dialog
  3704. // is never a child of a layout container, nor can you specify the size of
  3705. // Dialog in order to control the size of an inner widget.
  3706. doLayout: false,
  3707. // draggable: Boolean
  3708. // Toggles the moveable aspect of the Dialog. If true, Dialog
  3709. // can be dragged by it's title. If false it will remain centered
  3710. // in the viewport.
  3711. draggable: true,
  3712. //aria-describedby: String
  3713. // Allows the user to add an aria-describedby attribute onto the dialog. The value should
  3714. // be the id of the container element of text that describes the dialog purpose (usually
  3715. // the first text in the dialog).
  3716. // <div dojoType="dijit.Dialog" aria-describedby="intro" .....>
  3717. // <div id="intro">Introductory text</div>
  3718. // <div>rest of dialog contents</div>
  3719. // </div>
  3720. "aria-describedby":"",
  3721. postMixInProperties: function(){
  3722. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  3723. dojo.mixin(this, _nlsResources);
  3724. this.inherited(arguments);
  3725. },
  3726. postCreate: function(){
  3727. dojo.style(this.domNode, {
  3728. display: "none",
  3729. position:"absolute"
  3730. });
  3731. dojo.body().appendChild(this.domNode);
  3732. this.inherited(arguments);
  3733. this.connect(this, "onExecute", "hide");
  3734. this.connect(this, "onCancel", "hide");
  3735. this._modalconnects = [];
  3736. },
  3737. onLoad: function(){
  3738. // summary:
  3739. // Called when data has been loaded from an href.
  3740. // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
  3741. // but should *not* be overridden.
  3742. // tags:
  3743. // callback
  3744. // when href is specified we need to reposition the dialog after the data is loaded
  3745. // and find the focusable elements
  3746. this._position();
  3747. if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
  3748. this._getFocusItems(this.domNode);
  3749. dijit.focus(this._firstFocusItem);
  3750. }
  3751. this.inherited(arguments);
  3752. },
  3753. _endDrag: function(){
  3754. // summary:
  3755. // Called after dragging the Dialog. Saves the position of the dialog in the viewport
  3756. // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
  3757. // tags:
  3758. // private
  3759. var nodePosition = dojo.position(this.domNode),
  3760. viewport = dojo.window.getBox();
  3761. nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
  3762. nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
  3763. this._relativePosition = nodePosition;
  3764. this._position();
  3765. },
  3766. _setup: function(){
  3767. // summary:
  3768. // Stuff we need to do before showing the Dialog for the first
  3769. // time (but we defer it until right beforehand, for
  3770. // performance reasons).
  3771. // tags:
  3772. // private
  3773. var node = this.domNode;
  3774. if(this.titleBar && this.draggable){
  3775. this._moveable = (dojo.isIE == 6) ?
  3776. new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285
  3777. new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
  3778. this.connect(this._moveable, "onMoveStop", "_endDrag");
  3779. }else{
  3780. dojo.addClass(node,"dijitDialogFixed");
  3781. }
  3782. this.underlayAttrs = {
  3783. dialogId: this.id,
  3784. "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
  3785. };
  3786. },
  3787. _size: function(){
  3788. // summary:
  3789. // If necessary, shrink dialog contents so dialog fits in viewport
  3790. // tags:
  3791. // private
  3792. this._checkIfSingleChild();
  3793. // If we resized the dialog contents earlier, reset them back to original size, so
  3794. // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
  3795. // Need to do this before the dojo.marginBox(this.domNode) call below.
  3796. if(this._singleChild){
  3797. if(this._singleChildOriginalStyle){
  3798. this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
  3799. }
  3800. delete this._singleChildOriginalStyle;
  3801. }else{
  3802. dojo.style(this.containerNode, {
  3803. width:"auto",
  3804. height:"auto"
  3805. });
  3806. }
  3807. var mb = dojo._getMarginSize(this.domNode);
  3808. var viewport = dojo.window.getBox();
  3809. if(mb.w >= viewport.w || mb.h >= viewport.h){
  3810. // Reduce size of dialog contents so that dialog fits in viewport
  3811. var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
  3812. h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
  3813. if(this._singleChild && this._singleChild.resize){
  3814. this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
  3815. this._singleChild.resize({w: w, h: h});
  3816. }else{
  3817. dojo.style(this.containerNode, {
  3818. width: w + "px",
  3819. height: h + "px",
  3820. overflow: "auto",
  3821. position: "relative" // workaround IE bug moving scrollbar or dragging dialog
  3822. });
  3823. }
  3824. }else{
  3825. if(this._singleChild && this._singleChild.resize){
  3826. this._singleChild.resize();
  3827. }
  3828. }
  3829. },
  3830. _position: function(){
  3831. // summary:
  3832. // Position modal dialog in the viewport. If no relative offset
  3833. // in the viewport has been determined (by dragging, for instance),
  3834. // center the node. Otherwise, use the Dialog's stored relative offset,
  3835. // and position the node to top: left: values based on the viewport.
  3836. // tags:
  3837. // private
  3838. if(!dojo.hasClass(dojo.body(), "dojoMove")){ // don't do anything if called during auto-scroll
  3839. var node = this.domNode,
  3840. viewport = dojo.window.getBox(),
  3841. p = this._relativePosition,
  3842. bb = p ? null : dojo._getBorderBox(node),
  3843. l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
  3844. t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
  3845. ;
  3846. dojo.style(node,{
  3847. left: l + "px",
  3848. top: t + "px"
  3849. });
  3850. }
  3851. },
  3852. _onKey: function(/*Event*/ evt){
  3853. // summary:
  3854. // Handles the keyboard events for accessibility reasons
  3855. // tags:
  3856. // private
  3857. if(evt.charOrCode){
  3858. var dk = dojo.keys;
  3859. var node = evt.target;
  3860. if(evt.charOrCode === dk.TAB){
  3861. this._getFocusItems(this.domNode);
  3862. }
  3863. var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
  3864. // see if we are shift-tabbing from first focusable item on dialog
  3865. if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
  3866. if(!singleFocusItem){
  3867. dijit.focus(this._lastFocusItem); // send focus to last item in dialog
  3868. }
  3869. dojo.stopEvent(evt);
  3870. }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
  3871. if(!singleFocusItem){
  3872. dijit.focus(this._firstFocusItem); // send focus to first item in dialog
  3873. }
  3874. dojo.stopEvent(evt);
  3875. }else{
  3876. // see if the key is for the dialog
  3877. while(node){
  3878. if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
  3879. if(evt.charOrCode == dk.ESCAPE){
  3880. this.onCancel();
  3881. }else{
  3882. return; // just let it go
  3883. }
  3884. }
  3885. node = node.parentNode;
  3886. }
  3887. // this key is for the disabled document window
  3888. if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
  3889. dojo.stopEvent(evt);
  3890. // opera won't tab to a div
  3891. }else if(!dojo.isOpera){
  3892. try{
  3893. this._firstFocusItem.focus();
  3894. }catch(e){ /*squelch*/ }
  3895. }
  3896. }
  3897. }
  3898. },
  3899. show: function(){
  3900. // summary:
  3901. // Display the dialog
  3902. // returns: dojo.Deferred
  3903. // Deferred object that resolves when the display animation is complete
  3904. if(this.open){ return; }
  3905. if(!this._started){
  3906. this.startup();
  3907. }
  3908. // first time we show the dialog, there's some initialization stuff to do
  3909. if(!this._alreadyInitialized){
  3910. this._setup();
  3911. this._alreadyInitialized=true;
  3912. }
  3913. if(this._fadeOutDeferred){
  3914. // There's a hide() operation in progress, so cancel it, but still call DialogLevelManager.hide()
  3915. // as though the hide() completed, in preparation for the DialogLevelManager.show() call below.
  3916. this._fadeOutDeferred.cancel();
  3917. dijit._DialogLevelManager.hide(this);
  3918. }
  3919. this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
  3920. this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
  3921. // IE gives spurious resize events and can actually get stuck
  3922. // in an infinite loop if we don't ignore them
  3923. var viewport = dojo.window.getBox();
  3924. if(!this._oldViewport ||
  3925. viewport.h != this._oldViewport.h ||
  3926. viewport.w != this._oldViewport.w){
  3927. this.layout();
  3928. this._oldViewport = viewport;
  3929. }
  3930. }));
  3931. this._modalconnects.push(dojo.connect(this.domNode, "onkeypress", this, "_onKey"));
  3932. dojo.style(this.domNode, {
  3933. opacity:0,
  3934. display:""
  3935. });
  3936. this._set("open", true);
  3937. this._onShow(); // lazy load trigger
  3938. this._size();
  3939. this._position();
  3940. // fade-in Animation object, setup below
  3941. var fadeIn;
  3942. this._fadeInDeferred = new dojo.Deferred(dojo.hitch(this, function(){
  3943. fadeIn.stop();
  3944. delete this._fadeInDeferred;
  3945. }));
  3946. fadeIn = dojo.fadeIn({
  3947. node: this.domNode,
  3948. duration: this.duration,
  3949. beforeBegin: dojo.hitch(this, function(){
  3950. dijit._DialogLevelManager.show(this, this.underlayAttrs);
  3951. }),
  3952. onEnd: dojo.hitch(this, function(){
  3953. if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
  3954. // find focusable items each time dialog is shown since if dialog contains a widget the
  3955. // first focusable items can change
  3956. this._getFocusItems(this.domNode);
  3957. dijit.focus(this._firstFocusItem);
  3958. }
  3959. this._fadeInDeferred.callback(true);
  3960. delete this._fadeInDeferred;
  3961. })
  3962. }).play();
  3963. return this._fadeInDeferred;
  3964. },
  3965. hide: function(){
  3966. // summary:
  3967. // Hide the dialog
  3968. // returns: dojo.Deferred
  3969. // Deferred object that resolves when the hide animation is complete
  3970. // If we haven't been initialized yet then we aren't showing and we can just return.
  3971. // Likewise if we are already hidden, or are currently fading out.
  3972. if(!this._alreadyInitialized || !this.open){
  3973. return;
  3974. }
  3975. if(this._fadeInDeferred){
  3976. this._fadeInDeferred.cancel();
  3977. }
  3978. // fade-in Animation object, setup below
  3979. var fadeOut;
  3980. this._fadeOutDeferred = new dojo.Deferred(dojo.hitch(this, function(){
  3981. fadeOut.stop();
  3982. delete this._fadeOutDeferred;
  3983. }));
  3984. fadeOut = dojo.fadeOut({
  3985. node: this.domNode,
  3986. duration: this.duration,
  3987. onEnd: dojo.hitch(this, function(){
  3988. this.domNode.style.display = "none";
  3989. dijit._DialogLevelManager.hide(this);
  3990. this.onHide();
  3991. this._fadeOutDeferred.callback(true);
  3992. delete this._fadeOutDeferred;
  3993. })
  3994. }).play();
  3995. if(this._scrollConnected){
  3996. this._scrollConnected = false;
  3997. }
  3998. dojo.forEach(this._modalconnects, dojo.disconnect);
  3999. this._modalconnects = [];
  4000. if(this._relativePosition){
  4001. delete this._relativePosition;
  4002. }
  4003. this._set("open", false);
  4004. return this._fadeOutDeferred;
  4005. },
  4006. layout: function(){
  4007. // summary:
  4008. // Position the Dialog and the underlay
  4009. // tags:
  4010. // private
  4011. if(this.domNode.style.display != "none"){
  4012. if(dijit._underlay){ // avoid race condition during show()
  4013. dijit._underlay.layout();
  4014. }
  4015. this._position();
  4016. }
  4017. },
  4018. destroy: function(){
  4019. if(this._fadeInDeferred){
  4020. this._fadeInDeferred.cancel();
  4021. }
  4022. if(this._fadeOutDeferred){
  4023. this._fadeOutDeferred.cancel();
  4024. }
  4025. if(this._moveable){
  4026. this._moveable.destroy();
  4027. }
  4028. dojo.forEach(this._modalconnects, dojo.disconnect);
  4029. dijit._DialogLevelManager.hide(this);
  4030. this.inherited(arguments);
  4031. }
  4032. }
  4033. );
  4034. dojo.declare(
  4035. "dijit.Dialog",
  4036. [dijit.layout.ContentPane, dijit._DialogBase],
  4037. {}
  4038. );
  4039. dijit._DialogLevelManager = {
  4040. // summary:
  4041. // Controls the various active "levels" on the page, starting with the
  4042. // stuff initially visible on the page (at z-index 0), and then having an entry for
  4043. // each Dialog shown.
  4044. show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
  4045. // summary:
  4046. // Call right before fade-in animation for new dialog.
  4047. // Saves current focus, displays/adjusts underlay for new dialog,
  4048. // and sets the z-index of the dialog itself.
  4049. //
  4050. // New dialog will be displayed on top of all currently displayed dialogs.
  4051. //
  4052. // Caller is responsible for setting focus in new dialog after the fade-in
  4053. // animation completes.
  4054. var ds = dijit._dialogStack;
  4055. // Save current focus
  4056. ds[ds.length-1].focus = dijit.getFocus(dialog);
  4057. // Display the underlay, or if already displayed then adjust for this new dialog
  4058. var underlay = dijit._underlay;
  4059. if(!underlay || underlay._destroyed){
  4060. underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs);
  4061. }else{
  4062. underlay.set(dialog.underlayAttrs);
  4063. }
  4064. // Set z-index a bit above previous dialog
  4065. var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : 950;
  4066. if(ds.length == 1){ // first dialog
  4067. underlay.show();
  4068. }
  4069. dojo.style(dijit._underlay.domNode, 'zIndex', zIndex - 1);
  4070. // Dialog
  4071. dojo.style(dialog.domNode, 'zIndex', zIndex);
  4072. ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
  4073. },
  4074. hide: function(/*dijit._Widget*/ dialog){
  4075. // summary:
  4076. // Called when the specified dialog is hidden/destroyed, after the fade-out
  4077. // animation ends, in order to reset page focus, fix the underlay, etc.
  4078. // If the specified dialog isn't open then does nothing.
  4079. //
  4080. // Caller is responsible for either setting display:none on the dialog domNode,
  4081. // or calling dijit.popup.hide(), or removing it from the page DOM.
  4082. var ds = dijit._dialogStack;
  4083. if(ds[ds.length-1].dialog == dialog){
  4084. // Removing the top (or only) dialog in the stack, return focus
  4085. // to previous dialog
  4086. ds.pop();
  4087. var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
  4088. // Adjust underlay
  4089. if(ds.length == 1){
  4090. // Returning to original page.
  4091. // Hide the underlay, unless the underlay widget has already been destroyed
  4092. // because we are being called during page unload (when all widgets are destroyed)
  4093. if(!dijit._underlay._destroyed){
  4094. dijit._underlay.hide();
  4095. }
  4096. }else{
  4097. // Popping back to previous dialog, adjust underlay
  4098. dojo.style(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
  4099. dijit._underlay.set(pd.underlayAttrs);
  4100. }
  4101. // Adjust focus
  4102. if(dialog.refocus){
  4103. // If we are returning control to a previous dialog but for some reason
  4104. // that dialog didn't have a focused field, set focus to first focusable item.
  4105. // This situation could happen if two dialogs appeared at nearly the same time,
  4106. // since a dialog doesn't set it's focus until the fade-in is finished.
  4107. var focus = pd.focus;
  4108. if(!focus || (pd.dialog && !dojo.isDescendant(focus.node, pd.dialog.domNode))){
  4109. pd.dialog._getFocusItems(pd.dialog.domNode);
  4110. focus = pd.dialog._firstFocusItem;
  4111. }
  4112. try{
  4113. dijit.focus(focus);
  4114. }catch(e){
  4115. /* focus() will fail if user opened the dialog by clicking a non-focusable element */
  4116. }
  4117. }
  4118. }else{
  4119. // Removing a dialog out of order (#9944, #10705).
  4120. // Don't need to mess with underlay or z-index or anything.
  4121. var idx = dojo.indexOf(dojo.map(ds, function(elem){return elem.dialog}), dialog);
  4122. if(idx != -1){
  4123. ds.splice(idx, 1);
  4124. }
  4125. }
  4126. },
  4127. isTop: function(/*dijit._Widget*/ dialog){
  4128. // summary:
  4129. // Returns true if specified Dialog is the top in the task
  4130. var ds = dijit._dialogStack;
  4131. return ds[ds.length-1].dialog == dialog;
  4132. }
  4133. };
  4134. // Stack representing the various active "levels" on the page, starting with the
  4135. // stuff initially visible on the page (at z-index 0), and then having an entry for
  4136. // each Dialog shown.
  4137. // Each element in stack has form {
  4138. // dialog: dialogWidget,
  4139. // focus: returnFromGetFocus(),
  4140. // underlayAttrs: attributes to set on underlay (when this widget is active)
  4141. // }
  4142. dijit._dialogStack = [
  4143. {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
  4144. ];
  4145. }
  4146. if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4147. dojo._hasResource["dijit._editor.selection"] = true;
  4148. dojo.provide("dijit._editor.selection");
  4149. dojo.getObject("_editor.selection", true, dijit);
  4150. // FIXME:
  4151. // all of these methods branch internally for IE. This is probably
  4152. // sub-optimal in terms of runtime performance. We should investigate the
  4153. // size difference for differentiating at definition time.
  4154. dojo.mixin(dijit._editor.selection, {
  4155. getType: function(){
  4156. // summary:
  4157. // Get the selection type (like dojo.doc.select.type in IE).
  4158. if(dojo.isIE < 9){
  4159. return dojo.doc.selection.type.toLowerCase();
  4160. }else{
  4161. var stype = "text";
  4162. // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
  4163. var oSel;
  4164. try{
  4165. oSel = dojo.global.getSelection();
  4166. }catch(e){ /*squelch*/ }
  4167. if(oSel && oSel.rangeCount == 1){
  4168. var oRange = oSel.getRangeAt(0);
  4169. if( (oRange.startContainer == oRange.endContainer) &&
  4170. ((oRange.endOffset - oRange.startOffset) == 1) &&
  4171. (oRange.startContainer.nodeType != 3 /* text node*/)
  4172. ){
  4173. stype = "control";
  4174. }
  4175. }
  4176. return stype; //String
  4177. }
  4178. },
  4179. getSelectedText: function(){
  4180. // summary:
  4181. // Return the text (no html tags) included in the current selection or null if no text is selected
  4182. if(dojo.isIE < 9){
  4183. if(dijit._editor.selection.getType() == 'control'){
  4184. return null;
  4185. }
  4186. return dojo.doc.selection.createRange().text;
  4187. }else{
  4188. var selection = dojo.global.getSelection();
  4189. if(selection){
  4190. return selection.toString(); //String
  4191. }
  4192. }
  4193. return '';
  4194. },
  4195. getSelectedHtml: function(){
  4196. // summary:
  4197. // Return the html text of the current selection or null if unavailable
  4198. if(dojo.isIE < 9){
  4199. if(dijit._editor.selection.getType() == 'control'){
  4200. return null;
  4201. }
  4202. return dojo.doc.selection.createRange().htmlText;
  4203. }else{
  4204. var selection = dojo.global.getSelection();
  4205. if(selection && selection.rangeCount){
  4206. var i;
  4207. var html = "";
  4208. for(i = 0; i < selection.rangeCount; i++){
  4209. //Handle selections spanning ranges, such as Opera
  4210. var frag = selection.getRangeAt(i).cloneContents();
  4211. var div = dojo.doc.createElement("div");
  4212. div.appendChild(frag);
  4213. html += div.innerHTML;
  4214. }
  4215. return html; //String
  4216. }
  4217. return null;
  4218. }
  4219. },
  4220. getSelectedElement: function(){
  4221. // summary:
  4222. // Retrieves the selected element (if any), just in the case that
  4223. // a single element (object like and image or a table) is
  4224. // selected.
  4225. if(dijit._editor.selection.getType() == "control"){
  4226. if(dojo.isIE < 9){
  4227. var range = dojo.doc.selection.createRange();
  4228. if(range && range.item){
  4229. return dojo.doc.selection.createRange().item(0);
  4230. }
  4231. }else{
  4232. var selection = dojo.global.getSelection();
  4233. return selection.anchorNode.childNodes[ selection.anchorOffset ];
  4234. }
  4235. }
  4236. return null;
  4237. },
  4238. getParentElement: function(){
  4239. // summary:
  4240. // Get the parent element of the current selection
  4241. if(dijit._editor.selection.getType() == "control"){
  4242. var p = this.getSelectedElement();
  4243. if(p){ return p.parentNode; }
  4244. }else{
  4245. if(dojo.isIE < 9){
  4246. var r = dojo.doc.selection.createRange();
  4247. r.collapse(true);
  4248. return r.parentElement();
  4249. }else{
  4250. var selection = dojo.global.getSelection();
  4251. if(selection){
  4252. var node = selection.anchorNode;
  4253. while(node && (node.nodeType != 1)){ // not an element
  4254. node = node.parentNode;
  4255. }
  4256. return node;
  4257. }
  4258. }
  4259. }
  4260. return null;
  4261. },
  4262. hasAncestorElement: function(/*String*/tagName /* ... */){
  4263. // summary:
  4264. // Check whether current selection has a parent element which is
  4265. // of type tagName (or one of the other specified tagName)
  4266. // tagName: String
  4267. // The tag name to determine if it has an ancestor of.
  4268. return this.getAncestorElement.apply(this, arguments) != null; //Boolean
  4269. },
  4270. getAncestorElement: function(/*String*/tagName /* ... */){
  4271. // summary:
  4272. // Return the parent element of the current selection which is of
  4273. // type tagName (or one of the other specified tagName)
  4274. // tagName: String
  4275. // The tag name to determine if it has an ancestor of.
  4276. var node = this.getSelectedElement() || this.getParentElement();
  4277. return this.getParentOfType(node, arguments); //DOMNode
  4278. },
  4279. isTag: function(/*DomNode*/ node, /*String[]*/ tags){
  4280. // summary:
  4281. // Function to determine if a node is one of an array of tags.
  4282. // node:
  4283. // The node to inspect.
  4284. // tags:
  4285. // An array of tag name strings to check to see if the node matches.
  4286. if(node && node.tagName){
  4287. var _nlc = node.tagName.toLowerCase();
  4288. for(var i=0; i<tags.length; i++){
  4289. var _tlc = String(tags[i]).toLowerCase();
  4290. if(_nlc == _tlc){
  4291. return _tlc; // String
  4292. }
  4293. }
  4294. }
  4295. return "";
  4296. },
  4297. getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){
  4298. // summary:
  4299. // Function to locate a parent node that matches one of a set of tags
  4300. // node:
  4301. // The node to inspect.
  4302. // tags:
  4303. // An array of tag name strings to check to see if the node matches.
  4304. while(node){
  4305. if(this.isTag(node, tags).length){
  4306. return node; // DOMNode
  4307. }
  4308. node = node.parentNode;
  4309. }
  4310. return null;
  4311. },
  4312. collapse: function(/*Boolean*/beginning){
  4313. // summary:
  4314. // Function to collapse (clear), the current selection
  4315. // beginning: Boolean
  4316. // Boolean to indicate whether to collapse the cursor to the beginning of the selection or end.
  4317. if(window.getSelection){
  4318. var selection = dojo.global.getSelection();
  4319. if(selection.removeAllRanges){ // Mozilla
  4320. if(beginning){
  4321. selection.collapseToStart();
  4322. }else{
  4323. selection.collapseToEnd();
  4324. }
  4325. }else{ // Safari
  4326. // pulled from WebCore/ecma/kjs_window.cpp, line 2536
  4327. selection.collapse(beginning);
  4328. }
  4329. }else if(dojo.isIE){ // IE
  4330. var range = dojo.doc.selection.createRange();
  4331. range.collapse(beginning);
  4332. range.select();
  4333. }
  4334. },
  4335. remove: function(){
  4336. // summary:
  4337. // Function to delete the currently selected content from the document.
  4338. var sel = dojo.doc.selection;
  4339. if(dojo.isIE < 9){
  4340. if(sel.type.toLowerCase() != "none"){
  4341. sel.clear();
  4342. }
  4343. return sel; //Selection
  4344. }else{
  4345. sel = dojo.global.getSelection();
  4346. sel.deleteFromDocument();
  4347. return sel; //Selection
  4348. }
  4349. },
  4350. selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  4351. // summary:
  4352. // clear previous selection and select the content of the node
  4353. // (excluding the node itself)
  4354. // element: DOMNode
  4355. // The element you wish to select the children content of.
  4356. // nochangefocus: Boolean
  4357. // Boolean to indicate if the foxus should change or not.
  4358. var win = dojo.global;
  4359. var doc = dojo.doc;
  4360. var range;
  4361. element = dojo.byId(element);
  4362. if(doc.selection && dojo.isIE < 9 && dojo.body().createTextRange){ // IE
  4363. range = element.ownerDocument.body.createTextRange();
  4364. range.moveToElementText(element);
  4365. if(!nochangefocus){
  4366. try{
  4367. range.select(); // IE throws an exception here if the widget is hidden. See #5439
  4368. }catch(e){ /* squelch */}
  4369. }
  4370. }else if(win.getSelection){
  4371. var selection = dojo.global.getSelection();
  4372. if(dojo.isOpera){
  4373. //Opera's selectAllChildren doesn't seem to work right
  4374. //against <body> nodes and possibly others ... so
  4375. //we use the W3C range API
  4376. if(selection.rangeCount){
  4377. range = selection.getRangeAt(0);
  4378. }else{
  4379. range = doc.createRange();
  4380. }
  4381. range.setStart(element, 0);
  4382. range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length);
  4383. selection.addRange(range);
  4384. }else{
  4385. selection.selectAllChildren(element);
  4386. }
  4387. }
  4388. },
  4389. selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  4390. // summary:
  4391. // clear previous selection and select element (including all its children)
  4392. // element: DOMNode
  4393. // The element to select.
  4394. // nochangefocus: Boolean
  4395. // Boolean indicating if the focus should be changed. IE only.
  4396. var range;
  4397. var doc = dojo.doc;
  4398. var win = dojo.global;
  4399. element = dojo.byId(element);
  4400. if(dojo.isIE < 9 && dojo.body().createTextRange){
  4401. try{
  4402. var tg = element.tagName ? element.tagName.toLowerCase() : "";
  4403. if(tg === "img" || tg === "table"){
  4404. range = dojo.body().createControlRange();
  4405. }else{
  4406. range = dojo.body().createRange();
  4407. }
  4408. range.addElement(element);
  4409. if(!nochangefocus){
  4410. range.select();
  4411. }
  4412. }catch(e){
  4413. this.selectElementChildren(element,nochangefocus);
  4414. }
  4415. }else if(dojo.global.getSelection){
  4416. var selection = win.getSelection();
  4417. range = doc.createRange();
  4418. if(selection.removeAllRanges){ // Mozilla
  4419. // FIXME: does this work on Safari?
  4420. if(dojo.isOpera){
  4421. //Opera works if you use the current range on
  4422. //the selection if present.
  4423. if(selection.getRangeAt(0)){
  4424. range = selection.getRangeAt(0);
  4425. }
  4426. }
  4427. range.selectNode(element);
  4428. selection.removeAllRanges();
  4429. selection.addRange(range);
  4430. }
  4431. }
  4432. },
  4433. inSelection: function(node){
  4434. // summary:
  4435. // This function determines if 'node' is
  4436. // in the current selection.
  4437. // tags:
  4438. // public
  4439. if(node){
  4440. var newRange;
  4441. var doc = dojo.doc;
  4442. var range;
  4443. if(dojo.global.getSelection){
  4444. //WC3
  4445. var sel = dojo.global.getSelection();
  4446. if(sel && sel.rangeCount > 0){
  4447. range = sel.getRangeAt(0);
  4448. }
  4449. if(range && range.compareBoundaryPoints && doc.createRange){
  4450. try{
  4451. newRange = doc.createRange();
  4452. newRange.setStart(node, 0);
  4453. if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){
  4454. return true;
  4455. }
  4456. }catch(e){ /* squelch */}
  4457. }
  4458. }else if(doc.selection){
  4459. // Probably IE, so we can't use the range object as the pseudo
  4460. // range doesn't implement the boundry checking, we have to
  4461. // use IE specific crud.
  4462. range = doc.selection.createRange();
  4463. try{
  4464. newRange = node.ownerDocument.body.createControlRange();
  4465. if(newRange){
  4466. newRange.addElement(node);
  4467. }
  4468. }catch(e1){
  4469. try{
  4470. newRange = node.ownerDocument.body.createTextRange();
  4471. newRange.moveToElementText(node);
  4472. }catch(e2){/* squelch */}
  4473. }
  4474. if(range && newRange){
  4475. // We can finally compare similar to W3C
  4476. if(range.compareEndPoints("EndToStart", newRange) === 1){
  4477. return true;
  4478. }
  4479. }
  4480. }
  4481. }
  4482. return false; // boolean
  4483. }
  4484. });
  4485. }
  4486. if(!dojo._hasResource["dijit._editor.range"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4487. dojo._hasResource["dijit._editor.range"] = true;
  4488. dojo.provide("dijit._editor.range");
  4489. dijit.range={};
  4490. dijit.range.getIndex=function(/*DomNode*/node, /*DomNode*/parent){
  4491. // dojo.profile.start("dijit.range.getIndex");
  4492. var ret=[], retR=[];
  4493. var stop = parent;
  4494. var onode = node;
  4495. var pnode, n;
  4496. while(node != stop){
  4497. var i = 0;
  4498. pnode = node.parentNode;
  4499. while((n=pnode.childNodes[i++])){
  4500. if(n === node){
  4501. --i;
  4502. break;
  4503. }
  4504. }
  4505. //if(i>=pnode.childNodes.length){
  4506. //dojo.debug("Error finding index of a node in dijit.range.getIndex");
  4507. //}
  4508. ret.unshift(i);
  4509. retR.unshift(i-pnode.childNodes.length);
  4510. node = pnode;
  4511. }
  4512. //normalized() can not be called so often to prevent
  4513. //invalidating selection/range, so we have to detect
  4514. //here that any text nodes in a row
  4515. if(ret.length > 0 && onode.nodeType == 3){
  4516. n = onode.previousSibling;
  4517. while(n && n.nodeType == 3){
  4518. ret[ret.length-1]--;
  4519. n = n.previousSibling;
  4520. }
  4521. n = onode.nextSibling;
  4522. while(n && n.nodeType == 3){
  4523. retR[retR.length-1]++;
  4524. n = n.nextSibling;
  4525. }
  4526. }
  4527. // dojo.profile.end("dijit.range.getIndex");
  4528. return {o: ret, r:retR};
  4529. }
  4530. dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){
  4531. if(!dojo.isArray(index) || index.length == 0){
  4532. return parent;
  4533. }
  4534. var node = parent;
  4535. // if(!node)debugger
  4536. dojo.every(index, function(i){
  4537. if(i >= 0 && i < node.childNodes.length){
  4538. node = node.childNodes[i];
  4539. }else{
  4540. node = null;
  4541. //console.debug('Error: can not find node with index',index,'under parent node',parent );
  4542. return false; //terminate dojo.every
  4543. }
  4544. return true; //carry on the every loop
  4545. });
  4546. return node;
  4547. }
  4548. dijit.range.getCommonAncestor = function(n1,n2,root){
  4549. root = root||n1.ownerDocument.body;
  4550. var getAncestors = function(n){
  4551. var as=[];
  4552. while(n){
  4553. as.unshift(n);
  4554. if(n !== root){
  4555. n = n.parentNode;
  4556. }else{
  4557. break;
  4558. }
  4559. }
  4560. return as;
  4561. };
  4562. var n1as = getAncestors(n1);
  4563. var n2as = getAncestors(n2);
  4564. var m = Math.min(n1as.length,n2as.length);
  4565. var com = n1as[0]; //at least, one element should be in the array: the root (BODY by default)
  4566. for(var i=1;i<m;i++){
  4567. if(n1as[i] === n2as[i]){
  4568. com = n1as[i]
  4569. }else{
  4570. break;
  4571. }
  4572. }
  4573. return com;
  4574. }
  4575. dijit.range.getAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
  4576. root = root || node.ownerDocument.body;
  4577. while(node && node !== root){
  4578. var name = node.nodeName.toUpperCase() ;
  4579. if(regex.test(name)){
  4580. return node;
  4581. }
  4582. node = node.parentNode;
  4583. }
  4584. return null;
  4585. }
  4586. dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
  4587. dijit.range.getBlockAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
  4588. root = root || node.ownerDocument.body;
  4589. regex = regex || dijit.range.BlockTagNames;
  4590. var block=null, blockContainer;
  4591. while(node && node !== root){
  4592. var name = node.nodeName.toUpperCase() ;
  4593. if(!block && regex.test(name)){
  4594. block = node;
  4595. }
  4596. if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){
  4597. blockContainer = node;
  4598. }
  4599. node = node.parentNode;
  4600. }
  4601. return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body};
  4602. }
  4603. dijit.range.atBeginningOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
  4604. var atBeginning = false;
  4605. var offsetAtBeginning = (offset == 0);
  4606. if(!offsetAtBeginning && node.nodeType == 3){ //if this is a text node, check whether the left part is all space
  4607. if(/^[\s\xA0]+$/.test(node.nodeValue.substr(0,offset))){
  4608. offsetAtBeginning = true;
  4609. }
  4610. }
  4611. if(offsetAtBeginning){
  4612. var cnode = node;
  4613. atBeginning = true;
  4614. while(cnode && cnode !== container){
  4615. if(cnode.previousSibling){
  4616. atBeginning = false;
  4617. break;
  4618. }
  4619. cnode = cnode.parentNode;
  4620. }
  4621. }
  4622. return atBeginning;
  4623. }
  4624. dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
  4625. var atEnd = false;
  4626. var offsetAtEnd = (offset == (node.length || node.childNodes.length));
  4627. if(!offsetAtEnd && node.nodeType == 3){ //if this is a text node, check whether the right part is all space
  4628. if(/^[\s\xA0]+$/.test(node.nodeValue.substr(offset))){
  4629. offsetAtEnd = true;
  4630. }
  4631. }
  4632. if(offsetAtEnd){
  4633. var cnode = node;
  4634. atEnd = true;
  4635. while(cnode && cnode !== container){
  4636. if(cnode.nextSibling){
  4637. atEnd = false;
  4638. break;
  4639. }
  4640. cnode = cnode.parentNode;
  4641. }
  4642. }
  4643. return atEnd;
  4644. }
  4645. dijit.range.adjacentNoneTextNode=function(startnode, next){
  4646. var node = startnode;
  4647. var len = (0-startnode.length) || 0;
  4648. var prop = next?'nextSibling':'previousSibling';
  4649. while(node){
  4650. if(node.nodeType!=3){
  4651. break;
  4652. }
  4653. len += node.length
  4654. node = node[prop];
  4655. }
  4656. return [node,len];
  4657. }
  4658. dijit.range._w3c = Boolean(window['getSelection']);
  4659. dijit.range.create = function(/*Window?*/win){
  4660. if(dijit.range._w3c){
  4661. return (win || dojo.global).document.createRange();
  4662. }else{//IE
  4663. return new dijit.range.W3CRange;
  4664. }
  4665. }
  4666. dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){
  4667. if(dijit.range._w3c){
  4668. return win.getSelection();
  4669. }else{//IE
  4670. var s = new dijit.range.ie.selection(win);
  4671. if(!ignoreUpdate){
  4672. s._getCurrentSelection();
  4673. }
  4674. return s;
  4675. }
  4676. }
  4677. if(!dijit.range._w3c){
  4678. dijit.range.ie={
  4679. cachedSelection: {},
  4680. selection: function(win){
  4681. this._ranges = [];
  4682. this.addRange = function(r, /*boolean*/internal){
  4683. this._ranges.push(r);
  4684. if(!internal){
  4685. r._select();
  4686. }
  4687. this.rangeCount = this._ranges.length;
  4688. };
  4689. this.removeAllRanges = function(){
  4690. //don't detach, the range may be used later
  4691. // for(var i=0;i<this._ranges.length;i++){
  4692. // this._ranges[i].detach();
  4693. // }
  4694. this._ranges = [];
  4695. this.rangeCount = 0;
  4696. };
  4697. var _initCurrentRange = function(){
  4698. var r = win.document.selection.createRange();
  4699. var type=win.document.selection.type.toUpperCase();
  4700. if(type == "CONTROL"){
  4701. //TODO: multiple range selection(?)
  4702. return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
  4703. }else{
  4704. return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
  4705. }
  4706. };
  4707. this.getRangeAt = function(i){
  4708. return this._ranges[i];
  4709. };
  4710. this._getCurrentSelection = function(){
  4711. this.removeAllRanges();
  4712. var r=_initCurrentRange();
  4713. if(r){
  4714. this.addRange(r, true);
  4715. }
  4716. };
  4717. },
  4718. decomposeControlRange: function(range){
  4719. var firstnode = range.item(0), lastnode = range.item(range.length-1);
  4720. var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
  4721. var startOffset = dijit.range.getIndex(firstnode, startContainer).o;
  4722. var endOffset = dijit.range.getIndex(lastnode, endContainer).o+1;
  4723. return [startContainer, startOffset,endContainer, endOffset];
  4724. },
  4725. getEndPoint: function(range, end){
  4726. var atmrange = range.duplicate();
  4727. atmrange.collapse(!end);
  4728. var cmpstr = 'EndTo' + (end?'End':'Start');
  4729. var parentNode = atmrange.parentElement();
  4730. var startnode, startOffset, lastNode;
  4731. if(parentNode.childNodes.length>0){
  4732. dojo.every(parentNode.childNodes, function(node,i){
  4733. var calOffset;
  4734. if(node.nodeType != 3){
  4735. atmrange.moveToElementText(node);
  4736. if(atmrange.compareEndPoints(cmpstr,range) > 0){
  4737. //startnode = node.previousSibling;
  4738. if(lastNode && lastNode.nodeType == 3){
  4739. //where shall we put the start? in the text node or after?
  4740. startnode = lastNode;
  4741. calOffset = true;
  4742. }else{
  4743. startnode = parentNode;
  4744. startOffset = i;
  4745. return false;
  4746. }
  4747. }else{
  4748. if(i == parentNode.childNodes.length-1){
  4749. startnode = parentNode;
  4750. startOffset = parentNode.childNodes.length;
  4751. return false;
  4752. }
  4753. }
  4754. }else{
  4755. if(i == parentNode.childNodes.length-1){//at the end of this node
  4756. startnode = node;
  4757. calOffset = true;
  4758. }
  4759. }
  4760. // try{
  4761. if(calOffset && startnode){
  4762. var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
  4763. if(prevnode){
  4764. startnode = prevnode.nextSibling;
  4765. }else{
  4766. startnode = parentNode.firstChild; //firstChild must be a text node
  4767. }
  4768. var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
  4769. prevnode = prevnodeobj[0];
  4770. var lenoffset = prevnodeobj[1];
  4771. if(prevnode){
  4772. atmrange.moveToElementText(prevnode);
  4773. atmrange.collapse(false);
  4774. }else{
  4775. atmrange.moveToElementText(parentNode);
  4776. }
  4777. atmrange.setEndPoint(cmpstr, range);
  4778. startOffset = atmrange.text.length-lenoffset;
  4779. return false;
  4780. }
  4781. // }catch(e){ debugger }
  4782. lastNode = node;
  4783. return true;
  4784. });
  4785. }else{
  4786. startnode = parentNode;
  4787. startOffset = 0;
  4788. }
  4789. //if at the end of startnode and we are dealing with start container, then
  4790. //move the startnode to nextSibling if it is a text node
  4791. //TODO: do this for end container?
  4792. if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){
  4793. var nextnode=startnode.nextSibling;
  4794. if(nextnode && nextnode.nodeType == 3){
  4795. startnode = nextnode;
  4796. startOffset = 0;
  4797. }
  4798. }
  4799. return [startnode, startOffset];
  4800. },
  4801. setEndPoint: function(range, container, offset){
  4802. //text node
  4803. var atmrange = range.duplicate(), node, len;
  4804. if(container.nodeType!=3){ //normal node
  4805. if(offset > 0){
  4806. node = container.childNodes[offset-1];
  4807. if(node){
  4808. if(node.nodeType == 3){
  4809. container = node;
  4810. offset = node.length;
  4811. //pass through
  4812. }else{
  4813. if(node.nextSibling && node.nextSibling.nodeType == 3){
  4814. container=node.nextSibling;
  4815. offset=0;
  4816. //pass through
  4817. }else{
  4818. atmrange.moveToElementText(node.nextSibling?node:container);
  4819. var parent = node.parentNode;
  4820. var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling);
  4821. atmrange.collapse(false);
  4822. parent.removeChild(tempNode);
  4823. }
  4824. }
  4825. }
  4826. }else{
  4827. atmrange.moveToElementText(container);
  4828. atmrange.collapse(true);
  4829. }
  4830. }
  4831. if(container.nodeType == 3){
  4832. var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
  4833. var prevnode = prevnodeobj[0];
  4834. len = prevnodeobj[1];
  4835. if(prevnode){
  4836. atmrange.moveToElementText(prevnode);
  4837. atmrange.collapse(false);
  4838. //if contentEditable is not inherit, the above collapse won't make the end point
  4839. //in the correctly position: it always has a -1 offset, so compensate it
  4840. if(prevnode.contentEditable!='inherit'){
  4841. len++;
  4842. }
  4843. }else{
  4844. atmrange.moveToElementText(container.parentNode);
  4845. atmrange.collapse(true);
  4846. }
  4847. offset += len;
  4848. if(offset>0){
  4849. if(atmrange.move('character',offset) != offset){
  4850. console.error('Error when moving!');
  4851. }
  4852. }
  4853. }
  4854. return atmrange;
  4855. },
  4856. decomposeTextRange: function(range){
  4857. var tmpary = dijit.range.ie.getEndPoint(range);
  4858. var startContainer = tmpary[0], startOffset = tmpary[1];
  4859. var endContainer = tmpary[0], endOffset = tmpary[1];
  4860. if(range.htmlText.length){
  4861. if(range.htmlText == range.text){ //in the same text node
  4862. endOffset = startOffset+range.text.length;
  4863. }else{
  4864. tmpary = dijit.range.ie.getEndPoint(range,true);
  4865. endContainer = tmpary[0], endOffset = tmpary[1];
  4866. // if(startContainer.tagName == "BODY"){
  4867. // startContainer = startContainer.firstChild;
  4868. // }
  4869. }
  4870. }
  4871. return [startContainer, startOffset, endContainer, endOffset];
  4872. },
  4873. setRange: function(range, startContainer,
  4874. startOffset, endContainer, endOffset, collapsed){
  4875. var start=dijit.range.ie.setEndPoint(range, startContainer, startOffset);
  4876. range.setEndPoint('StartToStart',start);
  4877. if(!collapsed){
  4878. var end=dijit.range.ie.setEndPoint(range, endContainer, endOffset);
  4879. }
  4880. range.setEndPoint('EndToEnd',end || start);
  4881. return range;
  4882. }
  4883. }
  4884. dojo.declare("dijit.range.W3CRange",null, {
  4885. constructor: function(){
  4886. if(arguments.length>0){
  4887. this.setStart(arguments[0][0],arguments[0][1]);
  4888. this.setEnd(arguments[0][2],arguments[0][3]);
  4889. }else{
  4890. this.commonAncestorContainer = null;
  4891. this.startContainer = null;
  4892. this.startOffset = 0;
  4893. this.endContainer = null;
  4894. this.endOffset = 0;
  4895. this.collapsed = true;
  4896. }
  4897. },
  4898. _updateInternal: function(){
  4899. if(this.startContainer !== this.endContainer){
  4900. this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
  4901. }else{
  4902. this.commonAncestorContainer = this.startContainer;
  4903. }
  4904. this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
  4905. },
  4906. setStart: function(node, offset){
  4907. offset=parseInt(offset);
  4908. if(this.startContainer === node && this.startOffset == offset){
  4909. return;
  4910. }
  4911. delete this._cachedBookmark;
  4912. this.startContainer = node;
  4913. this.startOffset = offset;
  4914. if(!this.endContainer){
  4915. this.setEnd(node, offset);
  4916. }else{
  4917. this._updateInternal();
  4918. }
  4919. },
  4920. setEnd: function(node, offset){
  4921. offset=parseInt(offset);
  4922. if(this.endContainer === node && this.endOffset == offset){
  4923. return;
  4924. }
  4925. delete this._cachedBookmark;
  4926. this.endContainer = node;
  4927. this.endOffset = offset;
  4928. if(!this.startContainer){
  4929. this.setStart(node, offset);
  4930. }else{
  4931. this._updateInternal();
  4932. }
  4933. },
  4934. setStartAfter: function(node, offset){
  4935. this._setPoint('setStart', node, offset, 1);
  4936. },
  4937. setStartBefore: function(node, offset){
  4938. this._setPoint('setStart', node, offset, 0);
  4939. },
  4940. setEndAfter: function(node, offset){
  4941. this._setPoint('setEnd', node, offset, 1);
  4942. },
  4943. setEndBefore: function(node, offset){
  4944. this._setPoint('setEnd', node, offset, 0);
  4945. },
  4946. _setPoint: function(what, node, offset, ext){
  4947. var index = dijit.range.getIndex(node, node.parentNode).o;
  4948. this[what](node.parentNode, index.pop()+ext);
  4949. },
  4950. _getIERange: function(){
  4951. var r = (this._body || this.endContainer.ownerDocument.body).createTextRange();
  4952. dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed);
  4953. return r;
  4954. },
  4955. getBookmark: function(body){
  4956. this._getIERange();
  4957. return this._cachedBookmark;
  4958. },
  4959. _select: function(){
  4960. var r = this._getIERange();
  4961. r.select();
  4962. },
  4963. deleteContents: function(){
  4964. var r = this._getIERange();
  4965. r.pasteHTML('');
  4966. this.endContainer = this.startContainer;
  4967. this.endOffset = this.startOffset;
  4968. this.collapsed = true;
  4969. },
  4970. cloneRange: function(){
  4971. var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
  4972. this.endContainer,this.endOffset]);
  4973. r._body = this._body;
  4974. return r;
  4975. },
  4976. detach: function(){
  4977. this._body = null;
  4978. this.commonAncestorContainer = null;
  4979. this.startContainer = null;
  4980. this.startOffset = 0;
  4981. this.endContainer = null;
  4982. this.endOffset = 0;
  4983. this.collapsed = true;
  4984. }
  4985. });
  4986. } //if(!dijit.range._w3c)
  4987. }
  4988. if(!dojo._hasResource["dijit._editor.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4989. dojo._hasResource["dijit._editor.html"] = true;
  4990. dojo.provide("dijit._editor.html");
  4991. var exports = dojo.getObject("_editor", true, dijit);
  4992. var escape = exports.escapeXml=function(/*String*/str, /*Boolean?*/noSingleQuotes){
  4993. // summary:
  4994. // Adds escape sequences for special characters in XML: &<>"'
  4995. // Optionally skips escapes for single quotes
  4996. str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  4997. if(!noSingleQuotes){
  4998. str = str.replace(/'/gm, "&#39;");
  4999. }
  5000. return str; // string
  5001. };
  5002. exports.getNodeHtml = function(/*DomNode*/ node){
  5003. // summary:
  5004. // Return string representing HTML for node and it's children
  5005. var output = [];
  5006. exports.getNodeHtmlHelper(node, output);
  5007. return output.join("");
  5008. };
  5009. exports.getNodeHtmlHelper = function(/*DomNode*/ node, /*String[]*/ output){
  5010. // summary:
  5011. // Pushes array of strings into output[] which represent HTML for node and it's children
  5012. switch(node.nodeType){
  5013. case 1: //element node
  5014. var lName = node.nodeName.toLowerCase();
  5015. if(!lName || lName.charAt(0) == "/"){
  5016. // IE does some strange things with malformed HTML input, like
  5017. // treating a close tag </span> without an open tag <span>, as
  5018. // a new tag with tagName of /span. Corrupts output HTML, remove
  5019. // them. Other browsers don't prefix tags that way, so will
  5020. // never show up.
  5021. return "";
  5022. }
  5023. output.push('<', lName);
  5024. //store the list of attributes and sort it to have the
  5025. //attributes appear in the dictionary order
  5026. var attrarray = [];
  5027. var attr;
  5028. if(dojo.isIE < 9){
  5029. var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false);
  5030. var s = clone.outerHTML;
  5031. s = s.substr(0, s.indexOf('>'))
  5032. .replace(/(['"])[^"']*\1/g, ''); //to make the following regexp safe
  5033. var reg = /(\b\w+)\s?=/g;
  5034. var m, key;
  5035. while((m = reg.exec(s))){
  5036. key = m[1];
  5037. if(key.substr(0,3) != '_dj'){
  5038. if(key == 'src' || key == 'href'){
  5039. if(node.getAttribute('_djrealurl')){
  5040. attrarray.push([key,node.getAttribute('_djrealurl')]);
  5041. continue;
  5042. }
  5043. }
  5044. var val, match;
  5045. switch(key){
  5046. case 'style':
  5047. val = node.style.cssText.toLowerCase();
  5048. break;
  5049. case 'class':
  5050. val = node.className;
  5051. break;
  5052. case 'width':
  5053. if(lName === "img"){
  5054. // This somehow gets lost on IE for IMG tags and the like
  5055. // and we have to find it in outerHTML, known IE oddity.
  5056. match=/width=(\S+)/i.exec(s);
  5057. if(match){
  5058. val = match[1];
  5059. }
  5060. break;
  5061. }
  5062. case 'height':
  5063. if(lName === "img"){
  5064. // This somehow gets lost on IE for IMG tags and the like
  5065. // and we have to find it in outerHTML, known IE oddity.
  5066. match=/height=(\S+)/i.exec(s);
  5067. if(match){
  5068. val = match[1];
  5069. }
  5070. break;
  5071. }
  5072. default:
  5073. val = node.getAttribute(key);
  5074. }
  5075. if(val != null){
  5076. attrarray.push([key, val.toString()]);
  5077. }
  5078. }
  5079. }
  5080. }else{
  5081. var i = 0;
  5082. while((attr = node.attributes[i++])){
  5083. //ignore all attributes starting with _dj which are
  5084. //internal temporary attributes used by the editor
  5085. var n = attr.name;
  5086. if(n.substr(0,3) != '_dj' /*&&
  5087. (attr.specified == undefined || attr.specified)*/){
  5088. var v = attr.value;
  5089. if(n == 'src' || n == 'href'){
  5090. if(node.getAttribute('_djrealurl')){
  5091. v = node.getAttribute('_djrealurl');
  5092. }
  5093. }
  5094. attrarray.push([n,v]);
  5095. }
  5096. }
  5097. }
  5098. attrarray.sort(function(a,b){
  5099. return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1);
  5100. });
  5101. var j = 0;
  5102. while((attr = attrarray[j++])){
  5103. output.push(' ', attr[0], '="',
  5104. (dojo.isString(attr[1]) ? escape(attr[1], true) : attr[1]), '"');
  5105. }
  5106. switch(lName){
  5107. case 'br':
  5108. case 'hr':
  5109. case 'img':
  5110. case 'input':
  5111. case 'base':
  5112. case 'meta':
  5113. case 'area':
  5114. case 'basefont':
  5115. // These should all be singly closed
  5116. output.push(' />');
  5117. break;
  5118. case 'script':
  5119. // Browsers handle script tags differently in how you get content,
  5120. // but innerHTML always seems to work, so insert its content that way
  5121. // Yes, it's bad to allow script tags in the editor code, but some people
  5122. // seem to want to do it, so we need to at least return them right.
  5123. // other plugins/filters can strip them.
  5124. output.push('>', node.innerHTML, '</', lName, '>');
  5125. break;
  5126. default:
  5127. output.push('>');
  5128. if(node.hasChildNodes()){
  5129. exports.getChildrenHtmlHelper(node, output);
  5130. }
  5131. output.push('</', lName, '>');
  5132. }
  5133. break;
  5134. case 4: // cdata
  5135. case 3: // text
  5136. // FIXME:
  5137. output.push(escape(node.nodeValue, true));
  5138. break;
  5139. case 8: //comment
  5140. // FIXME:
  5141. output.push('<!--', escape(node.nodeValue, true), '-->');
  5142. break;
  5143. default:
  5144. output.push("<!-- Element not recognized - Type: ", node.nodeType, " Name: ", node.nodeName, "-->");
  5145. }
  5146. };
  5147. exports.getChildrenHtml = function(/*DomNode*/ node){
  5148. // summary:
  5149. // Returns the html content of a DomNode's children
  5150. var output = [];
  5151. exports.getChildrenHtmlHelper(node, output);
  5152. return output.join("");
  5153. };
  5154. exports.getChildrenHtmlHelper = function(/*DomNode*/ dom, /*String[]*/ output){
  5155. // summary:
  5156. // Pushes the html content of a DomNode's children into out[]
  5157. if(!dom){ return; }
  5158. var nodes = dom["childNodes"] || dom;
  5159. //IE issue.
  5160. //If we have an actual node we can check parent relationships on for IE,
  5161. //We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check
  5162. //And should just process it and hope for the best.
  5163. var checkParent = !dojo.isIE || nodes !== dom;
  5164. var node, i = 0;
  5165. while((node = nodes[i++])){
  5166. //IE is broken. DOMs are supposed to be a tree. But in the case of malformed HTML, IE generates a graph
  5167. //meaning one node ends up with multiple references (multiple parents). This is totally wrong and invalid, but
  5168. //such is what it is. We have to keep track and check for this because otherwise the source output HTML will have dups.
  5169. //No other browser generates a graph. Leave it to IE to break a fundamental DOM rule. So, we check the parent if we can
  5170. //If we can't, nothing more we can do other than walk it.
  5171. if(!checkParent || node.parentNode == dom){
  5172. exports.getNodeHtmlHelper(node, output);
  5173. }
  5174. }
  5175. };
  5176. }
  5177. if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  5178. dojo._hasResource["dijit._editor.RichText"] = true;
  5179. dojo.provide("dijit._editor.RichText");
  5180. // used to restore content when user leaves this page then comes back
  5181. // but do not try doing dojo.doc.write if we are using xd loading.
  5182. // dojo.doc.write will only work if RichText.js is included in the dojo.js
  5183. // file. If it is included in dojo.js and you want to allow rich text saving
  5184. // for back/forward actions, then set dojo.config.allowXdRichTextSave = true.
  5185. if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){
  5186. if(dojo._postLoad){
  5187. (function(){
  5188. var savetextarea = dojo.doc.createElement('textarea');
  5189. savetextarea.id = dijit._scopeName + "._editor.RichText.value";
  5190. dojo.style(savetextarea, {
  5191. display:'none',
  5192. position:'absolute',
  5193. top:"-100px",
  5194. height:"3px",
  5195. width:"3px"
  5196. });
  5197. dojo.body().appendChild(savetextarea);
  5198. })();
  5199. }else{
  5200. //dojo.body() is not available before onLoad is fired
  5201. try{
  5202. dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.value" ' +
  5203. 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
  5204. }catch(e){ }
  5205. }
  5206. }
  5207. dojo.declare("dijit._editor.RichText", [dijit._Widget, dijit._CssStateMixin], {
  5208. constructor: function(params){
  5209. // summary:
  5210. // dijit._editor.RichText is the core of dijit.Editor, which provides basic
  5211. // WYSIWYG editing features.
  5212. //
  5213. // description:
  5214. // dijit._editor.RichText is the core of dijit.Editor, which provides basic
  5215. // WYSIWYG editing features. It also encapsulates the differences
  5216. // of different js engines for various browsers. Do not use this widget
  5217. // with an HTML &lt;TEXTAREA&gt; tag, since the browser unescapes XML escape characters,
  5218. // like &lt;. This can have unexpected behavior and lead to security issues
  5219. // such as scripting attacks.
  5220. //
  5221. // tags:
  5222. // private
  5223. // contentPreFilters: Function(String)[]
  5224. // Pre content filter function register array.
  5225. // these filters will be executed before the actual
  5226. // editing area gets the html content.
  5227. this.contentPreFilters = [];
  5228. // contentPostFilters: Function(String)[]
  5229. // post content filter function register array.
  5230. // These will be used on the resulting html
  5231. // from contentDomPostFilters. The resulting
  5232. // content is the final html (returned by getValue()).
  5233. this.contentPostFilters = [];
  5234. // contentDomPreFilters: Function(DomNode)[]
  5235. // Pre content dom filter function register array.
  5236. // These filters are applied after the result from
  5237. // contentPreFilters are set to the editing area.
  5238. this.contentDomPreFilters = [];
  5239. // contentDomPostFilters: Function(DomNode)[]
  5240. // Post content dom filter function register array.
  5241. // These filters are executed on the editing area dom.
  5242. // The result from these will be passed to contentPostFilters.
  5243. this.contentDomPostFilters = [];
  5244. // editingAreaStyleSheets: dojo._URL[]
  5245. // array to store all the stylesheets applied to the editing area
  5246. this.editingAreaStyleSheets = [];
  5247. // Make a copy of this.events before we start writing into it, otherwise we
  5248. // will modify the prototype which leads to bad things on pages w/multiple editors
  5249. this.events = [].concat(this.events);
  5250. this._keyHandlers = {};
  5251. if(params && dojo.isString(params.value)){
  5252. this.value = params.value;
  5253. }
  5254. this.onLoadDeferred = new dojo.Deferred();
  5255. },
  5256. baseClass: "dijitEditor",
  5257. // inheritWidth: Boolean
  5258. // whether to inherit the parent's width or simply use 100%
  5259. inheritWidth: false,
  5260. // focusOnLoad: [deprecated] Boolean
  5261. // Focus into this widget when the page is loaded
  5262. focusOnLoad: false,
  5263. // name: String?
  5264. // Specifies the name of a (hidden) <textarea> node on the page that's used to save
  5265. // the editor content on page leave. Used to restore editor contents after navigating
  5266. // to a new page and then hitting the back button.
  5267. name: "",
  5268. // styleSheets: [const] String
  5269. // semicolon (";") separated list of css files for the editing area
  5270. styleSheets: "",
  5271. // height: String
  5272. // Set height to fix the editor at a specific height, with scrolling.
  5273. // By default, this is 300px. If you want to have the editor always
  5274. // resizes to accommodate the content, use AlwaysShowToolbar plugin
  5275. // and set height="". If this editor is used within a layout widget,
  5276. // set height="100%".
  5277. height: "300px",
  5278. // minHeight: String
  5279. // The minimum height that the editor should have.
  5280. minHeight: "1em",
  5281. // isClosed: [private] Boolean
  5282. isClosed: true,
  5283. // isLoaded: [private] Boolean
  5284. isLoaded: false,
  5285. // _SEPARATOR: [private] String
  5286. // Used to concat contents from multiple editors into a single string,
  5287. // so they can be saved into a single <textarea> node. See "name" attribute.
  5288. _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
  5289. // _NAME_CONTENT_SEP: [private] String
  5290. // USed to separate name from content. Just a colon isn't safe.
  5291. _NAME_CONTENT_SEP: "@@**%%:%%**@@",
  5292. // onLoadDeferred: [readonly] dojo.Deferred
  5293. // Deferred which is fired when the editor finishes loading.
  5294. // Call myEditor.onLoadDeferred.then(callback) it to be informed
  5295. // when the rich-text area initialization is finalized.
  5296. onLoadDeferred: null,
  5297. // isTabIndent: Boolean
  5298. // Make tab key and shift-tab indent and outdent rather than navigating.
  5299. // Caution: sing this makes web pages inaccessible to users unable to use a mouse.
  5300. isTabIndent: false,
  5301. // disableSpellCheck: [const] Boolean
  5302. // When true, disables the browser's native spell checking, if supported.
  5303. // Works only in Firefox.
  5304. disableSpellCheck: false,
  5305. postCreate: function(){
  5306. if("textarea" == this.domNode.tagName.toLowerCase()){
  5307. console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs.");
  5308. }
  5309. // Push in the builtin filters now, making them the first executed, but not over-riding anything
  5310. // users passed in. See: #6062
  5311. this.contentPreFilters = [dojo.hitch(this, "_preFixUrlAttributes")].concat(this.contentPreFilters);
  5312. if(dojo.isMoz){
  5313. this.contentPreFilters = [this._normalizeFontStyle].concat(this.contentPreFilters);
  5314. this.contentPostFilters = [this._removeMozBogus].concat(this.contentPostFilters);
  5315. }
  5316. if(dojo.isWebKit){
  5317. // Try to clean up WebKit bogus artifacts. The inserted classes
  5318. // made by WebKit sometimes messes things up.
  5319. this.contentPreFilters = [this._removeWebkitBogus].concat(this.contentPreFilters);
  5320. this.contentPostFilters = [this._removeWebkitBogus].concat(this.contentPostFilters);
  5321. }
  5322. if(dojo.isIE){
  5323. // IE generates <strong> and <em> but we want to normalize to <b> and <i>
  5324. this.contentPostFilters = [this._normalizeFontStyle].concat(this.contentPostFilters);
  5325. }
  5326. this.inherited(arguments);
  5327. dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]);
  5328. this.open();
  5329. this.setupDefaultShortcuts();
  5330. },
  5331. setupDefaultShortcuts: function(){
  5332. // summary:
  5333. // Add some default key handlers
  5334. // description:
  5335. // Overwrite this to setup your own handlers. The default
  5336. // implementation does not use Editor commands, but directly
  5337. // executes the builtin commands within the underlying browser
  5338. // support.
  5339. // tags:
  5340. // protected
  5341. var exec = dojo.hitch(this, function(cmd, arg){
  5342. return function(){
  5343. return !this.execCommand(cmd,arg);
  5344. };
  5345. });
  5346. var ctrlKeyHandlers = {
  5347. b: exec("bold"),
  5348. i: exec("italic"),
  5349. u: exec("underline"),
  5350. a: exec("selectall"),
  5351. s: function(){ this.save(true); },
  5352. m: function(){ this.isTabIndent = !this.isTabIndent; },
  5353. "1": exec("formatblock", "h1"),
  5354. "2": exec("formatblock", "h2"),
  5355. "3": exec("formatblock", "h3"),
  5356. "4": exec("formatblock", "h4"),
  5357. "\\": exec("insertunorderedlist")
  5358. };
  5359. if(!dojo.isIE){
  5360. ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
  5361. }
  5362. for(var key in ctrlKeyHandlers){
  5363. this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]);
  5364. }
  5365. },
  5366. // events: [private] String[]
  5367. // events which should be connected to the underlying editing area
  5368. events: ["onKeyPress", "onKeyDown", "onKeyUp"], // onClick handled specially
  5369. // captureEvents: [deprecated] String[]
  5370. // Events which should be connected to the underlying editing
  5371. // area, events in this array will be addListener with
  5372. // capture=true.
  5373. // TODO: looking at the code I don't see any distinction between events and captureEvents,
  5374. // so get rid of this for 2.0 if not sooner
  5375. captureEvents: [],
  5376. _editorCommandsLocalized: false,
  5377. _localizeEditorCommands: function(){
  5378. // summary:
  5379. // When IE is running in a non-English locale, the API actually changes,
  5380. // so that we have to say (for example) danraku instead of p (for paragraph).
  5381. // Handle that here.
  5382. // tags:
  5383. // private
  5384. if(dijit._editor._editorCommandsLocalized){
  5385. // Use the already generate cache of mappings.
  5386. this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames;
  5387. this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames;
  5388. return;
  5389. }
  5390. dijit._editor._editorCommandsLocalized = true;
  5391. dijit._editor._local2NativeFormatNames = {};
  5392. dijit._editor._native2LocalFormatNames = {};
  5393. this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames;
  5394. this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames;
  5395. //in IE, names for blockformat is locale dependent, so we cache the values here
  5396. //put p after div, so if IE returns Normal, we show it as paragraph
  5397. //We can distinguish p and div if IE returns Normal, however, in order to detect that,
  5398. //we have to call this.document.selection.createRange().parentElement() or such, which
  5399. //could slow things down. Leave it as it is for now
  5400. var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address'];
  5401. var localhtml = "", format, i=0;
  5402. while((format=formats[i++])){
  5403. //append a <br> after each element to separate the elements more reliably
  5404. if(format.charAt(1) !== 'l'){
  5405. localhtml += "<"+format+"><span>content</span></"+format+"><br/>";
  5406. }else{
  5407. localhtml += "<"+format+"><li>content</li></"+format+"><br/>";
  5408. }
  5409. }
  5410. // queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
  5411. // Also, IE9 does weird stuff unless we do it inside the editor iframe.
  5412. var style = { position: "absolute", top: "0px", zIndex: 10, opacity: 0.01 };
  5413. var div = dojo.create('div', {style: style, innerHTML: localhtml});
  5414. dojo.body().appendChild(div);
  5415. // IE9 has a timing issue with doing this right after setting
  5416. // the inner HTML, so put a delay in.
  5417. var inject = dojo.hitch(this, function(){
  5418. var node = div.firstChild;
  5419. while(node){
  5420. try{
  5421. dijit._editor.selection.selectElement(node.firstChild);
  5422. var nativename = node.tagName.toLowerCase();
  5423. this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");
  5424. this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
  5425. node = node.nextSibling.nextSibling;
  5426. //console.log("Mapped: ", nativename, " to: ", this._local2NativeFormatNames[nativename]);
  5427. }catch(e) { /*Sqelch the occasional IE9 error */ }
  5428. }
  5429. div.parentNode.removeChild(div);
  5430. div.innerHTML = "";
  5431. });
  5432. setTimeout(inject, 0);
  5433. },
  5434. open: function(/*DomNode?*/ element){
  5435. // summary:
  5436. // Transforms the node referenced in this.domNode into a rich text editing
  5437. // node.
  5438. // description:
  5439. // Sets up the editing area asynchronously. This will result in
  5440. // the creation and replacement with an iframe.
  5441. // tags:
  5442. // private
  5443. if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){
  5444. this.onLoadDeferred = new dojo.Deferred();
  5445. }
  5446. if(!this.isClosed){ this.close(); }
  5447. dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]);
  5448. if(arguments.length == 1 && element.nodeName){ // else unchanged
  5449. this.domNode = element;
  5450. }
  5451. var dn = this.domNode;
  5452. // "html" will hold the innerHTML of the srcNodeRef and will be used to
  5453. // initialize the editor.
  5454. var html;
  5455. if(dojo.isString(this.value)){
  5456. // Allow setting the editor content programmatically instead of
  5457. // relying on the initial content being contained within the target
  5458. // domNode.
  5459. html = this.value;
  5460. delete this.value;
  5461. dn.innerHTML = "";
  5462. }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){
  5463. // if we were created from a textarea, then we need to create a
  5464. // new editing harness node.
  5465. var ta = (this.textarea = dn);
  5466. this.name = ta.name;
  5467. html = ta.value;
  5468. dn = this.domNode = dojo.doc.createElement("div");
  5469. dn.setAttribute('widgetId', this.id);
  5470. ta.removeAttribute('widgetId');
  5471. dn.cssText = ta.cssText;
  5472. dn.className += " " + ta.className;
  5473. dojo.place(dn, ta, "before");
  5474. var tmpFunc = dojo.hitch(this, function(){
  5475. //some browsers refuse to submit display=none textarea, so
  5476. //move the textarea off screen instead
  5477. dojo.style(ta, {
  5478. display: "block",
  5479. position: "absolute",
  5480. top: "-1000px"
  5481. });
  5482. if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
  5483. var s = ta.style;
  5484. this.__overflow = s.overflow;
  5485. s.overflow = "hidden";
  5486. }
  5487. });
  5488. if(dojo.isIE){
  5489. setTimeout(tmpFunc, 10);
  5490. }else{
  5491. tmpFunc();
  5492. }
  5493. if(ta.form){
  5494. var resetValue = ta.value;
  5495. this.reset = function(){
  5496. var current = this.getValue();
  5497. if(current != resetValue){
  5498. this.replaceValue(resetValue);
  5499. }
  5500. };
  5501. dojo.connect(ta.form, "onsubmit", this, function(){
  5502. // Copy value to the <textarea> so it gets submitted along with form.
  5503. // FIXME: should we be calling close() here instead?
  5504. dojo.attr(ta, 'disabled', this.disabled); // don't submit the value if disabled
  5505. ta.value = this.getValue();
  5506. });
  5507. }
  5508. }else{
  5509. html = dijit._editor.getChildrenHtml(dn);
  5510. dn.innerHTML = "";
  5511. }
  5512. var content = dojo.contentBox(dn);
  5513. this._oldHeight = content.h;
  5514. this._oldWidth = content.w;
  5515. this.value = html;
  5516. // If we're a list item we have to put in a blank line to force the
  5517. // bullet to nicely align at the top of text
  5518. if(dn.nodeName && dn.nodeName == "LI"){
  5519. dn.innerHTML = " <br>";
  5520. }
  5521. // Construct the editor div structure.
  5522. this.header = dn.ownerDocument.createElement("div");
  5523. dn.appendChild(this.header);
  5524. this.editingArea = dn.ownerDocument.createElement("div");
  5525. dn.appendChild(this.editingArea);
  5526. this.footer = dn.ownerDocument.createElement("div");
  5527. dn.appendChild(this.footer);
  5528. if(!this.name){
  5529. this.name = this.id + "_AUTOGEN";
  5530. }
  5531. // User has pressed back/forward button so we lost the text in the editor, but it's saved
  5532. // in a hidden <textarea> (which contains the data for all the editors on this page),
  5533. // so get editor value from there
  5534. if(this.name !== "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){
  5535. var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value");
  5536. if(saveTextarea && saveTextarea.value !== ""){
  5537. var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
  5538. while((dat=datas[i++])){
  5539. var data = dat.split(this._NAME_CONTENT_SEP);
  5540. if(data[0] == this.name){
  5541. html = data[1];
  5542. datas = datas.splice(i, 1);
  5543. saveTextarea.value = datas.join(this._SEPARATOR);
  5544. break;
  5545. }
  5546. }
  5547. }
  5548. if(!dijit._editor._globalSaveHandler){
  5549. dijit._editor._globalSaveHandler = {};
  5550. dojo.addOnUnload(function() {
  5551. var id;
  5552. for(id in dijit._editor._globalSaveHandler){
  5553. var f = dijit._editor._globalSaveHandler[id];
  5554. if(dojo.isFunction(f)){
  5555. f();
  5556. }
  5557. }
  5558. });
  5559. }
  5560. dijit._editor._globalSaveHandler[this.id] = dojo.hitch(this, "_saveContent");
  5561. }
  5562. this.isClosed = false;
  5563. var ifr = (this.editorObject = this.iframe = dojo.doc.createElement('iframe'));
  5564. ifr.id = this.id+"_iframe";
  5565. this._iframeSrc = this._getIframeDocTxt();
  5566. ifr.style.border = "none";
  5567. ifr.style.width = "100%";
  5568. if(this._layoutMode){
  5569. // iframe should be 100% height, thus getting it's height from surrounding
  5570. // <div> (which has the correct height set by Editor)
  5571. ifr.style.height = "100%";
  5572. }else{
  5573. if(dojo.isIE >= 7){
  5574. if(this.height){
  5575. ifr.style.height = this.height;
  5576. }
  5577. if(this.minHeight){
  5578. ifr.style.minHeight = this.minHeight;
  5579. }
  5580. }else{
  5581. ifr.style.height = this.height ? this.height : this.minHeight;
  5582. }
  5583. }
  5584. ifr.frameBorder = 0;
  5585. ifr._loadFunc = dojo.hitch( this, function(win){
  5586. this.window = win;
  5587. this.document = this.window.document;
  5588. if(dojo.isIE){
  5589. this._localizeEditorCommands();
  5590. }
  5591. // Do final setup and set initial contents of editor
  5592. this.onLoad(html);
  5593. });
  5594. // Set the iframe's initial (blank) content.
  5595. var s = 'javascript:parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc';
  5596. ifr.setAttribute('src', s);
  5597. this.editingArea.appendChild(ifr);
  5598. if(dojo.isSafari <= 4){
  5599. var src = ifr.getAttribute("src");
  5600. if(!src || src.indexOf("javascript") == -1){
  5601. // Safari 4 and earlier sometimes act oddly
  5602. // So we have to set it again.
  5603. setTimeout(function(){ifr.setAttribute('src', s);},0);
  5604. }
  5605. }
  5606. // TODO: this is a guess at the default line-height, kinda works
  5607. if(dn.nodeName == "LI"){
  5608. dn.lastChild.style.marginTop = "-1.2em";
  5609. }
  5610. dojo.addClass(this.domNode, this.baseClass);
  5611. },
  5612. //static cache variables shared among all instance of this class
  5613. _local2NativeFormatNames: {},
  5614. _native2LocalFormatNames: {},
  5615. _getIframeDocTxt: function(){
  5616. // summary:
  5617. // Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>).
  5618. // Editor content (if not blank) should be added afterwards.
  5619. // tags:
  5620. // private
  5621. var _cs = dojo.getComputedStyle(this.domNode);
  5622. // The contents inside of <body>. The real contents are set later via a call to setValue().
  5623. var html = "";
  5624. var setBodyId = true;
  5625. if(dojo.isIE || dojo.isWebKit || (!this.height && !dojo.isMoz)){
  5626. // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly
  5627. // expand/contract the editor as the content changes.
  5628. html = "<div id='dijitEditorBody'></div>";
  5629. setBodyId = false;
  5630. }else if(dojo.isMoz){
  5631. // workaround bug where can't select then delete text (until user types something
  5632. // into the editor)... and/or issue where typing doesn't erase selected text
  5633. this._cursorToStart = true;
  5634. html = "&nbsp;";
  5635. }
  5636. var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
  5637. // line height is tricky - applying a units value will mess things up.
  5638. // if we can't get a non-units value, bail out.
  5639. var lineHeight = _cs.lineHeight;
  5640. if(lineHeight.indexOf("px") >= 0){
  5641. lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
  5642. // console.debug(lineHeight);
  5643. }else if(lineHeight.indexOf("em")>=0){
  5644. lineHeight = parseFloat(lineHeight);
  5645. }else{
  5646. // If we can't get a non-units value, just default
  5647. // it to the CSS spec default of 'normal'. Seems to
  5648. // work better, esp on IE, than '1.0'
  5649. lineHeight = "normal";
  5650. }
  5651. var userStyle = "";
  5652. var self = this;
  5653. this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){
  5654. match = match.replace(/^;/ig,"") + ';';
  5655. var s = match.split(":")[0];
  5656. if(s){
  5657. s = dojo.trim(s);
  5658. s = s.toLowerCase();
  5659. var i;
  5660. var sC = "";
  5661. for(i = 0; i < s.length; i++){
  5662. var c = s.charAt(i);
  5663. switch(c){
  5664. case "-":
  5665. i++;
  5666. c = s.charAt(i).toUpperCase();
  5667. default:
  5668. sC += c;
  5669. }
  5670. }
  5671. dojo.style(self.domNode, sC, "");
  5672. }
  5673. userStyle += match + ';';
  5674. });
  5675. // need to find any associated label element and update iframe document title
  5676. var label=dojo.query('label[for="'+this.id+'"]');
  5677. return [
  5678. this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n",
  5679. (dojo.isMoz && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""),
  5680. "<meta http-equiv='Content-Type' content='text/html'>\n",
  5681. "<style>\n",
  5682. "\tbody,html {\n",
  5683. "\t\tbackground:transparent;\n",
  5684. "\t\tpadding: 1px 0 0 0;\n",
  5685. "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox
  5686. // Set the html/body sizing. Webkit always needs this, other browsers
  5687. // only set it when height is defined (not auto-expanding), otherwise
  5688. // scrollers do not appear.
  5689. ((dojo.isWebKit)?"\t\twidth: 100%;\n":""),
  5690. ((dojo.isWebKit)?"\t\theight: 100%;\n":""),
  5691. "\t}\n",
  5692. // TODO: left positioning will cause contents to disappear out of view
  5693. // if it gets too wide for the visible area
  5694. "\tbody{\n",
  5695. "\t\ttop:0px;\n",
  5696. "\t\tleft:0px;\n",
  5697. "\t\tright:0px;\n",
  5698. "\t\tfont:", font, ";\n",
  5699. ((this.height||dojo.isOpera) ? "" : "\t\tposition: fixed;\n"),
  5700. // FIXME: IE 6 won't understand min-height?
  5701. "\t\tmin-height:", this.minHeight, ";\n",
  5702. "\t\tline-height:", lineHeight,";\n",
  5703. "\t}\n",
  5704. "\tp{ margin: 1em 0; }\n",
  5705. // Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all.
  5706. // But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand
  5707. // (Mainly IE) we need to kill the y scroller on body and html.
  5708. (!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""),
  5709. "\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + " outline: 0px;}\n",
  5710. "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n",
  5711. // Can't set min-height in IE9, it puts layout on li, which puts move/resize handles.
  5712. (!dojo.isIE ? "\tli{ min-height:1.2em; }\n" : ""),
  5713. "</style>\n",
  5714. this._applyEditingAreaStyleSheets(),"\n",
  5715. "</head>\n<body ",
  5716. (setBodyId?"id='dijitEditorBody' ":""),
  5717. "onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>"
  5718. ].join(""); // String
  5719. },
  5720. _applyEditingAreaStyleSheets: function(){
  5721. // summary:
  5722. // apply the specified css files in styleSheets
  5723. // tags:
  5724. // private
  5725. var files = [];
  5726. if(this.styleSheets){
  5727. files = this.styleSheets.split(';');
  5728. this.styleSheets = '';
  5729. }
  5730. //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
  5731. files = files.concat(this.editingAreaStyleSheets);
  5732. this.editingAreaStyleSheets = [];
  5733. var text='', i=0, url;
  5734. while((url=files[i++])){
  5735. var abstring = (new dojo._Url(dojo.global.location, url)).toString();
  5736. this.editingAreaStyleSheets.push(abstring);
  5737. text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>';
  5738. }
  5739. return text;
  5740. },
  5741. addStyleSheet: function(/*dojo._Url*/ uri){
  5742. // summary:
  5743. // add an external stylesheet for the editing area
  5744. // uri:
  5745. // A dojo.uri.Uri pointing to the url of the external css file
  5746. var url=uri.toString();
  5747. //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
  5748. if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
  5749. url = (new dojo._Url(dojo.global.location, url)).toString();
  5750. }
  5751. if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
  5752. // console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied");
  5753. return;
  5754. }
  5755. this.editingAreaStyleSheets.push(url);
  5756. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  5757. if(this.document.createStyleSheet){ //IE
  5758. this.document.createStyleSheet(url);
  5759. }else{ //other browser
  5760. var head = this.document.getElementsByTagName("head")[0];
  5761. var stylesheet = this.document.createElement("link");
  5762. stylesheet.rel="stylesheet";
  5763. stylesheet.type="text/css";
  5764. stylesheet.href=url;
  5765. head.appendChild(stylesheet);
  5766. }
  5767. }));
  5768. },
  5769. removeStyleSheet: function(/*dojo._Url*/ uri){
  5770. // summary:
  5771. // remove an external stylesheet for the editing area
  5772. var url=uri.toString();
  5773. //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
  5774. if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
  5775. url = (new dojo._Url(dojo.global.location, url)).toString();
  5776. }
  5777. var index = dojo.indexOf(this.editingAreaStyleSheets, url);
  5778. if(index == -1){
  5779. // console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied");
  5780. return;
  5781. }
  5782. delete this.editingAreaStyleSheets[index];
  5783. dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan();
  5784. },
  5785. // disabled: Boolean
  5786. // The editor is disabled; the text cannot be changed.
  5787. disabled: false,
  5788. _mozSettingProps: {'styleWithCSS':false},
  5789. _setDisabledAttr: function(/*Boolean*/ value){
  5790. value = !!value;
  5791. this._set("disabled", value);
  5792. if(!this.isLoaded){ return; } // this method requires init to be complete
  5793. if(dojo.isIE || dojo.isWebKit || dojo.isOpera){
  5794. var preventIEfocus = dojo.isIE && (this.isLoaded || !this.focusOnLoad);
  5795. if(preventIEfocus){ this.editNode.unselectable = "on"; }
  5796. this.editNode.contentEditable = !value;
  5797. if(preventIEfocus){
  5798. var _this = this;
  5799. setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0);
  5800. }
  5801. }else{ //moz
  5802. try{
  5803. this.document.designMode=(value?'off':'on');
  5804. }catch(e){ return; } // ! _disabledOK
  5805. if(!value && this._mozSettingProps){
  5806. var ps = this._mozSettingProps;
  5807. for(var n in ps){
  5808. if(ps.hasOwnProperty(n)){
  5809. try{
  5810. this.document.execCommand(n,false,ps[n]);
  5811. }catch(e2){}
  5812. }
  5813. }
  5814. }
  5815. // this.document.execCommand('contentReadOnly', false, value);
  5816. // if(value){
  5817. // this.blur(); //to remove the blinking caret
  5818. // }
  5819. }
  5820. this._disabledOK = true;
  5821. },
  5822. /* Event handlers
  5823. *****************/
  5824. onLoad: function(/*String*/ html){
  5825. // summary:
  5826. // Handler after the iframe finishes loading.
  5827. // html: String
  5828. // Editor contents should be set to this value
  5829. // tags:
  5830. // protected
  5831. // TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler?
  5832. if(!this.window.__registeredWindow){
  5833. this.window.__registeredWindow = true;
  5834. this._iframeRegHandle = dijit.registerIframe(this.iframe);
  5835. }
  5836. if(!dojo.isIE && !dojo.isWebKit && (this.height || dojo.isMoz)){
  5837. this.editNode=this.document.body;
  5838. }else{
  5839. // there's a wrapper div around the content, see _getIframeDocTxt().
  5840. this.editNode=this.document.body.firstChild;
  5841. var _this = this;
  5842. if(dojo.isIE){ // #4996 IE wants to focus the BODY tag
  5843. this.tabStop = dojo.create('div', { tabIndex: -1 }, this.editingArea);
  5844. this.iframe.onfocus = function(){ _this.editNode.setActive(); };
  5845. }
  5846. }
  5847. this.focusNode = this.editNode; // for InlineEditBox
  5848. var events = this.events.concat(this.captureEvents);
  5849. var ap = this.iframe ? this.document : this.editNode;
  5850. dojo.forEach(events, function(item){
  5851. this.connect(ap, item.toLowerCase(), item);
  5852. }, this);
  5853. this.connect(ap, "onmouseup", "onClick"); // mouseup in the margin does not generate an onclick event
  5854. if(dojo.isIE){ // IE contentEditable
  5855. this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus
  5856. // give the node Layout on IE
  5857. // TODO: this may no longer be needed, since we've reverted IE to using an iframe,
  5858. // not contentEditable. Removing it would also probably remove the need for creating
  5859. // the extra <div> in _getIframeDocTxt()
  5860. this.editNode.style.zoom = 1.0;
  5861. }else{
  5862. this.connect(this.document, "onmousedown", function(){
  5863. // Clear the moveToStart focus, as mouse
  5864. // down will set cursor point. Required to properly
  5865. // work with selection/position driven plugins and clicks in
  5866. // the window. refs: #10678
  5867. delete this._cursorToStart;
  5868. });
  5869. }
  5870. if(dojo.isWebKit){
  5871. //WebKit sometimes doesn't fire right on selections, so the toolbar
  5872. //doesn't update right. Therefore, help it out a bit with an additional
  5873. //listener. A mouse up will typically indicate a display change, so fire this
  5874. //and get the toolbar to adapt. Reference: #9532
  5875. this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged");
  5876. this.connect(this.document, "onmousedown", function(e){
  5877. var t = e.target;
  5878. if(t && (t === this.document.body || t === this.document)){
  5879. // Since WebKit uses the inner DIV, we need to check and set position.
  5880. // See: #12024 as to why the change was made.
  5881. setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
  5882. }
  5883. });
  5884. }
  5885. if(dojo.isIE){
  5886. // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE
  5887. // do). See #9103
  5888. try{
  5889. this.document.execCommand('RespectVisibilityInDesign', true, null);
  5890. }catch(e){/* squelch */}
  5891. }
  5892. this.isLoaded = true;
  5893. this.set('disabled', this.disabled); // initialize content to editable (or not)
  5894. // Note that setValue() call will only work after isLoaded is set to true (above)
  5895. // Set up a function to allow delaying the setValue until a callback is fired
  5896. // This ensures extensions like dijit.Editor have a way to hold the value set
  5897. // until plugins load (and do things like register filters).
  5898. var setContent = dojo.hitch(this, function(){
  5899. this.setValue(html);
  5900. if(this.onLoadDeferred){
  5901. this.onLoadDeferred.callback(true);
  5902. }
  5903. this.onDisplayChanged();
  5904. if(this.focusOnLoad){
  5905. // after the document loads, then set focus after updateInterval expires so that
  5906. // onNormalizedDisplayChanged has run to avoid input caret issues
  5907. dojo.addOnLoad(dojo.hitch(this, function(){ setTimeout(dojo.hitch(this, "focus"), this.updateInterval); }));
  5908. }
  5909. // Save off the initial content now
  5910. this.value = this.getValue(true);
  5911. });
  5912. if(this.setValueDeferred){
  5913. this.setValueDeferred.addCallback(setContent);
  5914. }else{
  5915. setContent();
  5916. }
  5917. },
  5918. onKeyDown: function(/* Event */ e){
  5919. // summary:
  5920. // Handler for onkeydown event
  5921. // tags:
  5922. // protected
  5923. // we need this event at the moment to get the events from control keys
  5924. // such as the backspace. It might be possible to add this to Dojo, so that
  5925. // keyPress events can be emulated by the keyDown and keyUp detection.
  5926. if(e.keyCode === dojo.keys.TAB && this.isTabIndent ){
  5927. dojo.stopEvent(e); //prevent tab from moving focus out of editor
  5928. // FIXME: this is a poor-man's indent/outdent. It would be
  5929. // better if it added 4 "&nbsp;" chars in an undoable way.
  5930. // Unfortunately pasteHTML does not prove to be undoable
  5931. if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){
  5932. this.execCommand((e.shiftKey ? "outdent" : "indent"));
  5933. }
  5934. }
  5935. if(dojo.isIE){
  5936. if(e.keyCode == dojo.keys.TAB && !this.isTabIndent){
  5937. if(e.shiftKey && !e.ctrlKey && !e.altKey){
  5938. // focus the BODY so the browser will tab away from it instead
  5939. this.iframe.focus();
  5940. }else if(!e.shiftKey && !e.ctrlKey && !e.altKey){
  5941. // focus the BODY so the browser will tab away from it instead
  5942. this.tabStop.focus();
  5943. }
  5944. }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
  5945. // IE has a bug where if a non-text object is selected in the editor,
  5946. // hitting backspace would act as if the browser's back button was
  5947. // clicked instead of deleting the object. see #1069
  5948. dojo.stopEvent(e);
  5949. this.execCommand("delete");
  5950. }else if((65 <= e.keyCode && e.keyCode <= 90) ||
  5951. (e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead!
  5952. ){ //arrow keys
  5953. e.charCode = e.keyCode;
  5954. this.onKeyPress(e);
  5955. }
  5956. }
  5957. return true;
  5958. },
  5959. onKeyUp: function(e){
  5960. // summary:
  5961. // Handler for onkeyup event
  5962. // tags:
  5963. // callback
  5964. return;
  5965. },
  5966. setDisabled: function(/*Boolean*/ disabled){
  5967. // summary:
  5968. // Deprecated, use set('disabled', ...) instead.
  5969. // tags:
  5970. // deprecated
  5971. dojo.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0);
  5972. this.set('disabled',disabled);
  5973. },
  5974. _setValueAttr: function(/*String*/ value){
  5975. // summary:
  5976. // Registers that attr("value", foo) should call setValue(foo)
  5977. this.setValue(value);
  5978. },
  5979. _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){
  5980. if(this.document){
  5981. dojo.attr(this.document.body, "spellcheck", !disabled);
  5982. }else{
  5983. // try again after the editor is finished loading
  5984. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  5985. dojo.attr(this.document.body, "spellcheck", !disabled);
  5986. }));
  5987. }
  5988. this._set("disableSpellCheck", disabled);
  5989. },
  5990. onKeyPress: function(e){
  5991. // summary:
  5992. // Handle the various key events
  5993. // tags:
  5994. // protected
  5995. var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode,
  5996. handlers = this._keyHandlers[c],
  5997. args = arguments;
  5998. if(handlers && !e.altKey){
  5999. dojo.some(handlers, function(h){
  6000. // treat meta- same as ctrl-, for benefit of mac users
  6001. if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){
  6002. if(!h.handler.apply(this, args)){
  6003. e.preventDefault();
  6004. }
  6005. return true;
  6006. }
  6007. }, this);
  6008. }
  6009. // function call after the character has been inserted
  6010. if(!this._onKeyHitch){
  6011. this._onKeyHitch = dojo.hitch(this, "onKeyPressed");
  6012. }
  6013. setTimeout(this._onKeyHitch, 1);
  6014. return true;
  6015. },
  6016. addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){
  6017. // summary:
  6018. // Add a handler for a keyboard shortcut
  6019. // description:
  6020. // The key argument should be in lowercase if it is a letter character
  6021. // tags:
  6022. // protected
  6023. if(!dojo.isArray(this._keyHandlers[key])){
  6024. this._keyHandlers[key] = [];
  6025. }
  6026. //TODO: would be nice to make this a hash instead of an array for quick lookups
  6027. this._keyHandlers[key].push({
  6028. shift: shift || false,
  6029. ctrl: ctrl || false,
  6030. handler: handler
  6031. });
  6032. },
  6033. onKeyPressed: function(){
  6034. // summary:
  6035. // Handler for after the user has pressed a key, and the display has been updated.
  6036. // (Runs on a timer so that it runs after the display is updated)
  6037. // tags:
  6038. // private
  6039. this.onDisplayChanged(/*e*/); // can't pass in e
  6040. },
  6041. onClick: function(/*Event*/ e){
  6042. // summary:
  6043. // Handler for when the user clicks.
  6044. // tags:
  6045. // private
  6046. // console.info('onClick',this._tryDesignModeOn);
  6047. this.onDisplayChanged(e);
  6048. },
  6049. _onIEMouseDown: function(/*Event*/ e){
  6050. // summary:
  6051. // IE only to prevent 2 clicks to focus
  6052. // tags:
  6053. // protected
  6054. if(!this._focused && !this.disabled){
  6055. this.focus();
  6056. }
  6057. },
  6058. _onBlur: function(e){
  6059. // summary:
  6060. // Called from focus manager when focus has moved away from this editor
  6061. // tags:
  6062. // protected
  6063. // console.info('_onBlur')
  6064. this.inherited(arguments);
  6065. var newValue = this.getValue(true);
  6066. if(newValue != this.value){
  6067. this.onChange(newValue);
  6068. }
  6069. this._set("value", newValue);
  6070. },
  6071. _onFocus: function(/*Event*/ e){
  6072. // summary:
  6073. // Called from focus manager when focus has moved into this editor
  6074. // tags:
  6075. // protected
  6076. // console.info('_onFocus')
  6077. if(!this.disabled){
  6078. if(!this._disabledOK){
  6079. this.set('disabled', false);
  6080. }
  6081. this.inherited(arguments);
  6082. }
  6083. },
  6084. // TODO: remove in 2.0
  6085. blur: function(){
  6086. // summary:
  6087. // Remove focus from this instance.
  6088. // tags:
  6089. // deprecated
  6090. if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){
  6091. this.window.document.documentElement.focus();
  6092. }else if(dojo.doc.body.focus){
  6093. dojo.doc.body.focus();
  6094. }
  6095. },
  6096. focus: function(){
  6097. // summary:
  6098. // Move focus to this editor
  6099. if(!this.isLoaded){
  6100. this.focusOnLoad = true;
  6101. return;
  6102. }
  6103. if(this._cursorToStart){
  6104. delete this._cursorToStart;
  6105. if(this.editNode.childNodes){
  6106. this.placeCursorAtStart(); // this calls focus() so return
  6107. return;
  6108. }
  6109. }
  6110. if(!dojo.isIE){
  6111. dijit.focus(this.iframe);
  6112. }else if(this.editNode && this.editNode.focus){
  6113. // editNode may be hidden in display:none div, lets just punt in this case
  6114. //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe
  6115. // if we fire the event manually and let the browser handle the focusing, the latest
  6116. // cursor position is focused like in FF
  6117. this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE
  6118. // }else{
  6119. // TODO: should we throw here?
  6120. // console.debug("Have no idea how to focus into the editor!");
  6121. }
  6122. },
  6123. // _lastUpdate: 0,
  6124. updateInterval: 200,
  6125. _updateTimer: null,
  6126. onDisplayChanged: function(/*Event*/ e){
  6127. // summary:
  6128. // This event will be fired everytime the display context
  6129. // changes and the result needs to be reflected in the UI.
  6130. // description:
  6131. // If you don't want to have update too often,
  6132. // onNormalizedDisplayChanged should be used instead
  6133. // tags:
  6134. // private
  6135. // var _t=new Date();
  6136. if(this._updateTimer){
  6137. clearTimeout(this._updateTimer);
  6138. }
  6139. if(!this._updateHandler){
  6140. this._updateHandler = dojo.hitch(this,"onNormalizedDisplayChanged");
  6141. }
  6142. this._updateTimer = setTimeout(this._updateHandler, this.updateInterval);
  6143. // Technically this should trigger a call to watch("value", ...) registered handlers,
  6144. // but getValue() is too slow to call on every keystroke so we don't.
  6145. },
  6146. onNormalizedDisplayChanged: function(){
  6147. // summary:
  6148. // This event is fired every updateInterval ms or more
  6149. // description:
  6150. // If something needs to happen immediately after a
  6151. // user change, please use onDisplayChanged instead.
  6152. // tags:
  6153. // private
  6154. delete this._updateTimer;
  6155. },
  6156. onChange: function(newContent){
  6157. // summary:
  6158. // This is fired if and only if the editor loses focus and
  6159. // the content is changed.
  6160. },
  6161. _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){
  6162. // summary:
  6163. // Used as the advice function by dojo.connect to map our
  6164. // normalized set of commands to those supported by the target
  6165. // browser.
  6166. // tags:
  6167. // private
  6168. var command = cmd.toLowerCase();
  6169. if(command == "formatblock"){
  6170. if(dojo.isSafari && argument === undefined){ command = "heading"; }
  6171. }else if(command == "hilitecolor" && !dojo.isMoz){
  6172. command = "backcolor";
  6173. }
  6174. return command;
  6175. },
  6176. _qcaCache: {},
  6177. queryCommandAvailable: function(/*String*/ command){
  6178. // summary:
  6179. // Tests whether a command is supported by the host. Clients
  6180. // SHOULD check whether a command is supported before attempting
  6181. // to use it, behaviour for unsupported commands is undefined.
  6182. // command:
  6183. // The command to test for
  6184. // tags:
  6185. // private
  6186. // memoizing version. See _queryCommandAvailable for computing version
  6187. var ca = this._qcaCache[command];
  6188. if(ca !== undefined){ return ca; }
  6189. return (this._qcaCache[command] = this._queryCommandAvailable(command));
  6190. },
  6191. _queryCommandAvailable: function(/*String*/ command){
  6192. // summary:
  6193. // See queryCommandAvailable().
  6194. // tags:
  6195. // private
  6196. var ie = 1;
  6197. var mozilla = 1 << 1;
  6198. var webkit = 1 << 2;
  6199. var opera = 1 << 3;
  6200. function isSupportedBy(browsers){
  6201. return {
  6202. ie: Boolean(browsers & ie),
  6203. mozilla: Boolean(browsers & mozilla),
  6204. webkit: Boolean(browsers & webkit),
  6205. opera: Boolean(browsers & opera)
  6206. };
  6207. }
  6208. var supportedBy = null;
  6209. switch(command.toLowerCase()){
  6210. case "bold": case "italic": case "underline":
  6211. case "subscript": case "superscript":
  6212. case "fontname": case "fontsize":
  6213. case "forecolor": case "hilitecolor":
  6214. case "justifycenter": case "justifyfull": case "justifyleft":
  6215. case "justifyright": case "delete": case "selectall": case "toggledir":
  6216. supportedBy = isSupportedBy(mozilla | ie | webkit | opera);
  6217. break;
  6218. case "createlink": case "unlink": case "removeformat":
  6219. case "inserthorizontalrule": case "insertimage":
  6220. case "insertorderedlist": case "insertunorderedlist":
  6221. case "indent": case "outdent": case "formatblock":
  6222. case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent":
  6223. supportedBy = isSupportedBy(mozilla | ie | opera | webkit);
  6224. break;
  6225. case "blockdirltr": case "blockdirrtl":
  6226. case "dirltr": case "dirrtl":
  6227. case "inlinedirltr": case "inlinedirrtl":
  6228. supportedBy = isSupportedBy(ie);
  6229. break;
  6230. case "cut": case "copy": case "paste":
  6231. supportedBy = isSupportedBy( ie | mozilla | webkit);
  6232. break;
  6233. case "inserttable":
  6234. supportedBy = isSupportedBy(mozilla | ie);
  6235. break;
  6236. case "insertcell": case "insertcol": case "insertrow":
  6237. case "deletecells": case "deletecols": case "deleterows":
  6238. case "mergecells": case "splitcell":
  6239. supportedBy = isSupportedBy(ie | mozilla);
  6240. break;
  6241. default: return false;
  6242. }
  6243. return (dojo.isIE && supportedBy.ie) ||
  6244. (dojo.isMoz && supportedBy.mozilla) ||
  6245. (dojo.isWebKit && supportedBy.webkit) ||
  6246. (dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
  6247. },
  6248. execCommand: function(/*String*/ command, argument){
  6249. // summary:
  6250. // Executes a command in the Rich Text area
  6251. // command:
  6252. // The command to execute
  6253. // argument:
  6254. // An optional argument to the command
  6255. // tags:
  6256. // protected
  6257. var returnValue;
  6258. //focus() is required for IE to work
  6259. //In addition, focus() makes sure after the execution of
  6260. //the command, the editor receives the focus as expected
  6261. this.focus();
  6262. command = this._normalizeCommand(command, argument);
  6263. if(argument !== undefined){
  6264. if(command == "heading"){
  6265. throw new Error("unimplemented");
  6266. }else if((command == "formatblock") && dojo.isIE){
  6267. argument = '<'+argument+'>';
  6268. }
  6269. }
  6270. //Check to see if we have any over-rides for commands, they will be functions on this
  6271. //widget of the form _commandImpl. If we don't, fall through to the basic native
  6272. //exec command of the browser.
  6273. var implFunc = "_" + command + "Impl";
  6274. if(this[implFunc]){
  6275. returnValue = this[implFunc](argument);
  6276. }else{
  6277. argument = arguments.length > 1 ? argument : null;
  6278. if(argument || command!="createlink"){
  6279. returnValue = this.document.execCommand(command, false, argument);
  6280. }
  6281. }
  6282. this.onDisplayChanged();
  6283. return returnValue;
  6284. },
  6285. queryCommandEnabled: function(/*String*/ command){
  6286. // summary:
  6287. // Check whether a command is enabled or not.
  6288. // tags:
  6289. // protected
  6290. if(this.disabled || !this._disabledOK){ return false; }
  6291. command = this._normalizeCommand(command);
  6292. if(dojo.isMoz || dojo.isWebKit){
  6293. if(command == "unlink"){ // mozilla returns true always
  6294. // console.debug(this._sCall("hasAncestorElement", ['a']));
  6295. return this._sCall("hasAncestorElement", ["a"]);
  6296. }else if(command == "inserttable"){
  6297. return true;
  6298. }
  6299. }
  6300. //see #4109
  6301. if(dojo.isWebKit){
  6302. if(command == "cut" || command == "copy") {
  6303. // WebKit deems clipboard activity as a security threat and natively would return false
  6304. var sel = this.window.getSelection();
  6305. if(sel){ sel = sel.toString(); }
  6306. return !!sel;
  6307. }else if(command == "paste"){
  6308. return true;
  6309. }
  6310. }
  6311. var elem = dojo.isIE ? this.document.selection.createRange() : this.document;
  6312. try{
  6313. return elem.queryCommandEnabled(command);
  6314. }catch(e){
  6315. //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
  6316. return false;
  6317. }
  6318. },
  6319. queryCommandState: function(command){
  6320. // summary:
  6321. // Check the state of a given command and returns true or false.
  6322. // tags:
  6323. // protected
  6324. if(this.disabled || !this._disabledOK){ return false; }
  6325. command = this._normalizeCommand(command);
  6326. try{
  6327. return this.document.queryCommandState(command);
  6328. }catch(e){
  6329. //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
  6330. return false;
  6331. }
  6332. },
  6333. queryCommandValue: function(command){
  6334. // summary:
  6335. // Check the value of a given command. This matters most for
  6336. // custom selections and complex values like font value setting.
  6337. // tags:
  6338. // protected
  6339. if(this.disabled || !this._disabledOK){ return false; }
  6340. var r;
  6341. command = this._normalizeCommand(command);
  6342. if(dojo.isIE && command == "formatblock"){
  6343. r = this._native2LocalFormatNames[this.document.queryCommandValue(command)];
  6344. }else if(dojo.isMoz && command === "hilitecolor"){
  6345. var oldValue;
  6346. try{
  6347. oldValue = this.document.queryCommandValue("styleWithCSS");
  6348. }catch(e){
  6349. oldValue = false;
  6350. }
  6351. this.document.execCommand("styleWithCSS", false, true);
  6352. r = this.document.queryCommandValue(command);
  6353. this.document.execCommand("styleWithCSS", false, oldValue);
  6354. }else{
  6355. r = this.document.queryCommandValue(command);
  6356. }
  6357. return r;
  6358. },
  6359. // Misc.
  6360. _sCall: function(name, args){
  6361. // summary:
  6362. // Run the named method of dijit._editor.selection over the
  6363. // current editor instance's window, with the passed args.
  6364. // tags:
  6365. // private
  6366. return dojo.withGlobal(this.window, name, dijit._editor.selection, args);
  6367. },
  6368. // FIXME: this is a TON of code duplication. Why?
  6369. placeCursorAtStart: function(){
  6370. // summary:
  6371. // Place the cursor at the start of the editing area.
  6372. // tags:
  6373. // private
  6374. this.focus();
  6375. //see comments in placeCursorAtEnd
  6376. var isvalid=false;
  6377. if(dojo.isMoz){
  6378. // TODO: Is this branch even necessary?
  6379. var first=this.editNode.firstChild;
  6380. while(first){
  6381. if(first.nodeType == 3){
  6382. if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
  6383. isvalid=true;
  6384. this._sCall("selectElement", [ first ]);
  6385. break;
  6386. }
  6387. }else if(first.nodeType == 1){
  6388. isvalid=true;
  6389. var tg = first.tagName ? first.tagName.toLowerCase() : "";
  6390. // Collapse before childless tags.
  6391. if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){
  6392. this._sCall("selectElement", [ first ]);
  6393. }else{
  6394. // Collapse inside tags with children.
  6395. this._sCall("selectElementChildren", [ first ]);
  6396. }
  6397. break;
  6398. }
  6399. first = first.nextSibling;
  6400. }
  6401. }else{
  6402. isvalid=true;
  6403. this._sCall("selectElementChildren", [ this.editNode ]);
  6404. }
  6405. if(isvalid){
  6406. this._sCall("collapse", [ true ]);
  6407. }
  6408. },
  6409. placeCursorAtEnd: function(){
  6410. // summary:
  6411. // Place the cursor at the end of the editing area.
  6412. // tags:
  6413. // private
  6414. this.focus();
  6415. //In mozilla, if last child is not a text node, we have to use
  6416. // selectElementChildren on this.editNode.lastChild otherwise the
  6417. // cursor would be placed at the end of the closing tag of
  6418. //this.editNode.lastChild
  6419. var isvalid=false;
  6420. if(dojo.isMoz){
  6421. var last=this.editNode.lastChild;
  6422. while(last){
  6423. if(last.nodeType == 3){
  6424. if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
  6425. isvalid=true;
  6426. this._sCall("selectElement", [ last ]);
  6427. break;
  6428. }
  6429. }else if(last.nodeType == 1){
  6430. isvalid=true;
  6431. if(last.lastChild){
  6432. this._sCall("selectElement", [ last.lastChild ]);
  6433. }else{
  6434. this._sCall("selectElement", [ last ]);
  6435. }
  6436. break;
  6437. }
  6438. last = last.previousSibling;
  6439. }
  6440. }else{
  6441. isvalid=true;
  6442. this._sCall("selectElementChildren", [ this.editNode ]);
  6443. }
  6444. if(isvalid){
  6445. this._sCall("collapse", [ false ]);
  6446. }
  6447. },
  6448. getValue: function(/*Boolean?*/ nonDestructive){
  6449. // summary:
  6450. // Return the current content of the editing area (post filters
  6451. // are applied). Users should call get('value') instead.
  6452. // nonDestructive:
  6453. // defaults to false. Should the post-filtering be run over a copy
  6454. // of the live DOM? Most users should pass "true" here unless they
  6455. // *really* know that none of the installed filters are going to
  6456. // mess up the editing session.
  6457. // tags:
  6458. // private
  6459. if(this.textarea){
  6460. if(this.isClosed || !this.isLoaded){
  6461. return this.textarea.value;
  6462. }
  6463. }
  6464. return this._postFilterContent(null, nonDestructive);
  6465. },
  6466. _getValueAttr: function(){
  6467. // summary:
  6468. // Hook to make attr("value") work
  6469. return this.getValue(true);
  6470. },
  6471. setValue: function(/*String*/ html){
  6472. // summary:
  6473. // This function sets the content. No undo history is preserved.
  6474. // Users should use set('value', ...) instead.
  6475. // tags:
  6476. // deprecated
  6477. // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr()
  6478. if(!this.isLoaded){
  6479. // try again after the editor is finished loading
  6480. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  6481. this.setValue(html);
  6482. }));
  6483. return;
  6484. }
  6485. this._cursorToStart = true;
  6486. if(this.textarea && (this.isClosed || !this.isLoaded)){
  6487. this.textarea.value=html;
  6488. }else{
  6489. html = this._preFilterContent(html);
  6490. var node = this.isClosed ? this.domNode : this.editNode;
  6491. if(html && dojo.isMoz && html.toLowerCase() == "<p></p>"){
  6492. html = "<p>&nbsp;</p>";
  6493. }
  6494. // Use &nbsp; to avoid webkit problems where editor is disabled until the user clicks it
  6495. if(!html && dojo.isWebKit){
  6496. html = "&nbsp;";
  6497. }
  6498. node.innerHTML = html;
  6499. this._preDomFilterContent(node);
  6500. }
  6501. this.onDisplayChanged();
  6502. this._set("value", this.getValue(true));
  6503. },
  6504. replaceValue: function(/*String*/ html){
  6505. // summary:
  6506. // This function set the content while trying to maintain the undo stack
  6507. // (now only works fine with Moz, this is identical to setValue in all
  6508. // other browsers)
  6509. // tags:
  6510. // protected
  6511. if(this.isClosed){
  6512. this.setValue(html);
  6513. }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
  6514. // look ma! it's a totally f'd browser!
  6515. this.setValue(html);
  6516. }else if(this.window && this.window.getSelection){ // Moz
  6517. html = this._preFilterContent(html);
  6518. this.execCommand("selectall");
  6519. if(!html){
  6520. this._cursorToStart = true;
  6521. html = "&nbsp;";
  6522. }
  6523. this.execCommand("inserthtml", html);
  6524. this._preDomFilterContent(this.editNode);
  6525. }else if(this.document && this.document.selection){//IE
  6526. //In IE, when the first element is not a text node, say
  6527. //an <a> tag, when replacing the content of the editing
  6528. //area, the <a> tag will be around all the content
  6529. //so for now, use setValue for IE too
  6530. this.setValue(html);
  6531. }
  6532. this._set("value", this.getValue(true));
  6533. },
  6534. _preFilterContent: function(/*String*/ html){
  6535. // summary:
  6536. // Filter the input before setting the content of the editing
  6537. // area. DOM pre-filtering may happen after this
  6538. // string-based filtering takes place but as of 1.2, this is not
  6539. // guaranteed for operations such as the inserthtml command.
  6540. // tags:
  6541. // private
  6542. var ec = html;
  6543. dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
  6544. return ec;
  6545. },
  6546. _preDomFilterContent: function(/*DomNode*/ dom){
  6547. // summary:
  6548. // filter the input's live DOM. All filter operations should be
  6549. // considered to be "live" and operating on the DOM that the user
  6550. // will be interacting with in their editing session.
  6551. // tags:
  6552. // private
  6553. dom = dom || this.editNode;
  6554. dojo.forEach(this.contentDomPreFilters, function(ef){
  6555. if(ef && dojo.isFunction(ef)){
  6556. ef(dom);
  6557. }
  6558. }, this);
  6559. },
  6560. _postFilterContent: function(
  6561. /*DomNode|DomNode[]|String?*/ dom,
  6562. /*Boolean?*/ nonDestructive){
  6563. // summary:
  6564. // filter the output after getting the content of the editing area
  6565. //
  6566. // description:
  6567. // post-filtering allows plug-ins and users to specify any number
  6568. // of transforms over the editor's content, enabling many common
  6569. // use-cases such as transforming absolute to relative URLs (and
  6570. // vice-versa), ensuring conformance with a particular DTD, etc.
  6571. // The filters are registered in the contentDomPostFilters and
  6572. // contentPostFilters arrays. Each item in the
  6573. // contentDomPostFilters array is a function which takes a DOM
  6574. // Node or array of nodes as its only argument and returns the
  6575. // same. It is then passed down the chain for further filtering.
  6576. // The contentPostFilters array behaves the same way, except each
  6577. // member operates on strings. Together, the DOM and string-based
  6578. // filtering allow the full range of post-processing that should
  6579. // be necessaray to enable even the most agressive of post-editing
  6580. // conversions to take place.
  6581. //
  6582. // If nonDestructive is set to "true", the nodes are cloned before
  6583. // filtering proceeds to avoid potentially destructive transforms
  6584. // to the content which may still needed to be edited further.
  6585. // Once DOM filtering has taken place, the serialized version of
  6586. // the DOM which is passed is run through each of the
  6587. // contentPostFilters functions.
  6588. //
  6589. // dom:
  6590. // a node, set of nodes, which to filter using each of the current
  6591. // members of the contentDomPostFilters and contentPostFilters arrays.
  6592. //
  6593. // nonDestructive:
  6594. // defaults to "false". If true, ensures that filtering happens on
  6595. // a clone of the passed-in content and not the actual node
  6596. // itself.
  6597. //
  6598. // tags:
  6599. // private
  6600. var ec;
  6601. if(!dojo.isString(dom)){
  6602. dom = dom || this.editNode;
  6603. if(this.contentDomPostFilters.length){
  6604. if(nonDestructive){
  6605. dom = dojo.clone(dom);
  6606. }
  6607. dojo.forEach(this.contentDomPostFilters, function(ef){
  6608. dom = ef(dom);
  6609. });
  6610. }
  6611. ec = dijit._editor.getChildrenHtml(dom);
  6612. }else{
  6613. ec = dom;
  6614. }
  6615. if(!dojo.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){
  6616. ec = "";
  6617. }
  6618. // if(dojo.isIE){
  6619. // //removing appended <P>&nbsp;</P> for IE
  6620. // ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
  6621. // }
  6622. dojo.forEach(this.contentPostFilters, function(ef){
  6623. ec = ef(ec);
  6624. });
  6625. return ec;
  6626. },
  6627. _saveContent: function(/*Event*/ e){
  6628. // summary:
  6629. // Saves the content in an onunload event if the editor has not been closed
  6630. // tags:
  6631. // private
  6632. var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value");
  6633. if(saveTextarea.value){
  6634. saveTextarea.value += this._SEPARATOR;
  6635. }
  6636. saveTextarea.value += this.name + this._NAME_CONTENT_SEP + this.getValue(true);
  6637. },
  6638. escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){
  6639. // summary:
  6640. // Adds escape sequences for special characters in XML.
  6641. // Optionally skips escapes for single quotes
  6642. // tags:
  6643. // private
  6644. str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  6645. if(!noSingleQuotes){
  6646. str = str.replace(/'/gm, "&#39;");
  6647. }
  6648. return str; // string
  6649. },
  6650. getNodeHtml: function(/* DomNode */ node){
  6651. // summary:
  6652. // Deprecated. Use dijit._editor._getNodeHtml() instead.
  6653. // tags:
  6654. // deprecated
  6655. dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2);
  6656. return dijit._editor.getNodeHtml(node); // String
  6657. },
  6658. getNodeChildrenHtml: function(/* DomNode */ dom){
  6659. // summary:
  6660. // Deprecated. Use dijit._editor.getChildrenHtml() instead.
  6661. // tags:
  6662. // deprecated
  6663. dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2);
  6664. return dijit._editor.getChildrenHtml(dom);
  6665. },
  6666. close: function(/*Boolean?*/ save){
  6667. // summary:
  6668. // Kills the editor and optionally writes back the modified contents to the
  6669. // element from which it originated.
  6670. // save:
  6671. // Whether or not to save the changes. If false, the changes are discarded.
  6672. // tags:
  6673. // private
  6674. if(this.isClosed){ return; }
  6675. if(!arguments.length){ save = true; }
  6676. if(save){
  6677. this._set("value", this.getValue(true));
  6678. }
  6679. // line height is squashed for iframes
  6680. // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
  6681. if(this.interval){ clearInterval(this.interval); }
  6682. if(this._webkitListener){
  6683. //Cleaup of WebKit fix: #9532
  6684. this.disconnect(this._webkitListener);
  6685. delete this._webkitListener;
  6686. }
  6687. // Guard against memory leaks on IE (see #9268)
  6688. if(dojo.isIE){
  6689. this.iframe.onfocus = null;
  6690. }
  6691. this.iframe._loadFunc = null;
  6692. if(this._iframeRegHandle){
  6693. dijit.unregisterIframe(this._iframeRegHandle);
  6694. delete this._iframeRegHandle;
  6695. }
  6696. if(this.textarea){
  6697. var s = this.textarea.style;
  6698. s.position = "";
  6699. s.left = s.top = "";
  6700. if(dojo.isIE){
  6701. s.overflow = this.__overflow;
  6702. this.__overflow = null;
  6703. }
  6704. this.textarea.value = this.value;
  6705. dojo.destroy(this.domNode);
  6706. this.domNode = this.textarea;
  6707. }else{
  6708. // Note that this destroys the iframe
  6709. this.domNode.innerHTML = this.value;
  6710. }
  6711. delete this.iframe;
  6712. dojo.removeClass(this.domNode, this.baseClass);
  6713. this.isClosed = true;
  6714. this.isLoaded = false;
  6715. delete this.editNode;
  6716. delete this.focusNode;
  6717. if(this.window && this.window._frameElement){
  6718. this.window._frameElement = null;
  6719. }
  6720. this.window = null;
  6721. this.document = null;
  6722. this.editingArea = null;
  6723. this.editorObject = null;
  6724. },
  6725. destroy: function(){
  6726. if(!this.isClosed){ this.close(false); }
  6727. this.inherited(arguments);
  6728. if(dijit._editor._globalSaveHandler){
  6729. delete dijit._editor._globalSaveHandler[this.id];
  6730. }
  6731. },
  6732. _removeMozBogus: function(/* String */ html){
  6733. // summary:
  6734. // Post filter to remove unwanted HTML attributes generated by mozilla
  6735. // tags:
  6736. // private
  6737. return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String
  6738. },
  6739. _removeWebkitBogus: function(/* String */ html){
  6740. // summary:
  6741. // Post filter to remove unwanted HTML attributes generated by webkit
  6742. // tags:
  6743. // private
  6744. html = html.replace(/\sclass="webkit-block-placeholder"/gi, '');
  6745. html = html.replace(/\sclass="apple-style-span"/gi, '');
  6746. // For some reason copy/paste sometime adds extra meta tags for charset on
  6747. // webkit (chrome) on mac.They need to be removed. See: #12007"
  6748. html = html.replace(/<meta charset=\"utf-8\" \/>/gi, '');
  6749. return html; // String
  6750. },
  6751. _normalizeFontStyle: function(/* String */ html){
  6752. // summary:
  6753. // Convert 'strong' and 'em' to 'b' and 'i'.
  6754. // description:
  6755. // Moz can not handle strong/em tags correctly, so to help
  6756. // mozilla and also to normalize output, convert them to 'b' and 'i'.
  6757. //
  6758. // Note the IE generates 'strong' and 'em' rather than 'b' and 'i'
  6759. // tags:
  6760. // private
  6761. return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
  6762. .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String
  6763. },
  6764. _preFixUrlAttributes: function(/* String */ html){
  6765. // summary:
  6766. // Pre-filter to do fixing to href attributes on <a> and <img> tags
  6767. // tags:
  6768. // private
  6769. return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,
  6770. '$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
  6771. .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,
  6772. '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
  6773. },
  6774. /*****************************************************************************
  6775. The following functions implement HTML manipulation commands for various
  6776. browser/contentEditable implementations. The goal of them is to enforce
  6777. standard behaviors of them.
  6778. ******************************************************************************/
  6779. _inserthorizontalruleImpl: function(argument){
  6780. // summary:
  6781. // This function implements the insertion of HTML 'HR' tags.
  6782. // into a point on the page. IE doesn't to it right, so
  6783. // we have to use an alternate form
  6784. // argument:
  6785. // arguments to the exec command, if any.
  6786. // tags:
  6787. // protected
  6788. if(dojo.isIE){
  6789. return this._inserthtmlImpl("<hr>");
  6790. }
  6791. return this.document.execCommand("inserthorizontalrule", false, argument);
  6792. },
  6793. _unlinkImpl: function(argument){
  6794. // summary:
  6795. // This function implements the unlink of an 'a' tag.
  6796. // argument:
  6797. // arguments to the exec command, if any.
  6798. // tags:
  6799. // protected
  6800. if((this.queryCommandEnabled("unlink")) && (dojo.isMoz || dojo.isWebKit)){
  6801. var a = this._sCall("getAncestorElement", [ "a" ]);
  6802. this._sCall("selectElement", [ a ]);
  6803. return this.document.execCommand("unlink", false, null);
  6804. }
  6805. return this.document.execCommand("unlink", false, argument);
  6806. },
  6807. _hilitecolorImpl: function(argument){
  6808. // summary:
  6809. // This function implements the hilitecolor command
  6810. // argument:
  6811. // arguments to the exec command, if any.
  6812. // tags:
  6813. // protected
  6814. var returnValue;
  6815. if(dojo.isMoz){
  6816. // mozilla doesn't support hilitecolor properly when useCSS is
  6817. // set to false (bugzilla #279330)
  6818. this.document.execCommand("styleWithCSS", false, true);
  6819. returnValue = this.document.execCommand("hilitecolor", false, argument);
  6820. this.document.execCommand("styleWithCSS", false, false);
  6821. }else{
  6822. returnValue = this.document.execCommand("hilitecolor", false, argument);
  6823. }
  6824. return returnValue;
  6825. },
  6826. _backcolorImpl: function(argument){
  6827. // summary:
  6828. // This function implements the backcolor command
  6829. // argument:
  6830. // arguments to the exec command, if any.
  6831. // tags:
  6832. // protected
  6833. if(dojo.isIE){
  6834. // Tested under IE 6 XP2, no problem here, comment out
  6835. // IE weirdly collapses ranges when we exec these commands, so prevent it
  6836. // var tr = this.document.selection.createRange();
  6837. argument = argument ? argument : null;
  6838. }
  6839. return this.document.execCommand("backcolor", false, argument);
  6840. },
  6841. _forecolorImpl: function(argument){
  6842. // summary:
  6843. // This function implements the forecolor command
  6844. // argument:
  6845. // arguments to the exec command, if any.
  6846. // tags:
  6847. // protected
  6848. if(dojo.isIE){
  6849. // Tested under IE 6 XP2, no problem here, comment out
  6850. // IE weirdly collapses ranges when we exec these commands, so prevent it
  6851. // var tr = this.document.selection.createRange();
  6852. argument = argument? argument : null;
  6853. }
  6854. return this.document.execCommand("forecolor", false, argument);
  6855. },
  6856. _inserthtmlImpl: function(argument){
  6857. // summary:
  6858. // This function implements the insertion of HTML content into
  6859. // a point on the page.
  6860. // argument:
  6861. // The content to insert, if any.
  6862. // tags:
  6863. // protected
  6864. argument = this._preFilterContent(argument);
  6865. var rv = true;
  6866. if(dojo.isIE){
  6867. var insertRange = this.document.selection.createRange();
  6868. if(this.document.selection.type.toUpperCase() == 'CONTROL'){
  6869. var n=insertRange.item(0);
  6870. while(insertRange.length){
  6871. insertRange.remove(insertRange.item(0));
  6872. }
  6873. n.outerHTML=argument;
  6874. }else{
  6875. insertRange.pasteHTML(argument);
  6876. }
  6877. insertRange.select();
  6878. //insertRange.collapse(true);
  6879. }else if(dojo.isMoz && !argument.length){
  6880. //mozilla can not inserthtml an empty html to delete current selection
  6881. //so we delete the selection instead in this case
  6882. this._sCall("remove"); // FIXME
  6883. }else{
  6884. rv = this.document.execCommand("inserthtml", false, argument);
  6885. }
  6886. return rv;
  6887. },
  6888. _boldImpl: function(argument){
  6889. // summary:
  6890. // This function implements an over-ride of the bold command.
  6891. // argument:
  6892. // Not used, operates by selection.
  6893. // tags:
  6894. // protected
  6895. if(dojo.isIE){
  6896. this._adaptIESelection()
  6897. }
  6898. return this.document.execCommand("bold", false, argument);
  6899. },
  6900. _italicImpl: function(argument){
  6901. // summary:
  6902. // This function implements an over-ride of the italic command.
  6903. // argument:
  6904. // Not used, operates by selection.
  6905. // tags:
  6906. // protected
  6907. if(dojo.isIE){
  6908. this._adaptIESelection()
  6909. }
  6910. return this.document.execCommand("italic", false, argument);
  6911. },
  6912. _underlineImpl: function(argument){
  6913. // summary:
  6914. // This function implements an over-ride of the underline command.
  6915. // argument:
  6916. // Not used, operates by selection.
  6917. // tags:
  6918. // protected
  6919. if(dojo.isIE){
  6920. this._adaptIESelection()
  6921. }
  6922. return this.document.execCommand("underline", false, argument);
  6923. },
  6924. _strikethroughImpl: function(argument){
  6925. // summary:
  6926. // This function implements an over-ride of the strikethrough command.
  6927. // argument:
  6928. // Not used, operates by selection.
  6929. // tags:
  6930. // protected
  6931. if(dojo.isIE){
  6932. this._adaptIESelection()
  6933. }
  6934. return this.document.execCommand("strikethrough", false, argument);
  6935. },
  6936. getHeaderHeight: function(){
  6937. // summary:
  6938. // A function for obtaining the height of the header node
  6939. return this._getNodeChildrenHeight(this.header); // Number
  6940. },
  6941. getFooterHeight: function(){
  6942. // summary:
  6943. // A function for obtaining the height of the footer node
  6944. return this._getNodeChildrenHeight(this.footer); // Number
  6945. },
  6946. _getNodeChildrenHeight: function(node){
  6947. // summary:
  6948. // An internal function for computing the cumulative height of all child nodes of 'node'
  6949. // node:
  6950. // The node to process the children of;
  6951. var h = 0;
  6952. if(node && node.childNodes){
  6953. // IE didn't compute it right when position was obtained on the node directly is some cases,
  6954. // so we have to walk over all the children manually.
  6955. var i;
  6956. for(i = 0; i < node.childNodes.length; i++){
  6957. var size = dojo.position(node.childNodes[i]);
  6958. h += size.h;
  6959. }
  6960. }
  6961. return h; // Number
  6962. },
  6963. _isNodeEmpty: function(node, startOffset){
  6964. // summary:
  6965. // Function to test if a node is devoid of real content.
  6966. // node:
  6967. // The node to check.
  6968. // tags:
  6969. // private.
  6970. if(node.nodeType == 1/*element*/){
  6971. if(node.childNodes.length > 0){
  6972. return this._isNodeEmpty(node.childNodes[0], startOffset);
  6973. }
  6974. return true;
  6975. }else if(node.nodeType == 3/*text*/){
  6976. return (node.nodeValue.substring(startOffset) == "");
  6977. }
  6978. return false;
  6979. },
  6980. _removeStartingRangeFromRange: function(node, range){
  6981. // summary:
  6982. // Function to adjust selection range by removing the current
  6983. // start node.
  6984. // node:
  6985. // The node to remove from the starting range.
  6986. // range:
  6987. // The range to adapt.
  6988. // tags:
  6989. // private
  6990. if(node.nextSibling){
  6991. range.setStart(node.nextSibling,0);
  6992. }else{
  6993. var parent = node.parentNode;
  6994. while(parent && parent.nextSibling == null){
  6995. //move up the tree until we find a parent that has another node, that node will be the next node
  6996. parent = parent.parentNode;
  6997. }
  6998. if(parent){
  6999. range.setStart(parent.nextSibling,0);
  7000. }
  7001. }
  7002. return range;
  7003. },
  7004. _adaptIESelection: function(){
  7005. // summary:
  7006. // Function to adapt the IE range by removing leading 'newlines'
  7007. // Needed to fix issue with bold/italics/underline not working if
  7008. // range included leading 'newlines'.
  7009. // In IE, if a user starts a selection at the very end of a line,
  7010. // then the native browser commands will fail to execute correctly.
  7011. // To work around the issue, we can remove all empty nodes from
  7012. // the start of the range selection.
  7013. var selection = dijit.range.getSelection(this.window);
  7014. if(selection && selection.rangeCount && !selection.isCollapsed){
  7015. var range = selection.getRangeAt(0);
  7016. var firstNode = range.startContainer;
  7017. var startOffset = range.startOffset;
  7018. while(firstNode.nodeType == 3/*text*/ && startOffset >= firstNode.length && firstNode.nextSibling){
  7019. //traverse the text nodes until we get to the one that is actually highlighted
  7020. startOffset = startOffset - firstNode.length;
  7021. firstNode = firstNode.nextSibling;
  7022. }
  7023. //Remove the starting ranges until the range does not start with an empty node.
  7024. var lastNode=null;
  7025. while(this._isNodeEmpty(firstNode, startOffset) && firstNode != lastNode){
  7026. lastNode =firstNode; //this will break the loop in case we can't find the next sibling
  7027. range = this._removeStartingRangeFromRange(firstNode, range); //move the start container to the next node in the range
  7028. firstNode = range.startContainer;
  7029. startOffset = 0; //start at the beginning of the new starting range
  7030. }
  7031. selection.removeAllRanges();// this will work as long as users cannot select multiple ranges. I have not been able to do that in the editor.
  7032. selection.addRange(range);
  7033. }
  7034. }
  7035. });
  7036. }
  7037. if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7038. dojo._hasResource["dijit._KeyNavContainer"] = true;
  7039. dojo.provide("dijit._KeyNavContainer");
  7040. dojo.declare("dijit._KeyNavContainer",
  7041. dijit._Container,
  7042. {
  7043. // summary:
  7044. // A _Container with keyboard navigation of its children.
  7045. // description:
  7046. // To use this mixin, call connectKeyNavHandlers() in
  7047. // postCreate() and call startupKeyNavChildren() in startup().
  7048. // It provides normalized keyboard and focusing code for Container
  7049. // widgets.
  7050. /*=====
  7051. // focusedChild: [protected] Widget
  7052. // The currently focused child widget, or null if there isn't one
  7053. focusedChild: null,
  7054. =====*/
  7055. // tabIndex: Integer
  7056. // Tab index of the container; same as HTML tabIndex attribute.
  7057. // Note then when user tabs into the container, focus is immediately
  7058. // moved to the first item in the container.
  7059. tabIndex: "0",
  7060. _keyNavCodes: {},
  7061. connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
  7062. // summary:
  7063. // Call in postCreate() to attach the keyboard handlers
  7064. // to the container.
  7065. // preKeyCodes: dojo.keys[]
  7066. // Key codes for navigating to the previous child.
  7067. // nextKeyCodes: dojo.keys[]
  7068. // Key codes for navigating to the next child.
  7069. // tags:
  7070. // protected
  7071. var keyCodes = (this._keyNavCodes = {});
  7072. var prev = dojo.hitch(this, this.focusPrev);
  7073. var next = dojo.hitch(this, this.focusNext);
  7074. dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
  7075. dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
  7076. keyCodes[dojo.keys.HOME] = dojo.hitch(this, "focusFirstChild");
  7077. keyCodes[dojo.keys.END] = dojo.hitch(this, "focusLastChild");
  7078. this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
  7079. this.connect(this.domNode, "onfocus", "_onContainerFocus");
  7080. },
  7081. startupKeyNavChildren: function(){
  7082. // summary:
  7083. // Call in startup() to set child tabindexes to -1
  7084. // tags:
  7085. // protected
  7086. dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
  7087. },
  7088. addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
  7089. // summary:
  7090. // Add a child to our _Container
  7091. dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
  7092. this._startupChild(widget);
  7093. },
  7094. focus: function(){
  7095. // summary:
  7096. // Default focus() implementation: focus the first child.
  7097. this.focusFirstChild();
  7098. },
  7099. focusFirstChild: function(){
  7100. // summary:
  7101. // Focus the first focusable child in the container.
  7102. // tags:
  7103. // protected
  7104. var child = this._getFirstFocusableChild();
  7105. if(child){ // edge case: Menu could be empty or hidden
  7106. this.focusChild(child);
  7107. }
  7108. },
  7109. focusLastChild: function(){
  7110. // summary:
  7111. // Focus the last focusable child in the container.
  7112. // tags:
  7113. // protected
  7114. var child = this._getLastFocusableChild();
  7115. if(child){ // edge case: Menu could be empty or hidden
  7116. this.focusChild(child);
  7117. }
  7118. },
  7119. focusNext: function(){
  7120. // summary:
  7121. // Focus the next widget
  7122. // tags:
  7123. // protected
  7124. var child = this._getNextFocusableChild(this.focusedChild, 1);
  7125. this.focusChild(child);
  7126. },
  7127. focusPrev: function(){
  7128. // summary:
  7129. // Focus the last focusable node in the previous widget
  7130. // (ex: go to the ComboButton icon section rather than button section)
  7131. // tags:
  7132. // protected
  7133. var child = this._getNextFocusableChild(this.focusedChild, -1);
  7134. this.focusChild(child, true);
  7135. },
  7136. focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
  7137. // summary:
  7138. // Focus widget.
  7139. // widget:
  7140. // Reference to container's child widget
  7141. // last:
  7142. // If true and if widget has multiple focusable nodes, focus the
  7143. // last one instead of the first one
  7144. // tags:
  7145. // protected
  7146. if(this.focusedChild && widget !== this.focusedChild){
  7147. this._onChildBlur(this.focusedChild);
  7148. }
  7149. widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
  7150. widget.focus(last ? "end" : "start");
  7151. this._set("focusedChild", widget);
  7152. },
  7153. _startupChild: function(/*dijit._Widget*/ widget){
  7154. // summary:
  7155. // Setup for each child widget
  7156. // description:
  7157. // Sets tabIndex=-1 on each child, so that the tab key will
  7158. // leave the container rather than visiting each child.
  7159. // tags:
  7160. // private
  7161. widget.set("tabIndex", "-1");
  7162. this.connect(widget, "_onFocus", function(){
  7163. // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
  7164. widget.set("tabIndex", this.tabIndex);
  7165. });
  7166. this.connect(widget, "_onBlur", function(){
  7167. widget.set("tabIndex", "-1");
  7168. });
  7169. },
  7170. _onContainerFocus: function(evt){
  7171. // summary:
  7172. // Handler for when the container gets focus
  7173. // description:
  7174. // Initially the container itself has a tabIndex, but when it gets
  7175. // focus, switch focus to first child...
  7176. // tags:
  7177. // private
  7178. // Note that we can't use _onFocus() because switching focus from the
  7179. // _onFocus() handler confuses the focus.js code
  7180. // (because it causes _onFocusNode() to be called recursively)
  7181. // focus bubbles on Firefox,
  7182. // so just make sure that focus has really gone to the container
  7183. if(evt.target !== this.domNode){ return; }
  7184. this.focusFirstChild();
  7185. // and then set the container's tabIndex to -1,
  7186. // (don't remove as that breaks Safari 4)
  7187. // so that tab or shift-tab will go to the fields after/before
  7188. // the container, rather than the container itself
  7189. dojo.attr(this.domNode, "tabIndex", "-1");
  7190. },
  7191. _onBlur: function(evt){
  7192. // When focus is moved away the container, and its descendant (popup) widgets,
  7193. // then restore the container's tabIndex so that user can tab to it again.
  7194. // Note that using _onBlur() so that this doesn't happen when focus is shifted
  7195. // to one of my child widgets (typically a popup)
  7196. if(this.tabIndex){
  7197. dojo.attr(this.domNode, "tabIndex", this.tabIndex);
  7198. }
  7199. this.inherited(arguments);
  7200. },
  7201. _onContainerKeypress: function(evt){
  7202. // summary:
  7203. // When a key is pressed, if it's an arrow key etc. then
  7204. // it's handled here.
  7205. // tags:
  7206. // private
  7207. if(evt.ctrlKey || evt.altKey){ return; }
  7208. var func = this._keyNavCodes[evt.charOrCode];
  7209. if(func){
  7210. func();
  7211. dojo.stopEvent(evt);
  7212. }
  7213. },
  7214. _onChildBlur: function(/*dijit._Widget*/ widget){
  7215. // summary:
  7216. // Called when focus leaves a child widget to go
  7217. // to a sibling widget.
  7218. // tags:
  7219. // protected
  7220. },
  7221. _getFirstFocusableChild: function(){
  7222. // summary:
  7223. // Returns first child that can be focused
  7224. return this._getNextFocusableChild(null, 1); // dijit._Widget
  7225. },
  7226. _getLastFocusableChild: function(){
  7227. // summary:
  7228. // Returns last child that can be focused
  7229. return this._getNextFocusableChild(null, -1); // dijit._Widget
  7230. },
  7231. _getNextFocusableChild: function(child, dir){
  7232. // summary:
  7233. // Returns the next or previous focusable child, compared
  7234. // to "child"
  7235. // child: Widget
  7236. // The current widget
  7237. // dir: Integer
  7238. // * 1 = after
  7239. // * -1 = before
  7240. if(child){
  7241. child = this._getSiblingOfChild(child, dir);
  7242. }
  7243. var children = this.getChildren();
  7244. for(var i=0; i < children.length; i++){
  7245. if(!child){
  7246. child = children[(dir>0) ? 0 : (children.length-1)];
  7247. }
  7248. if(child.isFocusable()){
  7249. return child; // dijit._Widget
  7250. }
  7251. child = this._getSiblingOfChild(child, dir);
  7252. }
  7253. // no focusable child found
  7254. return null; // dijit._Widget
  7255. }
  7256. }
  7257. );
  7258. }
  7259. if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7260. dojo._hasResource["dijit.ToolbarSeparator"] = true;
  7261. dojo.provide("dijit.ToolbarSeparator");
  7262. dojo.declare("dijit.ToolbarSeparator",
  7263. [ dijit._Widget, dijit._Templated ],
  7264. {
  7265. // summary:
  7266. // A spacer between two `dijit.Toolbar` items
  7267. templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
  7268. buildRendering: function(){
  7269. this.inherited(arguments);
  7270. dojo.setSelectable(this.domNode, false);
  7271. },
  7272. isFocusable: function(){
  7273. // summary:
  7274. // This widget isn't focusable, so pass along that fact.
  7275. // tags:
  7276. // protected
  7277. return false;
  7278. }
  7279. });
  7280. }
  7281. if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7282. dojo._hasResource["dijit.Toolbar"] = true;
  7283. dojo.provide("dijit.Toolbar");
  7284. // Note: require of ToolbarSeparator is for back-compat, remove for 2.0
  7285. dojo.declare("dijit.Toolbar",
  7286. [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
  7287. {
  7288. // summary:
  7289. // A Toolbar widget, used to hold things like `dijit.Editor` buttons
  7290. templateString:
  7291. '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
  7292. // '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
  7293. // '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
  7294. // '</table>' +
  7295. '</div>',
  7296. baseClass: "dijitToolbar",
  7297. postCreate: function(){
  7298. this.inherited(arguments);
  7299. this.connectKeyNavHandlers(
  7300. this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
  7301. this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
  7302. );
  7303. },
  7304. startup: function(){
  7305. if(this._started){ return; }
  7306. this.startupKeyNavChildren();
  7307. this.inherited(arguments);
  7308. }
  7309. }
  7310. );
  7311. }
  7312. if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7313. dojo._hasResource["dijit._HasDropDown"] = true;
  7314. dojo.provide("dijit._HasDropDown");
  7315. dojo.declare("dijit._HasDropDown",
  7316. null,
  7317. {
  7318. // summary:
  7319. // Mixin for widgets that need drop down ability.
  7320. // _buttonNode: [protected] DomNode
  7321. // The button/icon/node to click to display the drop down.
  7322. // Can be set via a dojoAttachPoint assignment.
  7323. // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
  7324. _buttonNode: null,
  7325. // _arrowWrapperNode: [protected] DomNode
  7326. // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
  7327. // on where the drop down is set to be positioned.
  7328. // Can be set via a dojoAttachPoint assignment.
  7329. // If missing, then _buttonNode will be used.
  7330. _arrowWrapperNode: null,
  7331. // _popupStateNode: [protected] DomNode
  7332. // The node to set the popupActive class on.
  7333. // Can be set via a dojoAttachPoint assignment.
  7334. // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
  7335. _popupStateNode: null,
  7336. // _aroundNode: [protected] DomNode
  7337. // The node to display the popup around.
  7338. // Can be set via a dojoAttachPoint assignment.
  7339. // If missing, then domNode will be used.
  7340. _aroundNode: null,
  7341. // dropDown: [protected] Widget
  7342. // The widget to display as a popup. This widget *must* be
  7343. // defined before the startup function is called.
  7344. dropDown: null,
  7345. // autoWidth: [protected] Boolean
  7346. // Set to true to make the drop down at least as wide as this
  7347. // widget. Set to false if the drop down should just be its
  7348. // default width
  7349. autoWidth: true,
  7350. // forceWidth: [protected] Boolean
  7351. // Set to true to make the drop down exactly as wide as this
  7352. // widget. Overrides autoWidth.
  7353. forceWidth: false,
  7354. // maxHeight: [protected] Integer
  7355. // The max height for our dropdown.
  7356. // Any dropdown taller than this will have scrollbars.
  7357. // Set to 0 for no max height, or -1 to limit height to available space in viewport
  7358. maxHeight: 0,
  7359. // dropDownPosition: [const] String[]
  7360. // This variable controls the position of the drop down.
  7361. // It's an array of strings with the following values:
  7362. //
  7363. // * before: places drop down to the left of the target node/widget, or to the right in
  7364. // the case of RTL scripts like Hebrew and Arabic
  7365. // * after: places drop down to the right of the target node/widget, or to the left in
  7366. // the case of RTL scripts like Hebrew and Arabic
  7367. // * above: drop down goes above target node
  7368. // * below: drop down goes below target node
  7369. //
  7370. // The list is positions is tried, in order, until a position is found where the drop down fits
  7371. // within the viewport.
  7372. //
  7373. dropDownPosition: ["below","above"],
  7374. // _stopClickEvents: Boolean
  7375. // When set to false, the click events will not be stopped, in
  7376. // case you want to use them in your subwidget
  7377. _stopClickEvents: true,
  7378. _onDropDownMouseDown: function(/*Event*/ e){
  7379. // summary:
  7380. // Callback when the user mousedown's on the arrow icon
  7381. if(this.disabled || this.readOnly){ return; }
  7382. // Prevent default to stop things like text selection, but don't stop propogation, so that:
  7383. // 1. TimeTextBox etc. can focusthe <input> on mousedown
  7384. // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
  7385. // 3. user defined onMouseDown handler fires
  7386. e.preventDefault();
  7387. this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
  7388. this.toggleDropDown();
  7389. },
  7390. _onDropDownMouseUp: function(/*Event?*/ e){
  7391. // summary:
  7392. // Callback when the user lifts their mouse after mouse down on the arrow icon.
  7393. // If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
  7394. // dropDown node. If the event is missing, then we are not
  7395. // a mouseup event.
  7396. //
  7397. // This is useful for the common mouse movement pattern
  7398. // with native browser <select> nodes:
  7399. // 1. mouse down on the select node (probably on the arrow)
  7400. // 2. move mouse to a menu item while holding down the mouse button
  7401. // 3. mouse up. this selects the menu item as though the user had clicked it.
  7402. if(e && this._docHandler){
  7403. this.disconnect(this._docHandler);
  7404. }
  7405. var dropDown = this.dropDown, overMenu = false;
  7406. if(e && this._opened){
  7407. // This code deals with the corner-case when the drop down covers the original widget,
  7408. // because it's so large. In that case mouse-up shouldn't select a value from the menu.
  7409. // Find out if our target is somewhere in our dropdown widget,
  7410. // but not over our _buttonNode (the clickable node)
  7411. var c = dojo.position(this._buttonNode, true);
  7412. if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
  7413. !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
  7414. var t = e.target;
  7415. while(t && !overMenu){
  7416. if(dojo.hasClass(t, "dijitPopup")){
  7417. overMenu = true;
  7418. }else{
  7419. t = t.parentNode;
  7420. }
  7421. }
  7422. if(overMenu){
  7423. t = e.target;
  7424. if(dropDown.onItemClick){
  7425. var menuItem;
  7426. while(t && !(menuItem = dijit.byNode(t))){
  7427. t = t.parentNode;
  7428. }
  7429. if(menuItem && menuItem.onClick && menuItem.getParent){
  7430. menuItem.getParent().onItemClick(menuItem, e);
  7431. }
  7432. }
  7433. return;
  7434. }
  7435. }
  7436. }
  7437. if(this._opened && dropDown.focus && dropDown.autoFocus !== false){
  7438. // Focus the dropdown widget - do it on a delay so that we
  7439. // don't steal our own focus.
  7440. window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
  7441. }
  7442. },
  7443. _onDropDownClick: function(/*Event*/ e){
  7444. // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
  7445. if(this._stopClickEvents){
  7446. dojo.stopEvent(e);
  7447. }
  7448. },
  7449. buildRendering: function(){
  7450. this.inherited(arguments);
  7451. this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
  7452. this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
  7453. // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
  7454. // based on where drop down will normally appear
  7455. var defaultPos = {
  7456. "after" : this.isLeftToRight() ? "Right" : "Left",
  7457. "before" : this.isLeftToRight() ? "Left" : "Right",
  7458. "above" : "Up",
  7459. "below" : "Down",
  7460. "left" : "Left",
  7461. "right" : "Right"
  7462. }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
  7463. dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
  7464. },
  7465. postCreate: function(){
  7466. // summary:
  7467. // set up nodes and connect our mouse and keypress events
  7468. this.inherited(arguments);
  7469. this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
  7470. this.connect(this._buttonNode, "onclick", "_onDropDownClick");
  7471. this.connect(this.focusNode, "onkeypress", "_onKey");
  7472. this.connect(this.focusNode, "onkeyup", "_onKeyUp");
  7473. },
  7474. destroy: function(){
  7475. if(this.dropDown){
  7476. // Destroy the drop down, unless it's already been destroyed. This can happen because
  7477. // the drop down is a direct child of <body> even though it's logically my child.
  7478. if(!this.dropDown._destroyed){
  7479. this.dropDown.destroyRecursive();
  7480. }
  7481. delete this.dropDown;
  7482. }
  7483. this.inherited(arguments);
  7484. },
  7485. _onKey: function(/*Event*/ e){
  7486. // summary:
  7487. // Callback when the user presses a key while focused on the button node
  7488. if(this.disabled || this.readOnly){ return; }
  7489. var d = this.dropDown, target = e.target;
  7490. if(d && this._opened && d.handleKey){
  7491. if(d.handleKey(e) === false){
  7492. /* false return code means that the drop down handled the key */
  7493. dojo.stopEvent(e);
  7494. return;
  7495. }
  7496. }
  7497. if(d && this._opened && e.charOrCode == dojo.keys.ESCAPE){
  7498. this.closeDropDown();
  7499. dojo.stopEvent(e);
  7500. }else if(!this._opened &&
  7501. (e.charOrCode == dojo.keys.DOWN_ARROW ||
  7502. ( (e.charOrCode == dojo.keys.ENTER || e.charOrCode == " ") &&
  7503. //ignore enter and space if the event is for a text input
  7504. ((target.tagName || "").toLowerCase() !== 'input' ||
  7505. (target.type && target.type.toLowerCase() !== 'text'))))){
  7506. // Toggle the drop down, but wait until keyup so that the drop down doesn't
  7507. // get a stray keyup event, or in the case of key-repeat (because user held
  7508. // down key for too long), stray keydown events
  7509. this._toggleOnKeyUp = true;
  7510. dojo.stopEvent(e);
  7511. }
  7512. },
  7513. _onKeyUp: function(){
  7514. if(this._toggleOnKeyUp){
  7515. delete this._toggleOnKeyUp;
  7516. this.toggleDropDown();
  7517. var d = this.dropDown; // drop down may not exist until toggleDropDown() call
  7518. if(d && d.focus){
  7519. setTimeout(dojo.hitch(d, "focus"), 1);
  7520. }
  7521. }
  7522. },
  7523. _onBlur: function(){
  7524. // summary:
  7525. // Called magically when focus has shifted away from this widget and it's dropdown
  7526. // Don't focus on button if the user has explicitly focused on something else (happens
  7527. // when user clicks another control causing the current popup to close)..
  7528. // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
  7529. // it when you display:none a node with focus.
  7530. var focusMe = dijit._curFocus && this.dropDown && dojo.isDescendant(dijit._curFocus, this.dropDown.domNode);
  7531. this.closeDropDown(focusMe);
  7532. this.inherited(arguments);
  7533. },
  7534. isLoaded: function(){
  7535. // summary:
  7536. // Returns whether or not the dropdown is loaded. This can
  7537. // be overridden in order to force a call to loadDropDown().
  7538. // tags:
  7539. // protected
  7540. return true;
  7541. },
  7542. loadDropDown: function(/* Function */ loadCallback){
  7543. // summary:
  7544. // Loads the data for the dropdown, and at some point, calls
  7545. // the given callback. This is basically a callback when the
  7546. // user presses the down arrow button to open the drop down.
  7547. // tags:
  7548. // protected
  7549. loadCallback();
  7550. },
  7551. toggleDropDown: function(){
  7552. // summary:
  7553. // Callback when the user presses the down arrow button or presses
  7554. // the down arrow key to open/close the drop down.
  7555. // Toggle the drop-down widget; if it is up, close it, if not, open it
  7556. // tags:
  7557. // protected
  7558. if(this.disabled || this.readOnly){ return; }
  7559. if(!this._opened){
  7560. // If we aren't loaded, load it first so there isn't a flicker
  7561. if(!this.isLoaded()){
  7562. this.loadDropDown(dojo.hitch(this, "openDropDown"));
  7563. return;
  7564. }else{
  7565. this.openDropDown();
  7566. }
  7567. }else{
  7568. this.closeDropDown();
  7569. }
  7570. },
  7571. openDropDown: function(){
  7572. // summary:
  7573. // Opens the dropdown for this widget. To be called only when this.dropDown
  7574. // has been created and is ready to display (ie, it's data is loaded).
  7575. // returns:
  7576. // return value of dijit.popup.open()
  7577. // tags:
  7578. // protected
  7579. var dropDown = this.dropDown,
  7580. ddNode = dropDown.domNode,
  7581. aroundNode = this._aroundNode || this.domNode,
  7582. self = this;
  7583. // Prepare our popup's height and honor maxHeight if it exists.
  7584. // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
  7585. // ie, dependent on how much space is available (BK)
  7586. if(!this._preparedNode){
  7587. this._preparedNode = true;
  7588. // Check if we have explicitly set width and height on the dropdown widget dom node
  7589. if(ddNode.style.width){
  7590. this._explicitDDWidth = true;
  7591. }
  7592. if(ddNode.style.height){
  7593. this._explicitDDHeight = true;
  7594. }
  7595. }
  7596. // Code for resizing dropdown (height limitation, or increasing width to match my width)
  7597. if(this.maxHeight || this.forceWidth || this.autoWidth){
  7598. var myStyle = {
  7599. display: "",
  7600. visibility: "hidden"
  7601. };
  7602. if(!this._explicitDDWidth){
  7603. myStyle.width = "";
  7604. }
  7605. if(!this._explicitDDHeight){
  7606. myStyle.height = "";
  7607. }
  7608. dojo.style(ddNode, myStyle);
  7609. // Figure out maximum height allowed (if there is a height restriction)
  7610. var maxHeight = this.maxHeight;
  7611. if(maxHeight == -1){
  7612. // limit height to space available in viewport either above or below my domNode
  7613. // (whichever side has more room)
  7614. var viewport = dojo.window.getBox(),
  7615. position = dojo.position(aroundNode, false);
  7616. maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
  7617. }
  7618. // Attach dropDown to DOM and make make visibility:hidden rather than display:none
  7619. // so we call startup() and also get the size
  7620. if(dropDown.startup && !dropDown._started){
  7621. dropDown.startup();
  7622. }
  7623. dijit.popup.moveOffScreen(dropDown);
  7624. // Get size of drop down, and determine if vertical scroll bar needed
  7625. var mb = dojo._getMarginSize(ddNode);
  7626. var overHeight = (maxHeight && mb.h > maxHeight);
  7627. dojo.style(ddNode, {
  7628. overflowX: "hidden",
  7629. overflowY: overHeight ? "auto" : "hidden"
  7630. });
  7631. if(overHeight){
  7632. mb.h = maxHeight;
  7633. if("w" in mb){
  7634. mb.w += 16; // room for vertical scrollbar
  7635. }
  7636. }else{
  7637. delete mb.h;
  7638. }
  7639. // Adjust dropdown width to match or be larger than my width
  7640. if(this.forceWidth){
  7641. mb.w = aroundNode.offsetWidth;
  7642. }else if(this.autoWidth){
  7643. mb.w = Math.max(mb.w, aroundNode.offsetWidth);
  7644. }else{
  7645. delete mb.w;
  7646. }
  7647. // And finally, resize the dropdown to calculated height and width
  7648. if(dojo.isFunction(dropDown.resize)){
  7649. dropDown.resize(mb);
  7650. }else{
  7651. dojo.marginBox(ddNode, mb);
  7652. }
  7653. }
  7654. var retVal = dijit.popup.open({
  7655. parent: this,
  7656. popup: dropDown,
  7657. around: aroundNode,
  7658. orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
  7659. onExecute: function(){
  7660. self.closeDropDown(true);
  7661. },
  7662. onCancel: function(){
  7663. self.closeDropDown(true);
  7664. },
  7665. onClose: function(){
  7666. dojo.attr(self._popupStateNode, "popupActive", false);
  7667. dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
  7668. self._opened = false;
  7669. }
  7670. });
  7671. dojo.attr(this._popupStateNode, "popupActive", "true");
  7672. dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
  7673. this._opened=true;
  7674. // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
  7675. return retVal;
  7676. },
  7677. closeDropDown: function(/*Boolean*/ focus){
  7678. // summary:
  7679. // Closes the drop down on this widget
  7680. // focus:
  7681. // If true, refocuses the button widget
  7682. // tags:
  7683. // protected
  7684. if(this._opened){
  7685. if(focus){ this.focus(); }
  7686. dijit.popup.close(this.dropDown);
  7687. this._opened = false;
  7688. }
  7689. }
  7690. }
  7691. );
  7692. }
  7693. if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7694. dojo._hasResource["dijit.form.Button"] = true;
  7695. dojo.provide("dijit.form.Button");
  7696. dojo.declare("dijit.form.Button",
  7697. dijit.form._FormWidget,
  7698. {
  7699. // summary:
  7700. // Basically the same thing as a normal HTML button, but with special styling.
  7701. // description:
  7702. // Buttons can display a label, an icon, or both.
  7703. // A label should always be specified (through innerHTML) or the label
  7704. // attribute. It can be hidden via showLabel=false.
  7705. // example:
  7706. // | <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
  7707. //
  7708. // example:
  7709. // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
  7710. // | dojo.body().appendChild(button1.domNode);
  7711. // label: HTML String
  7712. // Text to display in button.
  7713. // If the label is hidden (showLabel=false) then and no title has
  7714. // been specified, then label is also set as title attribute of icon.
  7715. label: "",
  7716. // showLabel: Boolean
  7717. // Set this to true to hide the label text and display only the icon.
  7718. // (If showLabel=false then iconClass must be specified.)
  7719. // Especially useful for toolbars.
  7720. // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
  7721. //
  7722. // The exception case is for computers in high-contrast mode, where the label
  7723. // will still be displayed, since the icon doesn't appear.
  7724. showLabel: true,
  7725. // iconClass: String
  7726. // Class to apply to DOMNode in button to make it display an icon
  7727. iconClass: "",
  7728. // type: String
  7729. // Defines the type of button. "button", "submit", or "reset".
  7730. type: "button",
  7731. baseClass: "dijitButton",
  7732. templateString: dojo.cache("dijit.form", "templates/Button.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdojoAttachPoint=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
  7733. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  7734. value: "valueNode"
  7735. }),
  7736. _onClick: function(/*Event*/ e){
  7737. // summary:
  7738. // Internal function to handle click actions
  7739. if(this.disabled){
  7740. return false;
  7741. }
  7742. this._clicked(); // widget click actions
  7743. return this.onClick(e); // user click actions
  7744. },
  7745. _onButtonClick: function(/*Event*/ e){
  7746. // summary:
  7747. // Handler when the user activates the button portion.
  7748. if(this._onClick(e) === false){ // returning nothing is same as true
  7749. e.preventDefault(); // needed for checkbox
  7750. }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
  7751. for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
  7752. var widget=dijit.byNode(node);
  7753. if(widget && typeof widget._onSubmit == "function"){
  7754. widget._onSubmit(e);
  7755. break;
  7756. }
  7757. }
  7758. }else if(this.valueNode){
  7759. this.valueNode.click();
  7760. e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
  7761. }
  7762. },
  7763. buildRendering: function(){
  7764. this.inherited(arguments);
  7765. dojo.setSelectable(this.focusNode, false);
  7766. },
  7767. _fillContent: function(/*DomNode*/ source){
  7768. // Overrides _Templated._fillContent().
  7769. // If button label is specified as srcNodeRef.innerHTML rather than
  7770. // this.params.label, handle it here.
  7771. // TODO: remove the method in 2.0, parser will do it all for me
  7772. if(source && (!this.params || !("label" in this.params))){
  7773. this.set('label', source.innerHTML);
  7774. }
  7775. },
  7776. _setShowLabelAttr: function(val){
  7777. if(this.containerNode){
  7778. dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
  7779. }
  7780. this._set("showLabel", val);
  7781. },
  7782. onClick: function(/*Event*/ e){
  7783. // summary:
  7784. // Callback for when button is clicked.
  7785. // If type="submit", return true to perform submit, or false to cancel it.
  7786. // type:
  7787. // callback
  7788. return true; // Boolean
  7789. },
  7790. _clicked: function(/*Event*/ e){
  7791. // summary:
  7792. // Internal overridable function for when the button is clicked
  7793. },
  7794. setLabel: function(/*String*/ content){
  7795. // summary:
  7796. // Deprecated. Use set('label', ...) instead.
  7797. dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
  7798. this.set("label", content);
  7799. },
  7800. _setLabelAttr: function(/*String*/ content){
  7801. // summary:
  7802. // Hook for set('label', ...) to work.
  7803. // description:
  7804. // Set the label (text) of the button; takes an HTML string.
  7805. this._set("label", content);
  7806. this.containerNode.innerHTML = content;
  7807. if(this.showLabel == false && !this.params.title){
  7808. this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
  7809. }
  7810. },
  7811. _setIconClassAttr: function(/*String*/ val){
  7812. // Custom method so that icon node is hidden when not in use, to avoid excess padding/margin
  7813. // appearing around it (even if it's a 0x0 sized <img> node)
  7814. var oldVal = this.iconClass || "dijitNoIcon",
  7815. newVal = val || "dijitNoIcon";
  7816. dojo.replaceClass(this.iconNode, newVal, oldVal);
  7817. this._set("iconClass", val);
  7818. }
  7819. });
  7820. dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
  7821. // summary:
  7822. // A button with a drop down
  7823. //
  7824. // example:
  7825. // | <button dojoType="dijit.form.DropDownButton" label="Hello world">
  7826. // | <div dojotype="dijit.Menu">...</div>
  7827. // | </button>
  7828. //
  7829. // example:
  7830. // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
  7831. // | dojo.body().appendChild(button1);
  7832. //
  7833. baseClass : "dijitDropDownButton",
  7834. templateString: dojo.cache("dijit.form", "templates/DropDownButton.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\" dojoAttachPoint=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdojoAttachPoint=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdojoAttachPoint=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
  7835. _fillContent: function(){
  7836. // Overrides Button._fillContent().
  7837. //
  7838. // My inner HTML contains both the button contents and a drop down widget, like
  7839. // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
  7840. // The first node is assumed to be the button content. The widget is the popup.
  7841. if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
  7842. //FIXME: figure out how to filter out the widget and use all remaining nodes as button
  7843. // content, not just nodes[0]
  7844. var nodes = dojo.query("*", this.srcNodeRef);
  7845. dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
  7846. // save pointer to srcNode so we can grab the drop down widget after it's instantiated
  7847. this.dropDownContainer = this.srcNodeRef;
  7848. }
  7849. },
  7850. startup: function(){
  7851. if(this._started){ return; }
  7852. // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
  7853. // make it invisible, and store a reference to pass to the popup code.
  7854. if(!this.dropDown && this.dropDownContainer){
  7855. var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
  7856. this.dropDown = dijit.byNode(dropDownNode);
  7857. delete this.dropDownContainer;
  7858. }
  7859. if(this.dropDown){
  7860. dijit.popup.hide(this.dropDown);
  7861. }
  7862. this.inherited(arguments);
  7863. },
  7864. isLoaded: function(){
  7865. // Returns whether or not we are loaded - if our dropdown has an href,
  7866. // then we want to check that.
  7867. var dropDown = this.dropDown;
  7868. return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
  7869. },
  7870. loadDropDown: function(){
  7871. // Loads our dropdown
  7872. var dropDown = this.dropDown;
  7873. if(!dropDown){ return; }
  7874. if(!this.isLoaded()){
  7875. var handler = dojo.connect(dropDown, "onLoad", this, function(){
  7876. dojo.disconnect(handler);
  7877. this.openDropDown();
  7878. });
  7879. dropDown.refresh();
  7880. }else{
  7881. this.openDropDown();
  7882. }
  7883. },
  7884. isFocusable: function(){
  7885. // Overridden so that focus is handled by the _HasDropDown mixin, not by
  7886. // the _FormWidget mixin.
  7887. return this.inherited(arguments) && !this._mouseDown;
  7888. }
  7889. });
  7890. dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
  7891. // summary:
  7892. // A combination button and drop-down button.
  7893. // Users can click one side to "press" the button, or click an arrow
  7894. // icon to display the drop down.
  7895. //
  7896. // example:
  7897. // | <button dojoType="dijit.form.ComboButton" onClick="...">
  7898. // | <span>Hello world</span>
  7899. // | <div dojoType="dijit.Menu">...</div>
  7900. // | </button>
  7901. //
  7902. // example:
  7903. // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
  7904. // | dojo.body().appendChild(button1.domNode);
  7905. //
  7906. templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" dojoAttachPoint=\"buttonNode\" dojoAttachEvent=\"ondijitclick:_onButtonClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" role=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdojoAttachPoint=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" dojoAttachPoint=\"valueNode\"\n\t\t/></td></tr></tbody\n></table>\n"),
  7907. attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
  7908. id: "",
  7909. tabIndex: ["focusNode", "titleNode"],
  7910. title: "titleNode"
  7911. }),
  7912. // optionsTitle: String
  7913. // Text that describes the options menu (accessibility)
  7914. optionsTitle: "",
  7915. baseClass: "dijitComboButton",
  7916. // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
  7917. // mouse action over specified node
  7918. cssStateNodes: {
  7919. "buttonNode": "dijitButtonNode",
  7920. "titleNode": "dijitButtonContents",
  7921. "_popupStateNode": "dijitDownArrowButton"
  7922. },
  7923. _focusedNode: null,
  7924. _onButtonKeyPress: function(/*Event*/ evt){
  7925. // summary:
  7926. // Handler for right arrow key when focus is on left part of button
  7927. if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
  7928. dijit.focus(this._popupStateNode);
  7929. dojo.stopEvent(evt);
  7930. }
  7931. },
  7932. _onArrowKeyPress: function(/*Event*/ evt){
  7933. // summary:
  7934. // Handler for left arrow key when focus is on right part of button
  7935. if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
  7936. dijit.focus(this.titleNode);
  7937. dojo.stopEvent(evt);
  7938. }
  7939. },
  7940. focus: function(/*String*/ position){
  7941. // summary:
  7942. // Focuses this widget to according to position, if specified,
  7943. // otherwise on arrow node
  7944. // position:
  7945. // "start" or "end"
  7946. if(!this.disabled){
  7947. dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
  7948. }
  7949. }
  7950. });
  7951. dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
  7952. // summary:
  7953. // A button that can be in two states (checked or not).
  7954. // Can be base class for things like tabs or checkbox or radio buttons
  7955. baseClass: "dijitToggleButton",
  7956. // checked: Boolean
  7957. // Corresponds to the native HTML <input> element's attribute.
  7958. // In markup, specified as "checked='checked'" or just "checked".
  7959. // True if the button is depressed, or the checkbox is checked,
  7960. // or the radio button is selected, etc.
  7961. checked: false,
  7962. attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
  7963. checked:"focusNode"
  7964. }),
  7965. _clicked: function(/*Event*/ evt){
  7966. this.set('checked', !this.checked);
  7967. },
  7968. _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
  7969. this._set("checked", value);
  7970. dojo.attr(this.focusNode || this.domNode, "checked", value);
  7971. dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
  7972. this._handleOnChange(value, priorityChange);
  7973. },
  7974. setChecked: function(/*Boolean*/ checked){
  7975. // summary:
  7976. // Deprecated. Use set('checked', true/false) instead.
  7977. dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
  7978. this.set('checked', checked);
  7979. },
  7980. reset: function(){
  7981. // summary:
  7982. // Reset the widget's value to what it was at initialization time
  7983. this._hasBeenBlurred = false;
  7984. // set checked state to original setting
  7985. this.set('checked', this.params.checked || false);
  7986. }
  7987. });
  7988. }
  7989. if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7990. dojo._hasResource["dijit._editor._Plugin"] = true;
  7991. dojo.provide("dijit._editor._Plugin");
  7992. dojo.declare("dijit._editor._Plugin", null, {
  7993. // summary
  7994. // Base class for a "plugin" to the editor, which is usually
  7995. // a single button on the Toolbar and some associated code
  7996. constructor: function(/*Object?*/args, /*DomNode?*/node){
  7997. this.params = args || {};
  7998. dojo.mixin(this, this.params);
  7999. this._connects=[];
  8000. this._attrPairNames = {};
  8001. },
  8002. // editor: [const] dijit.Editor
  8003. // Points to the parent editor
  8004. editor: null,
  8005. // iconClassPrefix: [const] String
  8006. // The CSS class name for the button node is formed from `iconClassPrefix` and `command`
  8007. iconClassPrefix: "dijitEditorIcon",
  8008. // button: dijit._Widget?
  8009. // Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`)
  8010. // that is added to the toolbar to control this plugin.
  8011. // If not specified, will be created on initialization according to `buttonClass`
  8012. button: null,
  8013. // command: String
  8014. // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command.
  8015. // Passed to editor.execCommand() if `useDefaultCommand` is true.
  8016. command: "",
  8017. // useDefaultCommand: Boolean
  8018. // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`.
  8019. useDefaultCommand: true,
  8020. // buttonClass: Widget Class
  8021. // Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect)
  8022. // that is added to the toolbar to control this plugin.
  8023. // This is used to instantiate the button, unless `button` itself is specified directly.
  8024. buttonClass: dijit.form.Button,
  8025. // disabled: Boolean
  8026. // Flag to indicate if this plugin has been disabled and should do nothing
  8027. // helps control button state, among other things. Set via the setter api.
  8028. disabled: false,
  8029. getLabel: function(/*String*/key){
  8030. // summary:
  8031. // Returns the label to use for the button
  8032. // tags:
  8033. // private
  8034. return this.editor.commands[key]; // String
  8035. },
  8036. _initButton: function(){
  8037. // summary:
  8038. // Initialize the button or other widget that will control this plugin.
  8039. // This code only works for plugins controlling built-in commands in the editor.
  8040. // tags:
  8041. // protected extension
  8042. if(this.command.length){
  8043. var label = this.getLabel(this.command),
  8044. editor = this.editor,
  8045. className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
  8046. if(!this.button){
  8047. var props = dojo.mixin({
  8048. label: label,
  8049. dir: editor.dir,
  8050. lang: editor.lang,
  8051. showLabel: false,
  8052. iconClass: className,
  8053. dropDown: this.dropDown,
  8054. tabIndex: "-1"
  8055. }, this.params || {});
  8056. this.button = new this.buttonClass(props);
  8057. }
  8058. }
  8059. if(this.get("disabled") && this.button){
  8060. this.button.set("disabled", this.get("disabled"));
  8061. }
  8062. },
  8063. destroy: function(){
  8064. // summary:
  8065. // Destroy this plugin
  8066. dojo.forEach(this._connects, dojo.disconnect);
  8067. if(this.dropDown){
  8068. this.dropDown.destroyRecursive();
  8069. }
  8070. },
  8071. connect: function(o, f, tf){
  8072. // summary:
  8073. // Make a dojo.connect() that is automatically disconnected when this plugin is destroyed.
  8074. // Similar to `dijit._Widget.connect`.
  8075. // tags:
  8076. // protected
  8077. this._connects.push(dojo.connect(o, f, this, tf));
  8078. },
  8079. updateState: function(){
  8080. // summary:
  8081. // Change state of the plugin to respond to events in the editor.
  8082. // description:
  8083. // This is called on meaningful events in the editor, such as change of selection
  8084. // or caret position (but not simple typing of alphanumeric keys). It gives the
  8085. // plugin a chance to update the CSS of its button.
  8086. //
  8087. // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the
  8088. // characters next to the caret are bold or not.
  8089. //
  8090. // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`).
  8091. var e = this.editor,
  8092. c = this.command,
  8093. checked, enabled;
  8094. if(!e || !e.isLoaded || !c.length){ return; }
  8095. var disabled = this.get("disabled");
  8096. if(this.button){
  8097. try{
  8098. enabled = !disabled && e.queryCommandEnabled(c);
  8099. if(this.enabled !== enabled){
  8100. this.enabled = enabled;
  8101. this.button.set('disabled', !enabled);
  8102. }
  8103. if(typeof this.button.checked == 'boolean'){
  8104. checked = e.queryCommandState(c);
  8105. if(this.checked !== checked){
  8106. this.checked = checked;
  8107. this.button.set('checked', e.queryCommandState(c));
  8108. }
  8109. }
  8110. }catch(e){
  8111. console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error?
  8112. }
  8113. }
  8114. },
  8115. setEditor: function(/*dijit.Editor*/ editor){
  8116. // summary:
  8117. // Tell the plugin which Editor it is associated with.
  8118. // TODO: refactor code to just pass editor to constructor.
  8119. // FIXME: detach from previous editor!!
  8120. this.editor = editor;
  8121. // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
  8122. this._initButton();
  8123. // Processing for buttons that execute by calling editor.execCommand()
  8124. if(this.button && this.useDefaultCommand){
  8125. if(this.editor.queryCommandAvailable(this.command)){
  8126. this.connect(this.button, "onClick",
  8127. dojo.hitch(this.editor, "execCommand", this.command, this.commandArg)
  8128. );
  8129. }else{
  8130. // hide button because editor doesn't support command (due to browser limitations)
  8131. this.button.domNode.style.display = "none";
  8132. }
  8133. }
  8134. this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
  8135. },
  8136. setToolbar: function(/*dijit.Toolbar*/ toolbar){
  8137. // summary:
  8138. // Tell the plugin to add it's controller widget (often a button)
  8139. // to the toolbar. Does nothing if there is no controller widget.
  8140. // TODO: refactor code to just pass toolbar to constructor.
  8141. if(this.button){
  8142. toolbar.addChild(this.button);
  8143. }
  8144. // console.debug("adding", this.button, "to:", toolbar);
  8145. },
  8146. set: function(/* attribute */ name, /* anything */ value){
  8147. // summary:
  8148. // Set a property on a plugin
  8149. // name:
  8150. // The property to set.
  8151. // value:
  8152. // The value to set in the property.
  8153. // description:
  8154. // Sets named properties on a plugin which may potentially be handled by a
  8155. // setter in the plugin.
  8156. // For example, if the plugin has a properties "foo"
  8157. // and "bar" and a method named "_setFooAttr", calling:
  8158. // | plugin.set("foo", "Howdy!");
  8159. // would be equivalent to writing:
  8160. // | plugin._setFooAttr("Howdy!");
  8161. // and:
  8162. // | plugin.set("bar", 3);
  8163. // would be equivalent to writing:
  8164. // | plugin.bar = 3;
  8165. //
  8166. // set() may also be called with a hash of name/value pairs, ex:
  8167. // | plugin.set({
  8168. // | foo: "Howdy",
  8169. // | bar: 3
  8170. // | })
  8171. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  8172. if(typeof name === "object"){
  8173. for(var x in name){
  8174. this.set(x, name[x]);
  8175. }
  8176. return this;
  8177. }
  8178. var names = this._getAttrNames(name);
  8179. if(this[names.s]){
  8180. // use the explicit setter
  8181. var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
  8182. }else{
  8183. this._set(name, value);
  8184. }
  8185. return result || this;
  8186. },
  8187. get: function(name){
  8188. // summary:
  8189. // Get a property from a plugin.
  8190. // name:
  8191. // The property to get.
  8192. // description:
  8193. // Get a named property from a plugin. The property may
  8194. // potentially be retrieved via a getter method. If no getter is defined, this
  8195. // just retrieves the object's property.
  8196. // For example, if the plugin has a properties "foo"
  8197. // and "bar" and a method named "_getFooAttr", calling:
  8198. // | plugin.get("foo");
  8199. // would be equivalent to writing:
  8200. // | plugin._getFooAttr();
  8201. // and:
  8202. // | plugin.get("bar");
  8203. // would be equivalent to writing:
  8204. // | plugin.bar;
  8205. var names = this._getAttrNames(name);
  8206. return this[names.g] ? this[names.g]() : this[name];
  8207. },
  8208. _setDisabledAttr: function(disabled){
  8209. // summary:
  8210. // Function to set the plugin state and call updateState to make sure the
  8211. // button is updated appropriately.
  8212. this.disabled = disabled;
  8213. this.updateState();
  8214. },
  8215. _getAttrNames: function(name){
  8216. // summary:
  8217. // Helper function for get() and set().
  8218. // Caches attribute name values so we don't do the string ops every time.
  8219. // tags:
  8220. // private
  8221. var apn = this._attrPairNames;
  8222. if(apn[name]){ return apn[name]; }
  8223. var uc = name.charAt(0).toUpperCase() + name.substr(1);
  8224. return (apn[name] = {
  8225. s: "_set"+uc+"Attr",
  8226. g: "_get"+uc+"Attr"
  8227. });
  8228. },
  8229. _set: function(/*String*/ name, /*anything*/ value){
  8230. // summary:
  8231. // Helper function to set new value for specified attribute
  8232. var oldValue = this[name];
  8233. this[name] = value;
  8234. }
  8235. });
  8236. }
  8237. if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8238. dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true;
  8239. dojo.provide("dijit._editor.plugins.EnterKeyHandling");
  8240. dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, {
  8241. // summary:
  8242. // This plugin tries to make all browsers behave consistently with regard to
  8243. // how ENTER behaves in the editor window. It traps the ENTER key and alters
  8244. // the way DOM is constructed in certain cases to try to commonize the generated
  8245. // DOM and behaviors across browsers.
  8246. //
  8247. // description:
  8248. // This plugin has three modes:
  8249. //
  8250. // * blockModeForEnter=BR
  8251. // * blockModeForEnter=DIV
  8252. // * blockModeForEnter=P
  8253. //
  8254. // In blockModeForEnter=P, the ENTER key starts a new
  8255. // paragraph, and shift-ENTER starts a new line in the current paragraph.
  8256. // For example, the input:
  8257. //
  8258. // | first paragraph <shift-ENTER>
  8259. // | second line of first paragraph <ENTER>
  8260. // | second paragraph
  8261. //
  8262. // will generate:
  8263. //
  8264. // | <p>
  8265. // | first paragraph
  8266. // | <br/>
  8267. // | second line of first paragraph
  8268. // | </p>
  8269. // | <p>
  8270. // | second paragraph
  8271. // | </p>
  8272. //
  8273. // In BR and DIV mode, the ENTER key conceptually goes to a new line in the
  8274. // current paragraph, and users conceptually create a new paragraph by pressing ENTER twice.
  8275. // For example, if the user enters text into an editor like this:
  8276. //
  8277. // | one <ENTER>
  8278. // | two <ENTER>
  8279. // | three <ENTER>
  8280. // | <ENTER>
  8281. // | four <ENTER>
  8282. // | five <ENTER>
  8283. // | six <ENTER>
  8284. //
  8285. // It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates:
  8286. //
  8287. // BR:
  8288. // | one<br/>
  8289. // | two<br/>
  8290. // | three<br/>
  8291. // | <br/>
  8292. // | four<br/>
  8293. // | five<br/>
  8294. // | six<br/>
  8295. //
  8296. // DIV:
  8297. // | <div>one</div>
  8298. // | <div>two</div>
  8299. // | <div>three</div>
  8300. // | <div>&nbsp;</div>
  8301. // | <div>four</div>
  8302. // | <div>five</div>
  8303. // | <div>six</div>
  8304. // blockNodeForEnter: String
  8305. // This property decides the behavior of Enter key. It can be either P,
  8306. // DIV, BR, or empty (which means disable this feature). Anything else
  8307. // will trigger errors. The default is 'BR'
  8308. //
  8309. // See class description for more details.
  8310. blockNodeForEnter: 'BR',
  8311. constructor: function(args){
  8312. if(args){
  8313. if("blockNodeForEnter" in args){
  8314. args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase();
  8315. }
  8316. dojo.mixin(this,args);
  8317. }
  8318. },
  8319. setEditor: function(editor){
  8320. // Overrides _Plugin.setEditor().
  8321. if(this.editor === editor) { return; }
  8322. this.editor = editor;
  8323. if(this.blockNodeForEnter == 'BR'){
  8324. // While Moz has a mode tht mostly works, it's still a little different,
  8325. // So, try to just have a common mode and be consistent. Which means
  8326. // we need to enable customUndo, if not already enabled.
  8327. this.editor.customUndo = true;
  8328. editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){
  8329. this.connect(editor.document, "onkeypress", function(e){
  8330. if(e.charOrCode == dojo.keys.ENTER){
  8331. // Just do it manually. The handleEnterKey has a shift mode that
  8332. // Always acts like <br>, so just use it.
  8333. var ne = dojo.mixin({},e);
  8334. ne.shiftKey = true;
  8335. if(!this.handleEnterKey(ne)){
  8336. dojo.stopEvent(e);
  8337. }
  8338. }
  8339. });
  8340. if(dojo.isIE >= 9){
  8341. this.connect(editor.document.body, "onpaste", function(e){
  8342. setTimeout(dojo.hitch(this, function(){
  8343. // Use the old range/selection code to kick IE 9 into updating
  8344. // its range by moving it back, then forward, one 'character'.
  8345. var r = this.editor.document.selection.createRange();
  8346. r.move('character',-1);
  8347. r.select();
  8348. r.move('character',1);
  8349. r.select();
  8350. }),0);
  8351. });
  8352. }
  8353. return d;
  8354. }));
  8355. }else if(this.blockNodeForEnter){
  8356. // add enter key handler
  8357. // FIXME: need to port to the new event code!!
  8358. var h = dojo.hitch(this,this.handleEnterKey);
  8359. editor.addKeyHandler(13, 0, 0, h); //enter
  8360. editor.addKeyHandler(13, 0, 1, h); //shift+enter
  8361. this.connect(this.editor,'onKeyPressed','onKeyPressed');
  8362. }
  8363. },
  8364. onKeyPressed: function(e){
  8365. // summary:
  8366. // Handler for keypress events.
  8367. // tags:
  8368. // private
  8369. if(this._checkListLater){
  8370. if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){
  8371. var liparent=dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, ['LI']);
  8372. if(!liparent){
  8373. // circulate the undo detection code by calling RichText::execCommand directly
  8374. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
  8375. // set the innerHTML of the new block node
  8376. var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]);
  8377. if(block){
  8378. block.innerHTML=this.bogusHtmlContent;
  8379. if(dojo.isIE <= 9){
  8380. // move to the start by moving backwards one char
  8381. var r = this.editor.document.selection.createRange();
  8382. r.move('character',-1);
  8383. r.select();
  8384. }
  8385. }else{
  8386. console.error('onKeyPressed: Cannot find the new block node'); // FIXME
  8387. }
  8388. }else{
  8389. if(dojo.isMoz){
  8390. if(liparent.parentNode.parentNode.nodeName == 'LI'){
  8391. liparent=liparent.parentNode.parentNode;
  8392. }
  8393. }
  8394. var fc=liparent.firstChild;
  8395. if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){
  8396. liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'),fc);
  8397. var newrange = dijit.range.create(this.editor.window);
  8398. newrange.setStart(liparent.firstChild,0);
  8399. var selection = dijit.range.getSelection(this.editor.window, true);
  8400. selection.removeAllRanges();
  8401. selection.addRange(newrange);
  8402. }
  8403. }
  8404. }
  8405. this._checkListLater = false;
  8406. }
  8407. if(this._pressedEnterInBlock){
  8408. // the new created is the original current P, so we have previousSibling below
  8409. if(this._pressedEnterInBlock.previousSibling){
  8410. this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
  8411. }
  8412. delete this._pressedEnterInBlock;
  8413. }
  8414. },
  8415. // bogusHtmlContent: [private] String
  8416. // HTML to stick into a new empty block
  8417. bogusHtmlContent: '&nbsp;',
  8418. // blockNodes: [private] Regex
  8419. // Regex for testing if a given tag is a block level (display:block) tag
  8420. blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/,
  8421. handleEnterKey: function(e){
  8422. // summary:
  8423. // Handler for enter key events when blockModeForEnter is DIV or P.
  8424. // description:
  8425. // Manually handle enter key event to make the behavior consistent across
  8426. // all supported browsers. See class description for details.
  8427. // tags:
  8428. // private
  8429. var selection, range, newrange, startNode, endNode, brNode, doc=this.editor.document,br,rs,txt;
  8430. if(e.shiftKey){ // shift+enter always generates <br>
  8431. var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection);
  8432. var header = dijit.range.getAncestor(parent,this.blockNodes);
  8433. if(header){
  8434. if(header.tagName == 'LI'){
  8435. return true; // let browser handle
  8436. }
  8437. selection = dijit.range.getSelection(this.editor.window);
  8438. range = selection.getRangeAt(0);
  8439. if(!range.collapsed){
  8440. range.deleteContents();
  8441. selection = dijit.range.getSelection(this.editor.window);
  8442. range = selection.getRangeAt(0);
  8443. }
  8444. if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
  8445. br=doc.createElement('br');
  8446. newrange = dijit.range.create(this.editor.window);
  8447. header.insertBefore(br,header.firstChild);
  8448. newrange.setStartBefore(br.nextSibling);
  8449. selection.removeAllRanges();
  8450. selection.addRange(newrange);
  8451. }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){
  8452. newrange = dijit.range.create(this.editor.window);
  8453. br=doc.createElement('br');
  8454. header.appendChild(br);
  8455. header.appendChild(doc.createTextNode('\xA0'));
  8456. newrange.setStart(header.lastChild,0);
  8457. selection.removeAllRanges();
  8458. selection.addRange(newrange);
  8459. }else{
  8460. rs = range.startContainer;
  8461. if(rs && rs.nodeType == 3){
  8462. // Text node, we have to split it.
  8463. txt = rs.nodeValue;
  8464. dojo.withGlobal(this.editor.window, function(){
  8465. startNode = doc.createTextNode(txt.substring(0, range.startOffset));
  8466. endNode = doc.createTextNode(txt.substring(range.startOffset));
  8467. brNode = doc.createElement("br");
  8468. if(endNode.nodeValue == "" && dojo.isWebKit){
  8469. endNode = doc.createTextNode('\xA0')
  8470. }
  8471. dojo.place(startNode, rs, "after");
  8472. dojo.place(brNode, startNode, "after");
  8473. dojo.place(endNode, brNode, "after");
  8474. dojo.destroy(rs);
  8475. newrange = dijit.range.create(dojo.gobal);
  8476. newrange.setStart(endNode,0);
  8477. selection.removeAllRanges();
  8478. selection.addRange(newrange);
  8479. });
  8480. return false;
  8481. }
  8482. return true; // let browser handle
  8483. }
  8484. }else{
  8485. selection = dijit.range.getSelection(this.editor.window);
  8486. if(selection.rangeCount){
  8487. range = selection.getRangeAt(0);
  8488. if(range && range.startContainer){
  8489. if(!range.collapsed){
  8490. range.deleteContents();
  8491. selection = dijit.range.getSelection(this.editor.window);
  8492. range = selection.getRangeAt(0);
  8493. }
  8494. rs = range.startContainer;
  8495. if(rs && rs.nodeType == 3){
  8496. // Text node, we have to split it.
  8497. dojo.withGlobal(this.editor.window, dojo.hitch(this, function(){
  8498. var endEmpty = false;
  8499. var offset = range.startOffset;
  8500. if(rs.length < offset){
  8501. //We are not splitting the right node, try to locate the correct one
  8502. ret = this._adjustNodeAndOffset(rs, offset);
  8503. rs = ret.node;
  8504. offset = ret.offset;
  8505. }
  8506. txt = rs.nodeValue;
  8507. startNode = doc.createTextNode(txt.substring(0, offset));
  8508. endNode = doc.createTextNode(txt.substring(offset));
  8509. brNode = doc.createElement("br");
  8510. if(!endNode.length){
  8511. endNode = doc.createTextNode('\xA0');
  8512. endEmpty = true;
  8513. }
  8514. if(startNode.length){
  8515. dojo.place(startNode, rs, "after");
  8516. }else{
  8517. startNode = rs;
  8518. }
  8519. dojo.place(brNode, startNode, "after");
  8520. dojo.place(endNode, brNode, "after");
  8521. dojo.destroy(rs);
  8522. newrange = dijit.range.create(dojo.gobal);
  8523. newrange.setStart(endNode,0);
  8524. newrange.setEnd(endNode, endNode.length);
  8525. selection.removeAllRanges();
  8526. selection.addRange(newrange);
  8527. if(endEmpty && !dojo.isWebKit){
  8528. dijit._editor.selection.remove();
  8529. }else{
  8530. dijit._editor.selection.collapse(true);
  8531. }
  8532. }));
  8533. }else{
  8534. var targetNode;
  8535. if(range.startOffset >= 0){
  8536. targetNode = rs.childNodes[range.startOffset];
  8537. }
  8538. dojo.withGlobal(this.editor.window, dojo.hitch(this, function(){
  8539. var brNode = doc.createElement("br");
  8540. var endNode = doc.createTextNode('\xA0');
  8541. if(!targetNode){
  8542. rs.appendChild(brNode);
  8543. rs.appendChild(endNode);
  8544. }else{
  8545. dojo.place(brNode, targetNode, "before");
  8546. dojo.place(endNode, brNode, "after");
  8547. }
  8548. newrange = dijit.range.create(dojo.global);
  8549. newrange.setStart(endNode,0);
  8550. newrange.setEnd(endNode, endNode.length);
  8551. selection.removeAllRanges();
  8552. selection.addRange(newrange);
  8553. dijit._editor.selection.collapse(true);
  8554. }));
  8555. }
  8556. }
  8557. }else{
  8558. // don't change this: do not call this.execCommand, as that may have other logic in subclass
  8559. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
  8560. }
  8561. }
  8562. return false;
  8563. }
  8564. var _letBrowserHandle = true;
  8565. // first remove selection
  8566. selection = dijit.range.getSelection(this.editor.window);
  8567. range = selection.getRangeAt(0);
  8568. if(!range.collapsed){
  8569. range.deleteContents();
  8570. selection = dijit.range.getSelection(this.editor.window);
  8571. range = selection.getRangeAt(0);
  8572. }
  8573. var block = dijit.range.getBlockAncestor(range.endContainer, null, this.editor.editNode);
  8574. var blockNode = block.blockNode;
  8575. // if this is under a LI or the parent of the blockNode is LI, just let browser to handle it
  8576. if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){
  8577. if(dojo.isMoz){
  8578. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  8579. this._pressedEnterInBlock = blockNode;
  8580. }
  8581. // if this li only contains spaces, set the content to empty so the browser will outdent this item
  8582. if(/^(\s|&nbsp;|\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s|&nbsp;|\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){
  8583. // empty LI node
  8584. blockNode.innerHTML = '';
  8585. if(dojo.isWebKit){ // WebKit tosses the range when innerHTML is reset
  8586. newrange = dijit.range.create(this.editor.window);
  8587. newrange.setStart(blockNode, 0);
  8588. selection.removeAllRanges();
  8589. selection.addRange(newrange);
  8590. }
  8591. this._checkListLater = false; // nothing to check since the browser handles outdent
  8592. }
  8593. return true;
  8594. }
  8595. // text node directly under body, let's wrap them in a node
  8596. if(!block.blockNode || block.blockNode===this.editor.editNode){
  8597. try{
  8598. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
  8599. }catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ }
  8600. // get the newly created block node
  8601. // FIXME
  8602. block = {blockNode:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [this.blockNodeForEnter]),
  8603. blockContainer: this.editor.editNode};
  8604. if(block.blockNode){
  8605. if(block.blockNode != this.editor.editNode &&
  8606. (!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){
  8607. this.removeTrailingBr(block.blockNode);
  8608. return false;
  8609. }
  8610. }else{ // we shouldn't be here if formatblock worked
  8611. block.blockNode = this.editor.editNode;
  8612. }
  8613. selection = dijit.range.getSelection(this.editor.window);
  8614. range = selection.getRangeAt(0);
  8615. }
  8616. var newblock = doc.createElement(this.blockNodeForEnter);
  8617. newblock.innerHTML=this.bogusHtmlContent;
  8618. this.removeTrailingBr(block.blockNode);
  8619. var endOffset = range.endOffset;
  8620. var node = range.endContainer;
  8621. if(node.length < endOffset){
  8622. //We are not checking the right node, try to locate the correct one
  8623. var ret = this._adjustNodeAndOffset(node, endOffset);
  8624. node = ret.node;
  8625. endOffset = ret.offset;
  8626. }
  8627. if(dijit.range.atEndOfContainer(block.blockNode, node, endOffset)){
  8628. if(block.blockNode === block.blockContainer){
  8629. block.blockNode.appendChild(newblock);
  8630. }else{
  8631. dojo.place(newblock, block.blockNode, "after");
  8632. }
  8633. _letBrowserHandle = false;
  8634. // lets move caret to the newly created block
  8635. newrange = dijit.range.create(this.editor.window);
  8636. newrange.setStart(newblock, 0);
  8637. selection.removeAllRanges();
  8638. selection.addRange(newrange);
  8639. if(this.editor.height){
  8640. dojo.window.scrollIntoView(newblock);
  8641. }
  8642. }else if(dijit.range.atBeginningOfContainer(block.blockNode,
  8643. range.startContainer, range.startOffset)){
  8644. dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
  8645. if(newblock.nextSibling && this.editor.height){
  8646. // position input caret - mostly WebKit needs this
  8647. newrange = dijit.range.create(this.editor.window);
  8648. newrange.setStart(newblock.nextSibling, 0);
  8649. selection.removeAllRanges();
  8650. selection.addRange(newrange);
  8651. // browser does not scroll the caret position into view, do it manually
  8652. dojo.window.scrollIntoView(newblock.nextSibling);
  8653. }
  8654. _letBrowserHandle = false;
  8655. }else{ //press enter in the middle of P/DIV/Whatever/
  8656. if(block.blockNode === block.blockContainer){
  8657. block.blockNode.appendChild(newblock);
  8658. }else{
  8659. dojo.place(newblock, block.blockNode, "after");
  8660. }
  8661. _letBrowserHandle = false;
  8662. // Clone any block level styles.
  8663. if(block.blockNode.style){
  8664. if(newblock.style){
  8665. if(block.blockNode.style.cssText){
  8666. newblock.style.cssText = block.blockNode.style.cssText;
  8667. }
  8668. }
  8669. }
  8670. // Okay, we probably have to split.
  8671. rs = range.startContainer;
  8672. var firstNodeMoved;
  8673. if(rs && rs.nodeType == 3){
  8674. // Text node, we have to split it.
  8675. var nodeToMove, tNode;
  8676. endOffset = range.endOffset;
  8677. if(rs.length < endOffset){
  8678. //We are not splitting the right node, try to locate the correct one
  8679. ret = this._adjustNodeAndOffset(rs, endOffset);
  8680. rs = ret.node;
  8681. endOffset = ret.offset;
  8682. }
  8683. txt = rs.nodeValue;
  8684. startNode = doc.createTextNode(txt.substring(0, endOffset));
  8685. endNode = doc.createTextNode(txt.substring(endOffset, txt.length));
  8686. // Place the split, then remove original nodes.
  8687. dojo.place(startNode, rs, "before");
  8688. dojo.place(endNode, rs, "after");
  8689. dojo.destroy(rs);
  8690. // Okay, we split the text. Now we need to see if we're
  8691. // parented to the block element we're splitting and if
  8692. // not, we have to split all the way up. Ugh.
  8693. var parentC = startNode.parentNode;
  8694. while(parentC !== block.blockNode){
  8695. var tg = parentC.tagName;
  8696. var newTg = doc.createElement(tg);
  8697. // Clone over any 'style' data.
  8698. if(parentC.style){
  8699. if(newTg.style){
  8700. if(parentC.style.cssText){
  8701. newTg.style.cssText = parentC.style.cssText;
  8702. }
  8703. }
  8704. }
  8705. // If font also need to clone over any font data.
  8706. if(parentC.tagName === "FONT"){
  8707. if(parentC.color){
  8708. newTg.color = parentC.color;
  8709. }
  8710. if(parentC.face){
  8711. newTg.face = parentC.face;
  8712. }
  8713. if(parentC.size){ // this check was necessary on IE
  8714. newTg.size = parentC.size;
  8715. }
  8716. }
  8717. nodeToMove = endNode;
  8718. while(nodeToMove){
  8719. tNode = nodeToMove.nextSibling;
  8720. newTg.appendChild(nodeToMove);
  8721. nodeToMove = tNode;
  8722. }
  8723. dojo.place(newTg, parentC, "after");
  8724. startNode = parentC;
  8725. endNode = newTg;
  8726. parentC = parentC.parentNode;
  8727. }
  8728. // Lastly, move the split out tags to the new block.
  8729. // as they should now be split properly.
  8730. nodeToMove = endNode;
  8731. if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){
  8732. // Non-blank text and non-text nodes need to clear out that blank space
  8733. // before moving the contents.
  8734. newblock.innerHTML = "";
  8735. }
  8736. firstNodeMoved = nodeToMove;
  8737. while(nodeToMove){
  8738. tNode = nodeToMove.nextSibling;
  8739. newblock.appendChild(nodeToMove);
  8740. nodeToMove = tNode;
  8741. }
  8742. }
  8743. //lets move caret to the newly created block
  8744. newrange = dijit.range.create(this.editor.window);
  8745. var nodeForCursor;
  8746. var innerMostFirstNodeMoved = firstNodeMoved;
  8747. if(this.blockNodeForEnter !== 'BR'){
  8748. while(innerMostFirstNodeMoved){
  8749. nodeForCursor = innerMostFirstNodeMoved;
  8750. tNode = innerMostFirstNodeMoved.firstChild;
  8751. innerMostFirstNodeMoved = tNode;
  8752. }
  8753. if(nodeForCursor && nodeForCursor.parentNode){
  8754. newblock = nodeForCursor.parentNode;
  8755. newrange.setStart(newblock, 0);
  8756. selection.removeAllRanges();
  8757. selection.addRange(newrange);
  8758. if(this.editor.height){
  8759. dijit.scrollIntoView(newblock);
  8760. }
  8761. if(dojo.isMoz){
  8762. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  8763. this._pressedEnterInBlock = block.blockNode;
  8764. }
  8765. }else{
  8766. _letBrowserHandle = true;
  8767. }
  8768. }else{
  8769. newrange.setStart(newblock, 0);
  8770. selection.removeAllRanges();
  8771. selection.addRange(newrange);
  8772. if(this.editor.height){
  8773. dijit.scrollIntoView(newblock);
  8774. }
  8775. if(dojo.isMoz){
  8776. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  8777. this._pressedEnterInBlock = block.blockNode;
  8778. }
  8779. }
  8780. }
  8781. return _letBrowserHandle;
  8782. },
  8783. _adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){
  8784. // summary:
  8785. // In the case there are multiple text nodes in a row the offset may not be within the node. If the offset is larger than the node length, it will attempt to find
  8786. // the next text sibling until it locates the text node in which the offset refers to
  8787. // node:
  8788. // The node to check.
  8789. // offset:
  8790. // The position to find within the text node
  8791. // tags:
  8792. // private.
  8793. while(node.length < offset && node.nextSibling && node.nextSibling.nodeType==3){
  8794. //Adjust the offset and node in the case of multiple text nodes in a row
  8795. offset = offset - node.length;
  8796. node = node.nextSibling;
  8797. }
  8798. var ret = {"node": node, "offset": offset};
  8799. return ret;
  8800. },
  8801. removeTrailingBr: function(container){
  8802. // summary:
  8803. // If last child of container is a <br>, then remove it.
  8804. // tags:
  8805. // private
  8806. var para = /P|DIV|LI/i.test(container.tagName) ?
  8807. container : dijit._editor.selection.getParentOfType(container,['P','DIV','LI']);
  8808. if(!para){ return; }
  8809. if(para.lastChild){
  8810. if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
  8811. para.lastChild.tagName=='BR'){
  8812. dojo.destroy(para.lastChild);
  8813. }
  8814. }
  8815. if(!para.childNodes.length){
  8816. para.innerHTML=this.bogusHtmlContent;
  8817. }
  8818. }
  8819. });
  8820. }
  8821. if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8822. dojo._hasResource["dijit.Editor"] = true;
  8823. dojo.provide("dijit.Editor");
  8824. dojo.declare(
  8825. "dijit.Editor",
  8826. dijit._editor.RichText,
  8827. {
  8828. // summary:
  8829. // A rich text Editing widget
  8830. //
  8831. // description:
  8832. // This widget provides basic WYSIWYG editing features, based on the browser's
  8833. // underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`).
  8834. // A plugin model is available to extend the editor's capabilities as well as the
  8835. // the options available in the toolbar. Content generation may vary across
  8836. // browsers, and clipboard operations may have different results, to name
  8837. // a few limitations. Note: this widget should not be used with the HTML
  8838. // &lt;TEXTAREA&gt; tag -- see dijit._editor.RichText for details.
  8839. // plugins: [const] Object[]
  8840. // A list of plugin names (as strings) or instances (as objects)
  8841. // for this widget.
  8842. //
  8843. // When declared in markup, it might look like:
  8844. // | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]"
  8845. plugins: null,
  8846. // extraPlugins: [const] Object[]
  8847. // A list of extra plugin names which will be appended to plugins array
  8848. extraPlugins: null,
  8849. constructor: function(){
  8850. // summary:
  8851. // Runs on widget initialization to setup arrays etc.
  8852. // tags:
  8853. // private
  8854. if(!dojo.isArray(this.plugins)){
  8855. this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
  8856. "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull",
  8857. "dijit._editor.plugins.EnterKeyHandling" /*, "createLink"*/];
  8858. }
  8859. this._plugins=[];
  8860. this._editInterval = this.editActionInterval * 1000;
  8861. //IE will always lose focus when other element gets focus, while for FF and safari,
  8862. //when no iframe is used, focus will be lost whenever another element gets focus.
  8863. //For IE, we can connect to onBeforeDeactivate, which will be called right before
  8864. //the focus is lost, so we can obtain the selected range. For other browsers,
  8865. //no equivelent of onBeforeDeactivate, so we need to do two things to make sure
  8866. //selection is properly saved before focus is lost: 1) when user clicks another
  8867. //element in the page, in which case we listen to mousedown on the entire page and
  8868. //see whether user clicks out of a focus editor, if so, save selection (focus will
  8869. //only lost after onmousedown event is fired, so we can obtain correct caret pos.)
  8870. //2) when user tabs away from the editor, which is handled in onKeyDown below.
  8871. if(dojo.isIE){
  8872. this.events.push("onBeforeDeactivate");
  8873. this.events.push("onBeforeActivate");
  8874. }
  8875. },
  8876. postMixInProperties: function() {
  8877. // summary:
  8878. // Extension to make sure a deferred is in place before certain functions
  8879. // execute, like making sure all the plugins are properly inserted.
  8880. // Set up a deferred so that the value isn't applied to the editor
  8881. // until all the plugins load, needed to avoid timing condition
  8882. // reported in #10537.
  8883. this.setValueDeferred = new dojo.Deferred();
  8884. this.inherited(arguments);
  8885. },
  8886. postCreate: function(){
  8887. //for custom undo/redo, if enabled.
  8888. this._steps=this._steps.slice(0);
  8889. this._undoedSteps=this._undoedSteps.slice(0);
  8890. if(dojo.isArray(this.extraPlugins)){
  8891. this.plugins=this.plugins.concat(this.extraPlugins);
  8892. }
  8893. this.inherited(arguments);
  8894. this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
  8895. if(!this.toolbar){
  8896. // if we haven't been assigned a toolbar, create one
  8897. this.toolbar = new dijit.Toolbar({
  8898. dir: this.dir,
  8899. lang: this.lang
  8900. });
  8901. this.header.appendChild(this.toolbar.domNode);
  8902. }
  8903. dojo.forEach(this.plugins, this.addPlugin, this);
  8904. // Okay, denote the value can now be set.
  8905. this.setValueDeferred.callback(true);
  8906. dojo.addClass(this.iframe.parentNode, "dijitEditorIFrameContainer");
  8907. dojo.addClass(this.iframe, "dijitEditorIFrame");
  8908. dojo.attr(this.iframe, "allowTransparency", true);
  8909. if(dojo.isWebKit){
  8910. // Disable selecting the entire editor by inadvertant double-clicks.
  8911. // on buttons, title bar, etc. Otherwise clicking too fast on
  8912. // a button such as undo/redo selects the entire editor.
  8913. dojo.style(this.domNode, "KhtmlUserSelect", "none");
  8914. }
  8915. this.toolbar.startup();
  8916. this.onNormalizedDisplayChanged(); //update toolbar button status
  8917. },
  8918. destroy: function(){
  8919. dojo.forEach(this._plugins, function(p){
  8920. if(p && p.destroy){
  8921. p.destroy();
  8922. }
  8923. });
  8924. this._plugins=[];
  8925. this.toolbar.destroyRecursive();
  8926. delete this.toolbar;
  8927. this.inherited(arguments);
  8928. },
  8929. addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
  8930. // summary:
  8931. // takes a plugin name as a string or a plugin instance and
  8932. // adds it to the toolbar and associates it with this editor
  8933. // instance. The resulting plugin is added to the Editor's
  8934. // plugins array. If index is passed, it's placed in the plugins
  8935. // array at that index. No big magic, but a nice helper for
  8936. // passing in plugin names via markup.
  8937. //
  8938. // plugin: String, args object or plugin instance
  8939. //
  8940. // args:
  8941. // This object will be passed to the plugin constructor
  8942. //
  8943. // index: Integer
  8944. // Used when creating an instance from
  8945. // something already in this.plugins. Ensures that the new
  8946. // instance is assigned to this.plugins at that index.
  8947. var args=dojo.isString(plugin)?{name:plugin}:plugin;
  8948. if(!args.setEditor){
  8949. var o={"args":args,"plugin":null,"editor":this};
  8950. dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]);
  8951. if(!o.plugin){
  8952. var pc = dojo.getObject(args.name);
  8953. if(pc){
  8954. o.plugin=new pc(args);
  8955. }
  8956. }
  8957. if(!o.plugin){
  8958. console.warn('Cannot find plugin',plugin);
  8959. return;
  8960. }
  8961. plugin=o.plugin;
  8962. }
  8963. if(arguments.length > 1){
  8964. this._plugins[index] = plugin;
  8965. }else{
  8966. this._plugins.push(plugin);
  8967. }
  8968. plugin.setEditor(this);
  8969. if(dojo.isFunction(plugin.setToolbar)){
  8970. plugin.setToolbar(this.toolbar);
  8971. }
  8972. },
  8973. //the following 3 functions are required to make the editor play nice under a layout widget, see #4070
  8974. startup: function(){
  8975. // summary:
  8976. // Exists to make Editor work as a child of a layout widget.
  8977. // Developers don't need to call this method.
  8978. // tags:
  8979. // protected
  8980. //console.log('startup',arguments);
  8981. },
  8982. resize: function(size){
  8983. // summary:
  8984. // Resize the editor to the specified size, see `dijit.layout._LayoutWidget.resize`
  8985. if(size){
  8986. // we've been given a height/width for the entire editor (toolbar + contents), calls layout()
  8987. // to split the allocated size between the toolbar and the contents
  8988. dijit.layout._LayoutWidget.prototype.resize.apply(this, arguments);
  8989. }
  8990. /*
  8991. else{
  8992. // do nothing, the editor is already laid out correctly. The user has probably specified
  8993. // the height parameter, which was used to set a size on the iframe
  8994. }
  8995. */
  8996. },
  8997. layout: function(){
  8998. // summary:
  8999. // Called from `dijit.layout._LayoutWidget.resize`. This shouldn't be called directly
  9000. // tags:
  9001. // protected
  9002. // Converts the iframe (or rather the <div> surrounding it) to take all the available space
  9003. // except what's needed for the header (toolbars) and footer (breadcrumbs, etc).
  9004. // A class was added to the iframe container and some themes style it, so we have to
  9005. // calc off the added margins and padding too. See tracker: #10662
  9006. var areaHeight = (this._contentBox.h -
  9007. (this.getHeaderHeight() + this.getFooterHeight() +
  9008. dojo._getPadBorderExtents(this.iframe.parentNode).h +
  9009. dojo._getMarginExtents(this.iframe.parentNode).h));
  9010. this.editingArea.style.height = areaHeight + "px";
  9011. if(this.iframe){
  9012. this.iframe.style.height="100%";
  9013. }
  9014. this._layoutMode = true;
  9015. },
  9016. _onIEMouseDown: function(/*Event*/ e){
  9017. // summary:
  9018. // IE only to prevent 2 clicks to focus
  9019. // tags:
  9020. // private
  9021. var outsideClientArea;
  9022. // IE 8's componentFromPoint is broken, which is a shame since it
  9023. // was smaller code, but oh well. We have to do this brute force
  9024. // to detect if the click was scroller or not.
  9025. var b = this.document.body;
  9026. var clientWidth = b.clientWidth;
  9027. var clientHeight = b.clientHeight;
  9028. var clientLeft = b.clientLeft;
  9029. var offsetWidth = b.offsetWidth;
  9030. var offsetHeight = b.offsetHeight;
  9031. var offsetLeft = b.offsetLeft;
  9032. //Check for vertical scroller click.
  9033. bodyDir = b.dir ? b.dir.toLowerCase() : "";
  9034. if(bodyDir != "rtl"){
  9035. if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){
  9036. // Check the click was between width and offset width, if so, scroller
  9037. outsideClientArea = true;
  9038. }
  9039. }else{
  9040. // RTL mode, we have to go by the left offsets.
  9041. if(e.x < clientLeft && e.x > offsetLeft){
  9042. // Check the click was between width and offset width, if so, scroller
  9043. outsideClientArea = true;
  9044. }
  9045. }
  9046. if(!outsideClientArea){
  9047. // Okay, might be horiz scroller, check that.
  9048. if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){
  9049. // Horizontal scroller.
  9050. outsideClientArea = true;
  9051. }
  9052. }
  9053. if(!outsideClientArea){
  9054. delete this._cursorToStart; // Remove the force to cursor to start position.
  9055. delete this._savedSelection; // new mouse position overrides old selection
  9056. if(e.target.tagName == "BODY"){
  9057. setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
  9058. }
  9059. this.inherited(arguments);
  9060. }
  9061. },
  9062. onBeforeActivate: function(e){
  9063. this._restoreSelection();
  9064. },
  9065. onBeforeDeactivate: function(e){
  9066. // summary:
  9067. // Called on IE right before focus is lost. Saves the selected range.
  9068. // tags:
  9069. // private
  9070. if(this.customUndo){
  9071. this.endEditing(true);
  9072. }
  9073. //in IE, the selection will be lost when other elements get focus,
  9074. //let's save focus before the editor is deactivated
  9075. if(e.target.tagName != "BODY"){
  9076. this._saveSelection();
  9077. }
  9078. //console.log('onBeforeDeactivate',this);
  9079. },
  9080. /* beginning of custom undo/redo support */
  9081. // customUndo: Boolean
  9082. // Whether we shall use custom undo/redo support instead of the native
  9083. // browser support. By default, we now use custom undo. It works better
  9084. // than native browser support and provides a consistent behavior across
  9085. // browsers with a minimal performance hit. We already had the hit on
  9086. // the slowest browser, IE, anyway.
  9087. customUndo: true,
  9088. // editActionInterval: Integer
  9089. // When using customUndo, not every keystroke will be saved as a step.
  9090. // Instead typing (including delete) will be grouped together: after
  9091. // a user stops typing for editActionInterval seconds, a step will be
  9092. // saved; if a user resume typing within editActionInterval seconds,
  9093. // the timeout will be restarted. By default, editActionInterval is 3
  9094. // seconds.
  9095. editActionInterval: 3,
  9096. beginEditing: function(cmd){
  9097. // summary:
  9098. // Called to note that the user has started typing alphanumeric characters, if it's not already noted.
  9099. // Deals with saving undo; see editActionInterval parameter.
  9100. // tags:
  9101. // private
  9102. if(!this._inEditing){
  9103. this._inEditing=true;
  9104. this._beginEditing(cmd);
  9105. }
  9106. if(this.editActionInterval>0){
  9107. if(this._editTimer){
  9108. clearTimeout(this._editTimer);
  9109. }
  9110. this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
  9111. }
  9112. },
  9113. // TODO: declaring these in the prototype is meaningless, just create in the constructor/postCreate
  9114. _steps:[],
  9115. _undoedSteps:[],
  9116. execCommand: function(cmd){
  9117. // summary:
  9118. // Main handler for executing any commands to the editor, like paste, bold, etc.
  9119. // Called by plugins, but not meant to be called by end users.
  9120. // tags:
  9121. // protected
  9122. if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
  9123. return this[cmd]();
  9124. }else{
  9125. if(this.customUndo){
  9126. this.endEditing();
  9127. this._beginEditing();
  9128. }
  9129. var r;
  9130. var isClipboard = /copy|cut|paste/.test(cmd);
  9131. try{
  9132. r = this.inherited(arguments);
  9133. if(dojo.isWebKit && isClipboard && !r){ //see #4598: webkit does not guarantee clipboard support from js
  9134. throw { code: 1011 }; // throw an object like Mozilla's error
  9135. }
  9136. }catch(e){
  9137. //TODO: when else might we get an exception? Do we need the Mozilla test below?
  9138. if(e.code == 1011 /* Mozilla: service denied */ && isClipboard){
  9139. // Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
  9140. var sub = dojo.string.substitute,
  9141. accel = {cut:'X', copy:'C', paste:'V'};
  9142. alert(sub(this.commands.systemShortcut,
  9143. [this.commands[cmd], sub(this.commands[dojo.isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
  9144. }
  9145. r = false;
  9146. }
  9147. if(this.customUndo){
  9148. this._endEditing();
  9149. }
  9150. return r;
  9151. }
  9152. },
  9153. queryCommandEnabled: function(cmd){
  9154. // summary:
  9155. // Returns true if specified editor command is enabled.
  9156. // Used by the plugins to know when to highlight/not highlight buttons.
  9157. // tags:
  9158. // protected
  9159. if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
  9160. return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0);
  9161. }else{
  9162. return this.inherited(arguments);
  9163. }
  9164. },
  9165. _moveToBookmark: function(b){
  9166. // summary:
  9167. // Selects the text specified in bookmark b
  9168. // tags:
  9169. // private
  9170. var bookmark = b.mark;
  9171. var mark = b.mark;
  9172. var col = b.isCollapsed;
  9173. var r, sNode, eNode, sel;
  9174. if(mark){
  9175. if(dojo.isIE < 9){
  9176. if(dojo.isArray(mark)){
  9177. //IE CONTROL, have to use the native bookmark.
  9178. bookmark = [];
  9179. dojo.forEach(mark,function(n){
  9180. bookmark.push(dijit.range.getNode(n,this.editNode));
  9181. },this);
  9182. dojo.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]);
  9183. }else{
  9184. if(mark.startContainer && mark.endContainer){
  9185. // Use the pseudo WC3 range API. This works better for positions
  9186. // than the IE native bookmark code.
  9187. sel = dijit.range.getSelection(this.window);
  9188. if(sel && sel.removeAllRanges){
  9189. sel.removeAllRanges();
  9190. r = dijit.range.create(this.window);
  9191. sNode = dijit.range.getNode(mark.startContainer,this.editNode);
  9192. eNode = dijit.range.getNode(mark.endContainer,this.editNode);
  9193. if(sNode && eNode){
  9194. // Okay, we believe we found the position, so add it into the selection
  9195. // There are cases where it may not be found, particularly in undo/redo, when
  9196. // IE changes the underlying DOM on us (wraps text in a <p> tag or similar.
  9197. // So, in those cases, don't bother restoring selection.
  9198. r.setStart(sNode,mark.startOffset);
  9199. r.setEnd(eNode,mark.endOffset);
  9200. sel.addRange(r);
  9201. }
  9202. }
  9203. }
  9204. }
  9205. }else{//w3c range
  9206. sel = dijit.range.getSelection(this.window);
  9207. if(sel && sel.removeAllRanges){
  9208. sel.removeAllRanges();
  9209. r = dijit.range.create(this.window);
  9210. sNode = dijit.range.getNode(mark.startContainer,this.editNode);
  9211. eNode = dijit.range.getNode(mark.endContainer,this.editNode);
  9212. if(sNode && eNode){
  9213. // Okay, we believe we found the position, so add it into the selection
  9214. // There are cases where it may not be found, particularly in undo/redo, when
  9215. // formatting as been done and so on, so don't restore selection then.
  9216. r.setStart(sNode,mark.startOffset);
  9217. r.setEnd(eNode,mark.endOffset);
  9218. sel.addRange(r);
  9219. }
  9220. }
  9221. }
  9222. }
  9223. },
  9224. _changeToStep: function(from, to){
  9225. // summary:
  9226. // Reverts editor to "to" setting, from the undo stack.
  9227. // tags:
  9228. // private
  9229. this.setValue(to.text);
  9230. var b=to.bookmark;
  9231. if(!b){ return; }
  9232. this._moveToBookmark(b);
  9233. },
  9234. undo: function(){
  9235. // summary:
  9236. // Handler for editor undo (ex: ctrl-z) operation
  9237. // tags:
  9238. // private
  9239. //console.log('undo');
  9240. var ret = false;
  9241. if(!this._undoRedoActive){
  9242. this._undoRedoActive = true;
  9243. this.endEditing(true);
  9244. var s=this._steps.pop();
  9245. if(s && this._steps.length>0){
  9246. this.focus();
  9247. this._changeToStep(s,this._steps[this._steps.length-1]);
  9248. this._undoedSteps.push(s);
  9249. this.onDisplayChanged();
  9250. delete this._undoRedoActive;
  9251. ret = true;
  9252. }
  9253. delete this._undoRedoActive;
  9254. }
  9255. return ret;
  9256. },
  9257. redo: function(){
  9258. // summary:
  9259. // Handler for editor redo (ex: ctrl-y) operation
  9260. // tags:
  9261. // private
  9262. //console.log('redo');
  9263. var ret = false;
  9264. if(!this._undoRedoActive){
  9265. this._undoRedoActive = true;
  9266. this.endEditing(true);
  9267. var s=this._undoedSteps.pop();
  9268. if(s && this._steps.length>0){
  9269. this.focus();
  9270. this._changeToStep(this._steps[this._steps.length-1],s);
  9271. this._steps.push(s);
  9272. this.onDisplayChanged();
  9273. ret = true;
  9274. }
  9275. delete this._undoRedoActive;
  9276. }
  9277. return ret;
  9278. },
  9279. endEditing: function(ignore_caret){
  9280. // summary:
  9281. // Called to note that the user has stopped typing alphanumeric characters, if it's not already noted.
  9282. // Deals with saving undo; see editActionInterval parameter.
  9283. // tags:
  9284. // private
  9285. if(this._editTimer){
  9286. clearTimeout(this._editTimer);
  9287. }
  9288. if(this._inEditing){
  9289. this._endEditing(ignore_caret);
  9290. this._inEditing=false;
  9291. }
  9292. },
  9293. _getBookmark: function(){
  9294. // summary:
  9295. // Get the currently selected text
  9296. // tags:
  9297. // protected
  9298. var b=dojo.withGlobal(this.window,dijit.getBookmark);
  9299. var tmp=[];
  9300. if(b && b.mark){
  9301. var mark = b.mark;
  9302. if(dojo.isIE < 9){
  9303. // Try to use the pseudo range API on IE for better accuracy.
  9304. var sel = dijit.range.getSelection(this.window);
  9305. if(!dojo.isArray(mark)){
  9306. if(sel){
  9307. var range;
  9308. if(sel.rangeCount){
  9309. range = sel.getRangeAt(0);
  9310. }
  9311. if(range){
  9312. b.mark = range.cloneRange();
  9313. }else{
  9314. b.mark = dojo.withGlobal(this.window,dijit.getBookmark);
  9315. }
  9316. }
  9317. }else{
  9318. // Control ranges (img, table, etc), handle differently.
  9319. dojo.forEach(b.mark,function(n){
  9320. tmp.push(dijit.range.getIndex(n,this.editNode).o);
  9321. },this);
  9322. b.mark = tmp;
  9323. }
  9324. }
  9325. try{
  9326. if(b.mark && b.mark.startContainer){
  9327. tmp=dijit.range.getIndex(b.mark.startContainer,this.editNode).o;
  9328. b.mark={startContainer:tmp,
  9329. startOffset:b.mark.startOffset,
  9330. endContainer:b.mark.endContainer===b.mark.startContainer?tmp:dijit.range.getIndex(b.mark.endContainer,this.editNode).o,
  9331. endOffset:b.mark.endOffset};
  9332. }
  9333. }catch(e){
  9334. b.mark = null;
  9335. }
  9336. }
  9337. return b;
  9338. },
  9339. _beginEditing: function(cmd){
  9340. // summary:
  9341. // Called when the user starts typing alphanumeric characters.
  9342. // Deals with saving undo; see editActionInterval parameter.
  9343. // tags:
  9344. // private
  9345. if(this._steps.length === 0){
  9346. // You want to use the editor content without post filtering
  9347. // to make sure selection restores right for the 'initial' state.
  9348. // and undo is called. So not using this.value, as it was 'processed'
  9349. // and the line-up for selections may have been altered.
  9350. this._steps.push({'text':dijit._editor.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()});
  9351. }
  9352. },
  9353. _endEditing: function(ignore_caret){
  9354. // summary:
  9355. // Called when the user stops typing alphanumeric characters.
  9356. // Deals with saving undo; see editActionInterval parameter.
  9357. // tags:
  9358. // private
  9359. // Avoid filtering to make sure selections restore.
  9360. var v = dijit._editor.getChildrenHtml(this.editNode);
  9361. this._undoedSteps=[];//clear undoed steps
  9362. this._steps.push({text: v, bookmark: this._getBookmark()});
  9363. },
  9364. onKeyDown: function(e){
  9365. // summary:
  9366. // Handler for onkeydown event.
  9367. // tags:
  9368. // private
  9369. //We need to save selection if the user TAB away from this editor
  9370. //no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate
  9371. if(!dojo.isIE && !this.iframe && e.keyCode == dojo.keys.TAB && !this.tabIndent){
  9372. this._saveSelection();
  9373. }
  9374. if(!this.customUndo){
  9375. this.inherited(arguments);
  9376. return;
  9377. }
  9378. var k = e.keyCode, ks = dojo.keys;
  9379. if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
  9380. if(k == 90 || k == 122){ //z
  9381. dojo.stopEvent(e);
  9382. this.undo();
  9383. return;
  9384. }else if(k == 89 || k == 121){ //y
  9385. dojo.stopEvent(e);
  9386. this.redo();
  9387. return;
  9388. }
  9389. }
  9390. this.inherited(arguments);
  9391. switch(k){
  9392. case ks.ENTER:
  9393. case ks.BACKSPACE:
  9394. case ks.DELETE:
  9395. this.beginEditing();
  9396. break;
  9397. case 88: //x
  9398. case 86: //v
  9399. if(e.ctrlKey && !e.altKey && !e.metaKey){
  9400. this.endEditing();//end current typing step if any
  9401. if(e.keyCode == 88){
  9402. this.beginEditing('cut');
  9403. //use timeout to trigger after the cut is complete
  9404. setTimeout(dojo.hitch(this, this.endEditing), 1);
  9405. }else{
  9406. this.beginEditing('paste');
  9407. //use timeout to trigger after the paste is complete
  9408. setTimeout(dojo.hitch(this, this.endEditing), 1);
  9409. }
  9410. break;
  9411. }
  9412. //pass through
  9413. default:
  9414. if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
  9415. this.beginEditing();
  9416. break;
  9417. }
  9418. //pass through
  9419. case ks.ALT:
  9420. this.endEditing();
  9421. break;
  9422. case ks.UP_ARROW:
  9423. case ks.DOWN_ARROW:
  9424. case ks.LEFT_ARROW:
  9425. case ks.RIGHT_ARROW:
  9426. case ks.HOME:
  9427. case ks.END:
  9428. case ks.PAGE_UP:
  9429. case ks.PAGE_DOWN:
  9430. this.endEditing(true);
  9431. break;
  9432. //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
  9433. case ks.CTRL:
  9434. case ks.SHIFT:
  9435. case ks.TAB:
  9436. break;
  9437. }
  9438. },
  9439. _onBlur: function(){
  9440. // summary:
  9441. // Called from focus manager when focus has moved away from this editor
  9442. // tags:
  9443. // protected
  9444. //this._saveSelection();
  9445. this.inherited(arguments);
  9446. this.endEditing(true);
  9447. },
  9448. _saveSelection: function(){
  9449. // summary:
  9450. // Save the currently selected text in _savedSelection attribute
  9451. // tags:
  9452. // private
  9453. try{
  9454. this._savedSelection=this._getBookmark();
  9455. }catch(e){ /* Squelch any errors that occur if selection save occurs due to being hidden simultaniously. */}
  9456. },
  9457. _restoreSelection: function(){
  9458. // summary:
  9459. // Re-select the text specified in _savedSelection attribute;
  9460. // see _saveSelection().
  9461. // tags:
  9462. // private
  9463. if(this._savedSelection){
  9464. // Clear off cursor to start, we're deliberately going to a selection.
  9465. delete this._cursorToStart;
  9466. // only restore the selection if the current range is collapsed
  9467. // if not collapsed, then it means the editor does not lose
  9468. // selection and there is no need to restore it
  9469. if(dojo.withGlobal(this.window,'isCollapsed',dijit)){
  9470. this._moveToBookmark(this._savedSelection);
  9471. }
  9472. delete this._savedSelection;
  9473. }
  9474. },
  9475. onClick: function(){
  9476. // summary:
  9477. // Handler for when editor is clicked
  9478. // tags:
  9479. // protected
  9480. this.endEditing(true);
  9481. this.inherited(arguments);
  9482. },
  9483. replaceValue: function(/*String*/ html){
  9484. // summary:
  9485. // over-ride of replaceValue to support custom undo and stack maintainence.
  9486. // tags:
  9487. // protected
  9488. if(!this.customUndo){
  9489. this.inherited(arguments);
  9490. }else{
  9491. if(this.isClosed){
  9492. this.setValue(html);
  9493. }else{
  9494. this.beginEditing();
  9495. if(!html){
  9496. html = "&nbsp;"
  9497. }
  9498. this.setValue(html);
  9499. this.endEditing();
  9500. }
  9501. }
  9502. },
  9503. _setDisabledAttr: function(/*Boolean*/ value){
  9504. var disableFunc = dojo.hitch(this, function(){
  9505. if((!this.disabled && value) || (!this._buttonEnabledPlugins && value)){
  9506. // Disable editor: disable all enabled buttons and remember that list
  9507. dojo.forEach(this._plugins, function(p){
  9508. p.set("disabled", true);
  9509. });
  9510. }else if(this.disabled && !value){
  9511. // Restore plugins to being active.
  9512. dojo.forEach(this._plugins, function(p){
  9513. p.set("disabled", false);
  9514. });
  9515. }
  9516. });
  9517. this.setValueDeferred.addCallback(disableFunc);
  9518. this.inherited(arguments);
  9519. },
  9520. _setStateClass: function(){
  9521. try{
  9522. this.inherited(arguments);
  9523. // Let theme set the editor's text color based on editor enabled/disabled state.
  9524. // We need to jump through hoops because the main document (where the theme CSS is)
  9525. // is separate from the iframe's document.
  9526. if(this.document && this.document.body){
  9527. dojo.style(this.document.body, "color", dojo.style(this.iframe, "color"));
  9528. }
  9529. }catch(e){ /* Squelch any errors caused by focus change if hidden during a state change */}
  9530. }
  9531. }
  9532. );
  9533. // Register the "default plugins", ie, the built-in editor commands
  9534. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  9535. if(o.plugin){ return; }
  9536. var args = o.args, p;
  9537. var _p = dijit._editor._Plugin;
  9538. var name = args.name;
  9539. switch(name){
  9540. case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
  9541. case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
  9542. case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
  9543. case "selectAll": case "removeFormat": case "unlink":
  9544. case "insertHorizontalRule":
  9545. p = new _p({ command: name });
  9546. break;
  9547. case "bold": case "italic": case "underline": case "strikethrough":
  9548. case "subscript": case "superscript":
  9549. p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
  9550. break;
  9551. case "|":
  9552. p = new _p({ button: new dijit.ToolbarSeparator(), setEditor: function(editor) {this.editor = editor;} });
  9553. }
  9554. // console.log('name',name,p);
  9555. o.plugin=p;
  9556. });
  9557. }
  9558. if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9559. dojo._hasResource["dojo.regexp"] = true;
  9560. dojo.provide("dojo.regexp");
  9561. dojo.getObject("regexp", true, dojo);
  9562. /*=====
  9563. dojo.regexp = {
  9564. // summary: Regular expressions and Builder resources
  9565. };
  9566. =====*/
  9567. dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
  9568. // summary:
  9569. // Adds escape sequences for special characters in regular expressions
  9570. // except:
  9571. // a String with special characters to be left unescaped
  9572. return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
  9573. if(except && except.indexOf(ch) != -1){
  9574. return ch;
  9575. }
  9576. return "\\" + ch;
  9577. }); // String
  9578. };
  9579. dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
  9580. // summary:
  9581. // Builds a regular expression that groups subexpressions
  9582. // description:
  9583. // A utility function used by some of the RE generators. The
  9584. // subexpressions are constructed by the function, re, in the second
  9585. // parameter. re builds one subexpression for each elem in the array
  9586. // a, in the first parameter. Returns a string for a regular
  9587. // expression that groups all the subexpressions.
  9588. // arr:
  9589. // A single value or an array of values.
  9590. // re:
  9591. // A function. Takes one parameter and converts it to a regular
  9592. // expression.
  9593. // nonCapture:
  9594. // If true, uses non-capturing match, otherwise matches are retained
  9595. // by regular expression. Defaults to false
  9596. // case 1: a is a single value.
  9597. if(!(arr instanceof Array)){
  9598. return re(arr); // String
  9599. }
  9600. // case 2: a is an array
  9601. var b = [];
  9602. for(var i = 0; i < arr.length; i++){
  9603. // convert each elem to a RE
  9604. b.push(re(arr[i]));
  9605. }
  9606. // join the REs as alternatives in a RE group.
  9607. return dojo.regexp.group(b.join("|"), nonCapture); // String
  9608. };
  9609. dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
  9610. // summary:
  9611. // adds group match to expression
  9612. // nonCapture:
  9613. // If true, uses non-capturing match, otherwise matches are retained
  9614. // by regular expression.
  9615. return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
  9616. };
  9617. }
  9618. if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9619. dojo._hasResource["dojo.data.util.sorter"] = true;
  9620. dojo.provide("dojo.data.util.sorter");
  9621. dojo.getObject("data.util.sorter", true, dojo);
  9622. dojo.data.util.sorter.basicComparator = function( /*anything*/ a,
  9623. /*anything*/ b){
  9624. // summary:
  9625. // Basic comparision function that compares if an item is greater or less than another item
  9626. // description:
  9627. // returns 1 if a > b, -1 if a < b, 0 if equal.
  9628. // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
  9629. // And compared to each other, null is equivalent to undefined.
  9630. //null is a problematic compare, so if null, we set to undefined.
  9631. //Makes the check logic simple, compact, and consistent
  9632. //And (null == undefined) === true, so the check later against null
  9633. //works for undefined and is less bytes.
  9634. var r = -1;
  9635. if(a === null){
  9636. a = undefined;
  9637. }
  9638. if(b === null){
  9639. b = undefined;
  9640. }
  9641. if(a == b){
  9642. r = 0;
  9643. }else if(a > b || a == null){
  9644. r = 1;
  9645. }
  9646. return r; //int {-1,0,1}
  9647. };
  9648. dojo.data.util.sorter.createSortFunction = function( /* attributes array */sortSpec,
  9649. /*dojo.data.core.Read*/ store){
  9650. // summary:
  9651. // Helper function to generate the sorting function based off the list of sort attributes.
  9652. // description:
  9653. // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
  9654. // it will look in the mapping for comparisons function for the attributes. If one is found, it will
  9655. // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
  9656. // Returns the sorting function for this particular list of attributes and sorting directions.
  9657. //
  9658. // sortSpec: array
  9659. // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
  9660. // The objects should be formatted as follows:
  9661. // {
  9662. // attribute: "attributeName-string" || attribute,
  9663. // descending: true|false; // Default is false.
  9664. // }
  9665. // store: object
  9666. // The datastore object to look up item values from.
  9667. //
  9668. var sortFunctions=[];
  9669. function createSortFunction(attr, dir, comp, s){
  9670. //Passing in comp and s (comparator and store), makes this
  9671. //function much faster.
  9672. return function(itemA, itemB){
  9673. var a = s.getValue(itemA, attr);
  9674. var b = s.getValue(itemB, attr);
  9675. return dir * comp(a,b); //int
  9676. };
  9677. }
  9678. var sortAttribute;
  9679. var map = store.comparatorMap;
  9680. var bc = dojo.data.util.sorter.basicComparator;
  9681. for(var i = 0; i < sortSpec.length; i++){
  9682. sortAttribute = sortSpec[i];
  9683. var attr = sortAttribute.attribute;
  9684. if(attr){
  9685. var dir = (sortAttribute.descending) ? -1 : 1;
  9686. var comp = bc;
  9687. if(map){
  9688. if(typeof attr !== "string" && ("toString" in attr)){
  9689. attr = attr.toString();
  9690. }
  9691. comp = map[attr] || bc;
  9692. }
  9693. sortFunctions.push(createSortFunction(attr,
  9694. dir, comp, store));
  9695. }
  9696. }
  9697. return function(rowA, rowB){
  9698. var i=0;
  9699. while(i < sortFunctions.length){
  9700. var ret = sortFunctions[i++](rowA, rowB);
  9701. if(ret !== 0){
  9702. return ret;//int
  9703. }
  9704. }
  9705. return 0; //int
  9706. }; // Function
  9707. };
  9708. }
  9709. if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9710. dojo._hasResource["dojo.data.util.simpleFetch"] = true;
  9711. dojo.provide("dojo.data.util.simpleFetch");
  9712. dojo.getObject("data.util.simpleFetch", true, dojo);
  9713. dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
  9714. // summary:
  9715. // The simpleFetch mixin is designed to serve as a set of function(s) that can
  9716. // be mixed into other datastore implementations to accelerate their development.
  9717. // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
  9718. // call by returning an array of all the found items that matched the query. The simpleFetch mixin
  9719. // is not designed to work for datastores that respond to a fetch() call by incrementally
  9720. // loading items, or sequentially loading partial batches of the result
  9721. // set. For datastores that mixin simpleFetch, simpleFetch
  9722. // implements a fetch method that automatically handles eight of the fetch()
  9723. // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
  9724. // The class mixing in simpleFetch should not implement fetch(),
  9725. // but should instead implement a _fetchItems() method. The _fetchItems()
  9726. // method takes three arguments, the keywordArgs object that was passed
  9727. // to fetch(), a callback function to be called when the result array is
  9728. // available, and an error callback to be called if something goes wrong.
  9729. // The _fetchItems() method should ignore any keywordArgs parameters for
  9730. // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
  9731. // The _fetchItems() method needs to correctly handle any other keywordArgs
  9732. // parameters, including the query parameter and any optional parameters
  9733. // (such as includeChildren). The _fetchItems() method should create an array of
  9734. // result items and pass it to the fetchHandler along with the original request object
  9735. // -- or, the _fetchItems() method may, if it wants to, create an new request object
  9736. // with other specifics about the request that are specific to the datastore and pass
  9737. // that as the request object to the handler.
  9738. //
  9739. // For more information on this specific function, see dojo.data.api.Read.fetch()
  9740. request = request || {};
  9741. if(!request.store){
  9742. request.store = this;
  9743. }
  9744. var self = this;
  9745. var _errorHandler = function(errorData, requestObject){
  9746. if(requestObject.onError){
  9747. var scope = requestObject.scope || dojo.global;
  9748. requestObject.onError.call(scope, errorData, requestObject);
  9749. }
  9750. };
  9751. var _fetchHandler = function(items, requestObject){
  9752. var oldAbortFunction = requestObject.abort || null;
  9753. var aborted = false;
  9754. var startIndex = requestObject.start?requestObject.start:0;
  9755. var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
  9756. requestObject.abort = function(){
  9757. aborted = true;
  9758. if(oldAbortFunction){
  9759. oldAbortFunction.call(requestObject);
  9760. }
  9761. };
  9762. var scope = requestObject.scope || dojo.global;
  9763. if(!requestObject.store){
  9764. requestObject.store = self;
  9765. }
  9766. if(requestObject.onBegin){
  9767. requestObject.onBegin.call(scope, items.length, requestObject);
  9768. }
  9769. if(requestObject.sort){
  9770. items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
  9771. }
  9772. if(requestObject.onItem){
  9773. for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
  9774. var item = items[i];
  9775. if(!aborted){
  9776. requestObject.onItem.call(scope, item, requestObject);
  9777. }
  9778. }
  9779. }
  9780. if(requestObject.onComplete && !aborted){
  9781. var subset = null;
  9782. if(!requestObject.onItem){
  9783. subset = items.slice(startIndex, endIndex);
  9784. }
  9785. requestObject.onComplete.call(scope, subset, requestObject);
  9786. }
  9787. };
  9788. this._fetchItems(request, _fetchHandler, _errorHandler);
  9789. return request; // Object
  9790. };
  9791. }
  9792. if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9793. dojo._hasResource["dojo.data.util.filter"] = true;
  9794. dojo.provide("dojo.data.util.filter");
  9795. dojo.getObject("data.util.filter", true, dojo);
  9796. dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
  9797. // summary:
  9798. // Helper function to convert a simple pattern to a regular expression for matching.
  9799. // description:
  9800. // Returns a regular expression object that conforms to the defined conversion rules.
  9801. // For example:
  9802. // ca* -> /^ca.*$/
  9803. // *ca* -> /^.*ca.*$/
  9804. // *c\*a* -> /^.*c\*a.*$/
  9805. // *c\*a?* -> /^.*c\*a..*$/
  9806. // and so on.
  9807. //
  9808. // pattern: string
  9809. // A simple matching pattern to convert that follows basic rules:
  9810. // * Means match anything, so ca* means match anything starting with ca
  9811. // ? Means match single character. So, b?b will match to bob and bab, and so on.
  9812. // \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
  9813. // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
  9814. // represented by \\ to be treated as an ordinary \ character instead of an escape.
  9815. //
  9816. // ignoreCase:
  9817. // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
  9818. // By default, it is assumed case sensitive.
  9819. var rxp = "^";
  9820. var c = null;
  9821. for(var i = 0; i < pattern.length; i++){
  9822. c = pattern.charAt(i);
  9823. switch(c){
  9824. case '\\':
  9825. rxp += c;
  9826. i++;
  9827. rxp += pattern.charAt(i);
  9828. break;
  9829. case '*':
  9830. rxp += ".*"; break;
  9831. case '?':
  9832. rxp += "."; break;
  9833. case '$':
  9834. case '^':
  9835. case '/':
  9836. case '+':
  9837. case '.':
  9838. case '|':
  9839. case '(':
  9840. case ')':
  9841. case '{':
  9842. case '}':
  9843. case '[':
  9844. case ']':
  9845. rxp += "\\"; //fallthrough
  9846. default:
  9847. rxp += c;
  9848. }
  9849. }
  9850. rxp += "$";
  9851. if(ignoreCase){
  9852. return new RegExp(rxp,"mi"); //RegExp
  9853. }else{
  9854. return new RegExp(rxp,"m"); //RegExp
  9855. }
  9856. };
  9857. }
  9858. if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9859. dojo._hasResource["dijit.form.TextBox"] = true;
  9860. dojo.provide("dijit.form.TextBox");
  9861. dojo.declare(
  9862. "dijit.form.TextBox",
  9863. dijit.form._FormValueWidget,
  9864. {
  9865. // summary:
  9866. // A base class for textbox form inputs
  9867. // trim: Boolean
  9868. // Removes leading and trailing whitespace if true. Default is false.
  9869. trim: false,
  9870. // uppercase: Boolean
  9871. // Converts all characters to uppercase if true. Default is false.
  9872. uppercase: false,
  9873. // lowercase: Boolean
  9874. // Converts all characters to lowercase if true. Default is false.
  9875. lowercase: false,
  9876. // propercase: Boolean
  9877. // Converts the first character of each word to uppercase if true.
  9878. propercase: false,
  9879. // maxLength: String
  9880. // HTML INPUT tag maxLength declaration.
  9881. maxLength: "",
  9882. // selectOnClick: [const] Boolean
  9883. // If true, all text will be selected when focused with mouse
  9884. selectOnClick: false,
  9885. // placeHolder: String
  9886. // Defines a hint to help users fill out the input field (as defined in HTML 5).
  9887. // This should only contain plain text (no html markup).
  9888. placeHolder: "",
  9889. templateString: dojo.cache("dijit.form", "templates/TextBox.html", "<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
  9890. _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
  9891. _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
  9892. baseClass: "dijitTextBox",
  9893. attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
  9894. maxLength: "focusNode"
  9895. }),
  9896. postMixInProperties: function(){
  9897. var type = this.type.toLowerCase();
  9898. if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
  9899. this.templateString = this._singleNodeTemplate;
  9900. }
  9901. this.inherited(arguments);
  9902. },
  9903. _setPlaceHolderAttr: function(v){
  9904. this._set("placeHolder", v);
  9905. if(!this._phspan){
  9906. this._attachPoints.push('_phspan');
  9907. /* dijitInputField class gives placeHolder same padding as the input field
  9908. * parent node already has dijitInputField class but it doesn't affect this <span>
  9909. * since it's position: absolute.
  9910. */
  9911. this._phspan = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
  9912. }
  9913. this._phspan.innerHTML="";
  9914. this._phspan.appendChild(document.createTextNode(v));
  9915. this._updatePlaceHolder();
  9916. },
  9917. _updatePlaceHolder: function(){
  9918. if(this._phspan){
  9919. this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
  9920. }
  9921. },
  9922. _getValueAttr: function(){
  9923. // summary:
  9924. // Hook so get('value') works as we like.
  9925. // description:
  9926. // For `dijit.form.TextBox` this basically returns the value of the <input>.
  9927. //
  9928. // For `dijit.form.MappedTextBox` subclasses, which have both
  9929. // a "displayed value" and a separate "submit value",
  9930. // This treats the "displayed value" as the master value, computing the
  9931. // submit value from it via this.parse().
  9932. return this.parse(this.get('displayedValue'), this.constraints);
  9933. },
  9934. _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  9935. // summary:
  9936. // Hook so set('value', ...) works.
  9937. //
  9938. // description:
  9939. // Sets the value of the widget to "value" which can be of
  9940. // any type as determined by the widget.
  9941. //
  9942. // value:
  9943. // The visual element value is also set to a corresponding,
  9944. // but not necessarily the same, value.
  9945. //
  9946. // formattedValue:
  9947. // If specified, used to set the visual element value,
  9948. // otherwise a computed visual value is used.
  9949. //
  9950. // priorityChange:
  9951. // If true, an onChange event is fired immediately instead of
  9952. // waiting for the next blur event.
  9953. var filteredValue;
  9954. if(value !== undefined){
  9955. // TODO: this is calling filter() on both the display value and the actual value.
  9956. // I added a comment to the filter() definition about this, but it should be changed.
  9957. filteredValue = this.filter(value);
  9958. if(typeof formattedValue != "string"){
  9959. if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
  9960. formattedValue = this.filter(this.format(filteredValue, this.constraints));
  9961. }else{ formattedValue = ''; }
  9962. }
  9963. }
  9964. if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
  9965. this.textbox.value = formattedValue;
  9966. this._set("displayedValue", this.get("displayedValue"));
  9967. }
  9968. this._updatePlaceHolder();
  9969. this.inherited(arguments, [filteredValue, priorityChange]);
  9970. },
  9971. // displayedValue: String
  9972. // For subclasses like ComboBox where the displayed value
  9973. // (ex: Kentucky) and the serialized value (ex: KY) are different,
  9974. // this represents the displayed value.
  9975. //
  9976. // Setting 'displayedValue' through set('displayedValue', ...)
  9977. // updates 'value', and vice-versa. Otherwise 'value' is updated
  9978. // from 'displayedValue' periodically, like onBlur etc.
  9979. //
  9980. // TODO: move declaration to MappedTextBox?
  9981. // Problem is that ComboBox references displayedValue,
  9982. // for benefit of FilteringSelect.
  9983. displayedValue: "",
  9984. getDisplayedValue: function(){
  9985. // summary:
  9986. // Deprecated. Use get('displayedValue') instead.
  9987. // tags:
  9988. // deprecated
  9989. dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
  9990. return this.get('displayedValue');
  9991. },
  9992. _getDisplayedValueAttr: function(){
  9993. // summary:
  9994. // Hook so get('displayedValue') works.
  9995. // description:
  9996. // Returns the displayed value (what the user sees on the screen),
  9997. // after filtering (ie, trimming spaces etc.).
  9998. //
  9999. // For some subclasses of TextBox (like ComboBox), the displayed value
  10000. // is different from the serialized value that's actually
  10001. // sent to the server (see dijit.form.ValidationTextBox.serialize)
  10002. // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
  10003. // this method
  10004. // TODO: this isn't really the displayed value when the user is typing
  10005. return this.filter(this.textbox.value);
  10006. },
  10007. setDisplayedValue: function(/*String*/ value){
  10008. // summary:
  10009. // Deprecated. Use set('displayedValue', ...) instead.
  10010. // tags:
  10011. // deprecated
  10012. dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
  10013. this.set('displayedValue', value);
  10014. },
  10015. _setDisplayedValueAttr: function(/*String*/ value){
  10016. // summary:
  10017. // Hook so set('displayedValue', ...) works.
  10018. // description:
  10019. // Sets the value of the visual element to the string "value".
  10020. // The widget value is also set to a corresponding,
  10021. // but not necessarily the same, value.
  10022. if(value === null || value === undefined){ value = '' }
  10023. else if(typeof value != "string"){ value = String(value) }
  10024. this.textbox.value = value;
  10025. // sets the serialized value to something corresponding to specified displayedValue
  10026. // (if possible), and also updates the textbox.value, for example converting "123"
  10027. // to "123.00"
  10028. this._setValueAttr(this.get('value'), undefined);
  10029. this._set("displayedValue", this.get('displayedValue'));
  10030. },
  10031. format: function(/*String*/ value, /*Object*/ constraints){
  10032. // summary:
  10033. // Replacable function to convert a value to a properly formatted string.
  10034. // tags:
  10035. // protected extension
  10036. return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
  10037. },
  10038. parse: function(/*String*/ value, /*Object*/ constraints){
  10039. // summary:
  10040. // Replacable function to convert a formatted string to a value
  10041. // tags:
  10042. // protected extension
  10043. return value; // String
  10044. },
  10045. _refreshState: function(){
  10046. // summary:
  10047. // After the user types some characters, etc., this method is
  10048. // called to check the field for validity etc. The base method
  10049. // in `dijit.form.TextBox` does nothing, but subclasses override.
  10050. // tags:
  10051. // protected
  10052. },
  10053. _onInput: function(e){
  10054. if(e && e.type && /key/i.test(e.type) && e.keyCode){
  10055. switch(e.keyCode){
  10056. case dojo.keys.SHIFT:
  10057. case dojo.keys.ALT:
  10058. case dojo.keys.CTRL:
  10059. case dojo.keys.TAB:
  10060. return;
  10061. }
  10062. }
  10063. if(this.intermediateChanges){
  10064. var _this = this;
  10065. // the setTimeout allows the key to post to the widget input box
  10066. setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
  10067. }
  10068. this._refreshState();
  10069. // In case someone is watch()'ing for changes to displayedValue
  10070. this._set("displayedValue", this.get("displayedValue"));
  10071. },
  10072. postCreate: function(){
  10073. if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
  10074. // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
  10075. setTimeout(dojo.hitch(this, function(){
  10076. var s = dojo.getComputedStyle(this.domNode);
  10077. if(s){
  10078. var ff = s.fontFamily;
  10079. if(ff){
  10080. var inputs = this.domNode.getElementsByTagName("INPUT");
  10081. if(inputs){
  10082. for(var i=0; i < inputs.length; i++){
  10083. inputs[i].style.fontFamily = ff;
  10084. }
  10085. }
  10086. }
  10087. }
  10088. }), 0);
  10089. }
  10090. // setting the value here is needed since value="" in the template causes "undefined"
  10091. // and setting in the DOM (instead of the JS object) helps with form reset actions
  10092. this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
  10093. this.inherited(arguments);
  10094. if(dojo.isMoz || dojo.isOpera){
  10095. this.connect(this.textbox, "oninput", "_onInput");
  10096. }else{
  10097. this.connect(this.textbox, "onkeydown", "_onInput");
  10098. this.connect(this.textbox, "onkeyup", "_onInput");
  10099. this.connect(this.textbox, "onpaste", "_onInput");
  10100. this.connect(this.textbox, "oncut", "_onInput");
  10101. }
  10102. },
  10103. _blankValue: '', // if the textbox is blank, what value should be reported
  10104. filter: function(val){
  10105. // summary:
  10106. // Auto-corrections (such as trimming) that are applied to textbox
  10107. // value on blur or form submit.
  10108. // description:
  10109. // For MappedTextBox subclasses, this is called twice
  10110. // - once with the display value
  10111. // - once the value as set/returned by set('value', ...)
  10112. // and get('value'), ex: a Number for NumberTextBox.
  10113. //
  10114. // In the latter case it does corrections like converting null to NaN. In
  10115. // the former case the NumberTextBox.filter() method calls this.inherited()
  10116. // to execute standard trimming code in TextBox.filter().
  10117. //
  10118. // TODO: break this into two methods in 2.0
  10119. //
  10120. // tags:
  10121. // protected extension
  10122. if(val === null){ return this._blankValue; }
  10123. if(typeof val != "string"){ return val; }
  10124. if(this.trim){
  10125. val = dojo.trim(val);
  10126. }
  10127. if(this.uppercase){
  10128. val = val.toUpperCase();
  10129. }
  10130. if(this.lowercase){
  10131. val = val.toLowerCase();
  10132. }
  10133. if(this.propercase){
  10134. val = val.replace(/[^\s]+/g, function(word){
  10135. return word.substring(0,1).toUpperCase() + word.substring(1);
  10136. });
  10137. }
  10138. return val;
  10139. },
  10140. _setBlurValue: function(){
  10141. this._setValueAttr(this.get('value'), true);
  10142. },
  10143. _onBlur: function(e){
  10144. if(this.disabled){ return; }
  10145. this._setBlurValue();
  10146. this.inherited(arguments);
  10147. if(this._selectOnClickHandle){
  10148. this.disconnect(this._selectOnClickHandle);
  10149. }
  10150. if(this.selectOnClick && dojo.isMoz){
  10151. this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
  10152. }
  10153. this._updatePlaceHolder();
  10154. },
  10155. _onFocus: function(/*String*/ by){
  10156. if(this.disabled || this.readOnly){ return; }
  10157. // Select all text on focus via click if nothing already selected.
  10158. // Since mouse-up will clear the selection need to defer selection until after mouse-up.
  10159. // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
  10160. if(this.selectOnClick && by == "mouse"){
  10161. this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
  10162. // Only select all text on first click; otherwise users would have no way to clear
  10163. // the selection.
  10164. this.disconnect(this._selectOnClickHandle);
  10165. // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
  10166. // and if not, then select all the text
  10167. var textIsNotSelected;
  10168. if(dojo.isIE){
  10169. var range = dojo.doc.selection.createRange();
  10170. var parent = range.parentElement();
  10171. textIsNotSelected = parent == this.textbox && range.text.length == 0;
  10172. }else{
  10173. textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
  10174. }
  10175. if(textIsNotSelected){
  10176. dijit.selectInputText(this.textbox);
  10177. }
  10178. });
  10179. }
  10180. this._updatePlaceHolder();
  10181. // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
  10182. // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
  10183. this.inherited(arguments);
  10184. this._refreshState();
  10185. },
  10186. reset: function(){
  10187. // Overrides dijit._FormWidget.reset().
  10188. // Additionally resets the displayed textbox value to ''
  10189. this.textbox.value = '';
  10190. this.inherited(arguments);
  10191. }
  10192. }
  10193. );
  10194. dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
  10195. // summary:
  10196. // Select text in the input element argument, from start (default 0), to stop (default end).
  10197. // TODO: use functions in _editor/selection.js?
  10198. var _window = dojo.global;
  10199. var _document = dojo.doc;
  10200. element = dojo.byId(element);
  10201. if(isNaN(start)){ start = 0; }
  10202. if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
  10203. dijit.focus(element);
  10204. if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
  10205. if(element.createTextRange){
  10206. var r = element.createTextRange();
  10207. r.collapse(true);
  10208. r.moveStart("character", -99999); // move to 0
  10209. r.moveStart("character", start); // delta from 0 is the correct position
  10210. r.moveEnd("character", stop-start);
  10211. r.select();
  10212. }
  10213. }else if(_window["getSelection"]){
  10214. if(element.setSelectionRange){
  10215. element.setSelectionRange(start, stop);
  10216. }
  10217. }
  10218. };
  10219. }
  10220. if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10221. dojo._hasResource["dijit.Tooltip"] = true;
  10222. dojo.provide("dijit.Tooltip");
  10223. dojo.declare(
  10224. "dijit._MasterTooltip",
  10225. [dijit._Widget, dijit._Templated],
  10226. {
  10227. // summary:
  10228. // Internal widget that holds the actual tooltip markup,
  10229. // which occurs once per page.
  10230. // Called by Tooltip widgets which are just containers to hold
  10231. // the markup
  10232. // tags:
  10233. // protected
  10234. // duration: Integer
  10235. // Milliseconds to fade in/fade out
  10236. duration: dijit.defaultDuration,
  10237. templateString: dojo.cache("dijit", "templates/Tooltip.html", "<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" dojoAttachPoint=\"connectorNode\"></div\n></div>\n"),
  10238. postCreate: function(){
  10239. dojo.body().appendChild(this.domNode);
  10240. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  10241. // Setup fade-in and fade-out functions.
  10242. this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
  10243. this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
  10244. },
  10245. show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
  10246. // summary:
  10247. // Display tooltip w/specified contents to right of specified node
  10248. // (To left if there's no space on the right, or if rtl == true)
  10249. if(this.aroundNode && this.aroundNode === aroundNode){
  10250. return;
  10251. }
  10252. // reset width; it may have been set by orient() on a previous tooltip show()
  10253. this.domNode.width = "auto";
  10254. if(this.fadeOut.status() == "playing"){
  10255. // previous tooltip is being hidden; wait until the hide completes then show new one
  10256. this._onDeck=arguments;
  10257. return;
  10258. }
  10259. this.containerNode.innerHTML=innerHTML;
  10260. var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
  10261. // show it
  10262. dojo.style(this.domNode, "opacity", 0);
  10263. this.fadeIn.play();
  10264. this.isShowingNow = true;
  10265. this.aroundNode = aroundNode;
  10266. },
  10267. orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
  10268. // summary:
  10269. // Private function to set CSS for tooltip node based on which position it's in.
  10270. // This is called by the dijit popup code. It will also reduce the tooltip's
  10271. // width to whatever width is available
  10272. // tags:
  10273. // protected
  10274. this.connectorNode.style.top = ""; //reset to default
  10275. //Adjust the spaceAvailable width, without changing the spaceAvailable object
  10276. var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
  10277. node.className = "dijitTooltip " +
  10278. {
  10279. "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
  10280. "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
  10281. "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
  10282. "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
  10283. "BR-BL": "dijitTooltipRight",
  10284. "BL-BR": "dijitTooltipLeft"
  10285. }[aroundCorner + "-" + tooltipCorner];
  10286. // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
  10287. this.domNode.style.width = "auto";
  10288. var size = dojo.contentBox(this.domNode);
  10289. var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
  10290. var widthWasReduced = width < size.w;
  10291. this.domNode.style.width = width+"px";
  10292. //Adjust width for tooltips that have a really long word or a nowrap setting
  10293. if(widthWasReduced){
  10294. this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
  10295. var scrollWidth = this.containerNode.scrollWidth;
  10296. this.containerNode.style.overflow = "visible"; //change it back
  10297. if(scrollWidth > width){
  10298. scrollWidth = scrollWidth + dojo.style(this.domNode,"paddingLeft") + dojo.style(this.domNode,"paddingRight");
  10299. this.domNode.style.width = scrollWidth + "px";
  10300. }
  10301. }
  10302. // Reposition the tooltip connector.
  10303. if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
  10304. var mb = dojo.marginBox(node);
  10305. var tooltipConnectorHeight = this.connectorNode.offsetHeight;
  10306. if(mb.h > spaceAvailable.h){
  10307. // The tooltip starts at the top of the page and will extend past the aroundNode
  10308. var aroundNodePlacement = spaceAvailable.h - (aroundNodeCoords.h / 2) - (tooltipConnectorHeight / 2);
  10309. this.connectorNode.style.top = aroundNodePlacement + "px";
  10310. this.connectorNode.style.bottom = "";
  10311. }else{
  10312. // Align center of connector with center of aroundNode, except don't let bottom
  10313. // of connector extend below bottom of tooltip content, or top of connector
  10314. // extend past top of tooltip content
  10315. this.connectorNode.style.bottom = Math.min(
  10316. Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
  10317. mb.h - tooltipConnectorHeight) + "px";
  10318. this.connectorNode.style.top = "";
  10319. }
  10320. }else{
  10321. // reset the tooltip back to the defaults
  10322. this.connectorNode.style.top = "";
  10323. this.connectorNode.style.bottom = "";
  10324. }
  10325. return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
  10326. },
  10327. _onShow: function(){
  10328. // summary:
  10329. // Called at end of fade-in operation
  10330. // tags:
  10331. // protected
  10332. if(dojo.isIE){
  10333. // the arrow won't show up on a node w/an opacity filter
  10334. this.domNode.style.filter="";
  10335. }
  10336. },
  10337. hide: function(aroundNode){
  10338. // summary:
  10339. // Hide the tooltip
  10340. if(this._onDeck && this._onDeck[1] == aroundNode){
  10341. // this hide request is for a show() that hasn't even started yet;
  10342. // just cancel the pending show()
  10343. this._onDeck=null;
  10344. }else if(this.aroundNode === aroundNode){
  10345. // this hide request is for the currently displayed tooltip
  10346. this.fadeIn.stop();
  10347. this.isShowingNow = false;
  10348. this.aroundNode = null;
  10349. this.fadeOut.play();
  10350. }else{
  10351. // just ignore the call, it's for a tooltip that has already been erased
  10352. }
  10353. },
  10354. _onHide: function(){
  10355. // summary:
  10356. // Called at end of fade-out operation
  10357. // tags:
  10358. // protected
  10359. this.domNode.style.cssText=""; // to position offscreen again
  10360. this.containerNode.innerHTML="";
  10361. if(this._onDeck){
  10362. // a show request has been queued up; do it now
  10363. this.show.apply(this, this._onDeck);
  10364. this._onDeck=null;
  10365. }
  10366. }
  10367. }
  10368. );
  10369. dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
  10370. // summary:
  10371. // Display tooltip w/specified contents in specified position.
  10372. // See description of dijit.Tooltip.defaultPosition for details on position parameter.
  10373. // If position is not specified then dijit.Tooltip.defaultPosition is used.
  10374. if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
  10375. return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
  10376. };
  10377. dijit.hideTooltip = function(aroundNode){
  10378. // summary:
  10379. // Hide the tooltip
  10380. if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
  10381. return dijit._masterTT.hide(aroundNode);
  10382. };
  10383. dojo.declare(
  10384. "dijit.Tooltip",
  10385. dijit._Widget,
  10386. {
  10387. // summary:
  10388. // Pops up a tooltip (a help message) when you hover over a node.
  10389. // label: String
  10390. // Text to display in the tooltip.
  10391. // Specified as innerHTML when creating the widget from markup.
  10392. label: "",
  10393. // showDelay: Integer
  10394. // Number of milliseconds to wait after hovering over/focusing on the object, before
  10395. // the tooltip is displayed.
  10396. showDelay: 400,
  10397. // connectId: String|String[]
  10398. // Id of domNode(s) to attach the tooltip to.
  10399. // When user hovers over specified dom node, the tooltip will appear.
  10400. connectId: [],
  10401. // position: String[]
  10402. // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
  10403. position: [],
  10404. _setConnectIdAttr: function(/*String*/ newId){
  10405. // summary:
  10406. // Connect to node(s) (specified by id)
  10407. // Remove connections to old nodes (if there are any)
  10408. dojo.forEach(this._connections || [], function(nested){
  10409. dojo.forEach(nested, dojo.hitch(this, "disconnect"));
  10410. }, this);
  10411. // Make connections to nodes in newIds.
  10412. var ary = dojo.isArrayLike(newId) ? newId : (newId ? [newId] : []);
  10413. this._connections = dojo.map(ary, function(id){
  10414. var node = dojo.byId(id);
  10415. return node ? [
  10416. this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
  10417. this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
  10418. this.connect(node, "onfocus", "_onTargetFocus"),
  10419. this.connect(node, "onblur", "_onTargetBlur")
  10420. ] : [];
  10421. }, this);
  10422. this._set("connectId", newId);
  10423. this._connectIds = ary; // save as array
  10424. },
  10425. addTarget: function(/*DOMNODE || String*/ node){
  10426. // summary:
  10427. // Attach tooltip to specified node if it's not already connected
  10428. // TODO: remove in 2.0 and just use set("connectId", ...) interface
  10429. var id = node.id || node;
  10430. if(dojo.indexOf(this._connectIds, id) == -1){
  10431. this.set("connectId", this._connectIds.concat(id));
  10432. }
  10433. },
  10434. removeTarget: function(/*DOMNODE || String*/ node){
  10435. // summary:
  10436. // Detach tooltip from specified node
  10437. // TODO: remove in 2.0 and just use set("connectId", ...) interface
  10438. var id = node.id || node, // map from DOMNode back to plain id string
  10439. idx = dojo.indexOf(this._connectIds, id);
  10440. if(idx >= 0){
  10441. // remove id (modifies original this._connectIds but that's OK in this case)
  10442. this._connectIds.splice(idx, 1);
  10443. this.set("connectId", this._connectIds);
  10444. }
  10445. },
  10446. buildRendering: function(){
  10447. this.inherited(arguments);
  10448. dojo.addClass(this.domNode,"dijitTooltipData");
  10449. },
  10450. startup: function(){
  10451. this.inherited(arguments);
  10452. // If this tooltip was created in a template, or for some other reason the specified connectId[s]
  10453. // didn't exist during the widget's initialization, then connect now.
  10454. var ids = this.connectId;
  10455. dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
  10456. },
  10457. _onTargetMouseEnter: function(/*Event*/ e){
  10458. // summary:
  10459. // Handler for mouseenter event on the target node
  10460. // tags:
  10461. // private
  10462. this._onHover(e);
  10463. },
  10464. _onTargetMouseLeave: function(/*Event*/ e){
  10465. // summary:
  10466. // Handler for mouseleave event on the target node
  10467. // tags:
  10468. // private
  10469. this._onUnHover(e);
  10470. },
  10471. _onTargetFocus: function(/*Event*/ e){
  10472. // summary:
  10473. // Handler for focus event on the target node
  10474. // tags:
  10475. // private
  10476. this._focus = true;
  10477. this._onHover(e);
  10478. },
  10479. _onTargetBlur: function(/*Event*/ e){
  10480. // summary:
  10481. // Handler for blur event on the target node
  10482. // tags:
  10483. // private
  10484. this._focus = false;
  10485. this._onUnHover(e);
  10486. },
  10487. _onHover: function(/*Event*/ e){
  10488. // summary:
  10489. // Despite the name of this method, it actually handles both hover and focus
  10490. // events on the target node, setting a timer to show the tooltip.
  10491. // tags:
  10492. // private
  10493. if(!this._showTimer){
  10494. var target = e.target;
  10495. this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
  10496. }
  10497. },
  10498. _onUnHover: function(/*Event*/ e){
  10499. // summary:
  10500. // Despite the name of this method, it actually handles both mouseleave and blur
  10501. // events on the target node, hiding the tooltip.
  10502. // tags:
  10503. // private
  10504. // keep a tooltip open if the associated element still has focus (even though the
  10505. // mouse moved away)
  10506. if(this._focus){ return; }
  10507. if(this._showTimer){
  10508. clearTimeout(this._showTimer);
  10509. delete this._showTimer;
  10510. }
  10511. this.close();
  10512. },
  10513. open: function(/*DomNode*/ target){
  10514. // summary:
  10515. // Display the tooltip; usually not called directly.
  10516. // tags:
  10517. // private
  10518. if(this._showTimer){
  10519. clearTimeout(this._showTimer);
  10520. delete this._showTimer;
  10521. }
  10522. dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
  10523. this._connectNode = target;
  10524. this.onShow(target, this.position);
  10525. },
  10526. close: function(){
  10527. // summary:
  10528. // Hide the tooltip or cancel timer for show of tooltip
  10529. // tags:
  10530. // private
  10531. if(this._connectNode){
  10532. // if tooltip is currently shown
  10533. dijit.hideTooltip(this._connectNode);
  10534. delete this._connectNode;
  10535. this.onHide();
  10536. }
  10537. if(this._showTimer){
  10538. // if tooltip is scheduled to be shown (after a brief delay)
  10539. clearTimeout(this._showTimer);
  10540. delete this._showTimer;
  10541. }
  10542. },
  10543. onShow: function(target, position){
  10544. // summary:
  10545. // Called when the tooltip is shown
  10546. // tags:
  10547. // callback
  10548. },
  10549. onHide: function(){
  10550. // summary:
  10551. // Called when the tooltip is hidden
  10552. // tags:
  10553. // callback
  10554. },
  10555. uninitialize: function(){
  10556. this.close();
  10557. this.inherited(arguments);
  10558. }
  10559. }
  10560. );
  10561. // dijit.Tooltip.defaultPosition: String[]
  10562. // This variable controls the position of tooltips, if the position is not specified to
  10563. // the Tooltip widget or *TextBox widget itself. It's an array of strings with the following values:
  10564. //
  10565. // * before: places tooltip to the left of the target node/widget, or to the right in
  10566. // the case of RTL scripts like Hebrew and Arabic
  10567. // * after: places tooltip to the right of the target node/widget, or to the left in
  10568. // the case of RTL scripts like Hebrew and Arabic
  10569. // * above: tooltip goes above target node
  10570. // * below: tooltip goes below target node
  10571. //
  10572. // The list is positions is tried, in order, until a position is found where the tooltip fits
  10573. // within the viewport.
  10574. //
  10575. // Be careful setting this parameter. A value of "above" may work fine until the user scrolls
  10576. // the screen so that there's no room above the target node. Nodes with drop downs, like
  10577. // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
  10578. // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
  10579. // is only room below (or above) the target node, but not both.
  10580. dijit.Tooltip.defaultPosition = ["after", "before"];
  10581. }
  10582. if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10583. dojo._hasResource["dijit.form.ValidationTextBox"] = true;
  10584. dojo.provide("dijit.form.ValidationTextBox");
  10585. /*=====
  10586. dijit.form.ValidationTextBox.__Constraints = function(){
  10587. // locale: String
  10588. // locale used for validation, picks up value from this widget's lang attribute
  10589. // _flags_: anything
  10590. // various flags passed to regExpGen function
  10591. this.locale = "";
  10592. this._flags_ = "";
  10593. }
  10594. =====*/
  10595. dojo.declare(
  10596. "dijit.form.ValidationTextBox",
  10597. dijit.form.TextBox,
  10598. {
  10599. // summary:
  10600. // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
  10601. // tags:
  10602. // protected
  10603. templateString: dojo.cache("dijit.form", "templates/ValidationTextBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
  10604. baseClass: "dijitTextBox dijitValidationTextBox",
  10605. // required: Boolean
  10606. // User is required to enter data into this field.
  10607. required: false,
  10608. // promptMessage: String
  10609. // If defined, display this hint string immediately on focus to the textbox, if empty.
  10610. // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
  10611. // Think of this like a tooltip that tells the user what to do, not an error message
  10612. // that tells the user what they've done wrong.
  10613. //
  10614. // Message disappears when user starts typing.
  10615. promptMessage: "",
  10616. // invalidMessage: String
  10617. // The message to display if value is invalid.
  10618. // The translated string value is read from the message file by default.
  10619. // Set to "" to use the promptMessage instead.
  10620. invalidMessage: "$_unset_$",
  10621. // missingMessage: String
  10622. // The message to display if value is empty and the field is required.
  10623. // The translated string value is read from the message file by default.
  10624. // Set to "" to use the invalidMessage instead.
  10625. missingMessage: "$_unset_$",
  10626. // message: String
  10627. // Currently error/prompt message.
  10628. // When using the default tooltip implementation, this will only be
  10629. // displayed when the field is focused.
  10630. message: "",
  10631. // constraints: dijit.form.ValidationTextBox.__Constraints
  10632. // user-defined object needed to pass parameters to the validator functions
  10633. constraints: {},
  10634. // regExp: [extension protected] String
  10635. // regular expression string used to validate the input
  10636. // Do not specify both regExp and regExpGen
  10637. regExp: ".*",
  10638. regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ constraints){
  10639. // summary:
  10640. // Overridable function used to generate regExp when dependent on constraints.
  10641. // Do not specify both regExp and regExpGen.
  10642. // tags:
  10643. // extension protected
  10644. return this.regExp; // String
  10645. },
  10646. // state: [readonly] String
  10647. // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
  10648. state: "",
  10649. // tooltipPosition: String[]
  10650. // See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
  10651. tooltipPosition: [],
  10652. _setValueAttr: function(){
  10653. // summary:
  10654. // Hook so set('value', ...) works.
  10655. this.inherited(arguments);
  10656. this.validate(this._focused);
  10657. },
  10658. validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
  10659. // summary:
  10660. // Overridable function used to validate the text input against the regular expression.
  10661. // tags:
  10662. // protected
  10663. return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
  10664. (!this.required || !this._isEmpty(value)) &&
  10665. (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
  10666. },
  10667. _isValidSubset: function(){
  10668. // summary:
  10669. // Returns true if the value is either already valid or could be made valid by appending characters.
  10670. // This is used for validation while the user [may be] still typing.
  10671. return this.textbox.value.search(this._partialre) == 0;
  10672. },
  10673. isValid: function(/*Boolean*/ isFocused){
  10674. // summary:
  10675. // Tests if value is valid.
  10676. // Can override with your own routine in a subclass.
  10677. // tags:
  10678. // protected
  10679. return this.validator(this.textbox.value, this.constraints);
  10680. },
  10681. _isEmpty: function(value){
  10682. // summary:
  10683. // Checks for whitespace
  10684. return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
  10685. },
  10686. getErrorMessage: function(/*Boolean*/ isFocused){
  10687. // summary:
  10688. // Return an error message to show if appropriate
  10689. // tags:
  10690. // protected
  10691. return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
  10692. },
  10693. getPromptMessage: function(/*Boolean*/ isFocused){
  10694. // summary:
  10695. // Return a hint message to show when widget is first focused
  10696. // tags:
  10697. // protected
  10698. return this.promptMessage; // String
  10699. },
  10700. _maskValidSubsetError: true,
  10701. validate: function(/*Boolean*/ isFocused){
  10702. // summary:
  10703. // Called by oninit, onblur, and onkeypress.
  10704. // description:
  10705. // Show missing or invalid messages if appropriate, and highlight textbox field.
  10706. // tags:
  10707. // protected
  10708. var message = "";
  10709. var isValid = this.disabled || this.isValid(isFocused);
  10710. if(isValid){ this._maskValidSubsetError = true; }
  10711. var isEmpty = this._isEmpty(this.textbox.value);
  10712. var isValidSubset = !isValid && isFocused && this._isValidSubset();
  10713. this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
  10714. dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
  10715. if(this.state == "Error"){
  10716. this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
  10717. message = this.getErrorMessage(isFocused);
  10718. }else if(this.state == "Incomplete"){
  10719. message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
  10720. this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
  10721. }else if(isEmpty){
  10722. message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
  10723. }
  10724. this.set("message", message);
  10725. return isValid;
  10726. },
  10727. displayMessage: function(/*String*/ message){
  10728. // summary:
  10729. // Overridable method to display validation errors/hints.
  10730. // By default uses a tooltip.
  10731. // tags:
  10732. // extension
  10733. dijit.hideTooltip(this.domNode);
  10734. if(message && this._focused){
  10735. dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
  10736. }
  10737. },
  10738. _refreshState: function(){
  10739. // Overrides TextBox._refreshState()
  10740. this.validate(this._focused);
  10741. this.inherited(arguments);
  10742. },
  10743. //////////// INITIALIZATION METHODS ///////////////////////////////////////
  10744. constructor: function(){
  10745. this.constraints = {};
  10746. },
  10747. _setConstraintsAttr: function(/*Object*/ constraints){
  10748. if(!constraints.locale && this.lang){
  10749. constraints.locale = this.lang;
  10750. }
  10751. this._set("constraints", constraints);
  10752. this._computePartialRE();
  10753. },
  10754. _computePartialRE: function(){
  10755. var p = this.regExpGen(this.constraints);
  10756. this.regExp = p;
  10757. var partialre = "";
  10758. // parse the regexp and produce a new regexp that matches valid subsets
  10759. // if the regexp is .* then there's no use in matching subsets since everything is valid
  10760. if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
  10761. function (re){
  10762. switch(re.charAt(0)){
  10763. case '{':
  10764. case '+':
  10765. case '?':
  10766. case '*':
  10767. case '^':
  10768. case '$':
  10769. case '|':
  10770. case '(':
  10771. partialre += re;
  10772. break;
  10773. case ")":
  10774. partialre += "|$)";
  10775. break;
  10776. default:
  10777. partialre += "(?:"+re+"|$)";
  10778. break;
  10779. }
  10780. }
  10781. );}
  10782. try{ // this is needed for now since the above regexp parsing needs more test verification
  10783. "".search(partialre);
  10784. }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
  10785. partialre = this.regExp;
  10786. console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
  10787. } // should never be here unless the original RE is bad or the parsing is bad
  10788. this._partialre = "^(?:" + partialre + ")$";
  10789. },
  10790. postMixInProperties: function(){
  10791. this.inherited(arguments);
  10792. this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
  10793. if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
  10794. if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
  10795. if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
  10796. if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
  10797. this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
  10798. },
  10799. _setDisabledAttr: function(/*Boolean*/ value){
  10800. this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
  10801. this._refreshState();
  10802. },
  10803. _setRequiredAttr: function(/*Boolean*/ value){
  10804. this._set("required", value);
  10805. dijit.setWaiState(this.focusNode, "required", value);
  10806. this._refreshState();
  10807. },
  10808. _setMessageAttr: function(/*String*/ message){
  10809. this._set("message", message);
  10810. this.displayMessage(message);
  10811. },
  10812. reset:function(){
  10813. // Overrides dijit.form.TextBox.reset() by also
  10814. // hiding errors about partial matches
  10815. this._maskValidSubsetError = true;
  10816. this.inherited(arguments);
  10817. },
  10818. _onBlur: function(){
  10819. // the message still exists but for back-compat, and to erase the tooltip
  10820. // (if the message is being displayed as a tooltip), call displayMessage('')
  10821. this.displayMessage('');
  10822. this.inherited(arguments);
  10823. }
  10824. }
  10825. );
  10826. dojo.declare(
  10827. "dijit.form.MappedTextBox",
  10828. dijit.form.ValidationTextBox,
  10829. {
  10830. // summary:
  10831. // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
  10832. // a visible formatted display value, and a serializable
  10833. // value in a hidden input field which is actually sent to the server.
  10834. // description:
  10835. // The visible display may
  10836. // be locale-dependent and interactive. The value sent to the server is stored in a hidden
  10837. // input field which uses the `name` attribute declared by the original widget. That value sent
  10838. // to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
  10839. // locale-neutral.
  10840. // tags:
  10841. // protected
  10842. postMixInProperties: function(){
  10843. this.inherited(arguments);
  10844. // we want the name attribute to go to the hidden <input>, not the displayed <input>,
  10845. // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
  10846. this.nameAttrSetting = "";
  10847. },
  10848. serialize: function(/*anything*/ val, /*Object?*/ options){
  10849. // summary:
  10850. // Overridable function used to convert the get('value') result to a canonical
  10851. // (non-localized) string. For example, will print dates in ISO format, and
  10852. // numbers the same way as they are represented in javascript.
  10853. // tags:
  10854. // protected extension
  10855. return val.toString ? val.toString() : ""; // String
  10856. },
  10857. toString: function(){
  10858. // summary:
  10859. // Returns widget as a printable string using the widget's value
  10860. // tags:
  10861. // protected
  10862. var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
  10863. return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
  10864. },
  10865. validate: function(){
  10866. // Overrides `dijit.form.TextBox.validate`
  10867. this.valueNode.value = this.toString();
  10868. return this.inherited(arguments);
  10869. },
  10870. buildRendering: function(){
  10871. // Overrides `dijit._Templated.buildRendering`
  10872. this.inherited(arguments);
  10873. // Create a hidden <input> node with the serialized value used for submit
  10874. // (as opposed to the displayed value).
  10875. // Passing in name as markup rather than calling dojo.create() with an attrs argument
  10876. // to make dojo.query(input[name=...]) work on IE. (see #8660)
  10877. this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
  10878. },
  10879. reset: function(){
  10880. // Overrides `dijit.form.ValidationTextBox.reset` to
  10881. // reset the hidden textbox value to ''
  10882. this.valueNode.value = '';
  10883. this.inherited(arguments);
  10884. }
  10885. }
  10886. );
  10887. /*=====
  10888. dijit.form.RangeBoundTextBox.__Constraints = function(){
  10889. // min: Number
  10890. // Minimum signed value. Default is -Infinity
  10891. // max: Number
  10892. // Maximum signed value. Default is +Infinity
  10893. this.min = min;
  10894. this.max = max;
  10895. }
  10896. =====*/
  10897. dojo.declare(
  10898. "dijit.form.RangeBoundTextBox",
  10899. dijit.form.MappedTextBox,
  10900. {
  10901. // summary:
  10902. // Base class for textbox form widgets which defines a range of valid values.
  10903. // rangeMessage: String
  10904. // The message to display if value is out-of-range
  10905. rangeMessage: "",
  10906. /*=====
  10907. // constraints: dijit.form.RangeBoundTextBox.__Constraints
  10908. constraints: {},
  10909. ======*/
  10910. rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
  10911. // summary:
  10912. // Overridable function used to validate the range of the numeric input value.
  10913. // tags:
  10914. // protected
  10915. return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
  10916. ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
  10917. },
  10918. isInRange: function(/*Boolean*/ isFocused){
  10919. // summary:
  10920. // Tests if the value is in the min/max range specified in constraints
  10921. // tags:
  10922. // protected
  10923. return this.rangeCheck(this.get('value'), this.constraints);
  10924. },
  10925. _isDefinitelyOutOfRange: function(){
  10926. // summary:
  10927. // Returns true if the value is out of range and will remain
  10928. // out of range even if the user types more characters
  10929. var val = this.get('value');
  10930. var isTooLittle = false;
  10931. var isTooMuch = false;
  10932. if("min" in this.constraints){
  10933. var min = this.constraints.min;
  10934. min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
  10935. isTooLittle = (typeof min == "number") && min < 0;
  10936. }
  10937. if("max" in this.constraints){
  10938. var max = this.constraints.max;
  10939. max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
  10940. isTooMuch = (typeof max == "number") && max > 0;
  10941. }
  10942. return isTooLittle || isTooMuch;
  10943. },
  10944. _isValidSubset: function(){
  10945. // summary:
  10946. // Overrides `dijit.form.ValidationTextBox._isValidSubset`.
  10947. // Returns true if the input is syntactically valid, and either within
  10948. // range or could be made in range by more typing.
  10949. return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
  10950. },
  10951. isValid: function(/*Boolean*/ isFocused){
  10952. // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
  10953. return this.inherited(arguments) &&
  10954. ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
  10955. },
  10956. getErrorMessage: function(/*Boolean*/ isFocused){
  10957. // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
  10958. var v = this.get('value');
  10959. if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
  10960. return this.rangeMessage; // String
  10961. }
  10962. return this.inherited(arguments);
  10963. },
  10964. postMixInProperties: function(){
  10965. this.inherited(arguments);
  10966. if(!this.rangeMessage){
  10967. this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
  10968. this.rangeMessage = this.messages.rangeMessage;
  10969. }
  10970. },
  10971. _setConstraintsAttr: function(/*Object*/ constraints){
  10972. this.inherited(arguments);
  10973. if(this.focusNode){ // not set when called from postMixInProperties
  10974. if(this.constraints.min !== undefined){
  10975. dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
  10976. }else{
  10977. dijit.removeWaiState(this.focusNode, "valuemin");
  10978. }
  10979. if(this.constraints.max !== undefined){
  10980. dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
  10981. }else{
  10982. dijit.removeWaiState(this.focusNode, "valuemax");
  10983. }
  10984. }
  10985. },
  10986. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  10987. // summary:
  10988. // Hook so set('value', ...) works.
  10989. dijit.setWaiState(this.focusNode, "valuenow", value);
  10990. this.inherited(arguments);
  10991. }
  10992. }
  10993. );
  10994. }
  10995. if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10996. dojo._hasResource["dijit.form.ComboBox"] = true;
  10997. dojo.provide("dijit.form.ComboBox");
  10998. dojo.declare(
  10999. "dijit.form.ComboBoxMixin",
  11000. dijit._HasDropDown,
  11001. {
  11002. // summary:
  11003. // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
  11004. // description:
  11005. // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
  11006. // tags:
  11007. // protected
  11008. // item: Object
  11009. // This is the item returned by the dojo.data.store implementation that
  11010. // provides the data for this ComboBox, it's the currently selected item.
  11011. item: null,
  11012. // pageSize: Integer
  11013. // Argument to data provider.
  11014. // Specifies number of search results per page (before hitting "next" button)
  11015. pageSize: Infinity,
  11016. // store: [const] Object
  11017. // Reference to data provider object used by this ComboBox
  11018. store: null,
  11019. // fetchProperties: Object
  11020. // Mixin to the dojo.data store's fetch.
  11021. // For example, to set the sort order of the ComboBox menu, pass:
  11022. // | { sort: [{attribute:"name",descending: true}] }
  11023. // To override the default queryOptions so that deep=false, do:
  11024. // | { queryOptions: {ignoreCase: true, deep: false} }
  11025. fetchProperties:{},
  11026. // query: Object
  11027. // A query that can be passed to 'store' to initially filter the items,
  11028. // before doing further filtering based on `searchAttr` and the key.
  11029. // Any reference to the `searchAttr` is ignored.
  11030. query: {},
  11031. // autoComplete: Boolean
  11032. // If user types in a partial string, and then tab out of the `<input>` box,
  11033. // automatically copy the first entry displayed in the drop down list to
  11034. // the `<input>` field
  11035. autoComplete: true,
  11036. // highlightMatch: String
  11037. // One of: "first", "all" or "none".
  11038. //
  11039. // If the ComboBox/FilteringSelect opens with the search results and the searched
  11040. // string can be found, it will be highlighted. If set to "all"
  11041. // then will probably want to change `queryExpr` parameter to '*${0}*'
  11042. //
  11043. // Highlighting is only performed when `labelType` is "text", so as to not
  11044. // interfere with any HTML markup an HTML label might contain.
  11045. highlightMatch: "first",
  11046. // searchDelay: Integer
  11047. // Delay in milliseconds between when user types something and we start
  11048. // searching based on that value
  11049. searchDelay: 100,
  11050. // searchAttr: String
  11051. // Search for items in the data store where this attribute (in the item)
  11052. // matches what the user typed
  11053. searchAttr: "name",
  11054. // labelAttr: String?
  11055. // The entries in the drop down list come from this attribute in the
  11056. // dojo.data items.
  11057. // If not specified, the searchAttr attribute is used instead.
  11058. labelAttr: "",
  11059. // labelType: String
  11060. // Specifies how to interpret the labelAttr in the data store items.
  11061. // Can be "html" or "text".
  11062. labelType: "text",
  11063. // queryExpr: String
  11064. // This specifies what query ComboBox/FilteringSelect sends to the data store,
  11065. // based on what the user has typed. Changing this expression will modify
  11066. // whether the drop down shows only exact matches, a "starting with" match,
  11067. // etc. Use it in conjunction with highlightMatch.
  11068. // dojo.data query expression pattern.
  11069. // `${0}` will be substituted for the user text.
  11070. // `*` is used for wildcards.
  11071. // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
  11072. queryExpr: "${0}*",
  11073. // ignoreCase: Boolean
  11074. // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
  11075. ignoreCase: true,
  11076. // hasDownArrow: Boolean
  11077. // Set this textbox to have a down arrow button, to display the drop down list.
  11078. // Defaults to true.
  11079. hasDownArrow: true,
  11080. templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"),
  11081. baseClass: "dijitTextBox dijitComboBox",
  11082. // dropDownClass: [protected extension] String
  11083. // Name of the dropdown widget class used to select a date/time.
  11084. // Subclasses should specify this.
  11085. dropDownClass: "dijit.form._ComboBoxMenu",
  11086. // Set classes like dijitDownArrowButtonHover depending on
  11087. // mouse action over button node
  11088. cssStateNodes: {
  11089. "_buttonNode": "dijitDownArrowButton"
  11090. },
  11091. // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
  11092. maxHeight: -1,
  11093. // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
  11094. _stopClickEvents: false,
  11095. _getCaretPos: function(/*DomNode*/ element){
  11096. // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
  11097. var pos = 0;
  11098. if(typeof(element.selectionStart) == "number"){
  11099. // FIXME: this is totally borked on Moz < 1.3. Any recourse?
  11100. pos = element.selectionStart;
  11101. }else if(dojo.isIE){
  11102. // in the case of a mouse click in a popup being handled,
  11103. // then the dojo.doc.selection is not the textarea, but the popup
  11104. // var r = dojo.doc.selection.createRange();
  11105. // hack to get IE 6 to play nice. What a POS browser.
  11106. var tr = dojo.doc.selection.createRange().duplicate();
  11107. var ntr = element.createTextRange();
  11108. tr.move("character",0);
  11109. ntr.move("character",0);
  11110. try{
  11111. // If control doesn't have focus, you get an exception.
  11112. // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
  11113. // There appears to be no workaround for this - googled for quite a while.
  11114. ntr.setEndPoint("EndToEnd", tr);
  11115. pos = String(ntr.text).replace(/\r/g,"").length;
  11116. }catch(e){
  11117. // If focus has shifted, 0 is fine for caret pos.
  11118. }
  11119. }
  11120. return pos;
  11121. },
  11122. _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
  11123. location = parseInt(location);
  11124. dijit.selectInputText(element, location, location);
  11125. },
  11126. _setDisabledAttr: function(/*Boolean*/ value){
  11127. // Additional code to set disabled state of ComboBox node.
  11128. // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
  11129. this.inherited(arguments);
  11130. dijit.setWaiState(this.domNode, "disabled", value);
  11131. },
  11132. _abortQuery: function(){
  11133. // stop in-progress query
  11134. if(this.searchTimer){
  11135. clearTimeout(this.searchTimer);
  11136. this.searchTimer = null;
  11137. }
  11138. if(this._fetchHandle){
  11139. if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
  11140. this._fetchHandle = null;
  11141. }
  11142. },
  11143. _onInput: function(/*Event*/ evt){
  11144. // summary:
  11145. // Handles paste events
  11146. if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
  11147. this.searchTimer = setTimeout(dojo.hitch(this, function(){
  11148. this._onKey({charOrCode: 229}); // fake IME key to cause a search
  11149. }), 100); // long delay that will probably be preempted by keyboard input
  11150. }
  11151. this.inherited(arguments);
  11152. },
  11153. _onKey: function(/*Event*/ evt){
  11154. // summary:
  11155. // Handles keyboard events
  11156. var key = evt.charOrCode;
  11157. // except for cutting/pasting case - ctrl + x/v
  11158. if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
  11159. return; // throw out weird key combinations and spurious events
  11160. }
  11161. var doSearch = false;
  11162. var pw = this.dropDown;
  11163. var dk = dojo.keys;
  11164. var highlighted = null;
  11165. this._prev_key_backspace = false;
  11166. this._abortQuery();
  11167. // _HasDropDown will do some of the work:
  11168. // 1. when drop down is not yet shown:
  11169. // - if user presses the down arrow key, call loadDropDown()
  11170. // 2. when drop down is already displayed:
  11171. // - on ESC key, call closeDropDown()
  11172. // - otherwise, call dropDown.handleKey() to process the keystroke
  11173. this.inherited(arguments);
  11174. if(this._opened){
  11175. highlighted = pw.getHighlightedOption();
  11176. }
  11177. switch(key){
  11178. case dk.PAGE_DOWN:
  11179. case dk.DOWN_ARROW:
  11180. case dk.PAGE_UP:
  11181. case dk.UP_ARROW:
  11182. // Keystroke caused ComboBox_menu to move to a different item.
  11183. // Copy new item to <input> box.
  11184. if(this._opened){
  11185. this._announceOption(highlighted);
  11186. }
  11187. dojo.stopEvent(evt);
  11188. break;
  11189. case dk.ENTER:
  11190. // prevent submitting form if user presses enter. Also
  11191. // prevent accepting the value if either Next or Previous
  11192. // are selected
  11193. if(highlighted){
  11194. // only stop event on prev/next
  11195. if(highlighted == pw.nextButton){
  11196. this._nextSearch(1);
  11197. dojo.stopEvent(evt);
  11198. break;
  11199. }else if(highlighted == pw.previousButton){
  11200. this._nextSearch(-1);
  11201. dojo.stopEvent(evt);
  11202. break;
  11203. }
  11204. }else{
  11205. // Update 'value' (ex: KY) according to currently displayed text
  11206. this._setBlurValue(); // set value if needed
  11207. this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
  11208. }
  11209. // default case:
  11210. // if enter pressed while drop down is open, or for FilteringSelect,
  11211. // if we are in the middle of a query to convert a directly typed in value to an item,
  11212. // prevent submit
  11213. if(this._opened || this._fetchHandle){
  11214. dojo.stopEvent(evt);
  11215. }
  11216. // fall through
  11217. case dk.TAB:
  11218. var newvalue = this.get('displayedValue');
  11219. // if the user had More Choices selected fall into the
  11220. // _onBlur handler
  11221. if(pw && (
  11222. newvalue == pw._messages["previousMessage"] ||
  11223. newvalue == pw._messages["nextMessage"])
  11224. ){
  11225. break;
  11226. }
  11227. if(highlighted){
  11228. this._selectOption();
  11229. }
  11230. if(this._opened){
  11231. this._lastQuery = null; // in case results come back later
  11232. this.closeDropDown();
  11233. }
  11234. break;
  11235. case ' ':
  11236. if(highlighted){
  11237. // user is effectively clicking a choice in the drop down menu
  11238. dojo.stopEvent(evt);
  11239. this._selectOption();
  11240. this.closeDropDown();
  11241. }else{
  11242. // user typed a space into the input box, treat as normal character
  11243. doSearch = true;
  11244. }
  11245. break;
  11246. case dk.DELETE:
  11247. case dk.BACKSPACE:
  11248. this._prev_key_backspace = true;
  11249. doSearch = true;
  11250. break;
  11251. default:
  11252. // Non char keys (F1-F12 etc..) shouldn't open list.
  11253. // Ascii characters and IME input (Chinese, Japanese etc.) should.
  11254. //IME input produces keycode == 229.
  11255. doSearch = typeof key == 'string' || key == 229;
  11256. }
  11257. if(doSearch){
  11258. // need to wait a tad before start search so that the event
  11259. // bubbles through DOM and we have value visible
  11260. this.item = undefined; // undefined means item needs to be set
  11261. this.searchTimer = setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
  11262. }
  11263. },
  11264. _autoCompleteText: function(/*String*/ text){
  11265. // summary:
  11266. // Fill in the textbox with the first item from the drop down
  11267. // list, and highlight the characters that were
  11268. // auto-completed. For example, if user typed "CA" and the
  11269. // drop down list appeared, the textbox would be changed to
  11270. // "California" and "ifornia" would be highlighted.
  11271. var fn = this.focusNode;
  11272. // IE7: clear selection so next highlight works all the time
  11273. dijit.selectInputText(fn, fn.value.length);
  11274. // does text autoComplete the value in the textbox?
  11275. var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
  11276. if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
  11277. var cpos = this._getCaretPos(fn);
  11278. // only try to extend if we added the last character at the end of the input
  11279. if((cpos+1) > fn.value.length){
  11280. // only add to input node as we would overwrite Capitalisation of chars
  11281. // actually, that is ok
  11282. fn.value = text;//.substr(cpos);
  11283. // visually highlight the autocompleted characters
  11284. dijit.selectInputText(fn, cpos);
  11285. }
  11286. }else{
  11287. // text does not autoComplete; replace the whole value and highlight
  11288. fn.value = text;
  11289. dijit.selectInputText(fn);
  11290. }
  11291. },
  11292. _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
  11293. // summary:
  11294. // Callback when a search completes.
  11295. // description:
  11296. // 1. generates drop-down list and calls _showResultList() to display it
  11297. // 2. if this result list is from user pressing "more choices"/"previous choices"
  11298. // then tell screen reader to announce new option
  11299. this._fetchHandle = null;
  11300. if( this.disabled ||
  11301. this.readOnly ||
  11302. (dataObject.query[this.searchAttr] != this._lastQuery)
  11303. ){
  11304. return;
  11305. }
  11306. var wasSelected = this.dropDown._highlighted_option && dojo.hasClass(this.dropDown._highlighted_option, "dijitMenuItemSelected");
  11307. this.dropDown.clearResultList();
  11308. if(!results.length && !this._maxOptions){ // if no results and not just the previous choices button
  11309. this.closeDropDown();
  11310. return;
  11311. }
  11312. // Fill in the textbox with the first item from the drop down list,
  11313. // and highlight the characters that were auto-completed. For
  11314. // example, if user typed "CA" and the drop down list appeared, the
  11315. // textbox would be changed to "California" and "ifornia" would be
  11316. // highlighted.
  11317. dataObject._maxOptions = this._maxOptions;
  11318. var nodes = this.dropDown.createOptions(
  11319. results,
  11320. dataObject,
  11321. dojo.hitch(this, "_getMenuLabelFromItem")
  11322. );
  11323. // show our list (only if we have content, else nothing)
  11324. this._showResultList();
  11325. // #4091:
  11326. // tell the screen reader that the paging callback finished by
  11327. // shouting the next choice
  11328. if(dataObject.direction){
  11329. if(1 == dataObject.direction){
  11330. this.dropDown.highlightFirstOption();
  11331. }else if(-1 == dataObject.direction){
  11332. this.dropDown.highlightLastOption();
  11333. }
  11334. if(wasSelected){
  11335. this._announceOption(this.dropDown.getHighlightedOption());
  11336. }
  11337. }else if(this.autoComplete && !this._prev_key_backspace
  11338. // when the user clicks the arrow button to show the full list,
  11339. // startSearch looks for "*".
  11340. // it does not make sense to autocomplete
  11341. // if they are just previewing the options available.
  11342. && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
  11343. this._announceOption(nodes[1]); // 1st real item
  11344. }
  11345. },
  11346. _showResultList: function(){
  11347. // summary:
  11348. // Display the drop down if not already displayed, or if it is displayed, then
  11349. // reposition it if necessary (reposition may be necessary if drop down's height changed).
  11350. this.closeDropDown(true);
  11351. // hide the tooltip
  11352. this.displayMessage("");
  11353. this.openDropDown();
  11354. dijit.setWaiState(this.domNode, "expanded", "true");
  11355. },
  11356. loadDropDown: function(/*Function*/ callback){
  11357. // Overrides _HasDropDown.loadDropDown().
  11358. // This is called when user has pressed button icon or pressed the down arrow key
  11359. // to open the drop down.
  11360. this._startSearchAll();
  11361. },
  11362. isLoaded: function(){
  11363. // signal to _HasDropDown that it needs to call loadDropDown() to load the
  11364. // drop down asynchronously before displaying it
  11365. return false;
  11366. },
  11367. closeDropDown: function(){
  11368. // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
  11369. // This method is the callback when the user types ESC or clicking
  11370. // the button icon while the drop down is open. It's also called by other code.
  11371. this._abortQuery();
  11372. if(this._opened){
  11373. this.inherited(arguments);
  11374. dijit.setWaiState(this.domNode, "expanded", "false");
  11375. dijit.removeWaiState(this.focusNode,"activedescendant");
  11376. }
  11377. },
  11378. _setBlurValue: function(){
  11379. // if the user clicks away from the textbox OR tabs away, set the
  11380. // value to the textbox value
  11381. // #4617:
  11382. // if value is now more choices or previous choices, revert
  11383. // the value
  11384. var newvalue = this.get('displayedValue');
  11385. var pw = this.dropDown;
  11386. if(pw && (
  11387. newvalue == pw._messages["previousMessage"] ||
  11388. newvalue == pw._messages["nextMessage"]
  11389. )
  11390. ){
  11391. this._setValueAttr(this._lastValueReported, true);
  11392. }else if(typeof this.item == "undefined"){
  11393. // Update 'value' (ex: KY) according to currently displayed text
  11394. this.item = null;
  11395. this.set('displayedValue', newvalue);
  11396. }else{
  11397. if(this.value != this._lastValueReported){
  11398. dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
  11399. }
  11400. this._refreshState();
  11401. }
  11402. },
  11403. _onBlur: function(){
  11404. // summary:
  11405. // Called magically when focus has shifted away from this widget and it's drop down
  11406. this.closeDropDown();
  11407. this.inherited(arguments);
  11408. },
  11409. _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  11410. // summary:
  11411. // Set the displayed valued in the input box, and the hidden value
  11412. // that gets submitted, based on a dojo.data store item.
  11413. // description:
  11414. // Users shouldn't call this function; they should be calling
  11415. // set('item', value)
  11416. // tags:
  11417. // private
  11418. if(!displayedValue){
  11419. displayedValue = this.store.getValue(item, this.searchAttr);
  11420. }
  11421. var value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
  11422. this._set("item", item);
  11423. dijit.form.ComboBox.superclass._setValueAttr.call(this, value, priorityChange, displayedValue);
  11424. },
  11425. _announceOption: function(/*Node*/ node){
  11426. // summary:
  11427. // a11y code that puts the highlighted option in the textbox.
  11428. // This way screen readers will know what is happening in the
  11429. // menu.
  11430. if(!node){
  11431. return;
  11432. }
  11433. // pull the text value from the item attached to the DOM node
  11434. var newValue;
  11435. if(node == this.dropDown.nextButton ||
  11436. node == this.dropDown.previousButton){
  11437. newValue = node.innerHTML;
  11438. this.item = undefined;
  11439. this.value = '';
  11440. }else{
  11441. newValue = this.store.getValue(node.item, this.searchAttr).toString();
  11442. this.set('item', node.item, false, newValue);
  11443. }
  11444. // get the text that the user manually entered (cut off autocompleted text)
  11445. this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
  11446. // set up ARIA activedescendant
  11447. dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
  11448. // autocomplete the rest of the option to announce change
  11449. this._autoCompleteText(newValue);
  11450. },
  11451. _selectOption: function(/*Event*/ evt){
  11452. // summary:
  11453. // Menu callback function, called when an item in the menu is selected.
  11454. if(evt){
  11455. this._announceOption(evt.target);
  11456. }
  11457. this.closeDropDown();
  11458. this._setCaretPos(this.focusNode, this.focusNode.value.length);
  11459. dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
  11460. },
  11461. _startSearchAll: function(){
  11462. this._startSearch('');
  11463. },
  11464. _startSearchFromInput: function(){
  11465. this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
  11466. },
  11467. _getQueryString: function(/*String*/ text){
  11468. return dojo.string.substitute(this.queryExpr, [text]);
  11469. },
  11470. _startSearch: function(/*String*/ key){
  11471. // summary:
  11472. // Starts a search for elements matching key (key=="" means to return all items),
  11473. // and calls _openResultList() when the search completes, to display the results.
  11474. if(!this.dropDown){
  11475. var popupId = this.id + "_popup",
  11476. dropDownConstructor = dojo.getObject(this.dropDownClass, false);
  11477. this.dropDown = new dropDownConstructor({
  11478. onChange: dojo.hitch(this, this._selectOption),
  11479. id: popupId,
  11480. dir: this.dir
  11481. });
  11482. dijit.removeWaiState(this.focusNode,"activedescendant");
  11483. dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
  11484. }
  11485. // create a new query to prevent accidentally querying for a hidden
  11486. // value from FilteringSelect's keyField
  11487. var query = dojo.clone(this.query); // #5970
  11488. this._lastInput = key; // Store exactly what was entered by the user.
  11489. this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
  11490. // #5970: set _lastQuery, *then* start the timeout
  11491. // otherwise, if the user types and the last query returns before the timeout,
  11492. // _lastQuery won't be set and their input gets rewritten
  11493. this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
  11494. this.searchTimer = null;
  11495. var fetch = {
  11496. queryOptions: {
  11497. ignoreCase: this.ignoreCase,
  11498. deep: true
  11499. },
  11500. query: query,
  11501. onBegin: dojo.hitch(this, "_setMaxOptions"),
  11502. onComplete: dojo.hitch(this, "_openResultList"),
  11503. onError: function(errText){
  11504. _this._fetchHandle = null;
  11505. console.error('dijit.form.ComboBox: ' + errText);
  11506. _this.closeDropDown();
  11507. },
  11508. start: 0,
  11509. count: this.pageSize
  11510. };
  11511. dojo.mixin(fetch, _this.fetchProperties);
  11512. this._fetchHandle = _this.store.fetch(fetch);
  11513. var nextSearch = function(dataObject, direction){
  11514. dataObject.start += dataObject.count*direction;
  11515. // #4091:
  11516. // tell callback the direction of the paging so the screen
  11517. // reader knows which menu option to shout
  11518. dataObject.direction = direction;
  11519. this._fetchHandle = this.store.fetch(dataObject);
  11520. this.focus();
  11521. };
  11522. this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
  11523. }, query, this), this.searchDelay);
  11524. },
  11525. _setMaxOptions: function(size, request){
  11526. this._maxOptions = size;
  11527. },
  11528. _getValueField: function(){
  11529. // summary:
  11530. // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
  11531. // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
  11532. return this.searchAttr;
  11533. },
  11534. //////////// INITIALIZATION METHODS ///////////////////////////////////////
  11535. constructor: function(){
  11536. this.query={};
  11537. this.fetchProperties={};
  11538. },
  11539. postMixInProperties: function(){
  11540. if(!this.store){
  11541. var srcNodeRef = this.srcNodeRef;
  11542. // if user didn't specify store, then assume there are option tags
  11543. this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
  11544. // if there is no value set and there is an option list, set
  11545. // the value to the first value to be consistent with native
  11546. // Select
  11547. // Firefox and Safari set value
  11548. // IE6 and Opera set selectedIndex, which is automatically set
  11549. // by the selected attribute of an option tag
  11550. // IE6 does not set value, Opera sets value = selectedIndex
  11551. if(!("value" in this.params)){
  11552. var item = (this.item = this.store.fetchSelectedItem());
  11553. if(item){
  11554. var valueField = this._getValueField();
  11555. this.value = this.store.getValue(item, valueField);
  11556. }
  11557. }
  11558. }
  11559. this.inherited(arguments);
  11560. },
  11561. postCreate: function(){
  11562. // summary:
  11563. // Subclasses must call this method from their postCreate() methods
  11564. // tags:
  11565. // protected
  11566. // find any associated label element and add to ComboBox node.
  11567. var label=dojo.query('label[for="'+this.id+'"]');
  11568. if(label.length){
  11569. label[0].id = (this.id+"_label");
  11570. dijit.setWaiState(this.domNode, "labelledby", label[0].id);
  11571. }
  11572. this.inherited(arguments);
  11573. },
  11574. _setHasDownArrowAttr: function(val){
  11575. this.hasDownArrow = val;
  11576. this._buttonNode.style.display = val ? "" : "none";
  11577. },
  11578. _getMenuLabelFromItem: function(/*Item*/ item){
  11579. var label = this.labelFunc(item, this.store),
  11580. labelType = this.labelType;
  11581. // If labelType is not "text" we don't want to screw any markup ot whatever.
  11582. if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
  11583. label = this.doHighlight(label, this._escapeHtml(this._lastInput));
  11584. labelType = "html";
  11585. }
  11586. return {html: labelType == "html", label: label};
  11587. },
  11588. doHighlight: function(/*String*/ label, /*String*/ find){
  11589. // summary:
  11590. // Highlights the string entered by the user in the menu. By default this
  11591. // highlights the first occurrence found. Override this method
  11592. // to implement your custom highlighting.
  11593. // tags:
  11594. // protected
  11595. var
  11596. // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
  11597. modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
  11598. i = this.queryExpr.indexOf("${0}");
  11599. find = dojo.regexp.escapeString(find); // escape regexp special chars
  11600. return this._escapeHtml(label).replace(
  11601. // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
  11602. new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
  11603. '<span class="dijitComboBoxHighlightMatch">$1</span>'
  11604. ); // returns String, (almost) valid HTML (entities encoded)
  11605. },
  11606. _escapeHtml: function(/*String*/ str){
  11607. // TODO Should become dojo.html.entities(), when exists use instead
  11608. // summary:
  11609. // Adds escape sequences for special characters in XML: &<>"'
  11610. str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
  11611. .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  11612. return str; // string
  11613. },
  11614. reset: function(){
  11615. // Overrides the _FormWidget.reset().
  11616. // Additionally reset the .item (to clean up).
  11617. this.item = null;
  11618. this.inherited(arguments);
  11619. },
  11620. labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
  11621. // summary:
  11622. // Computes the label to display based on the dojo.data store item.
  11623. // returns:
  11624. // The label that the ComboBox should display
  11625. // tags:
  11626. // private
  11627. // Use toString() because XMLStore returns an XMLItem whereas this
  11628. // method is expected to return a String (#9354)
  11629. return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
  11630. }
  11631. }
  11632. );
  11633. dojo.declare(
  11634. "dijit.form._ComboBoxMenu",
  11635. [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  11636. {
  11637. // summary:
  11638. // Focus-less menu for internal use in `dijit.form.ComboBox`
  11639. // tags:
  11640. // private
  11641. templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
  11642. +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' role='option'></li>"
  11643. +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' role='option'></li>"
  11644. +"</ul>",
  11645. // _messages: Object
  11646. // Holds "next" and "previous" text for paging buttons on drop down
  11647. _messages: null,
  11648. baseClass: "dijitComboBoxMenu",
  11649. postMixInProperties: function(){
  11650. this.inherited(arguments);
  11651. this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
  11652. },
  11653. buildRendering: function(){
  11654. this.inherited(arguments);
  11655. // fill in template with i18n messages
  11656. this.previousButton.innerHTML = this._messages["previousMessage"];
  11657. this.nextButton.innerHTML = this._messages["nextMessage"];
  11658. },
  11659. _setValueAttr: function(/*Object*/ value){
  11660. this.value = value;
  11661. this.onChange(value);
  11662. },
  11663. // stubs
  11664. onChange: function(/*Object*/ value){
  11665. // summary:
  11666. // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
  11667. // Probably should be called onSelect.
  11668. // tags:
  11669. // callback
  11670. },
  11671. onPage: function(/*Number*/ direction){
  11672. // summary:
  11673. // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
  11674. // tags:
  11675. // callback
  11676. },
  11677. onClose: function(){
  11678. // summary:
  11679. // Callback from dijit.popup code to this widget, notifying it that it closed
  11680. // tags:
  11681. // private
  11682. this._blurOptionNode();
  11683. },
  11684. _createOption: function(/*Object*/ item, labelFunc){
  11685. // summary:
  11686. // Creates an option to appear on the popup menu subclassed by
  11687. // `dijit.form.FilteringSelect`.
  11688. var menuitem = dojo.create("li", {
  11689. "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
  11690. role: "option"
  11691. });
  11692. var labelObject = labelFunc(item);
  11693. if(labelObject.html){
  11694. menuitem.innerHTML = labelObject.label;
  11695. }else{
  11696. menuitem.appendChild(
  11697. dojo.doc.createTextNode(labelObject.label)
  11698. );
  11699. }
  11700. // #3250: in blank options, assign a normal height
  11701. if(menuitem.innerHTML == ""){
  11702. menuitem.innerHTML = "&nbsp;";
  11703. }
  11704. menuitem.item=item;
  11705. return menuitem;
  11706. },
  11707. createOptions: function(results, dataObject, labelFunc){
  11708. // summary:
  11709. // Fills in the items in the drop down list
  11710. // results:
  11711. // Array of dojo.data items
  11712. // dataObject:
  11713. // dojo.data store
  11714. // labelFunc:
  11715. // Function to produce a label in the drop down list from a dojo.data item
  11716. //this._dataObject=dataObject;
  11717. //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
  11718. // display "Previous . . ." button
  11719. this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
  11720. dojo.attr(this.previousButton, "id", this.id + "_prev");
  11721. // create options using _createOption function defined by parent
  11722. // ComboBox (or FilteringSelect) class
  11723. // #2309:
  11724. // iterate over cache nondestructively
  11725. dojo.forEach(results, function(item, i){
  11726. var menuitem = this._createOption(item, labelFunc);
  11727. dojo.attr(menuitem, "id", this.id + i);
  11728. this.domNode.insertBefore(menuitem, this.nextButton);
  11729. }, this);
  11730. // display "Next . . ." button
  11731. var displayMore = false;
  11732. //Try to determine if we should show 'more'...
  11733. if(dataObject._maxOptions && dataObject._maxOptions != -1){
  11734. if((dataObject.start + dataObject.count) < dataObject._maxOptions){
  11735. displayMore = true;
  11736. }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
  11737. //Weird return from a datastore, where a start + count > maxOptions
  11738. // implies maxOptions isn't really valid and we have to go into faking it.
  11739. //And more or less assume more if count == results.length
  11740. displayMore = true;
  11741. }
  11742. }else if(dataObject.count == results.length){
  11743. //Don't know the size, so we do the best we can based off count alone.
  11744. //So, if we have an exact match to count, assume more.
  11745. displayMore = true;
  11746. }
  11747. this.nextButton.style.display = displayMore ? "" : "none";
  11748. dojo.attr(this.nextButton,"id", this.id + "_next");
  11749. return this.domNode.childNodes;
  11750. },
  11751. clearResultList: function(){
  11752. // summary:
  11753. // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
  11754. while(this.domNode.childNodes.length>2){
  11755. this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
  11756. }
  11757. this._blurOptionNode();
  11758. },
  11759. _onMouseDown: function(/*Event*/ evt){
  11760. dojo.stopEvent(evt);
  11761. },
  11762. _onMouseUp: function(/*Event*/ evt){
  11763. if(evt.target === this.domNode || !this._highlighted_option){
  11764. // !this._highlighted_option check to prevent immediate selection when menu appears on top
  11765. // of <input>, see #9898. Note that _HasDropDown also has code to prevent this.
  11766. return;
  11767. }else if(evt.target == this.previousButton){
  11768. this._blurOptionNode();
  11769. this.onPage(-1);
  11770. }else if(evt.target == this.nextButton){
  11771. this._blurOptionNode();
  11772. this.onPage(1);
  11773. }else{
  11774. var tgt = evt.target;
  11775. // while the clicked node is inside the div
  11776. while(!tgt.item){
  11777. // recurse to the top
  11778. tgt = tgt.parentNode;
  11779. }
  11780. this._setValueAttr({ target: tgt }, true);
  11781. }
  11782. },
  11783. _onMouseOver: function(/*Event*/ evt){
  11784. if(evt.target === this.domNode){ return; }
  11785. var tgt = evt.target;
  11786. if(!(tgt == this.previousButton || tgt == this.nextButton)){
  11787. // while the clicked node is inside the div
  11788. while(!tgt.item){
  11789. // recurse to the top
  11790. tgt = tgt.parentNode;
  11791. }
  11792. }
  11793. this._focusOptionNode(tgt);
  11794. },
  11795. _onMouseOut: function(/*Event*/ evt){
  11796. if(evt.target === this.domNode){ return; }
  11797. this._blurOptionNode();
  11798. },
  11799. _focusOptionNode: function(/*DomNode*/ node){
  11800. // summary:
  11801. // Does the actual highlight.
  11802. if(this._highlighted_option != node){
  11803. this._blurOptionNode();
  11804. this._highlighted_option = node;
  11805. dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
  11806. }
  11807. },
  11808. _blurOptionNode: function(){
  11809. // summary:
  11810. // Removes highlight on highlighted option.
  11811. if(this._highlighted_option){
  11812. dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
  11813. this._highlighted_option = null;
  11814. }
  11815. },
  11816. _highlightNextOption: function(){
  11817. // summary:
  11818. // Highlight the item just below the current selection.
  11819. // If nothing selected, highlight first option.
  11820. // because each press of a button clears the menu,
  11821. // the highlighted option sometimes becomes detached from the menu!
  11822. // test to see if the option has a parent to see if this is the case.
  11823. if(!this.getHighlightedOption()){
  11824. var fc = this.domNode.firstChild;
  11825. this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
  11826. }else{
  11827. var ns = this._highlighted_option.nextSibling;
  11828. if(ns && ns.style.display != "none"){
  11829. this._focusOptionNode(ns);
  11830. }else{
  11831. this.highlightFirstOption();
  11832. }
  11833. }
  11834. // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
  11835. dojo.window.scrollIntoView(this._highlighted_option);
  11836. },
  11837. highlightFirstOption: function(){
  11838. // summary:
  11839. // Highlight the first real item in the list (not Previous Choices).
  11840. var first = this.domNode.firstChild;
  11841. var second = first.nextSibling;
  11842. this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
  11843. dojo.window.scrollIntoView(this._highlighted_option);
  11844. },
  11845. highlightLastOption: function(){
  11846. // summary:
  11847. // Highlight the last real item in the list (not More Choices).
  11848. this._focusOptionNode(this.domNode.lastChild.previousSibling);
  11849. dojo.window.scrollIntoView(this._highlighted_option);
  11850. },
  11851. _highlightPrevOption: function(){
  11852. // summary:
  11853. // Highlight the item just above the current selection.
  11854. // If nothing selected, highlight last option (if
  11855. // you select Previous and try to keep scrolling up the list).
  11856. if(!this.getHighlightedOption()){
  11857. var lc = this.domNode.lastChild;
  11858. this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
  11859. }else{
  11860. var ps = this._highlighted_option.previousSibling;
  11861. if(ps && ps.style.display != "none"){
  11862. this._focusOptionNode(ps);
  11863. }else{
  11864. this.highlightLastOption();
  11865. }
  11866. }
  11867. dojo.window.scrollIntoView(this._highlighted_option);
  11868. },
  11869. _page: function(/*Boolean*/ up){
  11870. // summary:
  11871. // Handles page-up and page-down keypresses
  11872. var scrollamount = 0;
  11873. var oldscroll = this.domNode.scrollTop;
  11874. var height = dojo.style(this.domNode, "height");
  11875. // if no item is highlighted, highlight the first option
  11876. if(!this.getHighlightedOption()){
  11877. this._highlightNextOption();
  11878. }
  11879. while(scrollamount<height){
  11880. if(up){
  11881. // stop at option 1
  11882. if(!this.getHighlightedOption().previousSibling ||
  11883. this._highlighted_option.previousSibling.style.display == "none"){
  11884. break;
  11885. }
  11886. this._highlightPrevOption();
  11887. }else{
  11888. // stop at last option
  11889. if(!this.getHighlightedOption().nextSibling ||
  11890. this._highlighted_option.nextSibling.style.display == "none"){
  11891. break;
  11892. }
  11893. this._highlightNextOption();
  11894. }
  11895. // going backwards
  11896. var newscroll=this.domNode.scrollTop;
  11897. scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
  11898. oldscroll=newscroll;
  11899. }
  11900. },
  11901. pageUp: function(){
  11902. // summary:
  11903. // Handles pageup keypress.
  11904. // TODO: just call _page directly from handleKey().
  11905. // tags:
  11906. // private
  11907. this._page(true);
  11908. },
  11909. pageDown: function(){
  11910. // summary:
  11911. // Handles pagedown keypress.
  11912. // TODO: just call _page directly from handleKey().
  11913. // tags:
  11914. // private
  11915. this._page(false);
  11916. },
  11917. getHighlightedOption: function(){
  11918. // summary:
  11919. // Returns the highlighted option.
  11920. var ho = this._highlighted_option;
  11921. return (ho && ho.parentNode) ? ho : null;
  11922. },
  11923. handleKey: function(evt){
  11924. // summary:
  11925. // Handle keystroke event forwarded from ComboBox, returning false if it's
  11926. // a keystroke I recognize and process, true otherwise.
  11927. switch(evt.charOrCode){
  11928. case dojo.keys.DOWN_ARROW:
  11929. this._highlightNextOption();
  11930. return false;
  11931. case dojo.keys.PAGE_DOWN:
  11932. this.pageDown();
  11933. return false;
  11934. case dojo.keys.UP_ARROW:
  11935. this._highlightPrevOption();
  11936. return false;
  11937. case dojo.keys.PAGE_UP:
  11938. this.pageUp();
  11939. return false;
  11940. default:
  11941. return true;
  11942. }
  11943. }
  11944. }
  11945. );
  11946. dojo.declare(
  11947. "dijit.form.ComboBox",
  11948. [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
  11949. {
  11950. // summary:
  11951. // Auto-completing text box, and base class for dijit.form.FilteringSelect.
  11952. //
  11953. // description:
  11954. // The drop down box's values are populated from an class called
  11955. // a data provider, which returns a list of values based on the characters
  11956. // that the user has typed into the input box.
  11957. // If OPTION tags are used as the data provider via markup,
  11958. // then the OPTION tag's child text node is used as the widget value
  11959. // when selected. The OPTION tag's value attribute is ignored.
  11960. // To set the default value when using OPTION tags, specify the selected
  11961. // attribute on 1 of the child OPTION tags.
  11962. //
  11963. // Some of the options to the ComboBox are actually arguments to the data
  11964. // provider.
  11965. _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  11966. // summary:
  11967. // Hook so set('value', value) works.
  11968. // description:
  11969. // Sets the value of the select.
  11970. this._set("item", null); // value not looked up in store
  11971. if(!value){ value = ''; } // null translates to blank
  11972. dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
  11973. }
  11974. }
  11975. );
  11976. dojo.declare("dijit.form._ComboBoxDataStore", null, {
  11977. // summary:
  11978. // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
  11979. //
  11980. // description:
  11981. // Provides a store for inlined data like:
  11982. //
  11983. // | <select>
  11984. // | <option value="AL">Alabama</option>
  11985. // | ...
  11986. //
  11987. // Actually. just implements the subset of dojo.data.Read/Notification
  11988. // needed for ComboBox and FilteringSelect to work.
  11989. //
  11990. // Note that an item is just a pointer to the <option> DomNode.
  11991. constructor: function( /*DomNode*/ root){
  11992. this.root = root;
  11993. if(root.tagName != "SELECT" && root.firstChild){
  11994. root = dojo.query("select", root);
  11995. if(root.length > 0){ // SELECT is a child of srcNodeRef
  11996. root = root[0];
  11997. }else{ // no select, so create 1 to parent the option tags to define selectedIndex
  11998. this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
  11999. root = this.root.firstChild;
  12000. }
  12001. this.root = root;
  12002. }
  12003. dojo.query("> option", root).forEach(function(node){
  12004. // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be.
  12005. // If it is needed then can we just hide the select itself instead?
  12006. //node.style.display="none";
  12007. node.innerHTML = dojo.trim(node.innerHTML);
  12008. });
  12009. },
  12010. getValue: function( /*item*/ item,
  12011. /*attribute-name-string*/ attribute,
  12012. /*value?*/ defaultValue){
  12013. return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
  12014. },
  12015. isItemLoaded: function(/*anything*/ something){
  12016. return true;
  12017. },
  12018. getFeatures: function(){
  12019. return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
  12020. },
  12021. _fetchItems: function( /*Object*/ args,
  12022. /*Function*/ findCallback,
  12023. /*Function*/ errorCallback){
  12024. // summary:
  12025. // See dojo.data.util.simpleFetch.fetch()
  12026. if(!args.query){ args.query = {}; }
  12027. if(!args.query.name){ args.query.name = ""; }
  12028. if(!args.queryOptions){ args.queryOptions = {}; }
  12029. var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
  12030. items = dojo.query("> option", this.root).filter(function(option){
  12031. return (option.innerText || option.textContent || '').match(matcher);
  12032. } );
  12033. if(args.sort){
  12034. items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
  12035. }
  12036. findCallback(items, args);
  12037. },
  12038. close: function(/*dojo.data.api.Request || args || null*/ request){
  12039. return;
  12040. },
  12041. getLabel: function(/*item*/ item){
  12042. return item.innerHTML;
  12043. },
  12044. getIdentity: function(/*item*/ item){
  12045. return dojo.attr(item, "value");
  12046. },
  12047. fetchItemByIdentity: function(/*Object*/ args){
  12048. // summary:
  12049. // Given the identity of an item, this method returns the item that has
  12050. // that identity through the onItem callback.
  12051. // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
  12052. //
  12053. // description:
  12054. // Given arguments like:
  12055. //
  12056. // | {identity: "CA", onItem: function(item){...}
  12057. //
  12058. // Call `onItem()` with the DOM node `<option value="CA">California</option>`
  12059. var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
  12060. args.onItem(item);
  12061. },
  12062. fetchSelectedItem: function(){
  12063. // summary:
  12064. // Get the option marked as selected, like `<option selected>`.
  12065. // Not part of dojo.data API.
  12066. var root = this.root,
  12067. si = root.selectedIndex;
  12068. return typeof si == "number"
  12069. ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
  12070. : null; // dojo.data.Item
  12071. }
  12072. });
  12073. //Mix in the simple fetch implementation to this class.
  12074. dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
  12075. }
  12076. if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12077. dojo._hasResource["dijit.form.FilteringSelect"] = true;
  12078. dojo.provide("dijit.form.FilteringSelect");
  12079. dojo.declare(
  12080. "dijit.form.FilteringSelect",
  12081. [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
  12082. {
  12083. // summary:
  12084. // An enhanced version of the HTML SELECT tag, populated dynamically
  12085. //
  12086. // description:
  12087. // An enhanced version of the HTML SELECT tag, populated dynamically. It works
  12088. // very nicely with very large data sets because it can load and page data as needed.
  12089. // It also resembles ComboBox, but does not allow values outside of the provided ones.
  12090. // If OPTION tags are used as the data provider via markup, then the
  12091. // OPTION tag's child text node is used as the displayed value when selected
  12092. // while the OPTION tag's value attribute is used as the widget value on form submit.
  12093. // To set the default value when using OPTION tags, specify the selected
  12094. // attribute on 1 of the child OPTION tags.
  12095. //
  12096. // Similar features:
  12097. // - There is a drop down list of possible values.
  12098. // - You can only enter a value from the drop down list. (You can't
  12099. // enter an arbitrary value.)
  12100. // - The value submitted with the form is the hidden value (ex: CA),
  12101. // not the displayed value a.k.a. label (ex: California)
  12102. //
  12103. // Enhancements over plain HTML version:
  12104. // - If you type in some text then it will filter down the list of
  12105. // possible values in the drop down list.
  12106. // - List can be specified either as a static list or via a javascript
  12107. // function (that can get the list from a server)
  12108. // required: Boolean
  12109. // True (default) if user is required to enter a value into this field.
  12110. required: true,
  12111. _lastDisplayedValue: "",
  12112. _isValidSubset: function(){
  12113. return this._opened;
  12114. },
  12115. isValid: function(){
  12116. // Overrides ValidationTextBox.isValid()
  12117. return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
  12118. },
  12119. _refreshState: function(){
  12120. if(!this.searchTimer){ // state will be refreshed after results are returned
  12121. this.inherited(arguments);
  12122. }
  12123. },
  12124. _callbackSetLabel: function(
  12125. /*Array*/ result,
  12126. /*Object*/ dataObject,
  12127. /*Boolean?*/ priorityChange){
  12128. // summary:
  12129. // Callback from dojo.data after lookup of user entered value finishes
  12130. // setValue does a synchronous lookup,
  12131. // so it calls _callbackSetLabel directly,
  12132. // and so does not pass dataObject
  12133. // still need to test against _lastQuery in case it came too late
  12134. if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
  12135. return;
  12136. }
  12137. if(!result.length){
  12138. //#3268: don't modify display value on bad input
  12139. //#3285: change CSS to indicate error
  12140. this.valueNode.value = "";
  12141. dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
  12142. this._set("item", null);
  12143. this.validate(this._focused);
  12144. }else{
  12145. this.set('item', result[0], priorityChange);
  12146. }
  12147. },
  12148. _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
  12149. // Callback when a data store query completes.
  12150. // Overrides ComboBox._openResultList()
  12151. // #3285: tap into search callback to see if user's query resembles a match
  12152. if(dataObject.query[this.searchAttr] != this._lastQuery){
  12153. return;
  12154. }
  12155. dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
  12156. if(this.item === undefined){ // item == undefined for keyboard search
  12157. // If the search returned no items that means that the user typed
  12158. // in something invalid (and they can't make it valid by typing more characters),
  12159. // so flag the FilteringSelect as being in an invalid state
  12160. this.validate(true);
  12161. }
  12162. },
  12163. _getValueAttr: function(){
  12164. // summary:
  12165. // Hook for get('value') to work.
  12166. // don't get the textbox value but rather the previously set hidden value.
  12167. // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
  12168. return this.valueNode.value;
  12169. },
  12170. _getValueField: function(){
  12171. // Overrides ComboBox._getValueField()
  12172. return "value";
  12173. },
  12174. _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
  12175. // summary:
  12176. // Hook so set('value', value) works.
  12177. // description:
  12178. // Sets the value of the select.
  12179. // Also sets the label to the corresponding value by reverse lookup.
  12180. if(!this._onChangeActive){ priorityChange = null; }
  12181. this._lastQuery = value;
  12182. if(value === null || value === ''){
  12183. this._setDisplayedValueAttr('', priorityChange);
  12184. return;
  12185. }
  12186. //#3347: fetchItemByIdentity if no keyAttr specified
  12187. var self = this;
  12188. this.store.fetchItemByIdentity({
  12189. identity: value,
  12190. onItem: function(item){
  12191. self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
  12192. }
  12193. });
  12194. },
  12195. _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  12196. // summary:
  12197. // Set the displayed valued in the input box, and the hidden value
  12198. // that gets submitted, based on a dojo.data store item.
  12199. // description:
  12200. // Users shouldn't call this function; they should be calling
  12201. // set('item', value)
  12202. // tags:
  12203. // private
  12204. this.inherited(arguments);
  12205. this.valueNode.value = this.value;
  12206. this._lastDisplayedValue = this.textbox.value;
  12207. },
  12208. _getDisplayQueryString: function(/*String*/ text){
  12209. return text.replace(/([\\\*\?])/g, "\\$1");
  12210. },
  12211. _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
  12212. // summary:
  12213. // Hook so set('displayedValue', label) works.
  12214. // description:
  12215. // Sets textbox to display label. Also performs reverse lookup
  12216. // to set the hidden value. label should corresponding to item.searchAttr.
  12217. if(label == null){ label = ''; }
  12218. // This is called at initialization along with every custom setter.
  12219. // Usually (or always?) the call can be ignored. If it needs to be
  12220. // processed then at least make sure that the XHR request doesn't trigger an onChange()
  12221. // event, even if it returns after creation has finished
  12222. if(!this._created){
  12223. if(!("displayedValue" in this.params)){
  12224. return;
  12225. }
  12226. priorityChange = false;
  12227. }
  12228. // Do a reverse lookup to map the specified displayedValue to the hidden value.
  12229. // Note that if there's a custom labelFunc() this code
  12230. if(this.store){
  12231. this.closeDropDown();
  12232. var query = dojo.clone(this.query); // #6196: populate query with user-specifics
  12233. // escape meta characters of dojo.data.util.filter.patternToRegExp().
  12234. this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
  12235. // If the label is not valid, the callback will never set it,
  12236. // so the last valid value will get the warning textbox. Set the
  12237. // textbox value now so that the impending warning will make
  12238. // sense to the user
  12239. this.textbox.value = label;
  12240. this._lastDisplayedValue = label;
  12241. this._set("displayedValue", label); // for watch("displayedValue") notification
  12242. var _this = this;
  12243. var fetch = {
  12244. query: query,
  12245. queryOptions: {
  12246. ignoreCase: this.ignoreCase,
  12247. deep: true
  12248. },
  12249. onComplete: function(result, dataObject){
  12250. _this._fetchHandle = null;
  12251. dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
  12252. },
  12253. onError: function(errText){
  12254. _this._fetchHandle = null;
  12255. console.error('dijit.form.FilteringSelect: ' + errText);
  12256. dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
  12257. }
  12258. };
  12259. dojo.mixin(fetch, this.fetchProperties);
  12260. this._fetchHandle = this.store.fetch(fetch);
  12261. }
  12262. },
  12263. undo: function(){
  12264. this.set('displayedValue', this._lastDisplayedValue);
  12265. }
  12266. }
  12267. );
  12268. }
  12269. if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12270. dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
  12271. dojo.provide("dojo.data.ItemFileReadStore");
  12272. dojo.declare("dojo.data.ItemFileReadStore", null,{
  12273. // summary:
  12274. // The ItemFileReadStore implements the dojo.data.api.Read API and reads
  12275. // data from JSON files that have contents in this format --
  12276. // { items: [
  12277. // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
  12278. // { name:'Fozzie Bear', wears:['hat', 'tie']},
  12279. // { name:'Miss Piggy', pets:'Foo-Foo'}
  12280. // ]}
  12281. // Note that it can also contain an 'identifer' property that specified which attribute on the items
  12282. // in the array of items that acts as the unique identifier for that item.
  12283. //
  12284. constructor: function(/* Object */ keywordParameters){
  12285. // summary: constructor
  12286. // keywordParameters: {url: String}
  12287. // keywordParameters: {data: jsonObject}
  12288. // keywordParameters: {typeMap: object)
  12289. // The structure of the typeMap object is as follows:
  12290. // {
  12291. // type0: function || object,
  12292. // type1: function || object,
  12293. // ...
  12294. // typeN: function || object
  12295. // }
  12296. // Where if it is a function, it is assumed to be an object constructor that takes the
  12297. // value of _value as the initialization parameters. If it is an object, then it is assumed
  12298. // to be an object of general form:
  12299. // {
  12300. // type: function, //constructor.
  12301. // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
  12302. // }
  12303. this._arrayOfAllItems = [];
  12304. this._arrayOfTopLevelItems = [];
  12305. this._loadFinished = false;
  12306. this._jsonFileUrl = keywordParameters.url;
  12307. this._ccUrl = keywordParameters.url;
  12308. this.url = keywordParameters.url;
  12309. this._jsonData = keywordParameters.data;
  12310. this.data = null;
  12311. this._datatypeMap = keywordParameters.typeMap || {};
  12312. if(!this._datatypeMap['Date']){
  12313. //If no default mapping for dates, then set this as default.
  12314. //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
  12315. //of generically representing dates.
  12316. this._datatypeMap['Date'] = {
  12317. type: Date,
  12318. deserialize: function(value){
  12319. return dojo.date.stamp.fromISOString(value);
  12320. }
  12321. };
  12322. }
  12323. this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
  12324. this._itemsByIdentity = null;
  12325. this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
  12326. this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
  12327. this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
  12328. this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
  12329. this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
  12330. this._queuedFetches = [];
  12331. if(keywordParameters.urlPreventCache !== undefined){
  12332. this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
  12333. }
  12334. if(keywordParameters.hierarchical !== undefined){
  12335. this.hierarchical = keywordParameters.hierarchical?true:false;
  12336. }
  12337. if(keywordParameters.clearOnClose){
  12338. this.clearOnClose = true;
  12339. }
  12340. if("failOk" in keywordParameters){
  12341. this.failOk = keywordParameters.failOk?true:false;
  12342. }
  12343. },
  12344. url: "", // use "" rather than undefined for the benefit of the parser (#3539)
  12345. //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
  12346. //when clearOnClose and close is used.
  12347. _ccUrl: "",
  12348. data: null, // define this so that the parser can populate it
  12349. typeMap: null, //Define so parser can populate.
  12350. //Parameter to allow users to specify if a close call should force a reload or not.
  12351. //By default, it retains the old behavior of not clearing if close is called. But
  12352. //if set true, the store will be reset to default state. Note that by doing this,
  12353. //all item handles will become invalid and a new fetch must be issued.
  12354. clearOnClose: false,
  12355. //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
  12356. //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
  12357. //Added for tracker: #6072
  12358. urlPreventCache: false,
  12359. //Parameter for specifying that it is OK for the xhrGet call to fail silently.
  12360. failOk: false,
  12361. //Parameter to indicate to process data from the url as hierarchical
  12362. //(data items can contain other data items in js form). Default is true
  12363. //for backwards compatibility. False means only root items are processed
  12364. //as items, all child objects outside of type-mapped objects and those in
  12365. //specific reference format, are left straight JS data objects.
  12366. hierarchical: true,
  12367. _assertIsItem: function(/* item */ item){
  12368. // summary:
  12369. // This function tests whether the item passed in is indeed an item in the store.
  12370. // item:
  12371. // The item to test for being contained by the store.
  12372. if(!this.isItem(item)){
  12373. throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
  12374. }
  12375. },
  12376. _assertIsAttribute: function(/* attribute-name-string */ attribute){
  12377. // summary:
  12378. // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
  12379. // attribute:
  12380. // The attribute to test for being contained by the store.
  12381. if(typeof attribute !== "string"){
  12382. throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
  12383. }
  12384. },
  12385. getValue: function( /* item */ item,
  12386. /* attribute-name-string */ attribute,
  12387. /* value? */ defaultValue){
  12388. // summary:
  12389. // See dojo.data.api.Read.getValue()
  12390. var values = this.getValues(item, attribute);
  12391. return (values.length > 0)?values[0]:defaultValue; // mixed
  12392. },
  12393. getValues: function(/* item */ item,
  12394. /* attribute-name-string */ attribute){
  12395. // summary:
  12396. // See dojo.data.api.Read.getValues()
  12397. this._assertIsItem(item);
  12398. this._assertIsAttribute(attribute);
  12399. // Clone it before returning. refs: #10474
  12400. return (item[attribute] || []).slice(0); // Array
  12401. },
  12402. getAttributes: function(/* item */ item){
  12403. // summary:
  12404. // See dojo.data.api.Read.getAttributes()
  12405. this._assertIsItem(item);
  12406. var attributes = [];
  12407. for(var key in item){
  12408. // Save off only the real item attributes, not the special id marks for O(1) isItem.
  12409. if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
  12410. attributes.push(key);
  12411. }
  12412. }
  12413. return attributes; // Array
  12414. },
  12415. hasAttribute: function( /* item */ item,
  12416. /* attribute-name-string */ attribute){
  12417. // summary:
  12418. // See dojo.data.api.Read.hasAttribute()
  12419. this._assertIsItem(item);
  12420. this._assertIsAttribute(attribute);
  12421. return (attribute in item);
  12422. },
  12423. containsValue: function(/* item */ item,
  12424. /* attribute-name-string */ attribute,
  12425. /* anything */ value){
  12426. // summary:
  12427. // See dojo.data.api.Read.containsValue()
  12428. var regexp = undefined;
  12429. if(typeof value === "string"){
  12430. regexp = dojo.data.util.filter.patternToRegExp(value, false);
  12431. }
  12432. return this._containsValue(item, attribute, value, regexp); //boolean.
  12433. },
  12434. _containsValue: function( /* item */ item,
  12435. /* attribute-name-string */ attribute,
  12436. /* anything */ value,
  12437. /* RegExp?*/ regexp){
  12438. // summary:
  12439. // Internal function for looking at the values contained by the item.
  12440. // description:
  12441. // Internal function for looking at the values contained by the item. This
  12442. // function allows for denoting if the comparison should be case sensitive for
  12443. // strings or not (for handling filtering cases where string case should not matter)
  12444. //
  12445. // item:
  12446. // The data item to examine for attribute values.
  12447. // attribute:
  12448. // The attribute to inspect.
  12449. // value:
  12450. // The value to match.
  12451. // regexp:
  12452. // Optional regular expression generated off value if value was of string type to handle wildcarding.
  12453. // If present and attribute values are string, then it can be used for comparison instead of 'value'
  12454. return dojo.some(this.getValues(item, attribute), function(possibleValue){
  12455. if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
  12456. if(possibleValue.toString().match(regexp)){
  12457. return true; // Boolean
  12458. }
  12459. }else if(value === possibleValue){
  12460. return true; // Boolean
  12461. }
  12462. });
  12463. },
  12464. isItem: function(/* anything */ something){
  12465. // summary:
  12466. // See dojo.data.api.Read.isItem()
  12467. if(something && something[this._storeRefPropName] === this){
  12468. if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
  12469. return true;
  12470. }
  12471. }
  12472. return false; // Boolean
  12473. },
  12474. isItemLoaded: function(/* anything */ something){
  12475. // summary:
  12476. // See dojo.data.api.Read.isItemLoaded()
  12477. return this.isItem(something); //boolean
  12478. },
  12479. loadItem: function(/* object */ keywordArgs){
  12480. // summary:
  12481. // See dojo.data.api.Read.loadItem()
  12482. this._assertIsItem(keywordArgs.item);
  12483. },
  12484. getFeatures: function(){
  12485. // summary:
  12486. // See dojo.data.api.Read.getFeatures()
  12487. return this._features; //Object
  12488. },
  12489. getLabel: function(/* item */ item){
  12490. // summary:
  12491. // See dojo.data.api.Read.getLabel()
  12492. if(this._labelAttr && this.isItem(item)){
  12493. return this.getValue(item,this._labelAttr); //String
  12494. }
  12495. return undefined; //undefined
  12496. },
  12497. getLabelAttributes: function(/* item */ item){
  12498. // summary:
  12499. // See dojo.data.api.Read.getLabelAttributes()
  12500. if(this._labelAttr){
  12501. return [this._labelAttr]; //array
  12502. }
  12503. return null; //null
  12504. },
  12505. _fetchItems: function( /* Object */ keywordArgs,
  12506. /* Function */ findCallback,
  12507. /* Function */ errorCallback){
  12508. // summary:
  12509. // See dojo.data.util.simpleFetch.fetch()
  12510. var self = this,
  12511. filter = function(requestArgs, arrayOfItems){
  12512. var items = [],
  12513. i, key;
  12514. if(requestArgs.query){
  12515. var value,
  12516. ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
  12517. //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
  12518. //same value for each item examined. Much more efficient.
  12519. var regexpList = {};
  12520. for(key in requestArgs.query){
  12521. value = requestArgs.query[key];
  12522. if(typeof value === "string"){
  12523. regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
  12524. }else if(value instanceof RegExp){
  12525. regexpList[key] = value;
  12526. }
  12527. }
  12528. for(i = 0; i < arrayOfItems.length; ++i){
  12529. var match = true;
  12530. var candidateItem = arrayOfItems[i];
  12531. if(candidateItem === null){
  12532. match = false;
  12533. }else{
  12534. for(key in requestArgs.query){
  12535. value = requestArgs.query[key];
  12536. if(!self._containsValue(candidateItem, key, value, regexpList[key])){
  12537. match = false;
  12538. }
  12539. }
  12540. }
  12541. if(match){
  12542. items.push(candidateItem);
  12543. }
  12544. }
  12545. findCallback(items, requestArgs);
  12546. }else{
  12547. // We want a copy to pass back in case the parent wishes to sort the array.
  12548. // We shouldn't allow resort of the internal list, so that multiple callers
  12549. // can get lists and sort without affecting each other. We also need to
  12550. // filter out any null values that have been left as a result of deleteItem()
  12551. // calls in ItemFileWriteStore.
  12552. for(i = 0; i < arrayOfItems.length; ++i){
  12553. var item = arrayOfItems[i];
  12554. if(item !== null){
  12555. items.push(item);
  12556. }
  12557. }
  12558. findCallback(items, requestArgs);
  12559. }
  12560. };
  12561. if(this._loadFinished){
  12562. filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
  12563. }else{
  12564. //Do a check on the JsonFileUrl and crosscheck it.
  12565. //If it doesn't match the cross-check, it needs to be updated
  12566. //This allows for either url or _jsonFileUrl to he changed to
  12567. //reset the store load location. Done this way for backwards
  12568. //compatibility. People use _jsonFileUrl (even though officially
  12569. //private.
  12570. if(this._jsonFileUrl !== this._ccUrl){
  12571. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  12572. "To change the url, set the url property of the store," +
  12573. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  12574. this._ccUrl = this._jsonFileUrl;
  12575. this.url = this._jsonFileUrl;
  12576. }else if(this.url !== this._ccUrl){
  12577. this._jsonFileUrl = this.url;
  12578. this._ccUrl = this.url;
  12579. }
  12580. //See if there was any forced reset of data.
  12581. if(this.data != null){
  12582. this._jsonData = this.data;
  12583. this.data = null;
  12584. }
  12585. if(this._jsonFileUrl){
  12586. //If fetches come in before the loading has finished, but while
  12587. //a load is in progress, we have to defer the fetching to be
  12588. //invoked in the callback.
  12589. if(this._loadInProgress){
  12590. this._queuedFetches.push({args: keywordArgs, filter: filter});
  12591. }else{
  12592. this._loadInProgress = true;
  12593. var getArgs = {
  12594. url: self._jsonFileUrl,
  12595. handleAs: "json-comment-optional",
  12596. preventCache: this.urlPreventCache,
  12597. failOk: this.failOk
  12598. };
  12599. var getHandler = dojo.xhrGet(getArgs);
  12600. getHandler.addCallback(function(data){
  12601. try{
  12602. self._getItemsFromLoadedData(data);
  12603. self._loadFinished = true;
  12604. self._loadInProgress = false;
  12605. filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
  12606. self._handleQueuedFetches();
  12607. }catch(e){
  12608. self._loadFinished = true;
  12609. self._loadInProgress = false;
  12610. errorCallback(e, keywordArgs);
  12611. }
  12612. });
  12613. getHandler.addErrback(function(error){
  12614. self._loadInProgress = false;
  12615. errorCallback(error, keywordArgs);
  12616. });
  12617. //Wire up the cancel to abort of the request
  12618. //This call cancel on the deferred if it hasn't been called
  12619. //yet and then will chain to the simple abort of the
  12620. //simpleFetch keywordArgs
  12621. var oldAbort = null;
  12622. if(keywordArgs.abort){
  12623. oldAbort = keywordArgs.abort;
  12624. }
  12625. keywordArgs.abort = function(){
  12626. var df = getHandler;
  12627. if(df && df.fired === -1){
  12628. df.cancel();
  12629. df = null;
  12630. }
  12631. if(oldAbort){
  12632. oldAbort.call(keywordArgs);
  12633. }
  12634. };
  12635. }
  12636. }else if(this._jsonData){
  12637. try{
  12638. this._loadFinished = true;
  12639. this._getItemsFromLoadedData(this._jsonData);
  12640. this._jsonData = null;
  12641. filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
  12642. }catch(e){
  12643. errorCallback(e, keywordArgs);
  12644. }
  12645. }else{
  12646. errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
  12647. }
  12648. }
  12649. },
  12650. _handleQueuedFetches: function(){
  12651. // summary:
  12652. // Internal function to execute delayed request in the store.
  12653. //Execute any deferred fetches now.
  12654. if(this._queuedFetches.length > 0){
  12655. for(var i = 0; i < this._queuedFetches.length; i++){
  12656. var fData = this._queuedFetches[i],
  12657. delayedQuery = fData.args,
  12658. delayedFilter = fData.filter;
  12659. if(delayedFilter){
  12660. delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
  12661. }else{
  12662. this.fetchItemByIdentity(delayedQuery);
  12663. }
  12664. }
  12665. this._queuedFetches = [];
  12666. }
  12667. },
  12668. _getItemsArray: function(/*object?*/queryOptions){
  12669. // summary:
  12670. // Internal function to determine which list of items to search over.
  12671. // queryOptions: The query options parameter, if any.
  12672. if(queryOptions && queryOptions.deep){
  12673. return this._arrayOfAllItems;
  12674. }
  12675. return this._arrayOfTopLevelItems;
  12676. },
  12677. close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
  12678. // summary:
  12679. // See dojo.data.api.Read.close()
  12680. if(this.clearOnClose &&
  12681. this._loadFinished &&
  12682. !this._loadInProgress){
  12683. //Reset all internalsback to default state. This will force a reload
  12684. //on next fetch. This also checks that the data or url param was set
  12685. //so that the store knows it can get data. Without one of those being set,
  12686. //the next fetch will trigger an error.
  12687. if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
  12688. (this.url == "" || this.url == null)
  12689. ) && this.data == null){
  12690. console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " +
  12691. " information has not been provided." +
  12692. " Please set 'url' or 'data' to the appropriate value before" +
  12693. " the next fetch");
  12694. }
  12695. this._arrayOfAllItems = [];
  12696. this._arrayOfTopLevelItems = [];
  12697. this._loadFinished = false;
  12698. this._itemsByIdentity = null;
  12699. this._loadInProgress = false;
  12700. this._queuedFetches = [];
  12701. }
  12702. },
  12703. _getItemsFromLoadedData: function(/* Object */ dataObject){
  12704. // summary:
  12705. // Function to parse the loaded data into item format and build the internal items array.
  12706. // description:
  12707. // Function to parse the loaded data into item format and build the internal items array.
  12708. //
  12709. // dataObject:
  12710. // The JS data object containing the raw data to convery into item format.
  12711. //
  12712. // returns: array
  12713. // Array of items in store item format.
  12714. // First, we define a couple little utility functions...
  12715. var addingArrays = false,
  12716. self = this;
  12717. function valueIsAnItem(/* anything */ aValue){
  12718. // summary:
  12719. // Given any sort of value that could be in the raw json data,
  12720. // return true if we should interpret the value as being an
  12721. // item itself, rather than a literal value or a reference.
  12722. // example:
  12723. // | false == valueIsAnItem("Kermit");
  12724. // | false == valueIsAnItem(42);
  12725. // | false == valueIsAnItem(new Date());
  12726. // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
  12727. // | false == valueIsAnItem({_reference:'Kermit'});
  12728. // | true == valueIsAnItem({name:'Kermit', color:'green'});
  12729. // | true == valueIsAnItem({iggy:'pop'});
  12730. // | true == valueIsAnItem({foo:42});
  12731. var isItem = (
  12732. (aValue !== null) &&
  12733. (typeof aValue === "object") &&
  12734. (!dojo.isArray(aValue) || addingArrays) &&
  12735. (!dojo.isFunction(aValue)) &&
  12736. (aValue.constructor == Object || dojo.isArray(aValue)) &&
  12737. (typeof aValue._reference === "undefined") &&
  12738. (typeof aValue._type === "undefined") &&
  12739. (typeof aValue._value === "undefined") &&
  12740. self.hierarchical
  12741. );
  12742. return isItem;
  12743. }
  12744. function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
  12745. self._arrayOfAllItems.push(anItem);
  12746. for(var attribute in anItem){
  12747. var valueForAttribute = anItem[attribute];
  12748. if(valueForAttribute){
  12749. if(dojo.isArray(valueForAttribute)){
  12750. var valueArray = valueForAttribute;
  12751. for(var k = 0; k < valueArray.length; ++k){
  12752. var singleValue = valueArray[k];
  12753. if(valueIsAnItem(singleValue)){
  12754. addItemAndSubItemsToArrayOfAllItems(singleValue);
  12755. }
  12756. }
  12757. }else{
  12758. if(valueIsAnItem(valueForAttribute)){
  12759. addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
  12760. }
  12761. }
  12762. }
  12763. }
  12764. }
  12765. this._labelAttr = dataObject.label;
  12766. // We need to do some transformations to convert the data structure
  12767. // that we read from the file into a format that will be convenient
  12768. // to work with in memory.
  12769. // Step 1: Walk through the object hierarchy and build a list of all items
  12770. var i,
  12771. item;
  12772. this._arrayOfAllItems = [];
  12773. this._arrayOfTopLevelItems = dataObject.items;
  12774. for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
  12775. item = this._arrayOfTopLevelItems[i];
  12776. if(dojo.isArray(item)){
  12777. addingArrays = true;
  12778. }
  12779. addItemAndSubItemsToArrayOfAllItems(item);
  12780. item[this._rootItemPropName]=true;
  12781. }
  12782. // Step 2: Walk through all the attribute values of all the items,
  12783. // and replace single values with arrays. For example, we change this:
  12784. // { name:'Miss Piggy', pets:'Foo-Foo'}
  12785. // into this:
  12786. // { name:['Miss Piggy'], pets:['Foo-Foo']}
  12787. //
  12788. // We also store the attribute names so we can validate our store
  12789. // reference and item id special properties for the O(1) isItem
  12790. var allAttributeNames = {},
  12791. key;
  12792. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  12793. item = this._arrayOfAllItems[i];
  12794. for(key in item){
  12795. if(key !== this._rootItemPropName){
  12796. var value = item[key];
  12797. if(value !== null){
  12798. if(!dojo.isArray(value)){
  12799. item[key] = [value];
  12800. }
  12801. }else{
  12802. item[key] = [null];
  12803. }
  12804. }
  12805. allAttributeNames[key]=key;
  12806. }
  12807. }
  12808. // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
  12809. // This should go really fast, it will generally never even run the loop.
  12810. while(allAttributeNames[this._storeRefPropName]){
  12811. this._storeRefPropName += "_";
  12812. }
  12813. while(allAttributeNames[this._itemNumPropName]){
  12814. this._itemNumPropName += "_";
  12815. }
  12816. while(allAttributeNames[this._reverseRefMap]){
  12817. this._reverseRefMap += "_";
  12818. }
  12819. // Step 4: Some data files specify an optional 'identifier', which is
  12820. // the name of an attribute that holds the identity of each item.
  12821. // If this data file specified an identifier attribute, then build a
  12822. // hash table of items keyed by the identity of the items.
  12823. var arrayOfValues;
  12824. var identifier = dataObject.identifier;
  12825. if(identifier){
  12826. this._itemsByIdentity = {};
  12827. this._features['dojo.data.api.Identity'] = identifier;
  12828. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  12829. item = this._arrayOfAllItems[i];
  12830. arrayOfValues = item[identifier];
  12831. var identity = arrayOfValues[0];
  12832. if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
  12833. this._itemsByIdentity[identity] = item;
  12834. }else{
  12835. if(this._jsonFileUrl){
  12836. throw new Error("dojo.data.ItemFileReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
  12837. }else if(this._jsonData){
  12838. throw new Error("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
  12839. }
  12840. }
  12841. }
  12842. }else{
  12843. this._features['dojo.data.api.Identity'] = Number;
  12844. }
  12845. // Step 5: Walk through all the items, and set each item's properties
  12846. // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
  12847. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  12848. item = this._arrayOfAllItems[i];
  12849. item[this._storeRefPropName] = this;
  12850. item[this._itemNumPropName] = i;
  12851. }
  12852. // Step 6: We walk through all the attribute values of all the items,
  12853. // looking for type/value literals and item-references.
  12854. //
  12855. // We replace item-references with pointers to items. For example, we change:
  12856. // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  12857. // into this:
  12858. // { name:['Kermit'], friends:[miss_piggy] }
  12859. // (where miss_piggy is the object representing the 'Miss Piggy' item).
  12860. //
  12861. // We replace type/value pairs with typed-literals. For example, we change:
  12862. // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
  12863. // into this:
  12864. // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
  12865. //
  12866. // We also generate the associate map for all items for the O(1) isItem function.
  12867. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  12868. item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  12869. for(key in item){
  12870. arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
  12871. for(var j = 0; j < arrayOfValues.length; ++j){
  12872. value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
  12873. if(value !== null && typeof value == "object"){
  12874. if(("_type" in value) && ("_value" in value)){
  12875. var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
  12876. var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
  12877. if(!mappingObj){
  12878. throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
  12879. }else if(dojo.isFunction(mappingObj)){
  12880. arrayOfValues[j] = new mappingObj(value._value);
  12881. }else if(dojo.isFunction(mappingObj.deserialize)){
  12882. arrayOfValues[j] = mappingObj.deserialize(value._value);
  12883. }else{
  12884. throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
  12885. }
  12886. }
  12887. if(value._reference){
  12888. var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
  12889. if(!dojo.isObject(referenceDescription)){
  12890. // example: 'Miss Piggy'
  12891. // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
  12892. arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
  12893. }else{
  12894. // example: {name:'Miss Piggy'}
  12895. // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  12896. for(var k = 0; k < this._arrayOfAllItems.length; ++k){
  12897. var candidateItem = this._arrayOfAllItems[k],
  12898. found = true;
  12899. for(var refKey in referenceDescription){
  12900. if(candidateItem[refKey] != referenceDescription[refKey]){
  12901. found = false;
  12902. }
  12903. }
  12904. if(found){
  12905. arrayOfValues[j] = candidateItem;
  12906. }
  12907. }
  12908. }
  12909. if(this.referenceIntegrity){
  12910. var refItem = arrayOfValues[j];
  12911. if(this.isItem(refItem)){
  12912. this._addReferenceToMap(refItem, item, key);
  12913. }
  12914. }
  12915. }else if(this.isItem(value)){
  12916. //It's a child item (not one referenced through _reference).
  12917. //We need to treat this as a referenced item, so it can be cleaned up
  12918. //in a write store easily.
  12919. if(this.referenceIntegrity){
  12920. this._addReferenceToMap(value, item, key);
  12921. }
  12922. }
  12923. }
  12924. }
  12925. }
  12926. }
  12927. },
  12928. _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
  12929. // summary:
  12930. // Method to add an reference map entry for an item and attribute.
  12931. // description:
  12932. // Method to add an reference map entry for an item and attribute. //
  12933. // refItem:
  12934. // The item that is referenced.
  12935. // parentItem:
  12936. // The item that holds the new reference to refItem.
  12937. // attribute:
  12938. // The attribute on parentItem that contains the new reference.
  12939. //Stub function, does nothing. Real processing is in ItemFileWriteStore.
  12940. },
  12941. getIdentity: function(/* item */ item){
  12942. // summary:
  12943. // See dojo.data.api.Identity.getIdentity()
  12944. var identifier = this._features['dojo.data.api.Identity'];
  12945. if(identifier === Number){
  12946. return item[this._itemNumPropName]; // Number
  12947. }else{
  12948. var arrayOfValues = item[identifier];
  12949. if(arrayOfValues){
  12950. return arrayOfValues[0]; // Object || String
  12951. }
  12952. }
  12953. return null; // null
  12954. },
  12955. fetchItemByIdentity: function(/* Object */ keywordArgs){
  12956. // summary:
  12957. // See dojo.data.api.Identity.fetchItemByIdentity()
  12958. // Hasn't loaded yet, we have to trigger the load.
  12959. var item,
  12960. scope;
  12961. if(!this._loadFinished){
  12962. var self = this;
  12963. //Do a check on the JsonFileUrl and crosscheck it.
  12964. //If it doesn't match the cross-check, it needs to be updated
  12965. //This allows for either url or _jsonFileUrl to he changed to
  12966. //reset the store load location. Done this way for backwards
  12967. //compatibility. People use _jsonFileUrl (even though officially
  12968. //private.
  12969. if(this._jsonFileUrl !== this._ccUrl){
  12970. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  12971. "To change the url, set the url property of the store," +
  12972. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  12973. this._ccUrl = this._jsonFileUrl;
  12974. this.url = this._jsonFileUrl;
  12975. }else if(this.url !== this._ccUrl){
  12976. this._jsonFileUrl = this.url;
  12977. this._ccUrl = this.url;
  12978. }
  12979. //See if there was any forced reset of data.
  12980. if(this.data != null && this._jsonData == null){
  12981. this._jsonData = this.data;
  12982. this.data = null;
  12983. }
  12984. if(this._jsonFileUrl){
  12985. if(this._loadInProgress){
  12986. this._queuedFetches.push({args: keywordArgs});
  12987. }else{
  12988. this._loadInProgress = true;
  12989. var getArgs = {
  12990. url: self._jsonFileUrl,
  12991. handleAs: "json-comment-optional",
  12992. preventCache: this.urlPreventCache,
  12993. failOk: this.failOk
  12994. };
  12995. var getHandler = dojo.xhrGet(getArgs);
  12996. getHandler.addCallback(function(data){
  12997. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  12998. try{
  12999. self._getItemsFromLoadedData(data);
  13000. self._loadFinished = true;
  13001. self._loadInProgress = false;
  13002. item = self._getItemByIdentity(keywordArgs.identity);
  13003. if(keywordArgs.onItem){
  13004. keywordArgs.onItem.call(scope, item);
  13005. }
  13006. self._handleQueuedFetches();
  13007. }catch(error){
  13008. self._loadInProgress = false;
  13009. if(keywordArgs.onError){
  13010. keywordArgs.onError.call(scope, error);
  13011. }
  13012. }
  13013. });
  13014. getHandler.addErrback(function(error){
  13015. self._loadInProgress = false;
  13016. if(keywordArgs.onError){
  13017. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  13018. keywordArgs.onError.call(scope, error);
  13019. }
  13020. });
  13021. }
  13022. }else if(this._jsonData){
  13023. // Passed in data, no need to xhr.
  13024. self._getItemsFromLoadedData(self._jsonData);
  13025. self._jsonData = null;
  13026. self._loadFinished = true;
  13027. item = self._getItemByIdentity(keywordArgs.identity);
  13028. if(keywordArgs.onItem){
  13029. scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  13030. keywordArgs.onItem.call(scope, item);
  13031. }
  13032. }
  13033. }else{
  13034. // Already loaded. We can just look it up and call back.
  13035. item = this._getItemByIdentity(keywordArgs.identity);
  13036. if(keywordArgs.onItem){
  13037. scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  13038. keywordArgs.onItem.call(scope, item);
  13039. }
  13040. }
  13041. },
  13042. _getItemByIdentity: function(/* Object */ identity){
  13043. // summary:
  13044. // Internal function to look an item up by its identity map.
  13045. var item = null;
  13046. if(this._itemsByIdentity &&
  13047. Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
  13048. item = this._itemsByIdentity[identity];
  13049. }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
  13050. item = this._arrayOfAllItems[identity];
  13051. }
  13052. if(item === undefined){
  13053. item = null;
  13054. }
  13055. return item; // Object
  13056. },
  13057. getIdentityAttributes: function(/* item */ item){
  13058. // summary:
  13059. // See dojo.data.api.Identity.getIdentityAttributes()
  13060. var identifier = this._features['dojo.data.api.Identity'];
  13061. if(identifier === Number){
  13062. // If (identifier === Number) it means getIdentity() just returns
  13063. // an integer item-number for each item. The dojo.data.api.Identity
  13064. // spec says we need to return null if the identity is not composed
  13065. // of attributes
  13066. return null; // null
  13067. }else{
  13068. return [identifier]; // Array
  13069. }
  13070. },
  13071. _forceLoad: function(){
  13072. // summary:
  13073. // Internal function to force a load of the store if it hasn't occurred yet. This is required
  13074. // for specific functions to work properly.
  13075. var self = this;
  13076. //Do a check on the JsonFileUrl and crosscheck it.
  13077. //If it doesn't match the cross-check, it needs to be updated
  13078. //This allows for either url or _jsonFileUrl to he changed to
  13079. //reset the store load location. Done this way for backwards
  13080. //compatibility. People use _jsonFileUrl (even though officially
  13081. //private.
  13082. if(this._jsonFileUrl !== this._ccUrl){
  13083. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  13084. "To change the url, set the url property of the store," +
  13085. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  13086. this._ccUrl = this._jsonFileUrl;
  13087. this.url = this._jsonFileUrl;
  13088. }else if(this.url !== this._ccUrl){
  13089. this._jsonFileUrl = this.url;
  13090. this._ccUrl = this.url;
  13091. }
  13092. //See if there was any forced reset of data.
  13093. if(this.data != null){
  13094. this._jsonData = this.data;
  13095. this.data = null;
  13096. }
  13097. if(this._jsonFileUrl){
  13098. var getArgs = {
  13099. url: this._jsonFileUrl,
  13100. handleAs: "json-comment-optional",
  13101. preventCache: this.urlPreventCache,
  13102. failOk: this.failOk,
  13103. sync: true
  13104. };
  13105. var getHandler = dojo.xhrGet(getArgs);
  13106. getHandler.addCallback(function(data){
  13107. try{
  13108. //Check to be sure there wasn't another load going on concurrently
  13109. //So we don't clobber data that comes in on it. If there is a load going on
  13110. //then do not save this data. It will potentially clobber current data.
  13111. //We mainly wanted to sync/wait here.
  13112. //TODO: Revisit the loading scheme of this store to improve multi-initial
  13113. //request handling.
  13114. if(self._loadInProgress !== true && !self._loadFinished){
  13115. self._getItemsFromLoadedData(data);
  13116. self._loadFinished = true;
  13117. }else if(self._loadInProgress){
  13118. //Okay, we hit an error state we can't recover from. A forced load occurred
  13119. //while an async load was occurring. Since we cannot block at this point, the best
  13120. //that can be managed is to throw an error.
  13121. throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");
  13122. }
  13123. }catch(e){
  13124. console.log(e);
  13125. throw e;
  13126. }
  13127. });
  13128. getHandler.addErrback(function(error){
  13129. throw error;
  13130. });
  13131. }else if(this._jsonData){
  13132. self._getItemsFromLoadedData(self._jsonData);
  13133. self._jsonData = null;
  13134. self._loadFinished = true;
  13135. }
  13136. }
  13137. });
  13138. //Mix in the simple fetch implementation to this class.
  13139. dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
  13140. }
  13141. if(!dojo._hasResource["dijit._editor.plugins.FontChoice"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13142. dojo._hasResource["dijit._editor.plugins.FontChoice"] = true;
  13143. dojo.provide("dijit._editor.plugins.FontChoice");
  13144. dojo.declare("dijit._editor.plugins._FontDropDown",
  13145. [dijit._Widget, dijit._Templated],{
  13146. // summary:
  13147. // Base class for widgets that contains a label (like "Font:")
  13148. // and a FilteringSelect drop down to pick a value.
  13149. // Used as Toolbar entry.
  13150. // label: [public] String
  13151. // The label to apply to this particular FontDropDown.
  13152. label: "",
  13153. // widgetsInTemplate: [public] boolean
  13154. // Over-ride denoting the template has widgets to parse.
  13155. widgetsInTemplate: true,
  13156. // plainText: [public] boolean
  13157. // Flag to indicate that the returned label should be plain text
  13158. // instead of an example.
  13159. plainText: false,
  13160. // templateString: [public] String
  13161. // The template used to construct the labeled dropdown.
  13162. templateString:
  13163. "<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" +
  13164. "<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" +
  13165. "<input dojoType='dijit.form.FilteringSelect' required='false' labelType='html' labelAttr='label' searchAttr='name' " +
  13166. "tabIndex='-1' id='${selectId}' dojoAttachPoint='select' value=''/>" +
  13167. "</span>",
  13168. postMixInProperties: function(){
  13169. // summary:
  13170. // Over-ride to set specific properties.
  13171. this.inherited(arguments);
  13172. this.strings = dojo.i18n.getLocalization("dijit._editor", "FontChoice");
  13173. // Set some substitution variables used in the template
  13174. this.label = this.strings[this.command];
  13175. this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
  13176. this.selectId = this.id + "_select";
  13177. this.inherited(arguments);
  13178. },
  13179. postCreate: function(){
  13180. // summary:
  13181. // Over-ride for the default postCreate action
  13182. // This establishes the filtering selects and the like.
  13183. // Initialize the list of items in the drop down by creating data store with items like:
  13184. // {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" }
  13185. var items = dojo.map(this.values, function(value){
  13186. var name = this.strings[value] || value;
  13187. return {
  13188. label: this.getLabel(value, name),
  13189. name: name,
  13190. value: value
  13191. };
  13192. }, this);
  13193. this.select.store = new dojo.data.ItemFileReadStore({
  13194. data: {
  13195. identifier: "value",
  13196. items: items
  13197. }
  13198. });
  13199. this.select.set("value", "", false);
  13200. this.disabled = this.select.get("disabled");
  13201. },
  13202. _setValueAttr: function(value, priorityChange){
  13203. // summary:
  13204. // Over-ride for the default action of setting the
  13205. // widget value, maps the input to known values
  13206. // value: Object|String
  13207. // The value to set in the select.
  13208. // priorityChange:
  13209. // Optional parameter used to tell the select whether or not to fire
  13210. // onChange event.
  13211. //if the value is not a permitted value, just set empty string to prevent showing the warning icon
  13212. priorityChange = priorityChange !== false?true:false;
  13213. this.select.set('value', dojo.indexOf(this.values,value) < 0 ? "" : value, priorityChange);
  13214. if(!priorityChange){
  13215. // Clear the last state in case of updateState calls. Ref: #10466
  13216. this.select._lastValueReported=null;
  13217. }
  13218. },
  13219. _getValueAttr: function(){
  13220. // summary:
  13221. // Allow retreiving the value from the composite select on
  13222. // call to button.get("value");
  13223. return this.select.get('value');
  13224. },
  13225. focus: function(){
  13226. // summary:
  13227. // Over-ride for focus control of this widget. Delegates focus down to the
  13228. // filtering select.
  13229. this.select.focus();
  13230. },
  13231. _setDisabledAttr: function(value){
  13232. // summary:
  13233. // Over-ride for the button's 'disabled' attribute so that it can be
  13234. // disabled programmatically.
  13235. // Save off ths disabled state so the get retrieves it correctly
  13236. //without needing to have a function proxy it.
  13237. this.disabled = value;
  13238. this.select.set("disabled", value);
  13239. }
  13240. });
  13241. dojo.declare("dijit._editor.plugins._FontNameDropDown", dijit._editor.plugins._FontDropDown, {
  13242. // summary:
  13243. // Dropdown to select a font; goes in editor toolbar.
  13244. // generic: Boolean
  13245. // Use generic (web standard) font names
  13246. generic: false,
  13247. // command: [public] String
  13248. // The editor 'command' implemented by this plugin.
  13249. command: "fontName",
  13250. postMixInProperties: function(){
  13251. // summary:
  13252. // Over-ride for the default posr mixin control
  13253. if(!this.values){
  13254. this.values = this.generic ?
  13255. ["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics
  13256. ["Arial", "Times New Roman", "Comic Sans MS", "Courier New"];
  13257. }
  13258. this.inherited(arguments);
  13259. },
  13260. getLabel: function(value, name){
  13261. // summary:
  13262. // Function used to generate the labels of the format dropdown
  13263. // will return a formatted, or plain label based on the value
  13264. // of the plainText option.
  13265. // value: String
  13266. // The 'insert value' associated with a name
  13267. // name: String
  13268. // The text name of the value
  13269. if(this.plainText){
  13270. return name;
  13271. }else{
  13272. return "<div style='font-family: "+value+"'>" + name + "</div>";
  13273. }
  13274. },
  13275. _setValueAttr: function(value, priorityChange){
  13276. // summary:
  13277. // Over-ride for the default action of setting the
  13278. // widget value, maps the input to known values
  13279. priorityChange = priorityChange !== false?true:false;
  13280. if(this.generic){
  13281. var map = {
  13282. "Arial": "sans-serif",
  13283. "Helvetica": "sans-serif",
  13284. "Myriad": "sans-serif",
  13285. "Times": "serif",
  13286. "Times New Roman": "serif",
  13287. "Comic Sans MS": "cursive",
  13288. "Apple Chancery": "cursive",
  13289. "Courier": "monospace",
  13290. "Courier New": "monospace",
  13291. "Papyrus": "fantasy",
  13292. "Estrangelo Edessa": "cursive",
  13293. "Gabriola": "fantasy"
  13294. };
  13295. value = map[value] || value;
  13296. }
  13297. this.inherited(arguments, [value, priorityChange]);
  13298. }
  13299. });
  13300. dojo.declare("dijit._editor.plugins._FontSizeDropDown", dijit._editor.plugins._FontDropDown, {
  13301. // summary:
  13302. // Dropdown to select a font size; goes in editor toolbar.
  13303. // command: [public] String
  13304. // The editor 'command' implemented by this plugin.
  13305. command: "fontSize",
  13306. // values: [public] Number[]
  13307. // The HTML font size values supported by this plugin
  13308. values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE
  13309. getLabel: function(value, name){
  13310. // summary:
  13311. // Function used to generate the labels of the format dropdown
  13312. // will return a formatted, or plain label based on the value
  13313. // of the plainText option.
  13314. // We're stuck using the deprecated FONT tag to correspond
  13315. // with the size measurements used by the editor
  13316. // value: String
  13317. // The 'insert value' associated with a name
  13318. // name: String
  13319. // The text name of the value
  13320. if(this.plainText){
  13321. return name;
  13322. }else{
  13323. return "<font size=" + value + "'>" + name + "</font>";
  13324. }
  13325. },
  13326. _setValueAttr: function(value, priorityChange){
  13327. // summary:
  13328. // Over-ride for the default action of setting the
  13329. // widget value, maps the input to known values
  13330. priorityChange = priorityChange !== false?true:false;
  13331. if(value.indexOf && value.indexOf("px") != -1){
  13332. var pixels = parseInt(value, 10);
  13333. value = {10:1, 13:2, 16:3, 18:4, 24:5, 32:6, 48:7}[pixels] || value;
  13334. }
  13335. this.inherited(arguments, [value, priorityChange]);
  13336. }
  13337. });
  13338. dojo.declare("dijit._editor.plugins._FormatBlockDropDown", dijit._editor.plugins._FontDropDown, {
  13339. // summary:
  13340. // Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
  13341. // command: [public] String
  13342. // The editor 'command' implemented by this plugin.
  13343. command: "formatBlock",
  13344. // values: [public] Array
  13345. // The HTML format tags supported by this plugin
  13346. values: ["noFormat", "p", "h1", "h2", "h3", "pre"],
  13347. postCreate: function(){
  13348. // Init and set the default value to no formatting. Update state will adjust it
  13349. // as needed.
  13350. this.inherited(arguments);
  13351. this.set("value", "noFormat", false);
  13352. },
  13353. getLabel: function(value, name){
  13354. // summary:
  13355. // Function used to generate the labels of the format dropdown
  13356. // will return a formatted, or plain label based on the value
  13357. // of the plainText option.
  13358. // value: String
  13359. // The 'insert value' associated with a name
  13360. // name: String
  13361. // The text name of the value
  13362. if(this.plainText || value == "noFormat"){
  13363. return name;
  13364. }else{
  13365. return "<" + value + ">" + name + "</" + value + ">";
  13366. }
  13367. },
  13368. _execCommand: function(editor, command, choice){
  13369. // summary:
  13370. // Over-ride for default exec-command label.
  13371. // Allows us to treat 'none' as special.
  13372. if(choice === "noFormat"){
  13373. var start;
  13374. var end;
  13375. var sel = dijit.range.getSelection(editor.window);
  13376. if(sel && sel.rangeCount > 0){
  13377. var range = sel.getRangeAt(0);
  13378. var node, tag;
  13379. if(range){
  13380. start = range.startContainer;
  13381. end = range.endContainer;
  13382. // find containing nodes of start/end.
  13383. while(start && start !== editor.editNode &&
  13384. start !== editor.document.body &&
  13385. start.nodeType !== 1){
  13386. start = start.parentNode;
  13387. }
  13388. while(end && end !== editor.editNode &&
  13389. end !== editor.document.body &&
  13390. end.nodeType !== 1){
  13391. end = end.parentNode;
  13392. }
  13393. var processChildren = dojo.hitch(this, function(node, array){
  13394. if(node.childNodes && node.childNodes.length){
  13395. var i;
  13396. for(i = 0; i < node.childNodes.length; i++){
  13397. var c = node.childNodes[i];
  13398. if(c.nodeType == 1){
  13399. if(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [c])){
  13400. var tag = c.tagName? c.tagName.toLowerCase(): "";
  13401. if(dojo.indexOf(this.values, tag) !== -1){
  13402. array.push(c);
  13403. }
  13404. processChildren(c,array);
  13405. }
  13406. }
  13407. }
  13408. }
  13409. });
  13410. var unformatNodes = dojo.hitch(this, function(nodes){
  13411. // summary:
  13412. // Internal function to clear format nodes.
  13413. // nodes:
  13414. // The array of nodes to strip formatting from.
  13415. if(nodes && nodes.length){
  13416. editor.beginEditing();
  13417. while(nodes.length){
  13418. this._removeFormat(editor, nodes.pop());
  13419. }
  13420. editor.endEditing();
  13421. }
  13422. });
  13423. var clearNodes = [];
  13424. if(start == end){
  13425. //Contained within the same block, may be collapsed, but who cares, see if we
  13426. // have a block element to remove.
  13427. var block;
  13428. node = start;
  13429. while(node && node !== editor.editNode && node !== editor.document.body){
  13430. if(node.nodeType == 1){
  13431. tag = node.tagName? node.tagName.toLowerCase(): "";
  13432. if(dojo.indexOf(this.values, tag) !== -1){
  13433. block = node;
  13434. break;
  13435. }
  13436. }
  13437. node = node.parentNode;
  13438. }
  13439. //Also look for all child nodes in the selection that may need to be
  13440. //cleared of formatting
  13441. processChildren(start, clearNodes);
  13442. if(block) { clearNodes = [block].concat(clearNodes); }
  13443. unformatNodes(clearNodes);
  13444. }else{
  13445. // Probably a multi select, so we have to process it. Whee.
  13446. node = start;
  13447. while(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [node])){
  13448. if(node.nodeType == 1){
  13449. tag = node.tagName? node.tagName.toLowerCase(): "";
  13450. if(dojo.indexOf(this.values, tag) !== -1){
  13451. clearNodes.push(node);
  13452. }
  13453. processChildren(node,clearNodes);
  13454. }
  13455. node = node.nextSibling;
  13456. }
  13457. unformatNodes(clearNodes);
  13458. }
  13459. editor.onDisplayChanged();
  13460. }
  13461. }
  13462. }else{
  13463. editor.execCommand(command, choice);
  13464. }
  13465. },
  13466. _removeFormat: function(editor, node){
  13467. // summary:
  13468. // function to remove the block format node.
  13469. // node:
  13470. // The block format node to remove (and leave the contents behind)
  13471. if(editor.customUndo){
  13472. // So of course IE doesn't work right with paste-overs.
  13473. // We have to do this manually, which is okay since IE already uses
  13474. // customUndo and we turned it on for WebKit. WebKit pasted funny,
  13475. // so couldn't use the execCommand approach
  13476. while(node.firstChild){
  13477. dojo.place(node.firstChild, node, "before");
  13478. }
  13479. node.parentNode.removeChild(node);
  13480. }else{
  13481. // Everyone else works fine this way, a paste-over and is native
  13482. // undo friendly.
  13483. dojo.withGlobal(editor.window,
  13484. "selectElementChildren", dijit._editor.selection, [node]);
  13485. var html = dojo.withGlobal(editor.window,
  13486. "getSelectedHtml", dijit._editor.selection, [null]);
  13487. dojo.withGlobal(editor.window,
  13488. "selectElement", dijit._editor.selection, [node]);
  13489. editor.execCommand("inserthtml", html||"");
  13490. }
  13491. }
  13492. });
  13493. // TODO: for 2.0, split into FontChoice plugin into three separate classes,
  13494. // one for each command (and change registry below)
  13495. dojo.declare("dijit._editor.plugins.FontChoice", dijit._editor._Plugin,{
  13496. // summary:
  13497. // This plugin provides three drop downs for setting style in the editor
  13498. // (font, font size, and format block), as controlled by command.
  13499. //
  13500. // description:
  13501. // The commands provided by this plugin are:
  13502. //
  13503. // * fontName
  13504. // | Provides a drop down to select from a list of font names
  13505. // * fontSize
  13506. // | Provides a drop down to select from a list of font sizes
  13507. // * formatBlock
  13508. // | Provides a drop down to select from a list of block styles
  13509. // |
  13510. //
  13511. // which can easily be added to an editor by including one or more of the above commands
  13512. // in the `plugins` attribute as follows:
  13513. //
  13514. // | plugins="['fontName','fontSize',...]"
  13515. //
  13516. // It is possible to override the default dropdown list by providing an Array for the `custom` property when
  13517. // instantiating this plugin, e.g.
  13518. //
  13519. // | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', custom:['Verdana','Myriad','Garamond']},...]"
  13520. //
  13521. // Alternatively, for `fontName` only, `generic:true` may be specified to provide a dropdown with
  13522. // [CSS generic font families](http://www.w3.org/TR/REC-CSS2/fonts.html#generic-font-families)
  13523. //
  13524. // Note that the editor is often unable to properly handle font styling information defined outside
  13525. // the context of the current editor instance, such as pre-populated HTML.
  13526. // useDefaultCommand: [protected] booleam
  13527. // Override _Plugin.useDefaultCommand...
  13528. // processing is handled by this plugin, not by dijit.Editor.
  13529. useDefaultCommand: false,
  13530. _initButton: function(){
  13531. // summary:
  13532. // Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
  13533. // rather than a simple button.
  13534. // tags:
  13535. // protected
  13536. // Create the widget to go into the toolbar (the so-called "button")
  13537. var clazz = {
  13538. fontName: dijit._editor.plugins._FontNameDropDown,
  13539. fontSize: dijit._editor.plugins._FontSizeDropDown,
  13540. formatBlock: dijit._editor.plugins._FormatBlockDropDown
  13541. }[this.command],
  13542. params = this.params;
  13543. // For back-compat reasons support setting custom values via "custom" parameter
  13544. // rather than "values" parameter
  13545. if(this.params.custom){
  13546. params.values = this.params.custom;
  13547. }
  13548. var editor = this.editor;
  13549. this.button = new clazz(dojo.delegate({dir: editor.dir, lang: editor.lang}, params));
  13550. // Reflect changes to the drop down in the editor
  13551. this.connect(this.button.select, "onChange", function(choice){
  13552. // User invoked change, since all internal updates set priorityChange to false and will
  13553. // not trigger an onChange event.
  13554. this.editor.focus();
  13555. if(this.command == "fontName" && choice.indexOf(" ") != -1){ choice = "'" + choice + "'"; }
  13556. // Invoke, the editor already normalizes commands called through its
  13557. // execCommand.
  13558. if(this.button._execCommand){
  13559. this.button._execCommand(this.editor, this.command, choice);
  13560. }else{
  13561. this.editor.execCommand(this.command, choice);
  13562. }
  13563. });
  13564. },
  13565. updateState: function(){
  13566. // summary:
  13567. // Overrides _Plugin.updateState(). This controls updating the menu
  13568. // options to the right values on state changes in the document (that trigger a
  13569. // test of the actions.)
  13570. // It set value of drop down in toolbar to reflect font/font size/format block
  13571. // of text at current caret position.
  13572. // tags:
  13573. // protected
  13574. var _e = this.editor;
  13575. var _c = this.command;
  13576. if(!_e || !_e.isLoaded || !_c.length){ return; }
  13577. if(this.button){
  13578. var disabled = this.get("disabled");
  13579. this.button.set("disabled", disabled);
  13580. if(disabled){ return; }
  13581. var value;
  13582. try{
  13583. value = _e.queryCommandValue(_c) || "";
  13584. }catch(e){
  13585. //Firefox may throw error above if the editor is just loaded, ignore it
  13586. value = "";
  13587. }
  13588. // strip off single quotes, if any
  13589. var quoted = dojo.isString(value) && value.match(/'([^']*)'/);
  13590. if(quoted){ value = quoted[1]; }
  13591. if(_c === "formatBlock"){
  13592. if(!value || value == "p"){
  13593. // Some browsers (WebKit) doesn't actually get the tag info right.
  13594. // and IE returns paragraph when in a DIV!, so incorrect a lot,
  13595. // so we have double-check it.
  13596. value = null;
  13597. var elem;
  13598. // Try to find the current element where the caret is.
  13599. var sel = dijit.range.getSelection(this.editor.window);
  13600. if(sel && sel.rangeCount > 0){
  13601. var range = sel.getRangeAt(0);
  13602. if(range){
  13603. elem = range.endContainer;
  13604. }
  13605. }
  13606. // Okay, now see if we can find one of the formatting types we're in.
  13607. while(elem && elem !== _e.editNode && elem !== _e.document){
  13608. var tg = elem.tagName?elem.tagName.toLowerCase():"";
  13609. if(tg && dojo.indexOf(this.button.values, tg) > -1){
  13610. value = tg;
  13611. break;
  13612. }
  13613. elem = elem.parentNode;
  13614. }
  13615. if(!value){
  13616. // Still no value, so lets select 'none'.
  13617. value = "noFormat";
  13618. }
  13619. }else{
  13620. // Check that the block format is one allowed, if not,
  13621. // null it so that it gets set to empty.
  13622. if(dojo.indexOf(this.button.values, value) < 0){
  13623. value = "noFormat";
  13624. }
  13625. }
  13626. }
  13627. if(value !== this.button.get("value")){
  13628. // Set the value, but denote it is not a priority change, so no
  13629. // onchange fires.
  13630. this.button.set('value', value, false);
  13631. }
  13632. }
  13633. }
  13634. });
  13635. // Register this plugin.
  13636. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  13637. if(o.plugin){ return; }
  13638. switch(o.args.name){
  13639. case "fontName": case "fontSize": case "formatBlock":
  13640. o.plugin = new dijit._editor.plugins.FontChoice({
  13641. command: o.args.name,
  13642. plainText: o.args.plainText?o.args.plainText:false
  13643. });
  13644. }
  13645. });
  13646. }
  13647. if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13648. dojo._hasResource["dijit.form.DropDownButton"] = true;
  13649. dojo.provide("dijit.form.DropDownButton");
  13650. }
  13651. if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13652. dojo._hasResource["dijit.form._FormSelectWidget"] = true;
  13653. dojo.provide("dijit.form._FormSelectWidget");
  13654. /*=====
  13655. dijit.form.__SelectOption = function(){
  13656. // value: String
  13657. // The value of the option. Setting to empty (or missing) will
  13658. // place a separator at that location
  13659. // label: String
  13660. // The label for our option. It can contain html tags.
  13661. // selected: Boolean
  13662. // Whether or not we are a selected option
  13663. // disabled: Boolean
  13664. // Whether or not this specific option is disabled
  13665. this.value = value;
  13666. this.label = label;
  13667. this.selected = selected;
  13668. this.disabled = disabled;
  13669. }
  13670. =====*/
  13671. dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
  13672. // summary:
  13673. // Extends _FormValueWidget in order to provide "select-specific"
  13674. // values - i.e., those values that are unique to <select> elements.
  13675. // This also provides the mechanism for reading the elements from
  13676. // a store, if desired.
  13677. // multiple: [const] Boolean
  13678. // Whether or not we are multi-valued
  13679. multiple: false,
  13680. // options: dijit.form.__SelectOption[]
  13681. // The set of options for our select item. Roughly corresponds to
  13682. // the html <option> tag.
  13683. options: null,
  13684. // store: dojo.data.api.Identity
  13685. // A store which, at the very least impelements dojo.data.api.Identity
  13686. // to use for getting our list of options - rather than reading them
  13687. // from the <option> html tags.
  13688. store: null,
  13689. // query: object
  13690. // A query to use when fetching items from our store
  13691. query: null,
  13692. // queryOptions: object
  13693. // Query options to use when fetching from the store
  13694. queryOptions: null,
  13695. // onFetch: Function
  13696. // A callback to do with an onFetch - but before any items are actually
  13697. // iterated over (i.e. to filter even futher what you want to add)
  13698. onFetch: null,
  13699. // sortByLabel: Boolean
  13700. // Flag to sort the options returned from a store by the label of
  13701. // the store.
  13702. sortByLabel: true,
  13703. // loadChildrenOnOpen: Boolean
  13704. // By default loadChildren is called when the items are fetched from the
  13705. // store. This property allows delaying loadChildren (and the creation
  13706. // of the options/menuitems) until the user clicks the button to open the
  13707. // dropdown.
  13708. loadChildrenOnOpen: false,
  13709. getOptions: function(/*anything*/ valueOrIdx){
  13710. // summary:
  13711. // Returns a given option (or options).
  13712. // valueOrIdx:
  13713. // If passed in as a string, that string is used to look up the option
  13714. // in the array of options - based on the value property.
  13715. // (See dijit.form.__SelectOption).
  13716. //
  13717. // If passed in a number, then the option with the given index (0-based)
  13718. // within this select will be returned.
  13719. //
  13720. // If passed in a dijit.form.__SelectOption, the same option will be
  13721. // returned if and only if it exists within this select.
  13722. //
  13723. // If passed an array, then an array will be returned with each element
  13724. // in the array being looked up.
  13725. //
  13726. // If not passed a value, then all options will be returned
  13727. //
  13728. // returns:
  13729. // The option corresponding with the given value or index. null
  13730. // is returned if any of the following are true:
  13731. // - A string value is passed in which doesn't exist
  13732. // - An index is passed in which is outside the bounds of the array of options
  13733. // - A dijit.form.__SelectOption is passed in which is not a part of the select
  13734. // NOTE: the compare for passing in a dijit.form.__SelectOption checks
  13735. // if the value property matches - NOT if the exact option exists
  13736. // NOTE: if passing in an array, null elements will be placed in the returned
  13737. // array when a value is not found.
  13738. var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
  13739. if(lookupValue === undefined){
  13740. return opts; // dijit.form.__SelectOption[]
  13741. }
  13742. if(dojo.isArray(lookupValue)){
  13743. return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
  13744. }
  13745. if(dojo.isObject(valueOrIdx)){
  13746. // We were passed an option - so see if it's in our array (directly),
  13747. // and if it's not, try and find it by value.
  13748. if(!dojo.some(this.options, function(o, idx){
  13749. if(o === lookupValue ||
  13750. (o.value && o.value === lookupValue.value)){
  13751. lookupValue = idx;
  13752. return true;
  13753. }
  13754. return false;
  13755. })){
  13756. lookupValue = -1;
  13757. }
  13758. }
  13759. if(typeof lookupValue == "string"){
  13760. for(var i=0; i<l; i++){
  13761. if(opts[i].value === lookupValue){
  13762. lookupValue = i;
  13763. break;
  13764. }
  13765. }
  13766. }
  13767. if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
  13768. return this.options[lookupValue] // dijit.form.__SelectOption
  13769. }
  13770. return null; // null
  13771. },
  13772. addOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ option){
  13773. // summary:
  13774. // Adds an option or options to the end of the select. If value
  13775. // of the option is empty or missing, a separator is created instead.
  13776. // Passing in an array of options will yield slightly better performance
  13777. // since the children are only loaded once.
  13778. if(!dojo.isArray(option)){ option = [option]; }
  13779. dojo.forEach(option, function(i){
  13780. if(i && dojo.isObject(i)){
  13781. this.options.push(i);
  13782. }
  13783. }, this);
  13784. this._loadChildren();
  13785. },
  13786. removeOption: function(/*String|dijit.form.__SelectOption|Number|Array*/ valueOrIdx){
  13787. // summary:
  13788. // Removes the given option or options. You can remove by string
  13789. // (in which case the value is removed), number (in which case the
  13790. // index in the options array is removed), or select option (in
  13791. // which case, the select option with a matching value is removed).
  13792. // You can also pass in an array of those values for a slightly
  13793. // better performance since the children are only loaded once.
  13794. if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
  13795. var oldOpts = this.getOptions(valueOrIdx);
  13796. dojo.forEach(oldOpts, function(i){
  13797. // We can get null back in our array - if our option was not found. In
  13798. // that case, we don't want to blow up...
  13799. if(i){
  13800. this.options = dojo.filter(this.options, function(node, idx){
  13801. return (node.value !== i.value || node.label !== i.label);
  13802. });
  13803. this._removeOptionItem(i);
  13804. }
  13805. }, this);
  13806. this._loadChildren();
  13807. },
  13808. updateOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ newOption){
  13809. // summary:
  13810. // Updates the values of the given option. The option to update
  13811. // is matched based on the value of the entered option. Passing
  13812. // in an array of new options will yeild better performance since
  13813. // the children will only be loaded once.
  13814. if(!dojo.isArray(newOption)){ newOption = [newOption]; }
  13815. dojo.forEach(newOption, function(i){
  13816. var oldOpt = this.getOptions(i), k;
  13817. if(oldOpt){
  13818. for(k in i){ oldOpt[k] = i[k]; }
  13819. }
  13820. }, this);
  13821. this._loadChildren();
  13822. },
  13823. setStore: function(/*dojo.data.api.Identity*/ store,
  13824. /*anything?*/ selectedValue,
  13825. /*Object?*/ fetchArgs){
  13826. // summary:
  13827. // Sets the store you would like to use with this select widget.
  13828. // The selected value is the value of the new store to set. This
  13829. // function returns the original store, in case you want to reuse
  13830. // it or something.
  13831. // store: dojo.data.api.Identity
  13832. // The store you would like to use - it MUST implement Identity,
  13833. // and MAY implement Notification.
  13834. // selectedValue: anything?
  13835. // The value that this widget should set itself to *after* the store
  13836. // has been loaded
  13837. // fetchArgs: Object?
  13838. // The arguments that will be passed to the store's fetch() function
  13839. var oStore = this.store;
  13840. fetchArgs = fetchArgs || {};
  13841. if(oStore !== store){
  13842. // Our store has changed, so update our notifications
  13843. dojo.forEach(this._notifyConnections || [], dojo.disconnect);
  13844. delete this._notifyConnections;
  13845. if(store && store.getFeatures()["dojo.data.api.Notification"]){
  13846. this._notifyConnections = [
  13847. dojo.connect(store, "onNew", this, "_onNewItem"),
  13848. dojo.connect(store, "onDelete", this, "_onDeleteItem"),
  13849. dojo.connect(store, "onSet", this, "_onSetItem")
  13850. ];
  13851. }
  13852. this._set("store", store);
  13853. }
  13854. // Turn off change notifications while we make all these changes
  13855. this._onChangeActive = false;
  13856. // Remove existing options (if there are any)
  13857. if(this.options && this.options.length){
  13858. this.removeOption(this.options);
  13859. }
  13860. // Add our new options
  13861. if(store){
  13862. this._loadingStore = true;
  13863. store.fetch(dojo.delegate(fetchArgs, {
  13864. onComplete: function(items, opts){
  13865. if(this.sortByLabel && !fetchArgs.sort && items.length){
  13866. items.sort(dojo.data.util.sorter.createSortFunction([{
  13867. attribute: store.getLabelAttributes(items[0])[0]
  13868. }], store));
  13869. }
  13870. if(fetchArgs.onFetch){
  13871. items = fetchArgs.onFetch.call(this, items, opts);
  13872. }
  13873. // TODO: Add these guys as a batch, instead of separately
  13874. dojo.forEach(items, function(i){
  13875. this._addOptionForItem(i);
  13876. }, this);
  13877. // Set our value (which might be undefined), and then tweak
  13878. // it to send a change event with the real value
  13879. this._loadingStore = false;
  13880. this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
  13881. delete this._pendingValue;
  13882. if(!this.loadChildrenOnOpen){
  13883. this._loadChildren();
  13884. }else{
  13885. this._pseudoLoadChildren(items);
  13886. }
  13887. this._fetchedWith = opts;
  13888. this._lastValueReported = this.multiple ? [] : null;
  13889. this._onChangeActive = true;
  13890. this.onSetStore();
  13891. this._handleOnChange(this.value);
  13892. },
  13893. scope: this
  13894. }));
  13895. }else{
  13896. delete this._fetchedWith;
  13897. }
  13898. return oStore; // dojo.data.api.Identity
  13899. },
  13900. // TODO: implement set() and watch() for store and query, although not sure how to handle
  13901. // setting them individually rather than together (as in setStore() above)
  13902. _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  13903. // summary:
  13904. // set the value of the widget.
  13905. // If a string is passed, then we set our value from looking it up.
  13906. if(this._loadingStore){
  13907. // Our store is loading - so save our value, and we'll set it when
  13908. // we're done
  13909. this._pendingValue = newValue;
  13910. return;
  13911. }
  13912. var opts = this.getOptions() || [];
  13913. if(!dojo.isArray(newValue)){
  13914. newValue = [newValue];
  13915. }
  13916. dojo.forEach(newValue, function(i, idx){
  13917. if(!dojo.isObject(i)){
  13918. i = i + "";
  13919. }
  13920. if(typeof i === "string"){
  13921. newValue[idx] = dojo.filter(opts, function(node){
  13922. return node.value === i;
  13923. })[0] || {value: "", label: ""};
  13924. }
  13925. }, this);
  13926. // Make sure some sane default is set
  13927. newValue = dojo.filter(newValue, function(i){ return i && i.value; });
  13928. if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
  13929. newValue[0] = opts[0];
  13930. }
  13931. dojo.forEach(opts, function(i){
  13932. i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
  13933. });
  13934. var val = dojo.map(newValue, function(i){ return i.value; }),
  13935. disp = dojo.map(newValue, function(i){ return i.label; });
  13936. this._set("value", this.multiple ? val : val[0]);
  13937. this._setDisplay(this.multiple ? disp : disp[0]);
  13938. this._updateSelection();
  13939. this._handleOnChange(this.value, priorityChange);
  13940. },
  13941. _getDisplayedValueAttr: function(){
  13942. // summary:
  13943. // returns the displayed value of the widget
  13944. var val = this.get("value");
  13945. if(!dojo.isArray(val)){
  13946. val = [val];
  13947. }
  13948. var ret = dojo.map(this.getOptions(val), function(v){
  13949. if(v && "label" in v){
  13950. return v.label;
  13951. }else if(v){
  13952. return v.value;
  13953. }
  13954. return null;
  13955. }, this);
  13956. return this.multiple ? ret : ret[0];
  13957. },
  13958. _loadChildren: function(){
  13959. // summary:
  13960. // Loads the children represented by this widget's options.
  13961. // reset the menu to make it populatable on the next click
  13962. if(this._loadingStore){ return; }
  13963. dojo.forEach(this._getChildren(), function(child){
  13964. child.destroyRecursive();
  13965. });
  13966. // Add each menu item
  13967. dojo.forEach(this.options, this._addOptionItem, this);
  13968. // Update states
  13969. this._updateSelection();
  13970. },
  13971. _updateSelection: function(){
  13972. // summary:
  13973. // Sets the "selected" class on the item for styling purposes
  13974. this._set("value", this._getValueFromOpts());
  13975. var val = this.value;
  13976. if(!dojo.isArray(val)){
  13977. val = [val];
  13978. }
  13979. if(val && val[0]){
  13980. dojo.forEach(this._getChildren(), function(child){
  13981. var isSelected = dojo.some(val, function(v){
  13982. return child.option && (v === child.option.value);
  13983. });
  13984. dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
  13985. dijit.setWaiState(child.domNode, "selected", isSelected);
  13986. }, this);
  13987. }
  13988. },
  13989. _getValueFromOpts: function(){
  13990. // summary:
  13991. // Returns the value of the widget by reading the options for
  13992. // the selected flag
  13993. var opts = this.getOptions() || [];
  13994. if(!this.multiple && opts.length){
  13995. // Mirror what a select does - choose the first one
  13996. var opt = dojo.filter(opts, function(i){
  13997. return i.selected;
  13998. })[0];
  13999. if(opt && opt.value){
  14000. return opt.value
  14001. }else{
  14002. opts[0].selected = true;
  14003. return opts[0].value;
  14004. }
  14005. }else if(this.multiple){
  14006. // Set value to be the sum of all selected
  14007. return dojo.map(dojo.filter(opts, function(i){
  14008. return i.selected;
  14009. }), function(i){
  14010. return i.value;
  14011. }) || [];
  14012. }
  14013. return "";
  14014. },
  14015. // Internal functions to call when we have store notifications come in
  14016. _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
  14017. if(!parentInfo || !parentInfo.parent){
  14018. // Only add it if we are top-level
  14019. this._addOptionForItem(item);
  14020. }
  14021. },
  14022. _onDeleteItem: function(/*item*/ item){
  14023. var store = this.store;
  14024. this.removeOption(store.getIdentity(item));
  14025. },
  14026. _onSetItem: function(/*item*/ item){
  14027. this.updateOption(this._getOptionObjForItem(item));
  14028. },
  14029. _getOptionObjForItem: function(item){
  14030. // summary:
  14031. // Returns an option object based off the given item. The "value"
  14032. // of the option item will be the identity of the item, the "label"
  14033. // of the option will be the label of the item. If the item contains
  14034. // children, the children value of the item will be set
  14035. var store = this.store, label = store.getLabel(item),
  14036. value = (label ? store.getIdentity(item) : null);
  14037. return {value: value, label: label, item:item}; // dijit.form.__SelectOption
  14038. },
  14039. _addOptionForItem: function(/*item*/ item){
  14040. // summary:
  14041. // Creates (and adds) the option for the given item
  14042. var store = this.store;
  14043. if(!store.isItemLoaded(item)){
  14044. // We are not loaded - so let's load it and add later
  14045. store.loadItem({item: item, onComplete: function(i){
  14046. this._addOptionForItem(item);
  14047. },
  14048. scope: this});
  14049. return;
  14050. }
  14051. var newOpt = this._getOptionObjForItem(item);
  14052. this.addOption(newOpt);
  14053. },
  14054. constructor: function(/*Object*/ keywordArgs){
  14055. // summary:
  14056. // Saves off our value, if we have an initial one set so we
  14057. // can use it if we have a store as well (see startup())
  14058. this._oValue = (keywordArgs || {}).value || null;
  14059. },
  14060. buildRendering: function(){
  14061. this.inherited(arguments);
  14062. dojo.setSelectable(this.focusNode, false);
  14063. },
  14064. _fillContent: function(){
  14065. // summary:
  14066. // Loads our options and sets up our dropdown correctly. We
  14067. // don't want any content, so we don't call any inherit chain
  14068. // function.
  14069. var opts = this.options;
  14070. if(!opts){
  14071. opts = this.options = this.srcNodeRef ? dojo.query(">",
  14072. this.srcNodeRef).map(function(node){
  14073. if(node.getAttribute("type") === "separator"){
  14074. return { value: "", label: "", selected: false, disabled: false };
  14075. }
  14076. return {
  14077. value: (node.getAttribute("data-" + dojo._scopeName + "-value") || node.getAttribute("value")),
  14078. label: String(node.innerHTML),
  14079. // FIXME: disabled and selected are not valid on complex markup children (which is why we're
  14080. // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
  14081. // decide before 1.6
  14082. selected: node.getAttribute("selected") || false,
  14083. disabled: node.getAttribute("disabled") || false
  14084. };
  14085. }, this) : [];
  14086. }
  14087. if(!this.value){
  14088. this._set("value", this._getValueFromOpts());
  14089. }else if(this.multiple && typeof this.value == "string"){
  14090. this._set("value", this.value.split(","));
  14091. }
  14092. },
  14093. postCreate: function(){
  14094. // summary:
  14095. // sets up our event handling that we need for functioning
  14096. // as a select
  14097. this.inherited(arguments);
  14098. // Make our event connections for updating state
  14099. this.connect(this, "onChange", "_updateSelection");
  14100. this.connect(this, "startup", "_loadChildren");
  14101. this._setValueAttr(this.value, null);
  14102. },
  14103. startup: function(){
  14104. // summary:
  14105. // Connects in our store, if we have one defined
  14106. this.inherited(arguments);
  14107. var store = this.store, fetchArgs = {};
  14108. dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
  14109. if(this[i]){
  14110. fetchArgs[i] = this[i];
  14111. }
  14112. delete this[i];
  14113. }, this);
  14114. if(store && store.getFeatures()["dojo.data.api.Identity"]){
  14115. // Temporarily set our store to null so that it will get set
  14116. // and connected appropriately
  14117. this.store = null;
  14118. this.setStore(store, this._oValue, fetchArgs);
  14119. }
  14120. },
  14121. destroy: function(){
  14122. // summary:
  14123. // Clean up our connections
  14124. dojo.forEach(this._notifyConnections || [], dojo.disconnect);
  14125. this.inherited(arguments);
  14126. },
  14127. _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
  14128. // summary:
  14129. // User-overridable function which, for the given option, adds an
  14130. // item to the select. If the option doesn't have a value, then a
  14131. // separator is added in that place. Make sure to store the option
  14132. // in the created option widget.
  14133. },
  14134. _removeOptionItem: function(/*dijit.form.__SelectOption*/ option){
  14135. // summary:
  14136. // User-overridable function which, for the given option, removes
  14137. // its item from the select.
  14138. },
  14139. _setDisplay: function(/*String or String[]*/ newDisplay){
  14140. // summary:
  14141. // Overridable function which will set the display for the
  14142. // widget. newDisplay is either a string (in the case of
  14143. // single selects) or array of strings (in the case of multi-selects)
  14144. },
  14145. _getChildren: function(){
  14146. // summary:
  14147. // Overridable function to return the children that this widget contains.
  14148. return [];
  14149. },
  14150. _getSelectedOptionsAttr: function(){
  14151. // summary:
  14152. // hooks into this.attr to provide a mechanism for getting the
  14153. // option items for the current value of the widget.
  14154. return this.getOptions(this.get("value"));
  14155. },
  14156. _pseudoLoadChildren: function(/*item[]*/ items){
  14157. // summary:
  14158. // a function that will "fake" loading children, if needed, and
  14159. // if we have set to not load children until the widget opens.
  14160. // items:
  14161. // An array of items that will be loaded, when needed
  14162. },
  14163. onSetStore: function(){
  14164. // summary:
  14165. // a function that can be connected to in order to receive a
  14166. // notification that the store has finished loading and all options
  14167. // from that store are available
  14168. }
  14169. });
  14170. }
  14171. if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14172. dojo._hasResource["dijit.MenuItem"] = true;
  14173. dojo.provide("dijit.MenuItem");
  14174. dojo.declare("dijit.MenuItem",
  14175. [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
  14176. {
  14177. // summary:
  14178. // A line item in a Menu Widget
  14179. // Make 3 columns
  14180. // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
  14181. templateString: dojo.cache("dijit", "templates/MenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div dojoAttachPoint=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"),
  14182. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  14183. label: { node: "containerNode", type: "innerHTML" },
  14184. iconClass: { node: "iconNode", type: "class" }
  14185. }),
  14186. baseClass: "dijitMenuItem",
  14187. // label: String
  14188. // Menu text
  14189. label: '',
  14190. // iconClass: String
  14191. // Class to apply to DOMNode to make it display an icon.
  14192. iconClass: "",
  14193. // accelKey: String
  14194. // Text for the accelerator (shortcut) key combination.
  14195. // Note that although Menu can display accelerator keys there
  14196. // is no infrastructure to actually catch and execute these
  14197. // accelerators.
  14198. accelKey: "",
  14199. // disabled: Boolean
  14200. // If true, the menu item is disabled.
  14201. // If false, the menu item is enabled.
  14202. disabled: false,
  14203. _fillContent: function(/*DomNode*/ source){
  14204. // If button label is specified as srcNodeRef.innerHTML rather than
  14205. // this.params.label, handle it here.
  14206. if(source && !("label" in this.params)){
  14207. this.set('label', source.innerHTML);
  14208. }
  14209. },
  14210. buildRendering: function(){
  14211. this.inherited(arguments);
  14212. var label = this.id+"_text";
  14213. dojo.attr(this.containerNode, "id", label);
  14214. if(this.accelKeyNode){
  14215. dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
  14216. label += " " + this.id + "_accel";
  14217. }
  14218. dijit.setWaiState(this.domNode, "labelledby", label);
  14219. dojo.setSelectable(this.domNode, false);
  14220. },
  14221. _onHover: function(){
  14222. // summary:
  14223. // Handler when mouse is moved onto menu item
  14224. // tags:
  14225. // protected
  14226. this.getParent().onItemHover(this);
  14227. },
  14228. _onUnhover: function(){
  14229. // summary:
  14230. // Handler when mouse is moved off of menu item,
  14231. // possibly to a child menu, or maybe to a sibling
  14232. // menuitem or somewhere else entirely.
  14233. // tags:
  14234. // protected
  14235. // if we are unhovering the currently selected item
  14236. // then unselect it
  14237. this.getParent().onItemUnhover(this);
  14238. // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
  14239. // FF and IE don't generate an onmouseout event for the MenuItem.
  14240. // So, help out _CssStateMixin in this case.
  14241. this._set("hovering", false);
  14242. },
  14243. _onClick: function(evt){
  14244. // summary:
  14245. // Internal handler for click events on MenuItem.
  14246. // tags:
  14247. // private
  14248. this.getParent().onItemClick(this, evt);
  14249. dojo.stopEvent(evt);
  14250. },
  14251. onClick: function(/*Event*/ evt){
  14252. // summary:
  14253. // User defined function to handle clicks
  14254. // tags:
  14255. // callback
  14256. },
  14257. focus: function(){
  14258. // summary:
  14259. // Focus on this MenuItem
  14260. try{
  14261. if(dojo.isIE == 8){
  14262. // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
  14263. this.containerNode.focus();
  14264. }
  14265. dijit.focus(this.focusNode);
  14266. }catch(e){
  14267. // this throws on IE (at least) in some scenarios
  14268. }
  14269. },
  14270. _onFocus: function(){
  14271. // summary:
  14272. // This is called by the focus manager when focus
  14273. // goes to this MenuItem or a child menu.
  14274. // tags:
  14275. // protected
  14276. this._setSelected(true);
  14277. this.getParent()._onItemFocus(this);
  14278. this.inherited(arguments);
  14279. },
  14280. _setSelected: function(selected){
  14281. // summary:
  14282. // Indicate that this node is the currently selected one
  14283. // tags:
  14284. // private
  14285. /***
  14286. * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
  14287. * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
  14288. * That's not supposed to happen, but the problem is:
  14289. * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
  14290. * points to the parent Menu, bypassing the parent MenuItem... thus the
  14291. * MenuItem is not in the chain of active widgets and gets a premature call to
  14292. * _onBlur()
  14293. */
  14294. dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
  14295. },
  14296. setLabel: function(/*String*/ content){
  14297. // summary:
  14298. // Deprecated. Use set('label', ...) instead.
  14299. // tags:
  14300. // deprecated
  14301. dojo.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
  14302. this.set("label", content);
  14303. },
  14304. setDisabled: function(/*Boolean*/ disabled){
  14305. // summary:
  14306. // Deprecated. Use set('disabled', bool) instead.
  14307. // tags:
  14308. // deprecated
  14309. dojo.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
  14310. this.set('disabled', disabled);
  14311. },
  14312. _setDisabledAttr: function(/*Boolean*/ value){
  14313. // summary:
  14314. // Hook for attr('disabled', ...) to work.
  14315. // Enable or disable this menu item.
  14316. dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
  14317. this._set("disabled", value);
  14318. },
  14319. _setAccelKeyAttr: function(/*String*/ value){
  14320. // summary:
  14321. // Hook for attr('accelKey', ...) to work.
  14322. // Set accelKey on this menu item.
  14323. this.accelKeyNode.style.display=value?"":"none";
  14324. this.accelKeyNode.innerHTML=value;
  14325. //have to use colSpan to make it work in IE
  14326. dojo.attr(this.containerNode,'colSpan',value?"1":"2");
  14327. this._set("accelKey", value);
  14328. }
  14329. });
  14330. }
  14331. if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14332. dojo._hasResource["dijit.PopupMenuItem"] = true;
  14333. dojo.provide("dijit.PopupMenuItem");
  14334. dojo.declare("dijit.PopupMenuItem",
  14335. dijit.MenuItem,
  14336. {
  14337. _fillContent: function(){
  14338. // summary:
  14339. // When Menu is declared in markup, this code gets the menu label and
  14340. // the popup widget from the srcNodeRef.
  14341. // description:
  14342. // srcNodeRefinnerHTML contains both the menu item text and a popup widget
  14343. // The first part holds the menu item text and the second part is the popup
  14344. // example:
  14345. // | <div dojoType="dijit.PopupMenuItem">
  14346. // | <span>pick me</span>
  14347. // | <popup> ... </popup>
  14348. // | </div>
  14349. // tags:
  14350. // protected
  14351. if(this.srcNodeRef){
  14352. var nodes = dojo.query("*", this.srcNodeRef);
  14353. dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
  14354. // save pointer to srcNode so we can grab the drop down widget after it's instantiated
  14355. this.dropDownContainer = this.srcNodeRef;
  14356. }
  14357. },
  14358. startup: function(){
  14359. if(this._started){ return; }
  14360. this.inherited(arguments);
  14361. // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
  14362. // land now. move it to dojo.doc.body.
  14363. if(!this.popup){
  14364. var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
  14365. this.popup = dijit.byNode(node);
  14366. }
  14367. dojo.body().appendChild(this.popup.domNode);
  14368. this.popup.startup();
  14369. this.popup.domNode.style.display="none";
  14370. if(this.arrowWrapper){
  14371. dojo.style(this.arrowWrapper, "visibility", "");
  14372. }
  14373. dijit.setWaiState(this.focusNode, "haspopup", "true");
  14374. },
  14375. destroyDescendants: function(){
  14376. if(this.popup){
  14377. // Destroy the popup, unless it's already been destroyed. This can happen because
  14378. // the popup is a direct child of <body> even though it's logically my child.
  14379. if(!this.popup._destroyed){
  14380. this.popup.destroyRecursive();
  14381. }
  14382. delete this.popup;
  14383. }
  14384. this.inherited(arguments);
  14385. }
  14386. });
  14387. }
  14388. if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14389. dojo._hasResource["dijit.CheckedMenuItem"] = true;
  14390. dojo.provide("dijit.CheckedMenuItem");
  14391. dojo.declare("dijit.CheckedMenuItem",
  14392. dijit.MenuItem,
  14393. {
  14394. // summary:
  14395. // A checkbox-like menu item for toggling on and off
  14396. templateString: dojo.cache("dijit", "templates/CheckedMenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&nbsp;</td>\n</tr>\n"),
  14397. // checked: Boolean
  14398. // Our checked state
  14399. checked: false,
  14400. _setCheckedAttr: function(/*Boolean*/ checked){
  14401. // summary:
  14402. // Hook so attr('checked', bool) works.
  14403. // Sets the class and state for the check box.
  14404. dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
  14405. dijit.setWaiState(this.domNode, "checked", checked);
  14406. this._set("checked", checked);
  14407. },
  14408. onChange: function(/*Boolean*/ checked){
  14409. // summary:
  14410. // User defined function to handle check/uncheck events
  14411. // tags:
  14412. // callback
  14413. },
  14414. _onClick: function(/*Event*/ e){
  14415. // summary:
  14416. // Clicking this item just toggles its state
  14417. // tags:
  14418. // private
  14419. if(!this.disabled){
  14420. this.set("checked", !this.checked);
  14421. this.onChange(this.checked);
  14422. }
  14423. this.inherited(arguments);
  14424. }
  14425. });
  14426. }
  14427. if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14428. dojo._hasResource["dijit.MenuSeparator"] = true;
  14429. dojo.provide("dijit.MenuSeparator");
  14430. dojo.declare("dijit.MenuSeparator",
  14431. [dijit._Widget, dijit._Templated, dijit._Contained],
  14432. {
  14433. // summary:
  14434. // A line between two menu items
  14435. templateString: dojo.cache("dijit", "templates/MenuSeparator.html", "<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n"),
  14436. buildRendering: function(){
  14437. this.inherited(arguments);
  14438. dojo.setSelectable(this.domNode, false);
  14439. },
  14440. isFocusable: function(){
  14441. // summary:
  14442. // Override to always return false
  14443. // tags:
  14444. // protected
  14445. return false; // Boolean
  14446. }
  14447. });
  14448. }
  14449. if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14450. dojo._hasResource["dijit.Menu"] = true;
  14451. dojo.provide("dijit.Menu");
  14452. // "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
  14453. dojo.declare("dijit._MenuBase",
  14454. [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
  14455. {
  14456. // summary:
  14457. // Base class for Menu and MenuBar
  14458. // parentMenu: [readonly] Widget
  14459. // pointer to menu that displayed me
  14460. parentMenu: null,
  14461. // popupDelay: Integer
  14462. // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
  14463. popupDelay: 500,
  14464. startup: function(){
  14465. if(this._started){ return; }
  14466. dojo.forEach(this.getChildren(), function(child){ child.startup(); });
  14467. this.startupKeyNavChildren();
  14468. this.inherited(arguments);
  14469. },
  14470. onExecute: function(){
  14471. // summary:
  14472. // Attach point for notification about when a menu item has been executed.
  14473. // This is an internal mechanism used for Menus to signal to their parent to
  14474. // close them, because they are about to execute the onClick handler. In
  14475. // general developers should not attach to or override this method.
  14476. // tags:
  14477. // protected
  14478. },
  14479. onCancel: function(/*Boolean*/ closeAll){
  14480. // summary:
  14481. // Attach point for notification about when the user cancels the current menu
  14482. // This is an internal mechanism used for Menus to signal to their parent to
  14483. // close them. In general developers should not attach to or override this method.
  14484. // tags:
  14485. // protected
  14486. },
  14487. _moveToPopup: function(/*Event*/ evt){
  14488. // summary:
  14489. // This handles the right arrow key (left arrow key on RTL systems),
  14490. // which will either open a submenu, or move to the next item in the
  14491. // ancestor MenuBar
  14492. // tags:
  14493. // private
  14494. if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
  14495. this.focusedChild._onClick(evt);
  14496. }else{
  14497. var topMenu = this._getTopMenu();
  14498. if(topMenu && topMenu._isMenuBar){
  14499. topMenu.focusNext();
  14500. }
  14501. }
  14502. },
  14503. _onPopupHover: function(/*Event*/ evt){
  14504. // summary:
  14505. // This handler is called when the mouse moves over the popup.
  14506. // tags:
  14507. // private
  14508. // if the mouse hovers over a menu popup that is in pending-close state,
  14509. // then stop the close operation.
  14510. // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
  14511. if(this.currentPopup && this.currentPopup._pendingClose_timer){
  14512. var parentMenu = this.currentPopup.parentMenu;
  14513. // highlight the parent menu item pointing to this popup
  14514. if(parentMenu.focusedChild){
  14515. parentMenu.focusedChild._setSelected(false);
  14516. }
  14517. parentMenu.focusedChild = this.currentPopup.from_item;
  14518. parentMenu.focusedChild._setSelected(true);
  14519. // cancel the pending close
  14520. this._stopPendingCloseTimer(this.currentPopup);
  14521. }
  14522. },
  14523. onItemHover: function(/*MenuItem*/ item){
  14524. // summary:
  14525. // Called when cursor is over a MenuItem.
  14526. // tags:
  14527. // protected
  14528. // Don't do anything unless user has "activated" the menu by:
  14529. // 1) clicking it
  14530. // 2) opening it from a parent menu (which automatically focuses it)
  14531. if(this.isActive){
  14532. this.focusChild(item);
  14533. if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
  14534. this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
  14535. }
  14536. }
  14537. // if the user is mixing mouse and keyboard navigation,
  14538. // then the menu may not be active but a menu item has focus,
  14539. // but it's not the item that the mouse just hovered over.
  14540. // To avoid both keyboard and mouse selections, use the latest.
  14541. if(this.focusedChild){
  14542. this.focusChild(item);
  14543. }
  14544. this._hoveredChild = item;
  14545. },
  14546. _onChildBlur: function(item){
  14547. // summary:
  14548. // Called when a child MenuItem becomes inactive because focus
  14549. // has been removed from the MenuItem *and* it's descendant menus.
  14550. // tags:
  14551. // private
  14552. this._stopPopupTimer();
  14553. item._setSelected(false);
  14554. // Close all popups that are open and descendants of this menu
  14555. var itemPopup = item.popup;
  14556. if(itemPopup){
  14557. this._stopPendingCloseTimer(itemPopup);
  14558. itemPopup._pendingClose_timer = setTimeout(function(){
  14559. itemPopup._pendingClose_timer = null;
  14560. if(itemPopup.parentMenu){
  14561. itemPopup.parentMenu.currentPopup = null;
  14562. }
  14563. dijit.popup.close(itemPopup); // this calls onClose
  14564. }, this.popupDelay);
  14565. }
  14566. },
  14567. onItemUnhover: function(/*MenuItem*/ item){
  14568. // summary:
  14569. // Callback fires when mouse exits a MenuItem
  14570. // tags:
  14571. // protected
  14572. if(this.isActive){
  14573. this._stopPopupTimer();
  14574. }
  14575. if(this._hoveredChild == item){ this._hoveredChild = null; }
  14576. },
  14577. _stopPopupTimer: function(){
  14578. // summary:
  14579. // Cancels the popup timer because the user has stop hovering
  14580. // on the MenuItem, etc.
  14581. // tags:
  14582. // private
  14583. if(this.hover_timer){
  14584. clearTimeout(this.hover_timer);
  14585. this.hover_timer = null;
  14586. }
  14587. },
  14588. _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
  14589. // summary:
  14590. // Cancels the pending-close timer because the close has been preempted
  14591. // tags:
  14592. // private
  14593. if(popup._pendingClose_timer){
  14594. clearTimeout(popup._pendingClose_timer);
  14595. popup._pendingClose_timer = null;
  14596. }
  14597. },
  14598. _stopFocusTimer: function(){
  14599. // summary:
  14600. // Cancels the pending-focus timer because the menu was closed before focus occured
  14601. // tags:
  14602. // private
  14603. if(this._focus_timer){
  14604. clearTimeout(this._focus_timer);
  14605. this._focus_timer = null;
  14606. }
  14607. },
  14608. _getTopMenu: function(){
  14609. // summary:
  14610. // Returns the top menu in this chain of Menus
  14611. // tags:
  14612. // private
  14613. for(var top=this; top.parentMenu; top=top.parentMenu);
  14614. return top;
  14615. },
  14616. onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
  14617. // summary:
  14618. // Handle clicks on an item.
  14619. // tags:
  14620. // private
  14621. // this can't be done in _onFocus since the _onFocus events occurs asynchronously
  14622. if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
  14623. this._markActive();
  14624. }
  14625. this.focusChild(item);
  14626. if(item.disabled){ return false; }
  14627. if(item.popup){
  14628. this._openPopup();
  14629. }else{
  14630. // before calling user defined handler, close hierarchy of menus
  14631. // and restore focus to place it was when menu was opened
  14632. this.onExecute();
  14633. // user defined handler for click
  14634. item.onClick(evt);
  14635. }
  14636. },
  14637. _openPopup: function(){
  14638. // summary:
  14639. // Open the popup to the side of/underneath the current menu item
  14640. // tags:
  14641. // protected
  14642. this._stopPopupTimer();
  14643. var from_item = this.focusedChild;
  14644. if(!from_item){ return; } // the focused child lost focus since the timer was started
  14645. var popup = from_item.popup;
  14646. if(popup.isShowingNow){ return; }
  14647. if(this.currentPopup){
  14648. this._stopPendingCloseTimer(this.currentPopup);
  14649. dijit.popup.close(this.currentPopup);
  14650. }
  14651. popup.parentMenu = this;
  14652. popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
  14653. var self = this;
  14654. dijit.popup.open({
  14655. parent: this,
  14656. popup: popup,
  14657. around: from_item.domNode,
  14658. orient: this._orient || (this.isLeftToRight() ?
  14659. {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
  14660. {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
  14661. onCancel: function(){ // called when the child menu is canceled
  14662. // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
  14663. // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
  14664. self.focusChild(from_item); // put focus back on my node
  14665. self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
  14666. from_item._setSelected(true); // oops, _cleanUp() deselected the item
  14667. self.focusedChild = from_item; // and unset focusedChild
  14668. },
  14669. onExecute: dojo.hitch(this, "_cleanUp")
  14670. });
  14671. this.currentPopup = popup;
  14672. // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
  14673. popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
  14674. if(popup.focus){
  14675. // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
  14676. // if the cursor happens to collide with the popup, it will generate an onmouseover event
  14677. // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
  14678. // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
  14679. popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
  14680. this._focus_timer = null;
  14681. this.focus();
  14682. }), 0);
  14683. }
  14684. },
  14685. _markActive: function(){
  14686. // summary:
  14687. // Mark this menu's state as active.
  14688. // Called when this Menu gets focus from:
  14689. // 1) clicking it (mouse or via space/arrow key)
  14690. // 2) being opened by a parent menu.
  14691. // This is not called just from mouse hover.
  14692. // Focusing a menu via TAB does NOT automatically set isActive
  14693. // since TAB is a navigation operation and not a selection one.
  14694. // For Windows apps, pressing the ALT key focuses the menubar
  14695. // menus (similar to TAB navigation) but the menu is not active
  14696. // (ie no dropdown) until an item is clicked.
  14697. this.isActive = true;
  14698. dojo.replaceClass(this.domNode, "dijitMenuActive", "dijitMenuPassive");
  14699. },
  14700. onOpen: function(/*Event*/ e){
  14701. // summary:
  14702. // Callback when this menu is opened.
  14703. // This is called by the popup manager as notification that the menu
  14704. // was opened.
  14705. // tags:
  14706. // private
  14707. this.isShowingNow = true;
  14708. this._markActive();
  14709. },
  14710. _markInactive: function(){
  14711. // summary:
  14712. // Mark this menu's state as inactive.
  14713. this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
  14714. dojo.replaceClass(this.domNode, "dijitMenuPassive", "dijitMenuActive");
  14715. },
  14716. onClose: function(){
  14717. // summary:
  14718. // Callback when this menu is closed.
  14719. // This is called by the popup manager as notification that the menu
  14720. // was closed.
  14721. // tags:
  14722. // private
  14723. this._stopFocusTimer();
  14724. this._markInactive();
  14725. this.isShowingNow = false;
  14726. this.parentMenu = null;
  14727. },
  14728. _closeChild: function(){
  14729. // summary:
  14730. // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
  14731. // tags:
  14732. // private
  14733. this._stopPopupTimer();
  14734. var fromItem = this.focusedChild && this.focusedChild.from_item;
  14735. if(this.currentPopup){
  14736. // If focus is on my child menu then move focus to me,
  14737. // because IE doesn't like it when you display:none a node with focus
  14738. if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
  14739. this.focusedChild.focusNode.focus();
  14740. }
  14741. // Close all popups that are open and descendants of this menu
  14742. dijit.popup.close(this.currentPopup);
  14743. this.currentPopup = null;
  14744. }
  14745. if(this.focusedChild){ // unhighlight the focused item
  14746. this.focusedChild._setSelected(false);
  14747. this.focusedChild._onUnhover();
  14748. this.focusedChild = null;
  14749. }
  14750. },
  14751. _onItemFocus: function(/*MenuItem*/ item){
  14752. // summary:
  14753. // Called when child of this Menu gets focus from:
  14754. // 1) clicking it
  14755. // 2) tabbing into it
  14756. // 3) being opened by a parent menu.
  14757. // This is not called just from mouse hover.
  14758. if(this._hoveredChild && this._hoveredChild != item){
  14759. this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
  14760. }
  14761. },
  14762. _onBlur: function(){
  14763. // summary:
  14764. // Called when focus is moved away from this Menu and it's submenus.
  14765. // tags:
  14766. // protected
  14767. this._cleanUp();
  14768. this.inherited(arguments);
  14769. },
  14770. _cleanUp: function(){
  14771. // summary:
  14772. // Called when the user is done with this menu. Closes hierarchy of menus.
  14773. // tags:
  14774. // private
  14775. this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
  14776. if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
  14777. this._markInactive();
  14778. }
  14779. }
  14780. });
  14781. dojo.declare("dijit.Menu",
  14782. dijit._MenuBase,
  14783. {
  14784. // summary
  14785. // A context menu you can assign to multiple elements
  14786. // TODO: most of the code in here is just for context menu (right-click menu)
  14787. // support. In retrospect that should have been a separate class (dijit.ContextMenu).
  14788. // Split them for 2.0
  14789. constructor: function(){
  14790. this._bindings = [];
  14791. },
  14792. templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
  14793. baseClass: "dijitMenu",
  14794. // targetNodeIds: [const] String[]
  14795. // Array of dom node ids of nodes to attach to.
  14796. // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
  14797. targetNodeIds: [],
  14798. // contextMenuForWindow: [const] Boolean
  14799. // If true, right clicking anywhere on the window will cause this context menu to open.
  14800. // If false, must specify targetNodeIds.
  14801. contextMenuForWindow: false,
  14802. // leftClickToOpen: [const] Boolean
  14803. // If true, menu will open on left click instead of right click, similiar to a file menu.
  14804. leftClickToOpen: false,
  14805. // refocus: Boolean
  14806. // When this menu closes, re-focus the element which had focus before it was opened.
  14807. refocus: true,
  14808. postCreate: function(){
  14809. if(this.contextMenuForWindow){
  14810. this.bindDomNode(dojo.body());
  14811. }else{
  14812. // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
  14813. // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
  14814. // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
  14815. dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
  14816. }
  14817. var k = dojo.keys, l = this.isLeftToRight();
  14818. this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
  14819. this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
  14820. this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
  14821. },
  14822. _onKeyPress: function(/*Event*/ evt){
  14823. // summary:
  14824. // Handle keyboard based menu navigation.
  14825. // tags:
  14826. // protected
  14827. if(evt.ctrlKey || evt.altKey){ return; }
  14828. switch(evt.charOrCode){
  14829. case this._openSubMenuKey:
  14830. this._moveToPopup(evt);
  14831. dojo.stopEvent(evt);
  14832. break;
  14833. case this._closeSubMenuKey:
  14834. if(this.parentMenu){
  14835. if(this.parentMenu._isMenuBar){
  14836. this.parentMenu.focusPrev();
  14837. }else{
  14838. this.onCancel(false);
  14839. }
  14840. }else{
  14841. dojo.stopEvent(evt);
  14842. }
  14843. break;
  14844. }
  14845. },
  14846. // thanks burstlib!
  14847. _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
  14848. // summary:
  14849. // Returns the window reference of the passed iframe
  14850. // tags:
  14851. // private
  14852. var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
  14853. // Moz. TODO: is this available when defaultView isn't?
  14854. this._iframeContentDocument(iframe_el)['__parent__'] ||
  14855. (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
  14856. return win; // Window
  14857. },
  14858. _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
  14859. // summary:
  14860. // Returns a reference to the document object inside iframe_el
  14861. // tags:
  14862. // protected
  14863. var doc = iframe_el.contentDocument // W3
  14864. || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
  14865. || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
  14866. || null;
  14867. return doc; // HTMLDocument
  14868. },
  14869. bindDomNode: function(/*String|DomNode*/ node){
  14870. // summary:
  14871. // Attach menu to given node
  14872. node = dojo.byId(node);
  14873. var cn; // Connect node
  14874. // Support context menus on iframes. Rather than binding to the iframe itself we need
  14875. // to bind to the <body> node inside the iframe.
  14876. if(node.tagName.toLowerCase() == "iframe"){
  14877. var iframe = node,
  14878. win = this._iframeContentWindow(iframe);
  14879. cn = dojo.withGlobal(win, dojo.body);
  14880. }else{
  14881. // To capture these events at the top level, attach to <html>, not <body>.
  14882. // Otherwise right-click context menu just doesn't work.
  14883. cn = (node == dojo.body() ? dojo.doc.documentElement : node);
  14884. }
  14885. // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
  14886. var binding = {
  14887. node: node,
  14888. iframe: iframe
  14889. };
  14890. // Save info about binding in _bindings[], and make node itself record index(+1) into
  14891. // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
  14892. // start with a number, which fails on FF/safari.
  14893. dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
  14894. // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
  14895. // loading yet, in which case we need to wait for the onload event first, and then connect
  14896. // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
  14897. // we need to monitor keyboard events in addition to the oncontextmenu event.
  14898. var doConnects = dojo.hitch(this, function(cn){
  14899. return [
  14900. // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
  14901. // rather than shift-F10?
  14902. dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
  14903. // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
  14904. dojo.stopEvent(evt);
  14905. this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
  14906. }),
  14907. dojo.connect(cn, "onkeydown", this, function(evt){
  14908. if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
  14909. dojo.stopEvent(evt);
  14910. this._scheduleOpen(evt.target, iframe); // no coords - open near target node
  14911. }
  14912. })
  14913. ];
  14914. });
  14915. binding.connects = cn ? doConnects(cn) : [];
  14916. if(iframe){
  14917. // Setup handler to [re]bind to the iframe when the contents are initially loaded,
  14918. // and every time the contents change.
  14919. // Need to do this b/c we are actually binding to the iframe's <body> node.
  14920. // Note: can't use dojo.connect(), see #9609.
  14921. binding.onloadHandler = dojo.hitch(this, function(){
  14922. // want to remove old connections, but IE throws exceptions when trying to
  14923. // access the <body> node because it's already gone, or at least in a state of limbo
  14924. var win = this._iframeContentWindow(iframe);
  14925. cn = dojo.withGlobal(win, dojo.body);
  14926. binding.connects = doConnects(cn);
  14927. });
  14928. if(iframe.addEventListener){
  14929. iframe.addEventListener("load", binding.onloadHandler, false);
  14930. }else{
  14931. iframe.attachEvent("onload", binding.onloadHandler);
  14932. }
  14933. }
  14934. },
  14935. unBindDomNode: function(/*String|DomNode*/ nodeName){
  14936. // summary:
  14937. // Detach menu from given node
  14938. var node;
  14939. try{
  14940. node = dojo.byId(nodeName);
  14941. }catch(e){
  14942. // On IE the dojo.byId() call will get an exception if the attach point was
  14943. // the <body> node of an <iframe> that has since been reloaded (and thus the
  14944. // <body> node is in a limbo state of destruction.
  14945. return;
  14946. }
  14947. // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
  14948. var attrName = "_dijitMenu" + this.id;
  14949. if(node && dojo.hasAttr(node, attrName)){
  14950. var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
  14951. dojo.forEach(b.connects, dojo.disconnect);
  14952. // Remove listener for iframe onload events
  14953. var iframe = b.iframe;
  14954. if(iframe){
  14955. if(iframe.removeEventListener){
  14956. iframe.removeEventListener("load", b.onloadHandler, false);
  14957. }else{
  14958. iframe.detachEvent("onload", b.onloadHandler);
  14959. }
  14960. }
  14961. dojo.removeAttr(node, attrName);
  14962. delete this._bindings[bid];
  14963. }
  14964. },
  14965. _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
  14966. // summary:
  14967. // Set timer to display myself. Using a timer rather than displaying immediately solves
  14968. // two problems:
  14969. //
  14970. // 1. IE: without the delay, focus work in "open" causes the system
  14971. // context menu to appear in spite of stopEvent.
  14972. //
  14973. // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
  14974. // even after a dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the
  14975. // oncontextmenu event.)
  14976. if(!this._openTimer){
  14977. this._openTimer = setTimeout(dojo.hitch(this, function(){
  14978. delete this._openTimer;
  14979. this._openMyself({
  14980. target: target,
  14981. iframe: iframe,
  14982. coords: coords
  14983. });
  14984. }), 1);
  14985. }
  14986. },
  14987. _openMyself: function(args){
  14988. // summary:
  14989. // Internal function for opening myself when the user does a right-click or something similar.
  14990. // args:
  14991. // This is an Object containing:
  14992. // * target:
  14993. // The node that is being clicked
  14994. // * iframe:
  14995. // If an <iframe> is being clicked, iframe points to that iframe
  14996. // * coords:
  14997. // Put menu at specified x/y position in viewport, or if iframe is
  14998. // specified, then relative to iframe.
  14999. //
  15000. // _openMyself() formerly took the event object, and since various code references
  15001. // evt.target (after connecting to _openMyself()), using an Object for parameters
  15002. // (so that old code still works).
  15003. var target = args.target,
  15004. iframe = args.iframe,
  15005. coords = args.coords;
  15006. // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
  15007. // then near the node the menu is assigned to.
  15008. if(coords){
  15009. if(iframe){
  15010. // Specified coordinates are on <body> node of an <iframe>, convert to match main document
  15011. var od = target.ownerDocument,
  15012. ifc = dojo.position(iframe, true),
  15013. win = this._iframeContentWindow(iframe),
  15014. scroll = dojo.withGlobal(win, "_docScroll", dojo);
  15015. var cs = dojo.getComputedStyle(iframe),
  15016. tp = dojo._toPixelValue,
  15017. left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
  15018. top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
  15019. coords.x += ifc.x + left - scroll.x;
  15020. coords.y += ifc.y + top - scroll.y;
  15021. }
  15022. }else{
  15023. coords = dojo.position(target, true);
  15024. coords.x += 10;
  15025. coords.y += 10;
  15026. }
  15027. var self=this;
  15028. var savedFocus = dijit.getFocus(this);
  15029. function closeAndRestoreFocus(){
  15030. // user has clicked on a menu or popup
  15031. if(self.refocus){
  15032. dijit.focus(savedFocus);
  15033. }
  15034. dijit.popup.close(self);
  15035. }
  15036. dijit.popup.open({
  15037. popup: this,
  15038. x: coords.x,
  15039. y: coords.y,
  15040. onExecute: closeAndRestoreFocus,
  15041. onCancel: closeAndRestoreFocus,
  15042. orient: this.isLeftToRight() ? 'L' : 'R'
  15043. });
  15044. this.focus();
  15045. this._onBlur = function(){
  15046. this.inherited('_onBlur', arguments);
  15047. // Usually the parent closes the child widget but if this is a context
  15048. // menu then there is no parent
  15049. dijit.popup.close(this);
  15050. // don't try to restore focus; user has clicked another part of the screen
  15051. // and set focus there
  15052. };
  15053. },
  15054. uninitialize: function(){
  15055. dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
  15056. this.inherited(arguments);
  15057. }
  15058. }
  15059. );
  15060. }
  15061. if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15062. dojo._hasResource["dijit.form.Select"] = true;
  15063. dojo.provide("dijit.form.Select");
  15064. dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
  15065. // summary:
  15066. // An internally-used menu for dropdown that allows us a vertical scrollbar
  15067. buildRendering: function(){
  15068. // summary:
  15069. // Stub in our own changes, so that our domNode is not a table
  15070. // otherwise, we won't respond correctly to heights/overflows
  15071. this.inherited(arguments);
  15072. var o = (this.menuTableNode = this.domNode);
  15073. var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
  15074. if(o.parentNode){
  15075. o.parentNode.replaceChild(n, o);
  15076. }
  15077. dojo.removeClass(o, "dijitMenuTable");
  15078. n.className = o.className + " dijitSelectMenu";
  15079. o.className = "dijitReset dijitMenuTable";
  15080. dijit.setWaiRole(o,"listbox");
  15081. dijit.setWaiRole(n,"presentation");
  15082. n.appendChild(o);
  15083. },
  15084. postCreate: function(){
  15085. // summary:
  15086. // stop mousemove from selecting text on IE to be consistent with other browsers
  15087. this.inherited(arguments);
  15088. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  15089. },
  15090. resize: function(/*Object*/ mb){
  15091. // summary:
  15092. // Overridden so that we are able to handle resizing our
  15093. // internal widget. Note that this is not a "full" resize
  15094. // implementation - it only works correctly if you pass it a
  15095. // marginBox.
  15096. //
  15097. // mb: Object
  15098. // The margin box to set this dropdown to.
  15099. if(mb){
  15100. dojo.marginBox(this.domNode, mb);
  15101. if("w" in mb){
  15102. // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
  15103. // 100% is safer than a pixel value because there may be a scroll bar with
  15104. // browser/OS specific width.
  15105. this.menuTableNode.style.width = "100%";
  15106. }
  15107. }
  15108. }
  15109. });
  15110. dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
  15111. // summary:
  15112. // This is a "styleable" select box - it is basically a DropDownButton which
  15113. // can take a <select> as its input.
  15114. baseClass: "dijitSelect",
  15115. templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" dojoAttachPoint=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} dojoAttachPoint=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
  15116. // attributeMap: Object
  15117. // Add in our style to be applied to the focus node
  15118. attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
  15119. // required: Boolean
  15120. // Can be true or false, default is false.
  15121. required: false,
  15122. // state: String
  15123. // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
  15124. state: "",
  15125. // message: String
  15126. // Currently displayed error/prompt message
  15127. message: "",
  15128. // tooltipPosition: String[]
  15129. // See description of dijit.Tooltip.defaultPosition for details on this parameter.
  15130. tooltipPosition: [],
  15131. // emptyLabel: string
  15132. // What to display in an "empty" dropdown
  15133. emptyLabel: "&nbsp;",
  15134. // _isLoaded: Boolean
  15135. // Whether or not we have been loaded
  15136. _isLoaded: false,
  15137. // _childrenLoaded: Boolean
  15138. // Whether or not our children have been loaded
  15139. _childrenLoaded: false,
  15140. _fillContent: function(){
  15141. // summary:
  15142. // Set the value to be the first, or the selected index
  15143. this.inherited(arguments);
  15144. // set value from selected option
  15145. if(this.options.length && !this.value && this.srcNodeRef){
  15146. var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
  15147. this.value = this.options[si >= 0 ? si : 0].value;
  15148. }
  15149. // Create the dropDown widget
  15150. this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
  15151. dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
  15152. },
  15153. _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
  15154. // summary:
  15155. // For the given option, return the menu item that should be
  15156. // used to display it. This can be overridden as needed
  15157. if(!option.value && !option.label){
  15158. // We are a separator (no label set for it)
  15159. return new dijit.MenuSeparator();
  15160. }else{
  15161. // Just a regular menu option
  15162. var click = dojo.hitch(this, "_setValueAttr", option);
  15163. var item = new dijit.MenuItem({
  15164. option: option,
  15165. label: option.label || this.emptyLabel,
  15166. onClick: click,
  15167. disabled: option.disabled || false
  15168. });
  15169. dijit.setWaiRole(item.focusNode, "listitem");
  15170. return item;
  15171. }
  15172. },
  15173. _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
  15174. // summary:
  15175. // For the given option, add an option to our dropdown.
  15176. // If the option doesn't have a value, then a separator is added
  15177. // in that place.
  15178. if(this.dropDown){
  15179. this.dropDown.addChild(this._getMenuItemForOption(option));
  15180. }
  15181. },
  15182. _getChildren: function(){
  15183. if(!this.dropDown){
  15184. return [];
  15185. }
  15186. return this.dropDown.getChildren();
  15187. },
  15188. _loadChildren: function(/*Boolean*/ loadMenuItems){
  15189. // summary:
  15190. // Resets the menu and the length attribute of the button - and
  15191. // ensures that the label is appropriately set.
  15192. // loadMenuItems: Boolean
  15193. // actually loads the child menu items - we only do this when we are
  15194. // populating for showing the dropdown.
  15195. if(loadMenuItems === true){
  15196. // this.inherited destroys this.dropDown's child widgets (MenuItems).
  15197. // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
  15198. // issues later in _setSelected). (see #10296)
  15199. if(this.dropDown){
  15200. delete this.dropDown.focusedChild;
  15201. }
  15202. if(this.options.length){
  15203. this.inherited(arguments);
  15204. }else{
  15205. // Drop down menu is blank but add one blank entry just so something appears on the screen
  15206. // to let users know that they are no choices (mimicing native select behavior)
  15207. dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
  15208. var item = new dijit.MenuItem({label: "&nbsp;"});
  15209. this.dropDown.addChild(item);
  15210. }
  15211. }else{
  15212. this._updateSelection();
  15213. }
  15214. this._isLoaded = false;
  15215. this._childrenLoaded = true;
  15216. if(!this._loadingStore){
  15217. // Don't call this if we are loading - since we will handle it later
  15218. this._setValueAttr(this.value);
  15219. }
  15220. },
  15221. _setValueAttr: function(value){
  15222. this.inherited(arguments);
  15223. dojo.attr(this.valueNode, "value", this.get("value"));
  15224. },
  15225. _setDisplay: function(/*String*/ newDisplay){
  15226. // summary:
  15227. // sets the display for the given value (or values)
  15228. var lbl = newDisplay || this.emptyLabel;
  15229. this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
  15230. dijit.setWaiState(this.focusNode, "valuetext", lbl);
  15231. },
  15232. validate: function(/*Boolean*/ isFocused){
  15233. // summary:
  15234. // Called by oninit, onblur, and onkeypress.
  15235. // description:
  15236. // Show missing or invalid messages if appropriate, and highlight textbox field.
  15237. // Used when a select is initially set to no value and the user is required to
  15238. // set the value.
  15239. var isValid = this.isValid(isFocused);
  15240. this._set("state", isValid ? "" : "Error");
  15241. dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
  15242. var message = isValid ? "" : this._missingMsg;
  15243. if(this.message !== message){
  15244. this._set("message", message);
  15245. dijit.hideTooltip(this.domNode);
  15246. if(message){
  15247. dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
  15248. }
  15249. }
  15250. return isValid;
  15251. },
  15252. isValid: function(/*Boolean*/ isFocused){
  15253. // summary:
  15254. // Whether or not this is a valid value. The only way a Select
  15255. // can be invalid is when it's required but nothing is selected.
  15256. return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
  15257. },
  15258. reset: function(){
  15259. // summary:
  15260. // Overridden so that the state will be cleared.
  15261. this.inherited(arguments);
  15262. dijit.hideTooltip(this.domNode);
  15263. this._set("state", "");
  15264. this._set("message", "")
  15265. },
  15266. postMixInProperties: function(){
  15267. // summary:
  15268. // set the missing message
  15269. this.inherited(arguments);
  15270. this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
  15271. this.lang).missingMessage;
  15272. },
  15273. postCreate: function(){
  15274. // summary:
  15275. // stop mousemove from selecting text on IE to be consistent with other browsers
  15276. this.inherited(arguments);
  15277. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  15278. },
  15279. _setStyleAttr: function(/*String||Object*/ value){
  15280. this.inherited(arguments);
  15281. dojo.toggleClass(this.domNode, this.baseClass + "FixedWidth", !!this.tableNode.style.width);
  15282. },
  15283. isLoaded: function(){
  15284. return this._isLoaded;
  15285. },
  15286. loadDropDown: function(/*Function*/ loadCallback){
  15287. // summary:
  15288. // populates the menu
  15289. this._loadChildren(true);
  15290. this._isLoaded = true;
  15291. loadCallback();
  15292. },
  15293. closeDropDown: function(){
  15294. // overriding _HasDropDown.closeDropDown()
  15295. this.inherited(arguments);
  15296. if(this.dropDown && this.dropDown.menuTableNode){
  15297. // Erase possible width: 100% setting from _SelectMenu.resize().
  15298. // Leaving it would interfere with the next openDropDown() call, which
  15299. // queries the natural size of the drop down.
  15300. this.dropDown.menuTableNode.style.width = "";
  15301. }
  15302. },
  15303. uninitialize: function(preserveDom){
  15304. if(this.dropDown && !this.dropDown._destroyed){
  15305. this.dropDown.destroyRecursive(preserveDom);
  15306. delete this.dropDown;
  15307. }
  15308. this.inherited(arguments);
  15309. }
  15310. });
  15311. }
  15312. if(!dojo._hasResource["dijit._editor.plugins.LinkDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15313. dojo._hasResource["dijit._editor.plugins.LinkDialog"] = true;
  15314. dojo.provide("dijit._editor.plugins.LinkDialog");
  15315. dojo.declare("dijit._editor.plugins.LinkDialog", dijit._editor._Plugin, {
  15316. // summary:
  15317. // This plugin provides the basis for an 'anchor' (link) dialog and an extension of it
  15318. // provides the image link dialog.
  15319. //
  15320. // description:
  15321. // The command provided by this plugin is:
  15322. // * createLink
  15323. // Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton
  15324. // (which triggers a TooltipDialog).
  15325. buttonClass: dijit.form.DropDownButton,
  15326. // Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.
  15327. useDefaultCommand: false,
  15328. // urlRegExp: [protected] String
  15329. // Used for validating input as correct URL. While file:// urls are not terribly
  15330. // useful, they are technically valid.
  15331. urlRegExp: "((https?|ftps?|file)\\://|\./|/|)(/[a-zA-Z]{1,1}:/|)(((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)*(?:[a-zA-Z](?:[-\\da-zA-Z]{0,80}[\\da-zA-Z])?)\\.?)|(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])|(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]|(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]|(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])|0[xX]0*[\\da-fA-F]{1,8}|([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}\\:){6}((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])))(\\:\\d+)?(/(?:[^?#\\s/]+/)*(?:[^?#\\s/]{0,}(?:\\?[^?#\\s/]*)?(?:#.*)?)?)?",
  15332. // emailRegExp: [protected] String
  15333. // Used for validating input as correct email address. Taken from dojox.validate
  15334. emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +
  15335. "((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)+(?:[a-zA-Z](?:[-\\da-zA-Z]{0,6}[\\da-zA-Z])?)\\.?)|localhost|^[^-][a-zA-Z0-9_-]*>?", // host.
  15336. // htmlTemplate: [protected] String
  15337. // String used for templating the HTML to insert at the desired point.
  15338. htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +
  15339. " target=\"${targetSelect}\"" +
  15340. ">${textInput}</a>",
  15341. // tag: [protected] String
  15342. // Tag used for the link type.
  15343. tag: "a",
  15344. // _hostRxp [private] RegExp
  15345. // Regular expression used to validate url fragments (ip address, hostname, etc)
  15346. _hostRxp: new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
  15347. // _userAtRxp [private] RegExp
  15348. // Regular expression used to validate e-mail address fragment.
  15349. _userAtRxp: new RegExp("^([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+@", "i"),
  15350. // linkDialogTemplate: [protected] String
  15351. // Template for contents of TooltipDialog to pick URL
  15352. linkDialogTemplate: [
  15353. "<table><tr><td>",
  15354. "<label for='${id}_urlInput'>${url}</label>",
  15355. "</td><td>",
  15356. "<input dojoType='dijit.form.ValidationTextBox' required='true' " +
  15357. "id='${id}_urlInput' name='urlInput' intermediateChanges='true'/>",
  15358. "</td></tr><tr><td>",
  15359. "<label for='${id}_textInput'>${text}</label>",
  15360. "</td><td>",
  15361. "<input dojoType='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +
  15362. "name='textInput' intermediateChanges='true'/>",
  15363. "</td></tr><tr><td>",
  15364. "<label for='${id}_targetSelect'>${target}</label>",
  15365. "</td><td>",
  15366. "<select id='${id}_targetSelect' name='targetSelect' dojoType='dijit.form.Select'>",
  15367. "<option selected='selected' value='_self'>${currentWindow}</option>",
  15368. "<option value='_blank'>${newWindow}</option>",
  15369. "<option value='_top'>${topWindow}</option>",
  15370. "<option value='_parent'>${parentWindow}</option>",
  15371. "</select>",
  15372. "</td></tr><tr><td colspan='2'>",
  15373. "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  15374. "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  15375. "</td></tr></table>"
  15376. ].join(""),
  15377. _initButton: function(){
  15378. // Override _Plugin._initButton() to initialize DropDownButton and TooltipDialog.
  15379. var _this = this;
  15380. this.tag = this.command == 'insertImage' ? 'img' : 'a';
  15381. var messages = dojo.mixin(dojo.i18n.getLocalization("dijit", "common", this.lang),
  15382. dojo.i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));
  15383. var dropDown = (this.dropDown = new dijit.TooltipDialog({
  15384. title: messages[this.command + "Title"],
  15385. execute: dojo.hitch(this, "setValue"),
  15386. onOpen: function(){
  15387. _this._onOpenDialog();
  15388. dijit.TooltipDialog.prototype.onOpen.apply(this, arguments);
  15389. },
  15390. onCancel: function(){
  15391. setTimeout(dojo.hitch(_this, "_onCloseDialog"),0);
  15392. }
  15393. }));
  15394. messages.urlRegExp = this.urlRegExp;
  15395. messages.id = dijit.getUniqueId(this.editor.id);
  15396. this._uniqueId = messages.id;
  15397. this._setContent(dropDown.title +
  15398. "<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +
  15399. dojo.string.substitute(this.linkDialogTemplate, messages));
  15400. dropDown.startup();
  15401. this._urlInput = dijit.byId(this._uniqueId + "_urlInput");
  15402. this._textInput = dijit.byId(this._uniqueId + "_textInput");
  15403. this._setButton = dijit.byId(this._uniqueId + "_setButton");
  15404. this.connect(dijit.byId(this._uniqueId + "_cancelButton"), "onClick", function(){
  15405. this.dropDown.onCancel();
  15406. });
  15407. if(this._urlInput){
  15408. this.connect(this._urlInput, "onChange", "_checkAndFixInput");
  15409. }
  15410. if(this._textInput){
  15411. this.connect(this._textInput, "onChange", "_checkAndFixInput");
  15412. }
  15413. // Build up the dual check for http/https/file:, and mailto formats.
  15414. this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");
  15415. this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");
  15416. this._urlInput.isValid = dojo.hitch(this, function(){
  15417. // Function over-ride of isValid to test if the input matches a url or a mailto style link.
  15418. var value = this._urlInput.get("value");
  15419. return this._urlRegExp.test(value) || this._emailRegExp.test(value);
  15420. });
  15421. this._connectTagEvents();
  15422. this.inherited(arguments);
  15423. },
  15424. _checkAndFixInput: function(){
  15425. // summary:
  15426. // A function to listen for onChange events and test the input contents
  15427. // for valid information, such as valid urls with http/https/ftp and if
  15428. // not present, try and guess if the input url is relative or not, and if
  15429. // not, append http:// to it. Also validates other fields as determined by
  15430. // the internal _isValid function.
  15431. var self = this;
  15432. var url = this._urlInput.get("value");
  15433. var fixupUrl = function(url){
  15434. var appendHttp = false;
  15435. var appendMailto = false;
  15436. if(url && url.length > 1){
  15437. url = dojo.trim(url);
  15438. if(url.indexOf("mailto:") !== 0){
  15439. if(url.indexOf("/") > 0){
  15440. if(url.indexOf("://") === -1){
  15441. // Check that it doesn't start with / or ./, which would
  15442. // imply 'target server relativeness'
  15443. if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){
  15444. if(self._hostRxp.test(url)){
  15445. appendHttp = true;
  15446. }
  15447. }
  15448. }
  15449. }else if(self._userAtRxp.test(url)){
  15450. // If it looks like a foo@, append a mailto.
  15451. appendMailto = true;
  15452. }
  15453. }
  15454. }
  15455. if(appendHttp){
  15456. self._urlInput.set("value", "http://" + url);
  15457. }
  15458. if(appendMailto){
  15459. self._urlInput.set("value", "mailto:" + url);
  15460. }
  15461. self._setButton.set("disabled", !self._isValid());
  15462. };
  15463. if(this._delayedCheck){
  15464. clearTimeout(this._delayedCheck);
  15465. this._delayedCheck = null;
  15466. }
  15467. this._delayedCheck = setTimeout(function(){
  15468. fixupUrl(url);
  15469. }, 250);
  15470. },
  15471. _connectTagEvents: function(){
  15472. // summary:
  15473. // Over-ridable function that connects tag specific events.
  15474. this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  15475. this.connect(this.editor.editNode, "ondblclick", this._onDblClick);
  15476. }));
  15477. },
  15478. _isValid: function(){
  15479. // summary:
  15480. // Internal function to allow validating of the inputs
  15481. // for a link to determine if set should be disabled or not
  15482. // tags:
  15483. // protected
  15484. return this._urlInput.isValid() && this._textInput.isValid();
  15485. },
  15486. _setContent: function(staticPanel){
  15487. // summary:
  15488. // Helper for _initButton above. Not sure why it's a separate method.
  15489. this.dropDown.set({
  15490. parserScope: "dojo", // make parser search for dojoType/data-dojo-type even if page is multi-version
  15491. content: staticPanel
  15492. });
  15493. },
  15494. _checkValues: function(args){
  15495. // summary:
  15496. // Function to check the values in args and 'fix' them up as needed.
  15497. // args: Object
  15498. // Content being set.
  15499. // tags:
  15500. // protected
  15501. if(args && args.urlInput){
  15502. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  15503. }
  15504. return args;
  15505. },
  15506. setValue: function(args){
  15507. // summary:
  15508. // Callback from the dialog when user presses "set" button.
  15509. // tags:
  15510. // private
  15511. //TODO: prevent closing popup if the text is empty
  15512. this._onCloseDialog();
  15513. if(dojo.isIE < 9){ //see #4151
  15514. var sel = dijit.range.getSelection(this.editor.window);
  15515. var range = sel.getRangeAt(0);
  15516. var a = range.endContainer;
  15517. if(a.nodeType === 3){
  15518. // Text node, may be the link contents, so check parent.
  15519. // This plugin doesn't really support nested HTML elements
  15520. // in the link, it assumes all link content is text.
  15521. a = a.parentNode;
  15522. }
  15523. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  15524. // Stll nothing, one last thing to try on IE, as it might be 'img'
  15525. // and thus considered a control.
  15526. a = dojo.withGlobal(this.editor.window,
  15527. "getSelectedElement", dijit._editor.selection, [this.tag]);
  15528. }
  15529. if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){
  15530. // Okay, we do have a match. IE, for some reason, sometimes pastes before
  15531. // instead of removing the targetted paste-over element, so we unlink the
  15532. // old one first. If we do not the <a> tag remains, but it has no content,
  15533. // so isn't readily visible (but is wrong for the action).
  15534. if(this.editor.queryCommandEnabled("unlink")){
  15535. // Select all the link childent, then unlink. The following insert will
  15536. // then replace the selected text.
  15537. dojo.withGlobal(this.editor.window,
  15538. "selectElementChildren", dijit._editor.selection, [a]);
  15539. this.editor.execCommand("unlink");
  15540. }
  15541. }
  15542. }
  15543. // make sure values are properly escaped, etc.
  15544. args = this._checkValues(args);
  15545. this.editor.execCommand('inserthtml',
  15546. dojo.string.substitute(this.htmlTemplate, args));
  15547. // IE sometimes leaves a blank link, so we need to fix it up.
  15548. // Go ahead and do this for everyone just to avoid blank links
  15549. // in the page.
  15550. dojo.query("a", this.editor.document).forEach(function(a){
  15551. if(!a.innerHTML && !domAttr.has(a, "name")){
  15552. // Remove empty anchors that do not have "name" set.
  15553. // Empty ones with a name set could be a hidden hash
  15554. // anchor.
  15555. a.parentNode.removeChild(a);
  15556. }
  15557. }, this);
  15558. },
  15559. _onCloseDialog: function(){
  15560. // summary:
  15561. // Handler for close event on the dialog
  15562. this.editor.focus();
  15563. },
  15564. _getCurrentValues: function(a){
  15565. // summary:
  15566. // Over-ride for getting the values to set in the dropdown.
  15567. // a:
  15568. // The anchor/link to process for data for the dropdown.
  15569. // tags:
  15570. // protected
  15571. var url, text, target;
  15572. if(a && a.tagName.toLowerCase() === this.tag){
  15573. url = a.getAttribute('_djrealurl') || a.getAttribute('href');
  15574. target = a.getAttribute('target') || "_self";
  15575. text = a.textContent || a.innerText;
  15576. dojo.withGlobal(this.editor.window, "selectElement", dijit._editor.selection, [a, true]);
  15577. }else{
  15578. text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
  15579. }
  15580. return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;
  15581. },
  15582. _onOpenDialog: function(){
  15583. // summary:
  15584. // Handler for when the dialog is opened.
  15585. // If the caret is currently in a URL then populate the URL's info into the dialog.
  15586. var a;
  15587. if(dojo.isIE < 9){
  15588. // IE is difficult to select the element in, using the range unified
  15589. // API seems to work reasonably well.
  15590. var sel = dijit.range.getSelection(this.editor.window);
  15591. var range = sel.getRangeAt(0);
  15592. a = range.endContainer;
  15593. if(a.nodeType === 3){
  15594. // Text node, may be the link contents, so check parent.
  15595. // This plugin doesn't really support nested HTML elements
  15596. // in the link, it assumes all link content is text.
  15597. a = a.parentNode;
  15598. }
  15599. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  15600. // Stll nothing, one last thing to try on IE, as it might be 'img'
  15601. // and thus considered a control.
  15602. a = dojo.withGlobal(this.editor.window,
  15603. "getSelectedElement", dijit._editor.selection, [this.tag]);
  15604. }
  15605. }else{
  15606. a = dojo.withGlobal(this.editor.window,
  15607. "getAncestorElement", dijit._editor.selection, [this.tag]);
  15608. }
  15609. this.dropDown.reset();
  15610. this._setButton.set("disabled", true);
  15611. this.dropDown.set("value", this._getCurrentValues(a));
  15612. },
  15613. _onDblClick: function(e){
  15614. // summary:
  15615. // Function to define a behavior on double clicks on the element
  15616. // type this dialog edits to select it and pop up the editor
  15617. // dialog.
  15618. // e: Object
  15619. // The double-click event.
  15620. // tags:
  15621. // protected.
  15622. if(e && e.target){
  15623. var t = e.target;
  15624. var tg = t.tagName? t.tagName.toLowerCase() : "";
  15625. if(tg === this.tag && dojo.attr(t,"href")){
  15626. dojo.withGlobal(this.editor.window,
  15627. "selectElement",
  15628. dijit._editor.selection, [t]);
  15629. this.editor.onDisplayChanged();
  15630. setTimeout(dojo.hitch(this, function(){
  15631. // Focus shift outside the event handler.
  15632. // IE doesn't like focus changes in event handles.
  15633. this.button.set("disabled", false);
  15634. this.button.openDropDown();
  15635. }), 10);
  15636. }
  15637. }
  15638. }
  15639. });
  15640. dojo.declare("dijit._editor.plugins.ImgLinkDialog", [dijit._editor.plugins.LinkDialog], {
  15641. // summary:
  15642. // This plugin extends LinkDialog and adds in a plugin for handling image links.
  15643. // provides the image link dialog.
  15644. //
  15645. // description:
  15646. // The command provided by this plugin is:
  15647. // * insertImage
  15648. // linkDialogTemplate: [protected] String
  15649. // Over-ride for template since img dialog doesn't need target that anchor tags may.
  15650. linkDialogTemplate: [
  15651. "<table><tr><td>",
  15652. "<label for='${id}_urlInput'>${url}</label>",
  15653. "</td><td>",
  15654. "<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +
  15655. "required='true' id='${id}_urlInput' name='urlInput' intermediateChanges='true'/>",
  15656. "</td></tr><tr><td>",
  15657. "<label for='${id}_textInput'>${text}</label>",
  15658. "</td><td>",
  15659. "<input dojoType='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +
  15660. "name='textInput' intermediateChanges='true'/>",
  15661. "</td></tr><tr><td>",
  15662. "</td><td>",
  15663. "</td></tr><tr><td colspan='2'>",
  15664. "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  15665. "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  15666. "</td></tr></table>"
  15667. ].join(""),
  15668. // htmlTemplate: [protected] String
  15669. // String used for templating the <img> HTML to insert at the desired point.
  15670. htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",
  15671. // tag: [protected] String
  15672. // Tag used for the link type (img).
  15673. tag: "img",
  15674. _getCurrentValues: function(img){
  15675. // summary:
  15676. // Over-ride for getting the values to set in the dropdown.
  15677. // a:
  15678. // The anchor/link to process for data for the dropdown.
  15679. // tags:
  15680. // protected
  15681. var url, text;
  15682. if(img && img.tagName.toLowerCase() === this.tag){
  15683. url = img.getAttribute('_djrealurl') || img.getAttribute('src');
  15684. text = img.getAttribute('alt');
  15685. dojo.withGlobal(this.editor.window,
  15686. "selectElement", dijit._editor.selection, [img, true]);
  15687. }else{
  15688. text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
  15689. }
  15690. return {urlInput: url || '', textInput: text || ''}; //Object;
  15691. },
  15692. _isValid: function(){
  15693. // summary:
  15694. // Over-ride for images. You can have alt text of blank, it is valid.
  15695. // tags:
  15696. // protected
  15697. return this._urlInput.isValid();
  15698. },
  15699. _connectTagEvents: function(){
  15700. // summary:
  15701. // Over-ridable function that connects tag specific events.
  15702. this.inherited(arguments);
  15703. this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  15704. // Use onmousedown instead of onclick. Seems that IE eats the first onclick
  15705. // to wrap it in a selector box, then the second one acts as onclick. See #10420
  15706. this.connect(this.editor.editNode, "onmousedown", this._selectTag);
  15707. }));
  15708. },
  15709. _selectTag: function(e){
  15710. // summary:
  15711. // A simple event handler that lets me select an image if it is clicked on.
  15712. // makes it easier to select images in a standard way across browsers. Otherwise
  15713. // selecting an image for edit becomes difficult.
  15714. // e: Event
  15715. // The mousedown event.
  15716. // tags:
  15717. // private
  15718. if(e && e.target){
  15719. var t = e.target;
  15720. var tg = t.tagName? t.tagName.toLowerCase() : "";
  15721. if(tg === this.tag){
  15722. dojo.withGlobal(this.editor.window,
  15723. "selectElement",
  15724. dijit._editor.selection, [t]);
  15725. }
  15726. }
  15727. },
  15728. _checkValues: function(args){
  15729. // summary:
  15730. // Function to check the values in args and 'fix' them up as needed
  15731. // (special characters in the url or alt text)
  15732. // args: Object
  15733. // Content being set.
  15734. // tags:
  15735. // protected
  15736. if(args && args.urlInput){
  15737. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  15738. }
  15739. if(args && args.textInput){
  15740. args.textInput = args.textInput.replace(/"/g, "&quot;");
  15741. }
  15742. return args;
  15743. },
  15744. _onDblClick: function(e){
  15745. // summary:
  15746. // Function to define a behavior on double clicks on the element
  15747. // type this dialog edits to select it and pop up the editor
  15748. // dialog.
  15749. // e: Object
  15750. // The double-click event.
  15751. // tags:
  15752. // protected.
  15753. if(e && e.target){
  15754. var t = e.target;
  15755. var tg = t.tagName? t.tagName.toLowerCase() : "";
  15756. if(tg === this.tag && dojo.attr(t,"src")){
  15757. dojo.withGlobal(this.editor.window,
  15758. "selectElement",
  15759. dijit._editor.selection, [t]);
  15760. this.editor.onDisplayChanged();
  15761. setTimeout(dojo.hitch(this, function(){
  15762. // Focus shift outside the event handler.
  15763. // IE doesn't like focus changes in event handles.
  15764. this.button.set("disabled", false);
  15765. this.button.openDropDown();
  15766. }), 10);
  15767. }
  15768. }
  15769. }
  15770. });
  15771. // Register this plugin.
  15772. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  15773. if(o.plugin){ return; }
  15774. switch(o.args.name){
  15775. case "createLink":
  15776. o.plugin = new dijit._editor.plugins.LinkDialog({command: o.args.name});
  15777. break;
  15778. case "insertImage":
  15779. o.plugin = new dijit._editor.plugins.ImgLinkDialog({command: o.args.name});
  15780. break;
  15781. }
  15782. });
  15783. }
  15784. if(!dojo._hasResource["dijit.MenuBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15785. dojo._hasResource["dijit.MenuBar"] = true;
  15786. dojo.provide("dijit.MenuBar");
  15787. dojo.declare("dijit.MenuBar", dijit._MenuBase, {
  15788. // summary:
  15789. // A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
  15790. templateString: dojo.cache("dijit", "templates/MenuBar.html", "<div class=\"dijitMenuBar dijitMenuPassive\" dojoAttachPoint=\"containerNode\" role=\"menubar\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress: _onKeyPress\"></div>\n"),
  15791. baseClass: "dijitMenuBar",
  15792. // _isMenuBar: [protected] Boolean
  15793. // This is a MenuBar widget, not a (vertical) Menu widget.
  15794. _isMenuBar: true,
  15795. postCreate: function(){
  15796. var k = dojo.keys, l = this.isLeftToRight();
  15797. this.connectKeyNavHandlers(
  15798. l ? [k.LEFT_ARROW] : [k.RIGHT_ARROW],
  15799. l ? [k.RIGHT_ARROW] : [k.LEFT_ARROW]
  15800. );
  15801. // parameter to dijit.popup.open() about where to put popup (relative to this.domNode)
  15802. this._orient = this.isLeftToRight() ? {BL: 'TL'} : {BR: 'TR'};
  15803. },
  15804. focusChild: function(item){
  15805. // overload focusChild so that whenever the focus is moved to a new item,
  15806. // check the previous focused whether it has its popup open, if so, after
  15807. // focusing the new item, open its submenu immediately
  15808. var prev_item = this.focusedChild,
  15809. showpopup = prev_item && prev_item.popup && prev_item.popup.isShowingNow;
  15810. this.inherited(arguments);
  15811. if(showpopup && item.popup && !item.disabled){
  15812. this._openPopup(); // TODO: on down arrow, _openPopup() is called here and in onItemClick()
  15813. }
  15814. },
  15815. _onKeyPress: function(/*Event*/ evt){
  15816. // summary:
  15817. // Handle keyboard based menu navigation.
  15818. // tags:
  15819. // protected
  15820. if(evt.ctrlKey || evt.altKey){ return; }
  15821. switch(evt.charOrCode){
  15822. case dojo.keys.DOWN_ARROW:
  15823. this._moveToPopup(evt);
  15824. dojo.stopEvent(evt);
  15825. }
  15826. },
  15827. onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
  15828. // summary:
  15829. // Handle clicks on an item. Cancels a dropdown if already open.
  15830. // tags:
  15831. // private
  15832. if(item.popup && item.popup.isShowingNow){
  15833. item.popup.onCancel();
  15834. }else{
  15835. this.inherited(arguments);
  15836. }
  15837. }
  15838. });
  15839. }
  15840. if(!dojo._hasResource["dijit.MenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15841. dojo._hasResource["dijit.MenuBarItem"] = true;
  15842. dojo.provide("dijit.MenuBarItem");
  15843. dojo.declare("dijit._MenuBarItemMixin", null, {
  15844. templateString: dojo.cache("dijit", "templates/MenuBarItem.html", "<div class=\"dijitReset dijitInline dijitMenuItem dijitMenuItemLabel\" dojoAttachPoint=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<span dojoAttachPoint=\"containerNode\"></span>\n</div>\n"),
  15845. // overriding attributeMap because we don't have icon
  15846. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  15847. label: { node: "containerNode", type: "innerHTML" }
  15848. })
  15849. });
  15850. dojo.declare("dijit.MenuBarItem", [dijit.MenuItem, dijit._MenuBarItemMixin], {
  15851. // summary:
  15852. // Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
  15853. });
  15854. }
  15855. if(!dojo._hasResource["dijit.PopupMenuBarItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15856. dojo._hasResource["dijit.PopupMenuBarItem"] = true;
  15857. dojo.provide("dijit.PopupMenuBarItem");
  15858. dojo.declare("dijit.PopupMenuBarItem", [dijit.PopupMenuItem, dijit._MenuBarItemMixin], {
  15859. // summary:
  15860. // Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
  15861. });
  15862. }
  15863. if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15864. dojo._hasResource["dojo.number"] = true;
  15865. dojo.provide("dojo.number");
  15866. dojo.getObject("number", true, dojo);
  15867. /*=====
  15868. dojo.number = {
  15869. // summary: localized formatting and parsing routines for Number
  15870. }
  15871. dojo.number.__FormatOptions = function(){
  15872. // pattern: String?
  15873. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  15874. // with this string. Default value is based on locale. Overriding this property will defeat
  15875. // localization. Literal characters in patterns are not supported.
  15876. // type: String?
  15877. // choose a format type based on the locale from the following:
  15878. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  15879. // places: Number?
  15880. // fixed number of decimal places to show. This overrides any
  15881. // information in the provided pattern.
  15882. // round: Number?
  15883. // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
  15884. // means do not round.
  15885. // locale: String?
  15886. // override the locale used to determine formatting rules
  15887. // fractional: Boolean?
  15888. // If false, show no decimal places, overriding places and pattern settings.
  15889. this.pattern = pattern;
  15890. this.type = type;
  15891. this.places = places;
  15892. this.round = round;
  15893. this.locale = locale;
  15894. this.fractional = fractional;
  15895. }
  15896. =====*/
  15897. dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
  15898. // summary:
  15899. // Format a Number as a String, using locale-specific settings
  15900. // description:
  15901. // Create a string from a Number using a known localized pattern.
  15902. // Formatting patterns appropriate to the locale are chosen from the
  15903. // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
  15904. // delimiters.
  15905. // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
  15906. // value:
  15907. // the number to be formatted
  15908. options = dojo.mixin({}, options || {});
  15909. var locale = dojo.i18n.normalizeLocale(options.locale),
  15910. bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
  15911. options.customs = bundle;
  15912. var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
  15913. if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
  15914. return dojo.number._applyPattern(value, pattern, options); // String
  15915. };
  15916. //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
  15917. dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
  15918. dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
  15919. // summary:
  15920. // Apply pattern to format value as a string using options. Gives no
  15921. // consideration to local customs.
  15922. // value:
  15923. // the number to be formatted.
  15924. // pattern:
  15925. // a pattern string as described by
  15926. // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  15927. // options: dojo.number.__FormatOptions?
  15928. // _applyPattern is usually called via `dojo.number.format()` which
  15929. // populates an extra property in the options parameter, "customs".
  15930. // The customs object specifies group and decimal parameters if set.
  15931. //TODO: support escapes
  15932. options = options || {};
  15933. var group = options.customs.group,
  15934. decimal = options.customs.decimal,
  15935. patternList = pattern.split(';'),
  15936. positivePattern = patternList[0];
  15937. pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
  15938. //TODO: only test against unescaped
  15939. if(pattern.indexOf('%') != -1){
  15940. value *= 100;
  15941. }else if(pattern.indexOf('\u2030') != -1){
  15942. value *= 1000; // per mille
  15943. }else if(pattern.indexOf('\u00a4') != -1){
  15944. group = options.customs.currencyGroup || group;//mixins instead?
  15945. decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
  15946. pattern = pattern.replace(/\u00a4{1,3}/, function(match){
  15947. var prop = ["symbol", "currency", "displayName"][match.length-1];
  15948. return options[prop] || options.currency || "";
  15949. });
  15950. }else if(pattern.indexOf('E') != -1){
  15951. throw new Error("exponential notation not supported");
  15952. }
  15953. //TODO: support @ sig figs?
  15954. var numberPatternRE = dojo.number._numberPatternRE;
  15955. var numberPattern = positivePattern.match(numberPatternRE);
  15956. if(!numberPattern){
  15957. throw new Error("unable to find a number expression in pattern: "+pattern);
  15958. }
  15959. if(options.fractional === false){ options.places = 0; }
  15960. return pattern.replace(numberPatternRE,
  15961. dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
  15962. };
  15963. dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
  15964. // summary:
  15965. // Rounds to the nearest value with the given number of decimal places, away from zero
  15966. // description:
  15967. // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
  15968. // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
  15969. // fractional increments also, such as the nearest quarter.
  15970. // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
  15971. // value:
  15972. // The number to round
  15973. // places:
  15974. // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
  15975. // Must be non-negative.
  15976. // increment:
  15977. // Rounds next place to nearest value of increment/10. 10 by default.
  15978. // example:
  15979. // >>> dojo.number.round(-0.5)
  15980. // -1
  15981. // >>> dojo.number.round(162.295, 2)
  15982. // 162.29 // note floating point error. Should be 162.3
  15983. // >>> dojo.number.round(10.71, 0, 2.5)
  15984. // 10.75
  15985. var factor = 10 / (increment || 10);
  15986. return (factor * +value).toFixed(places) / factor; // Number
  15987. };
  15988. if((0.9).toFixed() == 0){
  15989. // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
  15990. // is just after the rounding place and is >=5
  15991. (function(){
  15992. var round = dojo.number.round;
  15993. dojo.number.round = function(v, p, m){
  15994. var d = Math.pow(10, -p || 0), a = Math.abs(v);
  15995. if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
  15996. d = 0;
  15997. }
  15998. return round(v, p, m) + (v > 0 ? d : -d);
  15999. };
  16000. })();
  16001. }
  16002. /*=====
  16003. dojo.number.__FormatAbsoluteOptions = function(){
  16004. // decimal: String?
  16005. // the decimal separator
  16006. // group: String?
  16007. // the group separator
  16008. // places: Number?|String?
  16009. // number of decimal places. the range "n,m" will format to m places.
  16010. // round: Number?
  16011. // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
  16012. // means don't round.
  16013. this.decimal = decimal;
  16014. this.group = group;
  16015. this.places = places;
  16016. this.round = round;
  16017. }
  16018. =====*/
  16019. dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
  16020. // summary:
  16021. // Apply numeric pattern to absolute value using options. Gives no
  16022. // consideration to local customs.
  16023. // value:
  16024. // the number to be formatted, ignores sign
  16025. // pattern:
  16026. // the number portion of a pattern (e.g. `#,##0.00`)
  16027. options = options || {};
  16028. if(options.places === true){options.places=0;}
  16029. if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
  16030. var patternParts = pattern.split("."),
  16031. comma = typeof options.places == "string" && options.places.indexOf(","),
  16032. maxPlaces = options.places;
  16033. if(comma){
  16034. maxPlaces = options.places.substring(comma + 1);
  16035. }else if(!(maxPlaces >= 0)){
  16036. maxPlaces = (patternParts[1] || []).length;
  16037. }
  16038. if(!(options.round < 0)){
  16039. value = dojo.number.round(value, maxPlaces, options.round);
  16040. }
  16041. var valueParts = String(Math.abs(value)).split("."),
  16042. fractional = valueParts[1] || "";
  16043. if(patternParts[1] || options.places){
  16044. if(comma){
  16045. options.places = options.places.substring(0, comma);
  16046. }
  16047. // Pad fractional with trailing zeros
  16048. var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
  16049. if(pad > fractional.length){
  16050. valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
  16051. }
  16052. // Truncate fractional
  16053. if(maxPlaces < fractional.length){
  16054. valueParts[1] = fractional.substr(0, maxPlaces);
  16055. }
  16056. }else{
  16057. if(valueParts[1]){ valueParts.pop(); }
  16058. }
  16059. // Pad whole with leading zeros
  16060. var patternDigits = patternParts[0].replace(',', '');
  16061. pad = patternDigits.indexOf("0");
  16062. if(pad != -1){
  16063. pad = patternDigits.length - pad;
  16064. if(pad > valueParts[0].length){
  16065. valueParts[0] = dojo.string.pad(valueParts[0], pad);
  16066. }
  16067. // Truncate whole
  16068. if(patternDigits.indexOf("#") == -1){
  16069. valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
  16070. }
  16071. }
  16072. // Add group separators
  16073. var index = patternParts[0].lastIndexOf(','),
  16074. groupSize, groupSize2;
  16075. if(index != -1){
  16076. groupSize = patternParts[0].length - index - 1;
  16077. var remainder = patternParts[0].substr(0, index);
  16078. index = remainder.lastIndexOf(',');
  16079. if(index != -1){
  16080. groupSize2 = remainder.length - index - 1;
  16081. }
  16082. }
  16083. var pieces = [];
  16084. for(var whole = valueParts[0]; whole;){
  16085. var off = whole.length - groupSize;
  16086. pieces.push((off > 0) ? whole.substr(off) : whole);
  16087. whole = (off > 0) ? whole.slice(0, off) : "";
  16088. if(groupSize2){
  16089. groupSize = groupSize2;
  16090. delete groupSize2;
  16091. }
  16092. }
  16093. valueParts[0] = pieces.reverse().join(options.group || ",");
  16094. return valueParts.join(options.decimal || ".");
  16095. };
  16096. /*=====
  16097. dojo.number.__RegexpOptions = function(){
  16098. // pattern: String?
  16099. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  16100. // with this string. Default value is based on locale. Overriding this property will defeat
  16101. // localization.
  16102. // type: String?
  16103. // choose a format type based on the locale from the following:
  16104. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  16105. // locale: String?
  16106. // override the locale used to determine formatting rules
  16107. // strict: Boolean?
  16108. // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
  16109. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
  16110. // places: Number|String?
  16111. // number of decimal places to accept: Infinity, a positive number, or
  16112. // a range "n,m". Defined by pattern or Infinity if pattern not provided.
  16113. this.pattern = pattern;
  16114. this.type = type;
  16115. this.locale = locale;
  16116. this.strict = strict;
  16117. this.places = places;
  16118. }
  16119. =====*/
  16120. dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
  16121. // summary:
  16122. // Builds the regular needed to parse a number
  16123. // description:
  16124. // Returns regular expression with positive and negative match, group
  16125. // and decimal separators
  16126. return dojo.number._parseInfo(options).regexp; // String
  16127. };
  16128. dojo.number._parseInfo = function(/*Object?*/options){
  16129. options = options || {};
  16130. var locale = dojo.i18n.normalizeLocale(options.locale),
  16131. bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
  16132. pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
  16133. //TODO: memoize?
  16134. group = bundle.group,
  16135. decimal = bundle.decimal,
  16136. factor = 1;
  16137. if(pattern.indexOf('%') != -1){
  16138. factor /= 100;
  16139. }else if(pattern.indexOf('\u2030') != -1){
  16140. factor /= 1000; // per mille
  16141. }else{
  16142. var isCurrency = pattern.indexOf('\u00a4') != -1;
  16143. if(isCurrency){
  16144. group = bundle.currencyGroup || group;
  16145. decimal = bundle.currencyDecimal || decimal;
  16146. }
  16147. }
  16148. //TODO: handle quoted escapes
  16149. var patternList = pattern.split(';');
  16150. if(patternList.length == 1){
  16151. patternList.push("-" + patternList[0]);
  16152. }
  16153. var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
  16154. pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
  16155. return pattern.replace(dojo.number._numberPatternRE, function(format){
  16156. var flags = {
  16157. signed: false,
  16158. separator: options.strict ? group : [group,""],
  16159. fractional: options.fractional,
  16160. decimal: decimal,
  16161. exponent: false
  16162. },
  16163. parts = format.split('.'),
  16164. places = options.places;
  16165. // special condition for percent (factor != 1)
  16166. // allow decimal places even if not specified in pattern
  16167. if(parts.length == 1 && factor != 1){
  16168. parts[1] = "###";
  16169. }
  16170. if(parts.length == 1 || places === 0){
  16171. flags.fractional = false;
  16172. }else{
  16173. if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
  16174. if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
  16175. if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
  16176. flags.places = places;
  16177. }
  16178. var groups = parts[0].split(',');
  16179. if(groups.length > 1){
  16180. flags.groupSize = groups.pop().length;
  16181. if(groups.length > 1){
  16182. flags.groupSize2 = groups.pop().length;
  16183. }
  16184. }
  16185. return "("+dojo.number._realNumberRegexp(flags)+")";
  16186. });
  16187. }, true);
  16188. if(isCurrency){
  16189. // substitute the currency symbol for the placeholder in the pattern
  16190. re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
  16191. var prop = ["symbol", "currency", "displayName"][target.length-1],
  16192. symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
  16193. before = before ? "[\\s\\xa0]" : "";
  16194. after = after ? "[\\s\\xa0]" : "";
  16195. if(!options.strict){
  16196. if(before){before += "*";}
  16197. if(after){after += "*";}
  16198. return "(?:"+before+symbol+after+")?";
  16199. }
  16200. return before+symbol+after;
  16201. });
  16202. }
  16203. //TODO: substitute localized sign/percent/permille/etc.?
  16204. // normalize whitespace and return
  16205. return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
  16206. };
  16207. /*=====
  16208. dojo.number.__ParseOptions = function(){
  16209. // pattern: String?
  16210. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  16211. // with this string. Default value is based on locale. Overriding this property will defeat
  16212. // localization. Literal characters in patterns are not supported.
  16213. // type: String?
  16214. // choose a format type based on the locale from the following:
  16215. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  16216. // locale: String?
  16217. // override the locale used to determine formatting rules
  16218. // strict: Boolean?
  16219. // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
  16220. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
  16221. // fractional: Boolean?|Array?
  16222. // Whether to include the fractional portion, where the number of decimal places are implied by pattern
  16223. // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
  16224. this.pattern = pattern;
  16225. this.type = type;
  16226. this.locale = locale;
  16227. this.strict = strict;
  16228. this.fractional = fractional;
  16229. }
  16230. =====*/
  16231. dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
  16232. // summary:
  16233. // Convert a properly formatted string to a primitive Number, using
  16234. // locale-specific settings.
  16235. // description:
  16236. // Create a Number from a string using a known localized pattern.
  16237. // Formatting patterns are chosen appropriate to the locale
  16238. // and follow the syntax described by
  16239. // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  16240. // Note that literal characters in patterns are not supported.
  16241. // expression:
  16242. // A string representation of a Number
  16243. var info = dojo.number._parseInfo(options),
  16244. results = (new RegExp("^"+info.regexp+"$")).exec(expression);
  16245. if(!results){
  16246. return NaN; //NaN
  16247. }
  16248. var absoluteMatch = results[1]; // match for the positive expression
  16249. if(!results[1]){
  16250. if(!results[2]){
  16251. return NaN; //NaN
  16252. }
  16253. // matched the negative pattern
  16254. absoluteMatch =results[2];
  16255. info.factor *= -1;
  16256. }
  16257. // Transform it to something Javascript can parse as a number. Normalize
  16258. // decimal point and strip out group separators or alternate forms of whitespace
  16259. absoluteMatch = absoluteMatch.
  16260. replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
  16261. replace(info.decimal, ".");
  16262. // Adjust for negative sign, percent, etc. as necessary
  16263. return absoluteMatch * info.factor; //Number
  16264. };
  16265. /*=====
  16266. dojo.number.__RealNumberRegexpFlags = function(){
  16267. // places: Number?
  16268. // The integer number of decimal places or a range given as "n,m". If
  16269. // not given, the decimal part is optional and the number of places is
  16270. // unlimited.
  16271. // decimal: String?
  16272. // A string for the character used as the decimal point. Default
  16273. // is ".".
  16274. // fractional: Boolean?|Array?
  16275. // Whether decimal places are used. Can be true, false, or [true,
  16276. // false]. Default is [true, false] which means optional.
  16277. // exponent: Boolean?|Array?
  16278. // Express in exponential notation. Can be true, false, or [true,
  16279. // false]. Default is [true, false], (i.e. will match if the
  16280. // exponential part is present are not).
  16281. // eSigned: Boolean?|Array?
  16282. // The leading plus-or-minus sign on the exponent. Can be true,
  16283. // false, or [true, false]. Default is [true, false], (i.e. will
  16284. // match if it is signed or unsigned). flags in regexp.integer can be
  16285. // applied.
  16286. this.places = places;
  16287. this.decimal = decimal;
  16288. this.fractional = fractional;
  16289. this.exponent = exponent;
  16290. this.eSigned = eSigned;
  16291. }
  16292. =====*/
  16293. dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
  16294. // summary:
  16295. // Builds a regular expression to match a real number in exponential
  16296. // notation
  16297. // assign default values to missing parameters
  16298. flags = flags || {};
  16299. //TODO: use mixin instead?
  16300. if(!("places" in flags)){ flags.places = Infinity; }
  16301. if(typeof flags.decimal != "string"){ flags.decimal = "."; }
  16302. if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
  16303. if(!("exponent" in flags)){ flags.exponent = [true, false]; }
  16304. if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
  16305. var integerRE = dojo.number._integerRegexp(flags),
  16306. decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
  16307. function(q){
  16308. var re = "";
  16309. if(q && (flags.places!==0)){
  16310. re = "\\" + flags.decimal;
  16311. if(flags.places == Infinity){
  16312. re = "(?:" + re + "\\d+)?";
  16313. }else{
  16314. re += "\\d{" + flags.places + "}";
  16315. }
  16316. }
  16317. return re;
  16318. },
  16319. true
  16320. );
  16321. var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
  16322. function(q){
  16323. if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
  16324. return "";
  16325. }
  16326. );
  16327. var realRE = integerRE + decimalRE;
  16328. // allow for decimals without integers, e.g. .25
  16329. if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
  16330. return realRE + exponentRE; // String
  16331. };
  16332. /*=====
  16333. dojo.number.__IntegerRegexpFlags = function(){
  16334. // signed: Boolean?
  16335. // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
  16336. // Default is `[true, false]`, (i.e. will match if it is signed
  16337. // or unsigned).
  16338. // separator: String?
  16339. // The character used as the thousands separator. Default is no
  16340. // separator. For more than one symbol use an array, e.g. `[",", ""]`,
  16341. // makes ',' optional.
  16342. // groupSize: Number?
  16343. // group size between separators
  16344. // groupSize2: Number?
  16345. // second grouping, where separators 2..n have a different interval than the first separator (for India)
  16346. this.signed = signed;
  16347. this.separator = separator;
  16348. this.groupSize = groupSize;
  16349. this.groupSize2 = groupSize2;
  16350. }
  16351. =====*/
  16352. dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
  16353. // summary:
  16354. // Builds a regular expression that matches an integer
  16355. // assign default values to missing parameters
  16356. flags = flags || {};
  16357. if(!("signed" in flags)){ flags.signed = [true, false]; }
  16358. if(!("separator" in flags)){
  16359. flags.separator = "";
  16360. }else if(!("groupSize" in flags)){
  16361. flags.groupSize = 3;
  16362. }
  16363. var signRE = dojo.regexp.buildGroupRE(flags.signed,
  16364. function(q){ return q ? "[-+]" : ""; },
  16365. true
  16366. );
  16367. var numberRE = dojo.regexp.buildGroupRE(flags.separator,
  16368. function(sep){
  16369. if(!sep){
  16370. return "(?:\\d+)";
  16371. }
  16372. sep = dojo.regexp.escapeString(sep);
  16373. if(sep == " "){ sep = "\\s"; }
  16374. else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
  16375. var grp = flags.groupSize, grp2 = flags.groupSize2;
  16376. //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
  16377. if(grp2){
  16378. var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
  16379. return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
  16380. }
  16381. return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
  16382. },
  16383. true
  16384. );
  16385. return signRE + numberRE; // String
  16386. };
  16387. }
  16388. if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16389. dojo._hasResource["dijit.ProgressBar"] = true;
  16390. dojo.provide("dijit.ProgressBar");
  16391. dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
  16392. // summary:
  16393. // A progress indication widget, showing the amount completed
  16394. // (often the percentage completed) of a task.
  16395. //
  16396. // example:
  16397. // | <div dojoType="ProgressBar"
  16398. // | places="0"
  16399. // | value="..." maximum="...">
  16400. // | </div>
  16401. // progress: [const] String (Percentage or Number)
  16402. // Number or percentage indicating amount of task completed.
  16403. // Deprecated. Use "value" instead.
  16404. progress: "0",
  16405. // value: String (Percentage or Number)
  16406. // Number or percentage indicating amount of task completed.
  16407. // With "%": percentage value, 0% <= progress <= 100%, or
  16408. // without "%": absolute value, 0 <= progress <= maximum.
  16409. // Infinity means that the progress bar is indeterminate.
  16410. value: "",
  16411. // maximum: [const] Float
  16412. // Max sample number
  16413. maximum: 100,
  16414. // places: [const] Number
  16415. // Number of places to show in values; 0 by default
  16416. places: 0,
  16417. // indeterminate: [const] Boolean
  16418. // If false: show progress value (number or percentage).
  16419. // If true: show that a process is underway but that the amount completed is unknown.
  16420. // Deprecated. Use "value" instead.
  16421. indeterminate: false,
  16422. // label: String?
  16423. // Label on progress bar. Defaults to percentage for determinate progress bar and
  16424. // blank for indeterminate progress bar.
  16425. label:"",
  16426. // name: String
  16427. // this is the field name (for a form) if set. This needs to be set if you want to use
  16428. // this widget in a dijit.form.Form widget (such as dijit.Dialog)
  16429. name: '',
  16430. templateString: dojo.cache("dijit", "templates/ProgressBar.html", "<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div dojoAttachPoint=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&nbsp;</span\n\t></div\n\t><div dojoAttachPoint=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img dojoAttachPoint=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"),
  16431. // _indeterminateHighContrastImagePath: [private] dojo._URL
  16432. // URL to image to use for indeterminate progress bar when display is in high contrast mode
  16433. _indeterminateHighContrastImagePath:
  16434. dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
  16435. postMixInProperties: function(){
  16436. this.inherited(arguments);
  16437. if(!("value" in this.params)){
  16438. this.value = this.indeterminate ? Infinity : this.progress;
  16439. }
  16440. },
  16441. buildRendering: function(){
  16442. this.inherited(arguments);
  16443. this.indeterminateHighContrastImage.setAttribute("src",
  16444. this._indeterminateHighContrastImagePath.toString());
  16445. this.update();
  16446. },
  16447. update: function(/*Object?*/attributes){
  16448. // summary:
  16449. // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
  16450. // set("value", ...) rather than calling this method directly.
  16451. // attributes:
  16452. // May provide progress and/or maximum properties on this parameter;
  16453. // see attribute specs for details.
  16454. // example:
  16455. // | myProgressBar.update({'indeterminate': true});
  16456. // | myProgressBar.update({'progress': 80});
  16457. // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
  16458. // tags:
  16459. // private
  16460. // TODO: deprecate this method and use set() instead
  16461. dojo.mixin(this, attributes || {});
  16462. var tip = this.internalProgress, ap = this.domNode;
  16463. var percent = 1;
  16464. if(this.indeterminate){
  16465. dijit.removeWaiState(ap, "valuenow");
  16466. dijit.removeWaiState(ap, "valuemin");
  16467. dijit.removeWaiState(ap, "valuemax");
  16468. }else{
  16469. if(String(this.progress).indexOf("%") != -1){
  16470. percent = Math.min(parseFloat(this.progress)/100, 1);
  16471. this.progress = percent * this.maximum;
  16472. }else{
  16473. this.progress = Math.min(this.progress, this.maximum);
  16474. percent = this.progress / this.maximum;
  16475. }
  16476. dijit.setWaiState(ap, "describedby", this.labelNode.id);
  16477. dijit.setWaiState(ap, "valuenow", this.progress);
  16478. dijit.setWaiState(ap, "valuemin", 0);
  16479. dijit.setWaiState(ap, "valuemax", this.maximum);
  16480. }
  16481. this.labelNode.innerHTML = this.report(percent);
  16482. dojo.toggleClass(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
  16483. tip.style.width = (percent * 100) + "%";
  16484. this.onChange();
  16485. },
  16486. _setValueAttr: function(v){
  16487. this._set("value", v);
  16488. if(v == Infinity){
  16489. this.update({indeterminate:true});
  16490. }else{
  16491. this.update({indeterminate:false, progress:v});
  16492. }
  16493. },
  16494. _setLabelAttr: function(label){
  16495. this._set("label", label);
  16496. this.update();
  16497. },
  16498. _setIndeterminateAttr: function(indeterminate){
  16499. // Deprecated, use set("value", ...) instead
  16500. this.indeterminate = indeterminate;
  16501. this.update();
  16502. },
  16503. report: function(/*float*/percent){
  16504. // summary:
  16505. // Generates message to show inside progress bar (normally indicating amount of task completed).
  16506. // May be overridden.
  16507. // tags:
  16508. // extension
  16509. return this.label ? this.label :
  16510. (this.indeterminate ? "&nbsp;" : dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
  16511. },
  16512. onChange: function(){
  16513. // summary:
  16514. // Callback fired when progress updates.
  16515. // tags:
  16516. // extension
  16517. }
  16518. });
  16519. }
  16520. if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16521. dojo._hasResource["dijit.TitlePane"] = true;
  16522. dojo.provide("dijit.TitlePane");
  16523. dojo.declare(
  16524. "dijit.TitlePane",
  16525. [dijit.layout.ContentPane, dijit._Templated, dijit._CssStateMixin],
  16526. {
  16527. // summary:
  16528. // A pane with a title on top, that can be expanded or collapsed.
  16529. //
  16530. // description:
  16531. // An accessible container with a title Heading, and a content
  16532. // section that slides open and closed. TitlePane is an extension to
  16533. // `dijit.layout.ContentPane`, providing all the useful content-control aspects from it.
  16534. //
  16535. // example:
  16536. // | // load a TitlePane from remote file:
  16537. // | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
  16538. // | foo.startup();
  16539. //
  16540. // example:
  16541. // | <!-- markup href example: -->
  16542. // | <div dojoType="dijit.TitlePane" href="foobar.html" title="Title"></div>
  16543. //
  16544. // example:
  16545. // | <!-- markup with inline data -->
  16546. // | <div dojoType="dijit.TitlePane" title="Title">
  16547. // | <p>I am content</p>
  16548. // | </div>
  16549. // title: String
  16550. // Title of the pane
  16551. title: "",
  16552. // open: Boolean
  16553. // Whether pane is opened or closed.
  16554. open: true,
  16555. // toggleable: Boolean
  16556. // Whether pane can be opened or closed by clicking the title bar.
  16557. toggleable: true,
  16558. // tabIndex: String
  16559. // Tabindex setting for the title (so users can tab to the title then
  16560. // use space/enter to open/close the title pane)
  16561. tabIndex: "0",
  16562. // duration: Integer
  16563. // Time in milliseconds to fade in/fade out
  16564. duration: dijit.defaultDuration,
  16565. // baseClass: [protected] String
  16566. // The root className to be placed on this widget's domNode.
  16567. baseClass: "dijitTitlePane",
  16568. templateString: dojo.cache("dijit", "templates/TitlePane.html", "<div>\n\t<div dojoAttachEvent=\"onclick:_onTitleClick, onkeypress:_onTitleKey\"\n\t\t\tclass=\"dijitTitlePaneTitle\" dojoAttachPoint=\"titleBarNode\">\n\t\t<div class=\"dijitTitlePaneTitleFocus\" dojoAttachPoint=\"focusNode\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"arrowNode\" class=\"dijitArrowNode\" role=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t\t><span dojoAttachPoint=\"titleNode\" class=\"dijitTitlePaneTextNode\"></span>\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" dojoAttachPoint=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" dojoAttachPoint=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" dojoAttachPoint=\"containerNode\" role=\"region\" id=\"${id}_pane\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"),
  16569. attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
  16570. title: { node: "titleNode", type: "innerHTML" },
  16571. tooltip: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
  16572. id:""
  16573. }),
  16574. buildRendering: function(){
  16575. this.inherited(arguments);
  16576. dojo.setSelectable(this.titleNode, false);
  16577. },
  16578. postCreate: function(){
  16579. this.inherited(arguments);
  16580. // Hover and focus effect on title bar, except for non-toggleable TitlePanes
  16581. // This should really be controlled from _setToggleableAttr() but _CssStateMixin
  16582. // doesn't provide a way to disconnect a previous _trackMouseState() call
  16583. if(this.toggleable){
  16584. this._trackMouseState(this.titleBarNode, "dijitTitlePaneTitle");
  16585. }
  16586. // setup open/close animations
  16587. var hideNode = this.hideNode, wipeNode = this.wipeNode;
  16588. this._wipeIn = dojo.fx.wipeIn({
  16589. node: this.wipeNode,
  16590. duration: this.duration,
  16591. beforeBegin: function(){
  16592. hideNode.style.display="";
  16593. }
  16594. });
  16595. this._wipeOut = dojo.fx.wipeOut({
  16596. node: this.wipeNode,
  16597. duration: this.duration,
  16598. onEnd: function(){
  16599. hideNode.style.display="none";
  16600. }
  16601. });
  16602. },
  16603. _setOpenAttr: function(/*Boolean*/ open, /*Boolean*/ animate){
  16604. // summary:
  16605. // Hook to make set("open", boolean) control the open/closed state of the pane.
  16606. // open: Boolean
  16607. // True if you want to open the pane, false if you want to close it.
  16608. dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
  16609. if(animation && animation.status() == "playing"){
  16610. animation.stop();
  16611. }
  16612. });
  16613. if(animate){
  16614. var anim = this[open ? "_wipeIn" : "_wipeOut"];
  16615. anim.play();
  16616. }else{
  16617. this.hideNode.style.display = this.wipeNode.style.display = open ? "" : "none";
  16618. }
  16619. // load content (if this is the first time we are opening the TitlePane
  16620. // and content is specified as an href, or href was set when hidden)
  16621. if(this._started){
  16622. if(open){
  16623. this._onShow();
  16624. }else{
  16625. this.onHide();
  16626. }
  16627. }
  16628. this.arrowNodeInner.innerHTML = open ? "-" : "+";
  16629. dijit.setWaiState(this.containerNode,"hidden", open ? "false" : "true");
  16630. dijit.setWaiState(this.focusNode, "pressed", open ? "true" : "false");
  16631. this._set("open", open);
  16632. this._setCss();
  16633. },
  16634. _setToggleableAttr: function(/*Boolean*/ canToggle){
  16635. // summary:
  16636. // Hook to make set("toggleable", boolean) work.
  16637. // canToggle: Boolean
  16638. // True to allow user to open/close pane by clicking title bar.
  16639. dijit.setWaiRole(this.focusNode, canToggle ? "button" : "heading");
  16640. if(canToggle){
  16641. // TODO: if canToggle is switched from true to false shouldn't we remove this setting?
  16642. dijit.setWaiState(this.focusNode, "controls", this.id+"_pane");
  16643. dojo.attr(this.focusNode, "tabIndex", this.tabIndex);
  16644. }else{
  16645. dojo.removeAttr(this.focusNode, "tabIndex");
  16646. }
  16647. this._set("toggleable", canToggle);
  16648. this._setCss();
  16649. },
  16650. _setContentAttr: function(/*String|DomNode|Nodelist*/ content){
  16651. // summary:
  16652. // Hook to make set("content", ...) work.
  16653. // Typically called when an href is loaded. Our job is to make the animation smooth.
  16654. if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
  16655. // we are currently *closing* the pane (or the pane is closed), so just let that continue
  16656. this.inherited(arguments);
  16657. }else{
  16658. if(this._wipeIn && this._wipeIn.status() == "playing"){
  16659. this._wipeIn.stop();
  16660. }
  16661. // freeze container at current height so that adding new content doesn't make it jump
  16662. dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });
  16663. // add the new content (erasing the old content, if any)
  16664. this.inherited(arguments);
  16665. // call _wipeIn.play() to animate from current height to new height
  16666. if(this._wipeIn){
  16667. this._wipeIn.play();
  16668. }else{
  16669. this.hideNode.style.display = "";
  16670. }
  16671. }
  16672. },
  16673. toggle: function(){
  16674. // summary:
  16675. // Switches between opened and closed state
  16676. // tags:
  16677. // private
  16678. this._setOpenAttr(!this.open, true);
  16679. },
  16680. _setCss: function(){
  16681. // summary:
  16682. // Set the open/close css state for the TitlePane
  16683. // tags:
  16684. // private
  16685. var node = this.titleBarNode || this.focusNode;
  16686. var oldCls = this._titleBarClass;
  16687. this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
  16688. dojo.replaceClass(node, this._titleBarClass, oldCls || "");
  16689. this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
  16690. },
  16691. _onTitleKey: function(/*Event*/ e){
  16692. // summary:
  16693. // Handler for when user hits a key
  16694. // tags:
  16695. // private
  16696. if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
  16697. if(this.toggleable){
  16698. this.toggle();
  16699. }
  16700. dojo.stopEvent(e);
  16701. }else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
  16702. this.containerNode.focus();
  16703. e.preventDefault();
  16704. }
  16705. },
  16706. _onTitleClick: function(){
  16707. // summary:
  16708. // Handler when user clicks the title bar
  16709. // tags:
  16710. // private
  16711. if(this.toggleable){
  16712. this.toggle();
  16713. }
  16714. },
  16715. setTitle: function(/*String*/ title){
  16716. // summary:
  16717. // Deprecated. Use set('title', ...) instead.
  16718. // tags:
  16719. // deprecated
  16720. dojo.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
  16721. this.set("title", title);
  16722. }
  16723. });
  16724. }
  16725. if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16726. dojo._hasResource["dojo.DeferredList"] = true;
  16727. dojo.provide("dojo.DeferredList");
  16728. dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
  16729. // summary:
  16730. // Provides event handling for a group of Deferred objects.
  16731. // description:
  16732. // DeferredList takes an array of existing deferreds and returns a new deferred of its own
  16733. // this new deferred will typically have its callback fired when all of the deferreds in
  16734. // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
  16735. // fireOnOneErrback, will fire before all the deferreds as appropriate
  16736. //
  16737. // list:
  16738. // The list of deferreds to be synchronizied with this DeferredList
  16739. // fireOnOneCallback:
  16740. // Will cause the DeferredLists callback to be fired as soon as any
  16741. // of the deferreds in its list have been fired instead of waiting until
  16742. // the entire list has finished
  16743. // fireonOneErrback:
  16744. // Will cause the errback to fire upon any of the deferreds errback
  16745. // canceller:
  16746. // A deferred canceller function, see dojo.Deferred
  16747. var resultList = [];
  16748. dojo.Deferred.call(this);
  16749. var self = this;
  16750. if(list.length === 0 && !fireOnOneCallback){
  16751. this.resolve([0, []]);
  16752. }
  16753. var finished = 0;
  16754. dojo.forEach(list, function(item, i){
  16755. item.then(function(result){
  16756. if(fireOnOneCallback){
  16757. self.resolve([i, result]);
  16758. }else{
  16759. addResult(true, result);
  16760. }
  16761. },function(error){
  16762. if(fireOnOneErrback){
  16763. self.reject(error);
  16764. }else{
  16765. addResult(false, error);
  16766. }
  16767. if(consumeErrors){
  16768. return null;
  16769. }
  16770. throw error;
  16771. });
  16772. function addResult(succeeded, result){
  16773. resultList[i] = [succeeded, result];
  16774. finished++;
  16775. if(finished === list.length){
  16776. self.resolve(resultList);
  16777. }
  16778. }
  16779. });
  16780. };
  16781. dojo.DeferredList.prototype = new dojo.Deferred();
  16782. dojo.DeferredList.prototype.gatherResults= function(deferredList){
  16783. // summary:
  16784. // Gathers the results of the deferreds for packaging
  16785. // as the parameters to the Deferred Lists' callback
  16786. var d = new dojo.DeferredList(deferredList, false, true, false);
  16787. d.addCallback(function(results){
  16788. var ret = [];
  16789. dojo.forEach(results, function(result){
  16790. ret.push(result[1]);
  16791. });
  16792. return ret;
  16793. });
  16794. return d;
  16795. };
  16796. }
  16797. if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16798. dojo._hasResource["dojo.cookie"] = true;
  16799. dojo.provide("dojo.cookie");
  16800. /*=====
  16801. dojo.__cookieProps = function(){
  16802. // expires: Date|String|Number?
  16803. // If a number, the number of days from today at which the cookie
  16804. // will expire. If a date, the date past which the cookie will expire.
  16805. // If expires is in the past, the cookie will be deleted.
  16806. // If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
  16807. // path: String?
  16808. // The path to use for the cookie.
  16809. // domain: String?
  16810. // The domain to use for the cookie.
  16811. // secure: Boolean?
  16812. // Whether to only send the cookie on secure connections
  16813. this.expires = expires;
  16814. this.path = path;
  16815. this.domain = domain;
  16816. this.secure = secure;
  16817. }
  16818. =====*/
  16819. dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
  16820. // summary:
  16821. // Get or set a cookie.
  16822. // description:
  16823. // If one argument is passed, returns the value of the cookie
  16824. // For two or more arguments, acts as a setter.
  16825. // name:
  16826. // Name of the cookie
  16827. // value:
  16828. // Value for the cookie
  16829. // props:
  16830. // Properties for the cookie
  16831. // example:
  16832. // set a cookie with the JSON-serialized contents of an object which
  16833. // will expire 5 days from now:
  16834. // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
  16835. //
  16836. // example:
  16837. // de-serialize a cookie back into a JavaScript object:
  16838. // | var config = dojo.fromJson(dojo.cookie("configObj"));
  16839. //
  16840. // example:
  16841. // delete a cookie:
  16842. // | dojo.cookie("configObj", null, {expires: -1});
  16843. var c = document.cookie;
  16844. if(arguments.length == 1){
  16845. var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
  16846. return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
  16847. }else{
  16848. props = props || {};
  16849. // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
  16850. var exp = props.expires;
  16851. if(typeof exp == "number"){
  16852. var d = new Date();
  16853. d.setTime(d.getTime() + exp*24*60*60*1000);
  16854. exp = props.expires = d;
  16855. }
  16856. if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
  16857. value = encodeURIComponent(value);
  16858. var updatedCookie = name + "=" + value, propName;
  16859. for(propName in props){
  16860. updatedCookie += "; " + propName;
  16861. var propValue = props[propName];
  16862. if(propValue !== true){ updatedCookie += "=" + propValue; }
  16863. }
  16864. document.cookie = updatedCookie;
  16865. }
  16866. };
  16867. dojo.cookie.isSupported = function(){
  16868. // summary:
  16869. // Use to determine if the current browser supports cookies or not.
  16870. //
  16871. // Returns true if user allows cookies.
  16872. // Returns false if user doesn't allow cookies.
  16873. if(!("cookieEnabled" in navigator)){
  16874. this("__djCookieTest__", "CookiesAllowed");
  16875. navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
  16876. if(navigator.cookieEnabled){
  16877. this("__djCookieTest__", "", {expires: -1});
  16878. }
  16879. }
  16880. return navigator.cookieEnabled;
  16881. };
  16882. }
  16883. if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16884. dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
  16885. dojo.provide("dijit.tree.TreeStoreModel");
  16886. dojo.declare(
  16887. "dijit.tree.TreeStoreModel",
  16888. null,
  16889. {
  16890. // summary:
  16891. // Implements dijit.Tree.model connecting to a store with a single
  16892. // root item. Any methods passed into the constructor will override
  16893. // the ones defined here.
  16894. // store: dojo.data.Store
  16895. // Underlying store
  16896. store: null,
  16897. // childrenAttrs: String[]
  16898. // One or more attribute names (attributes in the dojo.data item) that specify that item's children
  16899. childrenAttrs: ["children"],
  16900. // newItemIdAttr: String
  16901. // Name of attribute in the Object passed to newItem() that specifies the id.
  16902. //
  16903. // If newItemIdAttr is set then it's used when newItem() is called to see if an
  16904. // item with the same id already exists, and if so just links to the old item
  16905. // (so that the old item ends up with two parents).
  16906. //
  16907. // Setting this to null or "" will make every drop create a new item.
  16908. newItemIdAttr: "id",
  16909. // labelAttr: String
  16910. // If specified, get label for tree node from this attribute, rather
  16911. // than by calling store.getLabel()
  16912. labelAttr: "",
  16913. // root: [readonly] dojo.data.Item
  16914. // Pointer to the root item (read only, not a parameter)
  16915. root: null,
  16916. // query: anything
  16917. // Specifies datastore query to return the root item for the tree.
  16918. // Must only return a single item. Alternately can just pass in pointer
  16919. // to root item.
  16920. // example:
  16921. // | {id:'ROOT'}
  16922. query: null,
  16923. // deferItemLoadingUntilExpand: Boolean
  16924. // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
  16925. // until they are expanded. This allows for lazying loading where only one
  16926. // loadItem (and generally one network call, consequently) per expansion
  16927. // (rather than one for each child).
  16928. // This relies on partial loading of the children items; each children item of a
  16929. // fully loaded item should contain the label and info about having children.
  16930. deferItemLoadingUntilExpand: false,
  16931. constructor: function(/* Object */ args){
  16932. // summary:
  16933. // Passed the arguments listed above (store, etc)
  16934. // tags:
  16935. // private
  16936. dojo.mixin(this, args);
  16937. this.connects = [];
  16938. var store = this.store;
  16939. if(!store.getFeatures()['dojo.data.api.Identity']){
  16940. throw new Error("dijit.Tree: store must support dojo.data.Identity");
  16941. }
  16942. // if the store supports Notification, subscribe to the notification events
  16943. if(store.getFeatures()['dojo.data.api.Notification']){
  16944. this.connects = this.connects.concat([
  16945. dojo.connect(store, "onNew", this, "onNewItem"),
  16946. dojo.connect(store, "onDelete", this, "onDeleteItem"),
  16947. dojo.connect(store, "onSet", this, "onSetItem")
  16948. ]);
  16949. }
  16950. },
  16951. destroy: function(){
  16952. dojo.forEach(this.connects, dojo.disconnect);
  16953. // TODO: should cancel any in-progress processing of getRoot(), getChildren()
  16954. },
  16955. // =======================================================================
  16956. // Methods for traversing hierarchy
  16957. getRoot: function(onItem, onError){
  16958. // summary:
  16959. // Calls onItem with the root item for the tree, possibly a fabricated item.
  16960. // Calls onError on error.
  16961. if(this.root){
  16962. onItem(this.root);
  16963. }else{
  16964. this.store.fetch({
  16965. query: this.query,
  16966. onComplete: dojo.hitch(this, function(items){
  16967. if(items.length != 1){
  16968. throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
  16969. " items, but must return exactly one item");
  16970. }
  16971. this.root = items[0];
  16972. onItem(this.root);
  16973. }),
  16974. onError: onError
  16975. });
  16976. }
  16977. },
  16978. mayHaveChildren: function(/*dojo.data.Item*/ item){
  16979. // summary:
  16980. // Tells if an item has or may have children. Implementing logic here
  16981. // avoids showing +/- expando icon for nodes that we know don't have children.
  16982. // (For efficiency reasons we may not want to check if an element actually
  16983. // has children until user clicks the expando node)
  16984. return dojo.some(this.childrenAttrs, function(attr){
  16985. return this.store.hasAttribute(item, attr);
  16986. }, this);
  16987. },
  16988. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
  16989. // summary:
  16990. // Calls onComplete() with array of child items of given parent item, all loaded.
  16991. var store = this.store;
  16992. if(!store.isItemLoaded(parentItem)){
  16993. // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
  16994. // mode, so we will load it and just return the children (without loading each
  16995. // child item)
  16996. var getChildren = dojo.hitch(this, arguments.callee);
  16997. store.loadItem({
  16998. item: parentItem,
  16999. onItem: function(parentItem){
  17000. getChildren(parentItem, onComplete, onError);
  17001. },
  17002. onError: onError
  17003. });
  17004. return;
  17005. }
  17006. // get children of specified item
  17007. var childItems = [];
  17008. for(var i=0; i<this.childrenAttrs.length; i++){
  17009. var vals = store.getValues(parentItem, this.childrenAttrs[i]);
  17010. childItems = childItems.concat(vals);
  17011. }
  17012. // count how many items need to be loaded
  17013. var _waitCount = 0;
  17014. if(!this.deferItemLoadingUntilExpand){
  17015. dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
  17016. }
  17017. if(_waitCount == 0){
  17018. // all items are already loaded (or we aren't loading them). proceed...
  17019. onComplete(childItems);
  17020. }else{
  17021. // still waiting for some or all of the items to load
  17022. dojo.forEach(childItems, function(item, idx){
  17023. if(!store.isItemLoaded(item)){
  17024. store.loadItem({
  17025. item: item,
  17026. onItem: function(item){
  17027. childItems[idx] = item;
  17028. if(--_waitCount == 0){
  17029. // all nodes have been loaded, send them to the tree
  17030. onComplete(childItems);
  17031. }
  17032. },
  17033. onError: onError
  17034. });
  17035. }
  17036. });
  17037. }
  17038. },
  17039. // =======================================================================
  17040. // Inspecting items
  17041. isItem: function(/* anything */ something){
  17042. return this.store.isItem(something); // Boolean
  17043. },
  17044. fetchItemByIdentity: function(/* object */ keywordArgs){
  17045. this.store.fetchItemByIdentity(keywordArgs);
  17046. },
  17047. getIdentity: function(/* item */ item){
  17048. return this.store.getIdentity(item); // Object
  17049. },
  17050. getLabel: function(/*dojo.data.Item*/ item){
  17051. // summary:
  17052. // Get the label for an item
  17053. if(this.labelAttr){
  17054. return this.store.getValue(item,this.labelAttr); // String
  17055. }else{
  17056. return this.store.getLabel(item); // String
  17057. }
  17058. },
  17059. // =======================================================================
  17060. // Write interface
  17061. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  17062. // summary:
  17063. // Creates a new item. See `dojo.data.api.Write` for details on args.
  17064. // Used in drag & drop when item from external source dropped onto tree.
  17065. // description:
  17066. // Developers will need to override this method if new items get added
  17067. // to parents with multiple children attributes, in order to define which
  17068. // children attribute points to the new item.
  17069. var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
  17070. if(this.newItemIdAttr && args[this.newItemIdAttr]){
  17071. // Maybe there's already a corresponding item in the store; if so, reuse it.
  17072. this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
  17073. if(item){
  17074. // There's already a matching item in store, use it
  17075. this.pasteItem(item, null, parent, true, insertIndex);
  17076. }else{
  17077. // Create new item in the tree, based on the drag source.
  17078. LnewItem=this.store.newItem(args, pInfo);
  17079. if (LnewItem && (insertIndex!=undefined)){
  17080. // Move new item to desired position
  17081. this.pasteItem(LnewItem, parent, parent, false, insertIndex);
  17082. }
  17083. }
  17084. }});
  17085. }else{
  17086. // [as far as we know] there is no id so we must assume this is a new item
  17087. LnewItem=this.store.newItem(args, pInfo);
  17088. if (LnewItem && (insertIndex!=undefined)){
  17089. // Move new item to desired position
  17090. this.pasteItem(LnewItem, parent, parent, false, insertIndex);
  17091. }
  17092. }
  17093. },
  17094. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  17095. // summary:
  17096. // Move or copy an item from one parent item to another.
  17097. // Used in drag & drop
  17098. var store = this.store,
  17099. parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
  17100. // remove child from source item, and record the attribute that child occurred in
  17101. if(oldParentItem){
  17102. dojo.forEach(this.childrenAttrs, function(attr){
  17103. if(store.containsValue(oldParentItem, attr, childItem)){
  17104. if(!bCopy){
  17105. var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
  17106. return x != childItem;
  17107. });
  17108. store.setValues(oldParentItem, attr, values);
  17109. }
  17110. parentAttr = attr;
  17111. }
  17112. });
  17113. }
  17114. // modify target item's children attribute to include this item
  17115. if(newParentItem){
  17116. if(typeof insertIndex == "number"){
  17117. // call slice() to avoid modifying the original array, confusing the data store
  17118. var childItems = store.getValues(newParentItem, parentAttr).slice();
  17119. childItems.splice(insertIndex, 0, childItem);
  17120. store.setValues(newParentItem, parentAttr, childItems);
  17121. }else{
  17122. store.setValues(newParentItem, parentAttr,
  17123. store.getValues(newParentItem, parentAttr).concat(childItem));
  17124. }
  17125. }
  17126. },
  17127. // =======================================================================
  17128. // Callbacks
  17129. onChange: function(/*dojo.data.Item*/ item){
  17130. // summary:
  17131. // Callback whenever an item has changed, so that Tree
  17132. // can update the label, icon, etc. Note that changes
  17133. // to an item's children or parent(s) will trigger an
  17134. // onChildrenChange() so you can ignore those changes here.
  17135. // tags:
  17136. // callback
  17137. },
  17138. onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  17139. // summary:
  17140. // Callback to do notifications about new, updated, or deleted items.
  17141. // tags:
  17142. // callback
  17143. },
  17144. onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  17145. // summary:
  17146. // Callback when an item has been deleted.
  17147. // description:
  17148. // Note that there will also be an onChildrenChange() callback for the parent
  17149. // of this item.
  17150. // tags:
  17151. // callback
  17152. },
  17153. // =======================================================================
  17154. // Events from data store
  17155. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  17156. // summary:
  17157. // Handler for when new items appear in the store, either from a drop operation
  17158. // or some other way. Updates the tree view (if necessary).
  17159. // description:
  17160. // If the new item is a child of an existing item,
  17161. // calls onChildrenChange() with the new list of children
  17162. // for that existing item.
  17163. //
  17164. // tags:
  17165. // extension
  17166. // We only care about the new item if it has a parent that corresponds to a TreeNode
  17167. // we are currently displaying
  17168. if(!parentInfo){
  17169. return;
  17170. }
  17171. // Call onChildrenChange() on parent (ie, existing) item with new list of children
  17172. // In the common case, the new list of children is simply parentInfo.newValue or
  17173. // [ parentInfo.newValue ], although if items in the store has multiple
  17174. // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
  17175. // so call getChildren() to be sure to get right answer.
  17176. this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
  17177. this.onChildrenChange(parentInfo.item, children);
  17178. }));
  17179. },
  17180. onDeleteItem: function(/*Object*/ item){
  17181. // summary:
  17182. // Handler for delete notifications from underlying store
  17183. this.onDelete(item);
  17184. },
  17185. onSetItem: function(/* item */ item,
  17186. /* attribute-name-string */ attribute,
  17187. /* object | array */ oldValue,
  17188. /* object | array */ newValue){
  17189. // summary:
  17190. // Updates the tree view according to changes in the data store.
  17191. // description:
  17192. // Handles updates to an item's children by calling onChildrenChange(), and
  17193. // other updates to an item by calling onChange().
  17194. //
  17195. // See `onNewItem` for more details on handling updates to an item's children.
  17196. // tags:
  17197. // extension
  17198. if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
  17199. // item's children list changed
  17200. this.getChildren(item, dojo.hitch(this, function(children){
  17201. // See comments in onNewItem() about calling getChildren()
  17202. this.onChildrenChange(item, children);
  17203. }));
  17204. }else{
  17205. // item's label/icon/etc. changed.
  17206. this.onChange(item);
  17207. }
  17208. }
  17209. });
  17210. }
  17211. if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17212. dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
  17213. dojo.provide("dijit.tree.ForestStoreModel");
  17214. dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
  17215. // summary:
  17216. // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
  17217. // a.k.a. a store that has multiple "top level" items.
  17218. //
  17219. // description
  17220. // Use this class to wrap a dojo.data store, making all the items matching the specified query
  17221. // appear as children of a fabricated "root item". If no query is specified then all the
  17222. // items returned by fetch() on the underlying store become children of the root item.
  17223. // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
  17224. //
  17225. // When using this class the developer must override a number of methods according to their app and
  17226. // data, including:
  17227. // - onNewRootItem
  17228. // - onAddToRoot
  17229. // - onLeaveRoot
  17230. // - onNewItem
  17231. // - onSetItem
  17232. // Parameters to constructor
  17233. // rootId: String
  17234. // ID of fabricated root item
  17235. rootId: "$root$",
  17236. // rootLabel: String
  17237. // Label of fabricated root item
  17238. rootLabel: "ROOT",
  17239. // query: String
  17240. // Specifies the set of children of the root item.
  17241. // example:
  17242. // | {type:'continent'}
  17243. query: null,
  17244. // End of parameters to constructor
  17245. constructor: function(params){
  17246. // summary:
  17247. // Sets up variables, etc.
  17248. // tags:
  17249. // private
  17250. // Make dummy root item
  17251. this.root = {
  17252. store: this,
  17253. root: true,
  17254. id: params.rootId,
  17255. label: params.rootLabel,
  17256. children: params.rootChildren // optional param
  17257. };
  17258. },
  17259. // =======================================================================
  17260. // Methods for traversing hierarchy
  17261. mayHaveChildren: function(/*dojo.data.Item*/ item){
  17262. // summary:
  17263. // Tells if an item has or may have children. Implementing logic here
  17264. // avoids showing +/- expando icon for nodes that we know don't have children.
  17265. // (For efficiency reasons we may not want to check if an element actually
  17266. // has children until user clicks the expando node)
  17267. // tags:
  17268. // extension
  17269. return item === this.root || this.inherited(arguments);
  17270. },
  17271. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
  17272. // summary:
  17273. // Calls onComplete() with array of child items of given parent item, all loaded.
  17274. if(parentItem === this.root){
  17275. if(this.root.children){
  17276. // already loaded, just return
  17277. callback(this.root.children);
  17278. }else{
  17279. this.store.fetch({
  17280. query: this.query,
  17281. onComplete: dojo.hitch(this, function(items){
  17282. this.root.children = items;
  17283. callback(items);
  17284. }),
  17285. onError: onError
  17286. });
  17287. }
  17288. }else{
  17289. this.inherited(arguments);
  17290. }
  17291. },
  17292. // =======================================================================
  17293. // Inspecting items
  17294. isItem: function(/* anything */ something){
  17295. return (something === this.root) ? true : this.inherited(arguments);
  17296. },
  17297. fetchItemByIdentity: function(/* object */ keywordArgs){
  17298. if(keywordArgs.identity == this.root.id){
  17299. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  17300. if(keywordArgs.onItem){
  17301. keywordArgs.onItem.call(scope, this.root);
  17302. }
  17303. }else{
  17304. this.inherited(arguments);
  17305. }
  17306. },
  17307. getIdentity: function(/* item */ item){
  17308. return (item === this.root) ? this.root.id : this.inherited(arguments);
  17309. },
  17310. getLabel: function(/* item */ item){
  17311. return (item === this.root) ? this.root.label : this.inherited(arguments);
  17312. },
  17313. // =======================================================================
  17314. // Write interface
  17315. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  17316. // summary:
  17317. // Creates a new item. See dojo.data.api.Write for details on args.
  17318. // Used in drag & drop when item from external source dropped onto tree.
  17319. if(parent === this.root){
  17320. this.onNewRootItem(args);
  17321. return this.store.newItem(args);
  17322. }else{
  17323. return this.inherited(arguments);
  17324. }
  17325. },
  17326. onNewRootItem: function(args){
  17327. // summary:
  17328. // User can override this method to modify a new element that's being
  17329. // added to the root of the tree, for example to add a flag like root=true
  17330. },
  17331. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  17332. // summary:
  17333. // Move or copy an item from one parent item to another.
  17334. // Used in drag & drop
  17335. if(oldParentItem === this.root){
  17336. if(!bCopy){
  17337. // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
  17338. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  17339. // that this element is no longer a child of the root node
  17340. this.onLeaveRoot(childItem);
  17341. }
  17342. }
  17343. dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
  17344. oldParentItem === this.root ? null : oldParentItem,
  17345. newParentItem === this.root ? null : newParentItem,
  17346. bCopy,
  17347. insertIndex
  17348. );
  17349. if(newParentItem === this.root){
  17350. // It's onAddToRoot()'s responsibility to modify the item so it matches
  17351. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  17352. // that this element is now a child of the root node
  17353. this.onAddToRoot(childItem);
  17354. }
  17355. },
  17356. // =======================================================================
  17357. // Handling for top level children
  17358. onAddToRoot: function(/* item */ item){
  17359. // summary:
  17360. // Called when item added to root of tree; user must override this method
  17361. // to modify the item so that it matches the query for top level items
  17362. // example:
  17363. // | store.setValue(item, "root", true);
  17364. // tags:
  17365. // extension
  17366. console.log(this, ": item ", item, " added to root");
  17367. },
  17368. onLeaveRoot: function(/* item */ item){
  17369. // summary:
  17370. // Called when item removed from root of tree; user must override this method
  17371. // to modify the item so it doesn't match the query for top level items
  17372. // example:
  17373. // | store.unsetAttribute(item, "root");
  17374. // tags:
  17375. // extension
  17376. console.log(this, ": item ", item, " removed from root");
  17377. },
  17378. // =======================================================================
  17379. // Events from data store
  17380. _requeryTop: function(){
  17381. // reruns the query for the children of the root node,
  17382. // sending out an onSet notification if those children have changed
  17383. var oldChildren = this.root.children || [];
  17384. this.store.fetch({
  17385. query: this.query,
  17386. onComplete: dojo.hitch(this, function(newChildren){
  17387. this.root.children = newChildren;
  17388. // If the list of children or the order of children has changed...
  17389. if(oldChildren.length != newChildren.length ||
  17390. dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
  17391. this.onChildrenChange(this.root, newChildren);
  17392. }
  17393. })
  17394. });
  17395. },
  17396. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  17397. // summary:
  17398. // Handler for when new items appear in the store. Developers should override this
  17399. // method to be more efficient based on their app/data.
  17400. // description:
  17401. // Note that the default implementation requeries the top level items every time
  17402. // a new item is created, since any new item could be a top level item (even in
  17403. // addition to being a child of another item, since items can have multiple parents).
  17404. //
  17405. // If developers can detect which items are possible top level items (based on the item and the
  17406. // parentInfo parameters), they should override this method to only call _requeryTop() for top
  17407. // level items. Often all top level items have parentInfo==null, but
  17408. // that will depend on which store you use and what your data is like.
  17409. // tags:
  17410. // extension
  17411. this._requeryTop();
  17412. this.inherited(arguments);
  17413. },
  17414. onDeleteItem: function(/*Object*/ item){
  17415. // summary:
  17416. // Handler for delete notifications from underlying store
  17417. // check if this was a child of root, and if so send notification that root's children
  17418. // have changed
  17419. if(dojo.indexOf(this.root.children, item) != -1){
  17420. this._requeryTop();
  17421. }
  17422. this.inherited(arguments);
  17423. },
  17424. onSetItem: function(/* item */ item,
  17425. /* attribute-name-string */ attribute,
  17426. /* object | array */ oldValue,
  17427. /* object | array */ newValue){
  17428. // summary:
  17429. // Updates the tree view according to changes to an item in the data store.
  17430. // Developers should override this method to be more efficient based on their app/data.
  17431. // description:
  17432. // Handles updates to an item's children by calling onChildrenChange(), and
  17433. // other updates to an item by calling onChange().
  17434. //
  17435. // Also, any change to any item re-executes the query for the tree's top-level items,
  17436. // since this modified item may have started/stopped matching the query for top level items.
  17437. //
  17438. // If possible, developers should override this function to only call _requeryTop() when
  17439. // the change to the item has caused it to stop/start being a top level item in the tree.
  17440. // tags:
  17441. // extension
  17442. this._requeryTop();
  17443. this.inherited(arguments);
  17444. }
  17445. });
  17446. }
  17447. if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17448. dojo._hasResource["dojo.dnd.Container"] = true;
  17449. dojo.provide("dojo.dnd.Container");
  17450. /*
  17451. Container states:
  17452. "" - normal state
  17453. "Over" - mouse over a container
  17454. Container item states:
  17455. "" - normal state
  17456. "Over" - mouse over a container item
  17457. */
  17458. /*=====
  17459. dojo.declare("dojo.dnd.__ContainerArgs", [], {
  17460. creator: function(){
  17461. // summary:
  17462. // a creator function, which takes a data item, and returns an object like that:
  17463. // {node: newNode, data: usedData, type: arrayOfStrings}
  17464. },
  17465. // skipForm: Boolean
  17466. // don't start the drag operation, if clicked on form elements
  17467. skipForm: false,
  17468. // dropParent: Node||String
  17469. // node or node's id to use as the parent node for dropped items
  17470. // (must be underneath the 'node' parameter in the DOM)
  17471. dropParent: null,
  17472. // _skipStartup: Boolean
  17473. // skip startup(), which collects children, for deferred initialization
  17474. // (this is used in the markup mode)
  17475. _skipStartup: false
  17476. });
  17477. dojo.dnd.Item = function(){
  17478. // summary:
  17479. // Represents (one of) the source node(s) being dragged.
  17480. // Contains (at least) the "type" and "data" attributes.
  17481. // type: String[]
  17482. // Type(s) of this item, by default this is ["text"]
  17483. // data: Object
  17484. // Logical representation of the object being dragged.
  17485. // If the drag object's type is "text" then data is a String,
  17486. // if it's another type then data could be a different Object,
  17487. // perhaps a name/value hash.
  17488. this.type = type;
  17489. this.data = data;
  17490. }
  17491. =====*/
  17492. dojo.declare("dojo.dnd.Container", null, {
  17493. // summary:
  17494. // a Container object, which knows when mouse hovers over it,
  17495. // and over which element it hovers
  17496. // object attributes (for markup)
  17497. skipForm: false,
  17498. /*=====
  17499. // current: DomNode
  17500. // The DOM node the mouse is currently hovered over
  17501. current: null,
  17502. // map: Hash<String, dojo.dnd.Item>
  17503. // Map from an item's id (which is also the DOMNode's id) to
  17504. // the dojo.dnd.Item itself.
  17505. map: {},
  17506. =====*/
  17507. constructor: function(node, params){
  17508. // summary:
  17509. // a constructor of the Container
  17510. // node: Node
  17511. // node or node's id to build the container on
  17512. // params: dojo.dnd.__ContainerArgs
  17513. // a dictionary of parameters
  17514. this.node = dojo.byId(node);
  17515. if(!params){ params = {}; }
  17516. this.creator = params.creator || null;
  17517. this.skipForm = params.skipForm;
  17518. this.parent = params.dropParent && dojo.byId(params.dropParent);
  17519. // class-specific variables
  17520. this.map = {};
  17521. this.current = null;
  17522. // states
  17523. this.containerState = "";
  17524. dojo.addClass(this.node, "dojoDndContainer");
  17525. // mark up children
  17526. if(!(params && params._skipStartup)){
  17527. this.startup();
  17528. }
  17529. // set up events
  17530. this.events = [
  17531. dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
  17532. dojo.connect(this.node, "onmouseout", this, "onMouseOut"),
  17533. // cancel text selection and text dragging
  17534. dojo.connect(this.node, "ondragstart", this, "onSelectStart"),
  17535. dojo.connect(this.node, "onselectstart", this, "onSelectStart")
  17536. ];
  17537. },
  17538. // object attributes (for markup)
  17539. creator: function(){
  17540. // summary:
  17541. // creator function, dummy at the moment
  17542. },
  17543. // abstract access to the map
  17544. getItem: function(/*String*/ key){
  17545. // summary:
  17546. // returns a data item by its key (id)
  17547. return this.map[key]; // dojo.dnd.Item
  17548. },
  17549. setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
  17550. // summary:
  17551. // associates a data item with its key (id)
  17552. this.map[key] = data;
  17553. },
  17554. delItem: function(/*String*/ key){
  17555. // summary:
  17556. // removes a data item from the map by its key (id)
  17557. delete this.map[key];
  17558. },
  17559. forInItems: function(/*Function*/ f, /*Object?*/ o){
  17560. // summary:
  17561. // iterates over a data map skipping members that
  17562. // are present in the empty object (IE and/or 3rd-party libraries).
  17563. o = o || dojo.global;
  17564. var m = this.map, e = dojo.dnd._empty;
  17565. for(var i in m){
  17566. if(i in e){ continue; }
  17567. f.call(o, m[i], i, this);
  17568. }
  17569. return o; // Object
  17570. },
  17571. clearItems: function(){
  17572. // summary:
  17573. // removes all data items from the map
  17574. this.map = {};
  17575. },
  17576. // methods
  17577. getAllNodes: function(){
  17578. // summary:
  17579. // returns a list (an array) of all valid child nodes
  17580. return dojo.query("> .dojoDndItem", this.parent); // NodeList
  17581. },
  17582. sync: function(){
  17583. // summary:
  17584. // sync up the node list with the data map
  17585. var map = {};
  17586. this.getAllNodes().forEach(function(node){
  17587. if(node.id){
  17588. var item = this.getItem(node.id);
  17589. if(item){
  17590. map[node.id] = item;
  17591. return;
  17592. }
  17593. }else{
  17594. node.id = dojo.dnd.getUniqueId();
  17595. }
  17596. var type = node.getAttribute("dndType"),
  17597. data = node.getAttribute("dndData");
  17598. map[node.id] = {
  17599. data: data || node.innerHTML,
  17600. type: type ? type.split(/\s*,\s*/) : ["text"]
  17601. };
  17602. }, this);
  17603. this.map = map;
  17604. return this; // self
  17605. },
  17606. insertNodes: function(data, before, anchor){
  17607. // summary:
  17608. // inserts an array of new nodes before/after an anchor node
  17609. // data: Array
  17610. // a list of data items, which should be processed by the creator function
  17611. // before: Boolean
  17612. // insert before the anchor, if true, and after the anchor otherwise
  17613. // anchor: Node
  17614. // the anchor node to be used as a point of insertion
  17615. if(!this.parent.firstChild){
  17616. anchor = null;
  17617. }else if(before){
  17618. if(!anchor){
  17619. anchor = this.parent.firstChild;
  17620. }
  17621. }else{
  17622. if(anchor){
  17623. anchor = anchor.nextSibling;
  17624. }
  17625. }
  17626. if(anchor){
  17627. for(var i = 0; i < data.length; ++i){
  17628. var t = this._normalizedCreator(data[i]);
  17629. this.setItem(t.node.id, {data: t.data, type: t.type});
  17630. this.parent.insertBefore(t.node, anchor);
  17631. }
  17632. }else{
  17633. for(var i = 0; i < data.length; ++i){
  17634. var t = this._normalizedCreator(data[i]);
  17635. this.setItem(t.node.id, {data: t.data, type: t.type});
  17636. this.parent.appendChild(t.node);
  17637. }
  17638. }
  17639. return this; // self
  17640. },
  17641. destroy: function(){
  17642. // summary:
  17643. // prepares this object to be garbage-collected
  17644. dojo.forEach(this.events, dojo.disconnect);
  17645. this.clearItems();
  17646. this.node = this.parent = this.current = null;
  17647. },
  17648. // markup methods
  17649. markupFactory: function(params, node){
  17650. params._skipStartup = true;
  17651. return new dojo.dnd.Container(node, params);
  17652. },
  17653. startup: function(){
  17654. // summary:
  17655. // collects valid child items and populate the map
  17656. // set up the real parent node
  17657. if(!this.parent){
  17658. // use the standard algorithm, if not assigned
  17659. this.parent = this.node;
  17660. if(this.parent.tagName.toLowerCase() == "table"){
  17661. var c = this.parent.getElementsByTagName("tbody");
  17662. if(c && c.length){ this.parent = c[0]; }
  17663. }
  17664. }
  17665. this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
  17666. // process specially marked children
  17667. this.sync();
  17668. },
  17669. // mouse events
  17670. onMouseOver: function(e){
  17671. // summary:
  17672. // event processor for onmouseover
  17673. // e: Event
  17674. // mouse event
  17675. var n = e.relatedTarget;
  17676. while(n){
  17677. if(n == this.node){ break; }
  17678. try{
  17679. n = n.parentNode;
  17680. }catch(x){
  17681. n = null;
  17682. }
  17683. }
  17684. if(!n){
  17685. this._changeState("Container", "Over");
  17686. this.onOverEvent();
  17687. }
  17688. n = this._getChildByEvent(e);
  17689. if(this.current == n){ return; }
  17690. if(this.current){ this._removeItemClass(this.current, "Over"); }
  17691. if(n){ this._addItemClass(n, "Over"); }
  17692. this.current = n;
  17693. },
  17694. onMouseOut: function(e){
  17695. // summary:
  17696. // event processor for onmouseout
  17697. // e: Event
  17698. // mouse event
  17699. for(var n = e.relatedTarget; n;){
  17700. if(n == this.node){ return; }
  17701. try{
  17702. n = n.parentNode;
  17703. }catch(x){
  17704. n = null;
  17705. }
  17706. }
  17707. if(this.current){
  17708. this._removeItemClass(this.current, "Over");
  17709. this.current = null;
  17710. }
  17711. this._changeState("Container", "");
  17712. this.onOutEvent();
  17713. },
  17714. onSelectStart: function(e){
  17715. // summary:
  17716. // event processor for onselectevent and ondragevent
  17717. // e: Event
  17718. // mouse event
  17719. if(!this.skipForm || !dojo.dnd.isFormElement(e)){
  17720. dojo.stopEvent(e);
  17721. }
  17722. },
  17723. // utilities
  17724. onOverEvent: function(){
  17725. // summary:
  17726. // this function is called once, when mouse is over our container
  17727. },
  17728. onOutEvent: function(){
  17729. // summary:
  17730. // this function is called once, when mouse is out of our container
  17731. },
  17732. _changeState: function(type, newState){
  17733. // summary:
  17734. // changes a named state to new state value
  17735. // type: String
  17736. // a name of the state to change
  17737. // newState: String
  17738. // new state
  17739. var prefix = "dojoDnd" + type;
  17740. var state = type.toLowerCase() + "State";
  17741. //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  17742. dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  17743. this[state] = newState;
  17744. },
  17745. _addItemClass: function(node, type){
  17746. // summary:
  17747. // adds a class with prefix "dojoDndItem"
  17748. // node: Node
  17749. // a node
  17750. // type: String
  17751. // a variable suffix for a class name
  17752. dojo.addClass(node, "dojoDndItem" + type);
  17753. },
  17754. _removeItemClass: function(node, type){
  17755. // summary:
  17756. // removes a class with prefix "dojoDndItem"
  17757. // node: Node
  17758. // a node
  17759. // type: String
  17760. // a variable suffix for a class name
  17761. dojo.removeClass(node, "dojoDndItem" + type);
  17762. },
  17763. _getChildByEvent: function(e){
  17764. // summary:
  17765. // gets a child, which is under the mouse at the moment, or null
  17766. // e: Event
  17767. // a mouse event
  17768. var node = e.target;
  17769. if(node){
  17770. for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
  17771. if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
  17772. }
  17773. }
  17774. return null;
  17775. },
  17776. _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
  17777. // summary:
  17778. // adds all necessary data to the output of the user-supplied creator function
  17779. var t = (this.creator || this.defaultCreator).call(this, item, hint);
  17780. if(!dojo.isArray(t.type)){ t.type = ["text"]; }
  17781. if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
  17782. dojo.addClass(t.node, "dojoDndItem");
  17783. return t;
  17784. }
  17785. });
  17786. dojo.dnd._createNode = function(tag){
  17787. // summary:
  17788. // returns a function, which creates an element of given tag
  17789. // (SPAN by default) and sets its innerHTML to given text
  17790. // tag: String
  17791. // a tag name or empty for SPAN
  17792. if(!tag){ return dojo.dnd._createSpan; }
  17793. return function(text){ // Function
  17794. return dojo.create(tag, {innerHTML: text}); // Node
  17795. };
  17796. };
  17797. dojo.dnd._createTrTd = function(text){
  17798. // summary:
  17799. // creates a TR/TD structure with given text as an innerHTML of TD
  17800. // text: String
  17801. // a text for TD
  17802. var tr = dojo.create("tr");
  17803. dojo.create("td", {innerHTML: text}, tr);
  17804. return tr; // Node
  17805. };
  17806. dojo.dnd._createSpan = function(text){
  17807. // summary:
  17808. // creates a SPAN element with given text as its innerHTML
  17809. // text: String
  17810. // a text for SPAN
  17811. return dojo.create("span", {innerHTML: text}); // Node
  17812. };
  17813. // dojo.dnd._defaultCreatorNodes: Object
  17814. // a dictionary that maps container tag names to child tag names
  17815. dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
  17816. dojo.dnd._defaultCreator = function(node){
  17817. // summary:
  17818. // takes a parent node, and returns an appropriate creator function
  17819. // node: Node
  17820. // a container node
  17821. var tag = node.tagName.toLowerCase();
  17822. var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
  17823. dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
  17824. return function(item, hint){ // Function
  17825. var isObj = item && dojo.isObject(item), data, type, n;
  17826. if(isObj && item.tagName && item.nodeType && item.getAttribute){
  17827. // process a DOM node
  17828. data = item.getAttribute("dndData") || item.innerHTML;
  17829. type = item.getAttribute("dndType");
  17830. type = type ? type.split(/\s*,\s*/) : ["text"];
  17831. n = item; // this node is going to be moved rather than copied
  17832. }else{
  17833. // process a DnD item object or a string
  17834. data = (isObj && item.data) ? item.data : item;
  17835. type = (isObj && item.type) ? item.type : ["text"];
  17836. n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
  17837. }
  17838. if(!n.id){
  17839. n.id = dojo.dnd.getUniqueId();
  17840. }
  17841. return {node: n, data: data, type: type};
  17842. };
  17843. };
  17844. }
  17845. if(!dojo._hasResource["dijit.tree._dndContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17846. dojo._hasResource["dijit.tree._dndContainer"] = true;
  17847. dojo.provide("dijit.tree._dndContainer");
  17848. dojo.getObject("tree", true, dojo);
  17849. dijit.tree._compareNodes = function(n1, n2){
  17850. if(n1 === n2){
  17851. return 0;
  17852. }
  17853. if('sourceIndex' in document.documentElement){ //IE
  17854. //TODO: does not yet work if n1 and/or n2 is a text node
  17855. return n1.sourceIndex - n2.sourceIndex;
  17856. }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
  17857. return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
  17858. }else if(document.createRange){ //Webkit
  17859. var r1 = doc.createRange();
  17860. r1.setStartBefore(n1);
  17861. var r2 = doc.createRange();
  17862. r2.setStartBefore(n2);
  17863. return r1.compareBoundaryPoints(r1.END_TO_END, r2);
  17864. }else{
  17865. throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
  17866. }
  17867. };
  17868. dojo.declare("dijit.tree._dndContainer",
  17869. null,
  17870. {
  17871. // summary:
  17872. // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
  17873. // It's modeled after `dojo.dnd.Container`.
  17874. // tags:
  17875. // protected
  17876. /*=====
  17877. // current: DomNode
  17878. // The currently hovered TreeNode.rowNode (which is the DOM node
  17879. // associated w/a given node in the tree, excluding it's descendants)
  17880. current: null,
  17881. =====*/
  17882. constructor: function(tree, params){
  17883. // summary:
  17884. // A constructor of the Container
  17885. // tree: Node
  17886. // Node or node's id to build the container on
  17887. // params: dijit.tree.__SourceArgs
  17888. // A dict of parameters, which gets mixed into the object
  17889. // tags:
  17890. // private
  17891. this.tree = tree;
  17892. this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
  17893. dojo.mixin(this, params);
  17894. // class-specific variables
  17895. this.map = {};
  17896. this.current = null; // current TreeNode's DOM node
  17897. // states
  17898. this.containerState = "";
  17899. dojo.addClass(this.node, "dojoDndContainer");
  17900. // set up events
  17901. this.events = [
  17902. // container level events
  17903. dojo.connect(this.node, "onmouseenter", this, "onOverEvent"),
  17904. dojo.connect(this.node, "onmouseleave", this, "onOutEvent"),
  17905. // switching between TreeNodes
  17906. dojo.connect(this.tree, "_onNodeMouseEnter", this, "onMouseOver"),
  17907. dojo.connect(this.tree, "_onNodeMouseLeave", this, "onMouseOut"),
  17908. // cancel text selection and text dragging
  17909. dojo.connect(this.node, "ondragstart", dojo, "stopEvent"),
  17910. dojo.connect(this.node, "onselectstart", dojo, "stopEvent")
  17911. ];
  17912. },
  17913. getItem: function(/*String*/ key){
  17914. // summary:
  17915. // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
  17916. // Called by dojo.dnd.Source.checkAcceptance().
  17917. // tags:
  17918. // protected
  17919. var widget = this.selection[key],
  17920. ret = {
  17921. data: widget,
  17922. type: ["treeNode"]
  17923. };
  17924. return ret; // dojo.dnd.Item
  17925. },
  17926. destroy: function(){
  17927. // summary:
  17928. // Prepares this object to be garbage-collected
  17929. dojo.forEach(this.events, dojo.disconnect);
  17930. // this.clearItems();
  17931. this.node = this.parent = null;
  17932. },
  17933. // mouse events
  17934. onMouseOver: function(/*TreeNode*/ widget, /*Event*/ evt){
  17935. // summary:
  17936. // Called when mouse is moved over a TreeNode
  17937. // tags:
  17938. // protected
  17939. this.current = widget;
  17940. },
  17941. onMouseOut: function(/*TreeNode*/ widget, /*Event*/ evt){
  17942. // summary:
  17943. // Called when mouse is moved away from a TreeNode
  17944. // tags:
  17945. // protected
  17946. this.current = null;
  17947. },
  17948. _changeState: function(type, newState){
  17949. // summary:
  17950. // Changes a named state to new state value
  17951. // type: String
  17952. // A name of the state to change
  17953. // newState: String
  17954. // new state
  17955. var prefix = "dojoDnd" + type;
  17956. var state = type.toLowerCase() + "State";
  17957. //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  17958. dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  17959. this[state] = newState;
  17960. },
  17961. _addItemClass: function(node, type){
  17962. // summary:
  17963. // Adds a class with prefix "dojoDndItem"
  17964. // node: Node
  17965. // A node
  17966. // type: String
  17967. // A variable suffix for a class name
  17968. dojo.addClass(node, "dojoDndItem" + type);
  17969. },
  17970. _removeItemClass: function(node, type){
  17971. // summary:
  17972. // Removes a class with prefix "dojoDndItem"
  17973. // node: Node
  17974. // A node
  17975. // type: String
  17976. // A variable suffix for a class name
  17977. dojo.removeClass(node, "dojoDndItem" + type);
  17978. },
  17979. onOverEvent: function(){
  17980. // summary:
  17981. // This function is called once, when mouse is over our container
  17982. // tags:
  17983. // protected
  17984. this._changeState("Container", "Over");
  17985. },
  17986. onOutEvent: function(){
  17987. // summary:
  17988. // This function is called once, when mouse is out of our container
  17989. // tags:
  17990. // protected
  17991. this._changeState("Container", "");
  17992. }
  17993. });
  17994. }
  17995. if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17996. dojo._hasResource["dijit.tree._dndSelector"] = true;
  17997. dojo.provide("dijit.tree._dndSelector");
  17998. dojo.declare("dijit.tree._dndSelector",
  17999. dijit.tree._dndContainer,
  18000. {
  18001. // summary:
  18002. // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
  18003. // It's based on `dojo.dnd.Selector`.
  18004. // tags:
  18005. // protected
  18006. /*=====
  18007. // selection: Hash<String, DomNode>
  18008. // (id, DomNode) map for every TreeNode that's currently selected.
  18009. // The DOMNode is the TreeNode.rowNode.
  18010. selection: {},
  18011. =====*/
  18012. constructor: function(tree, params){
  18013. // summary:
  18014. // Initialization
  18015. // tags:
  18016. // private
  18017. this.selection={};
  18018. this.anchor = null;
  18019. dijit.setWaiState(this.tree.domNode, "multiselect", !this.singular);
  18020. this.events.push(
  18021. dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"),
  18022. dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"),
  18023. dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove")
  18024. );
  18025. },
  18026. // singular: Boolean
  18027. // Allows selection of only one element, if true.
  18028. // Tree hasn't been tested in singular=true mode, unclear if it works.
  18029. singular: false,
  18030. // methods
  18031. getSelectedTreeNodes: function(){
  18032. // summary:
  18033. // Returns a list of selected node(s).
  18034. // Used by dndSource on the start of a drag.
  18035. // tags:
  18036. // protected
  18037. var nodes=[], sel = this.selection;
  18038. for(var i in sel){
  18039. nodes.push(sel[i]);
  18040. }
  18041. return nodes;
  18042. },
  18043. selectNone: function(){
  18044. // summary:
  18045. // Unselects all items
  18046. // tags:
  18047. // private
  18048. this.setSelection([]);
  18049. return this; // self
  18050. },
  18051. destroy: function(){
  18052. // summary:
  18053. // Prepares the object to be garbage-collected
  18054. this.inherited(arguments);
  18055. this.selection = this.anchor = null;
  18056. },
  18057. addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
  18058. // summary
  18059. // add node to current selection
  18060. // node: Node
  18061. // node to add
  18062. // isAnchor: Boolean
  18063. // Whether the node should become anchor.
  18064. this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
  18065. if(isAnchor){ this.anchor = node; }
  18066. return node;
  18067. },
  18068. removeTreeNode: function(/*dijit._TreeNode*/node){
  18069. // summary
  18070. // remove node from current selection
  18071. // node: Node
  18072. // node to remove
  18073. this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]))
  18074. return node;
  18075. },
  18076. isTreeNodeSelected: function(/*dijit._TreeNode*/node){
  18077. // summary
  18078. // return true if node is currently selected
  18079. // node: Node
  18080. // the node to check whether it's in the current selection
  18081. return node.id && !!this.selection[node.id];
  18082. },
  18083. setSelection: function(/*dijit._treeNode[]*/ newSelection){
  18084. // summary
  18085. // set the list of selected nodes to be exactly newSelection. All changes to the
  18086. // selection should be passed through this function, which ensures that derived
  18087. // attributes are kept up to date. Anchor will be deleted if it has been removed
  18088. // from the selection, but no new anchor will be added by this function.
  18089. // newSelection: Node[]
  18090. // list of tree nodes to make selected
  18091. var oldSelection = this.getSelectedTreeNodes();
  18092. dojo.forEach(this._setDifference(oldSelection, newSelection), dojo.hitch(this, function(node){
  18093. node.setSelected(false);
  18094. if(this.anchor == node){
  18095. delete this.anchor;
  18096. }
  18097. delete this.selection[node.id];
  18098. }));
  18099. dojo.forEach(this._setDifference(newSelection, oldSelection), dojo.hitch(this, function(node){
  18100. node.setSelected(true);
  18101. this.selection[node.id] = node;
  18102. }));
  18103. this._updateSelectionProperties();
  18104. },
  18105. _setDifference: function(xs,ys){
  18106. // summary
  18107. // Returns a copy of xs which lacks any objects
  18108. // occurring in ys. Checks for membership by
  18109. // modifying and then reading the object, so it will
  18110. // not properly handle sets of numbers or strings.
  18111. dojo.forEach(ys, function(y){ y.__exclude__ = true; });
  18112. var ret = dojo.filter(xs, function(x){ return !x.__exclude__; });
  18113. // clean up after ourselves.
  18114. dojo.forEach(ys, function(y){ delete y['__exclude__'] });
  18115. return ret;
  18116. },
  18117. _updateSelectionProperties: function() {
  18118. // summary
  18119. // Update the following tree properties from the current selection:
  18120. // path[s], selectedItem[s], selectedNode[s]
  18121. var selected = this.getSelectedTreeNodes();
  18122. var paths = [], nodes = [];
  18123. dojo.forEach(selected, function(node) {
  18124. nodes.push(node);
  18125. paths.push(node.getTreePath());
  18126. });
  18127. var items = dojo.map(nodes,function(node) { return node.item; });
  18128. this.tree._set("paths", paths);
  18129. this.tree._set("path", paths[0] || []);
  18130. this.tree._set("selectedNodes", nodes);
  18131. this.tree._set("selectedNode", nodes[0] || null);
  18132. this.tree._set("selectedItems", items);
  18133. this.tree._set("selectedItem", items[0] || null);
  18134. },
  18135. // mouse events
  18136. onMouseDown: function(e){
  18137. // summary:
  18138. // Event processor for onmousedown
  18139. // e: Event
  18140. // mouse event
  18141. // tags:
  18142. // protected
  18143. // ignore click on expando node
  18144. if(!this.current || this.tree.isExpandoNode( e.target, this.current)){ return; }
  18145. if(e.button == dojo.mouseButtons.RIGHT){ return; } // ignore right-click
  18146. dojo.stopEvent(e);
  18147. var treeNode = this.current,
  18148. copy = dojo.isCopyKey(e), id = treeNode.id;
  18149. // if shift key is not pressed, and the node is already in the selection,
  18150. // delay deselection until onmouseup so in the case of DND, deselection
  18151. // will be canceled by onmousemove.
  18152. if(!this.singular && !e.shiftKey && this.selection[id]){
  18153. this._doDeselect = true;
  18154. return;
  18155. }else{
  18156. this._doDeselect = false;
  18157. }
  18158. this.userSelect(treeNode, copy, e.shiftKey);
  18159. },
  18160. onMouseUp: function(e){
  18161. // summary:
  18162. // Event processor for onmouseup
  18163. // e: Event
  18164. // mouse event
  18165. // tags:
  18166. // protected
  18167. // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
  18168. // a already selected item (to deselect the item), or click on a not-yet selected item
  18169. // (which should remove all current selection, and add the clicked item). This can not
  18170. // be done in onMouseDown, because the user may start a drag after mousedown. By moving
  18171. // the deselection logic here, the user can drags an already selected item.
  18172. if(!this._doDeselect){ return; }
  18173. this._doDeselect = false;
  18174. this.userSelect(this.current, dojo.isCopyKey( e ), e.shiftKey);
  18175. },
  18176. onMouseMove: function(e){
  18177. // summary
  18178. // event processor for onmousemove
  18179. // e: Event
  18180. // mouse event
  18181. this._doDeselect = false;
  18182. },
  18183. userSelect: function(node, multi, range){
  18184. // summary:
  18185. // Add or remove the given node from selection, responding
  18186. // to a user action such as a click or keypress.
  18187. // multi: Boolean
  18188. // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
  18189. // range: Boolean
  18190. // Indicates whether this is meant to be a ranged action (e.g. shift-click)
  18191. // tags:
  18192. // protected
  18193. if(this.singular){
  18194. if(this.anchor == node && multi){
  18195. this.selectNone();
  18196. }else{
  18197. this.setSelection([node]);
  18198. this.anchor = node;
  18199. }
  18200. }else{
  18201. if(range && this.anchor){
  18202. var cr = dijit.tree._compareNodes(this.anchor.rowNode, node.rowNode),
  18203. begin, end, anchor = this.anchor;
  18204. if(cr < 0){ //current is after anchor
  18205. begin = anchor;
  18206. end = node;
  18207. }else{ //current is before anchor
  18208. begin = node;
  18209. end = anchor;
  18210. }
  18211. nodes = [];
  18212. //add everything betweeen begin and end inclusively
  18213. while(begin != end) {
  18214. nodes.push(begin)
  18215. begin = this.tree._getNextNode(begin);
  18216. }
  18217. nodes.push(end)
  18218. this.setSelection(nodes);
  18219. }else{
  18220. if( this.selection[ node.id ] && multi ) {
  18221. this.removeTreeNode( node );
  18222. } else if(multi) {
  18223. this.addTreeNode(node, true);
  18224. } else {
  18225. this.setSelection([node]);
  18226. this.anchor = node;
  18227. }
  18228. }
  18229. }
  18230. },
  18231. forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
  18232. // summary:
  18233. // Iterates over selected items;
  18234. // see `dojo.dnd.Container.forInItems()` for details
  18235. o = o || dojo.global;
  18236. for(var id in this.selection){
  18237. // console.log("selected item id: " + id);
  18238. f.call(o, this.getItem(id), id, this);
  18239. }
  18240. }
  18241. });
  18242. }
  18243. if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18244. dojo._hasResource["dijit.Tree"] = true;
  18245. dojo.provide("dijit.Tree");
  18246. dojo.declare(
  18247. "dijit._TreeNode",
  18248. [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
  18249. {
  18250. // summary:
  18251. // Single node within a tree. This class is used internally
  18252. // by Tree and should not be accessed directly.
  18253. // tags:
  18254. // private
  18255. // item: [const] dojo.data.Item
  18256. // the dojo.data entry this tree represents
  18257. item: null,
  18258. // isTreeNode: [protected] Boolean
  18259. // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
  18260. // should not be accessed directly.
  18261. isTreeNode: true,
  18262. // label: String
  18263. // Text of this tree node
  18264. label: "",
  18265. // isExpandable: [private] Boolean
  18266. // This node has children, so show the expando node (+ sign)
  18267. isExpandable: null,
  18268. // isExpanded: [readonly] Boolean
  18269. // This node is currently expanded (ie, opened)
  18270. isExpanded: false,
  18271. // state: [private] String
  18272. // Dynamic loading-related stuff.
  18273. // When an empty folder node appears, it is "UNCHECKED" first,
  18274. // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
  18275. state: "UNCHECKED",
  18276. templateString: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n"),
  18277. baseClass: "dijitTreeNode",
  18278. // For hover effect for tree node, and focus effect for label
  18279. cssStateNodes: {
  18280. rowNode: "dijitTreeRow",
  18281. labelNode: "dijitTreeLabel"
  18282. },
  18283. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  18284. label: {node: "labelNode", type: "innerText"},
  18285. tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
  18286. }),
  18287. buildRendering: function(){
  18288. this.inherited(arguments);
  18289. // set expand icon for leaf
  18290. this._setExpando();
  18291. // set icon and label class based on item
  18292. this._updateItemClasses(this.item);
  18293. if(this.isExpandable){
  18294. dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
  18295. }
  18296. //aria-selected should be false on all selectable elements.
  18297. this.setSelected(false);
  18298. },
  18299. _setIndentAttr: function(indent){
  18300. // summary:
  18301. // Tell this node how many levels it should be indented
  18302. // description:
  18303. // 0 for top level nodes, 1 for their children, 2 for their
  18304. // grandchildren, etc.
  18305. // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
  18306. var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
  18307. dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
  18308. dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
  18309. dojo.forEach(this.getChildren(), function(child){
  18310. child.set("indent", indent+1);
  18311. });
  18312. this._set("indent", indent);
  18313. },
  18314. markProcessing: function(){
  18315. // summary:
  18316. // Visually denote that tree is loading data, etc.
  18317. // tags:
  18318. // private
  18319. this.state = "LOADING";
  18320. this._setExpando(true);
  18321. },
  18322. unmarkProcessing: function(){
  18323. // summary:
  18324. // Clear markup from markProcessing() call
  18325. // tags:
  18326. // private
  18327. this._setExpando(false);
  18328. },
  18329. _updateItemClasses: function(item){
  18330. // summary:
  18331. // Set appropriate CSS classes for icon and label dom node
  18332. // (used to allow for item updates to change respective CSS)
  18333. // tags:
  18334. // private
  18335. var tree = this.tree, model = tree.model;
  18336. if(tree._v10Compat && item === model.root){
  18337. // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
  18338. item = null;
  18339. }
  18340. this._applyClassAndStyle(item, "icon", "Icon");
  18341. this._applyClassAndStyle(item, "label", "Label");
  18342. this._applyClassAndStyle(item, "row", "Row");
  18343. },
  18344. _applyClassAndStyle: function(item, lower, upper){
  18345. // summary:
  18346. // Set the appropriate CSS classes and styles for labels, icons and rows.
  18347. //
  18348. // item:
  18349. // The data item.
  18350. //
  18351. // lower:
  18352. // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
  18353. //
  18354. // upper:
  18355. // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
  18356. //
  18357. // tags:
  18358. // private
  18359. var clsName = "_" + lower + "Class";
  18360. var nodeName = lower + "Node";
  18361. var oldCls = this[clsName];
  18362. this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
  18363. dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
  18364. dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
  18365. },
  18366. _updateLayout: function(){
  18367. // summary:
  18368. // Set appropriate CSS classes for this.domNode
  18369. // tags:
  18370. // private
  18371. var parent = this.getParent();
  18372. if(!parent || parent.rowNode.style.display == "none"){
  18373. /* if we are hiding the root node then make every first level child look like a root node */
  18374. dojo.addClass(this.domNode, "dijitTreeIsRoot");
  18375. }else{
  18376. dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
  18377. }
  18378. },
  18379. _setExpando: function(/*Boolean*/ processing){
  18380. // summary:
  18381. // Set the right image for the expando node
  18382. // tags:
  18383. // private
  18384. var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
  18385. "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
  18386. _a11yStates = ["*","-","+","*"],
  18387. idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
  18388. // apply the appropriate class to the expando node
  18389. dojo.replaceClass(this.expandoNode, styles[idx], styles);
  18390. // provide a non-image based indicator for images-off mode
  18391. this.expandoNodeText.innerHTML = _a11yStates[idx];
  18392. },
  18393. expand: function(){
  18394. // summary:
  18395. // Show my children
  18396. // returns:
  18397. // Deferred that fires when expansion is complete
  18398. // If there's already an expand in progress or we are already expanded, just return
  18399. if(this._expandDeferred){
  18400. return this._expandDeferred; // dojo.Deferred
  18401. }
  18402. // cancel in progress collapse operation
  18403. this._wipeOut && this._wipeOut.stop();
  18404. // All the state information for when a node is expanded, maybe this should be
  18405. // set when the animation completes instead
  18406. this.isExpanded = true;
  18407. dijit.setWaiState(this.labelNode, "expanded", "true");
  18408. if(this.tree.showRoot || this !== this.tree.rootNode){
  18409. dijit.setWaiRole(this.containerNode, "group");
  18410. }
  18411. dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
  18412. this._setExpando();
  18413. this._updateItemClasses(this.item);
  18414. if(this == this.tree.rootNode){
  18415. dijit.setWaiState(this.tree.domNode, "expanded", "true");
  18416. }
  18417. var def,
  18418. wipeIn = dojo.fx.wipeIn({
  18419. node: this.containerNode, duration: dijit.defaultDuration,
  18420. onEnd: function(){
  18421. def.callback(true);
  18422. }
  18423. });
  18424. // Deferred that fires when expand is complete
  18425. def = (this._expandDeferred = new dojo.Deferred(function(){
  18426. // Canceller
  18427. wipeIn.stop();
  18428. }));
  18429. wipeIn.play();
  18430. return def; // dojo.Deferred
  18431. },
  18432. collapse: function(){
  18433. // summary:
  18434. // Collapse this node (if it's expanded)
  18435. if(!this.isExpanded){ return; }
  18436. // cancel in progress expand operation
  18437. if(this._expandDeferred){
  18438. this._expandDeferred.cancel();
  18439. delete this._expandDeferred;
  18440. }
  18441. this.isExpanded = false;
  18442. dijit.setWaiState(this.labelNode, "expanded", "false");
  18443. if(this == this.tree.rootNode){
  18444. dijit.setWaiState(this.tree.domNode, "expanded", "false");
  18445. }
  18446. dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
  18447. this._setExpando();
  18448. this._updateItemClasses(this.item);
  18449. if(!this._wipeOut){
  18450. this._wipeOut = dojo.fx.wipeOut({
  18451. node: this.containerNode, duration: dijit.defaultDuration
  18452. });
  18453. }
  18454. this._wipeOut.play();
  18455. },
  18456. // indent: Integer
  18457. // Levels from this node to the root node
  18458. indent: 0,
  18459. setChildItems: function(/* Object[] */ items){
  18460. // summary:
  18461. // Sets the child items of this node, removing/adding nodes
  18462. // from current children to match specified items[] array.
  18463. // Also, if this.persist == true, expands any children that were previously
  18464. // opened.
  18465. // returns:
  18466. // Deferred object that fires after all previously opened children
  18467. // have been expanded again (or fires instantly if there are no such children).
  18468. var tree = this.tree,
  18469. model = tree.model,
  18470. defs = []; // list of deferreds that need to fire before I am complete
  18471. // Orphan all my existing children.
  18472. // If items contains some of the same items as before then we will reattach them.
  18473. // Don't call this.removeChild() because that will collapse the tree etc.
  18474. dojo.forEach(this.getChildren(), function(child){
  18475. dijit._Container.prototype.removeChild.call(this, child);
  18476. }, this);
  18477. this.state = "LOADED";
  18478. if(items && items.length > 0){
  18479. this.isExpandable = true;
  18480. // Create _TreeNode widget for each specified tree node, unless one already
  18481. // exists and isn't being used (presumably it's from a DnD move and was recently
  18482. // released
  18483. dojo.forEach(items, function(item){
  18484. var id = model.getIdentity(item),
  18485. existingNodes = tree._itemNodesMap[id],
  18486. node;
  18487. if(existingNodes){
  18488. for(var i=0;i<existingNodes.length;i++){
  18489. if(existingNodes[i] && !existingNodes[i].getParent()){
  18490. node = existingNodes[i];
  18491. node.set('indent', this.indent+1);
  18492. break;
  18493. }
  18494. }
  18495. }
  18496. if(!node){
  18497. node = this.tree._createTreeNode({
  18498. item: item,
  18499. tree: tree,
  18500. isExpandable: model.mayHaveChildren(item),
  18501. label: tree.getLabel(item),
  18502. tooltip: tree.getTooltip(item),
  18503. dir: tree.dir,
  18504. lang: tree.lang,
  18505. indent: this.indent + 1
  18506. });
  18507. if(existingNodes){
  18508. existingNodes.push(node);
  18509. }else{
  18510. tree._itemNodesMap[id] = [node];
  18511. }
  18512. }
  18513. this.addChild(node);
  18514. // If node was previously opened then open it again now (this may trigger
  18515. // more data store accesses, recursively)
  18516. if(this.tree.autoExpand || this.tree._state(item)){
  18517. defs.push(tree._expandNode(node));
  18518. }
  18519. }, this);
  18520. // note that updateLayout() needs to be called on each child after
  18521. // _all_ the children exist
  18522. dojo.forEach(this.getChildren(), function(child, idx){
  18523. child._updateLayout();
  18524. });
  18525. }else{
  18526. this.isExpandable=false;
  18527. }
  18528. if(this._setExpando){
  18529. // change expando to/from dot or + icon, as appropriate
  18530. this._setExpando(false);
  18531. }
  18532. // Set leaf icon or folder icon, as appropriate
  18533. this._updateItemClasses(this.item);
  18534. // On initial tree show, make the selected TreeNode as either the root node of the tree,
  18535. // or the first child, if the root node is hidden
  18536. if(this == tree.rootNode){
  18537. var fc = this.tree.showRoot ? this : this.getChildren()[0];
  18538. if(fc){
  18539. fc.setFocusable(true);
  18540. tree.lastFocused = fc;
  18541. }else{
  18542. // fallback: no nodes in tree so focus on Tree <div> itself
  18543. tree.domNode.setAttribute("tabIndex", "0");
  18544. }
  18545. }
  18546. return new dojo.DeferredList(defs); // dojo.Deferred
  18547. },
  18548. getTreePath: function(){
  18549. var node = this;
  18550. var path = [];
  18551. while(node && node !== this.tree.rootNode){
  18552. path.unshift(node.item);
  18553. node = node.getParent();
  18554. }
  18555. path.unshift(this.tree.rootNode.item);
  18556. return path;
  18557. },
  18558. getIdentity: function() {
  18559. return this.tree.model.getIdentity(this.item);
  18560. },
  18561. removeChild: function(/* treeNode */ node){
  18562. this.inherited(arguments);
  18563. var children = this.getChildren();
  18564. if(children.length == 0){
  18565. this.isExpandable = false;
  18566. this.collapse();
  18567. }
  18568. dojo.forEach(children, function(child){
  18569. child._updateLayout();
  18570. });
  18571. },
  18572. makeExpandable: function(){
  18573. // summary:
  18574. // if this node wasn't already showing the expando node,
  18575. // turn it into one and call _setExpando()
  18576. // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
  18577. this.isExpandable = true;
  18578. this._setExpando(false);
  18579. },
  18580. _onLabelFocus: function(evt){
  18581. // summary:
  18582. // Called when this row is focused (possibly programatically)
  18583. // Note that we aren't using _onFocus() builtin to dijit
  18584. // because it's called when focus is moved to a descendant TreeNode.
  18585. // tags:
  18586. // private
  18587. this.tree._onNodeFocus(this);
  18588. },
  18589. setSelected: function(/*Boolean*/ selected){
  18590. // summary:
  18591. // A Tree has a (single) currently selected node.
  18592. // Mark that this node is/isn't that currently selected node.
  18593. // description:
  18594. // In particular, setting a node as selected involves setting tabIndex
  18595. // so that when user tabs to the tree, focus will go to that node (only).
  18596. dijit.setWaiState(this.labelNode, "selected", selected);
  18597. dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
  18598. },
  18599. setFocusable: function(/*Boolean*/ selected){
  18600. // summary:
  18601. // A Tree has a (single) node that's focusable.
  18602. // Mark that this node is/isn't that currently focsuable node.
  18603. // description:
  18604. // In particular, setting a node as selected involves setting tabIndex
  18605. // so that when user tabs to the tree, focus will go to that node (only).
  18606. this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
  18607. },
  18608. _onClick: function(evt){
  18609. // summary:
  18610. // Handler for onclick event on a node
  18611. // tags:
  18612. // private
  18613. this.tree._onClick(this, evt);
  18614. },
  18615. _onDblClick: function(evt){
  18616. // summary:
  18617. // Handler for ondblclick event on a node
  18618. // tags:
  18619. // private
  18620. this.tree._onDblClick(this, evt);
  18621. },
  18622. _onMouseEnter: function(evt){
  18623. // summary:
  18624. // Handler for onmouseenter event on a node
  18625. // tags:
  18626. // private
  18627. this.tree._onNodeMouseEnter(this, evt);
  18628. },
  18629. _onMouseLeave: function(evt){
  18630. // summary:
  18631. // Handler for onmouseenter event on a node
  18632. // tags:
  18633. // private
  18634. this.tree._onNodeMouseLeave(this, evt);
  18635. }
  18636. });
  18637. dojo.declare(
  18638. "dijit.Tree",
  18639. [dijit._Widget, dijit._Templated],
  18640. {
  18641. // summary:
  18642. // This widget displays hierarchical data from a store.
  18643. // store: [deprecated] String||dojo.data.Store
  18644. // Deprecated. Use "model" parameter instead.
  18645. // The store to get data to display in the tree.
  18646. store: null,
  18647. // model: dijit.Tree.model
  18648. // Interface to read tree data, get notifications of changes to tree data,
  18649. // and for handling drop operations (i.e drag and drop onto the tree)
  18650. model: null,
  18651. // query: [deprecated] anything
  18652. // Deprecated. User should specify query to the model directly instead.
  18653. // Specifies datastore query to return the root item or top items for the tree.
  18654. query: null,
  18655. // label: [deprecated] String
  18656. // Deprecated. Use dijit.tree.ForestStoreModel directly instead.
  18657. // Used in conjunction with query parameter.
  18658. // If a query is specified (rather than a root node id), and a label is also specified,
  18659. // then a fake root node is created and displayed, with this label.
  18660. label: "",
  18661. // showRoot: [const] Boolean
  18662. // Should the root node be displayed, or hidden?
  18663. showRoot: true,
  18664. // childrenAttr: [deprecated] String[]
  18665. // Deprecated. This information should be specified in the model.
  18666. // One ore more attributes that holds children of a tree node
  18667. childrenAttr: ["children"],
  18668. // paths: String[][] or Item[][]
  18669. // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
  18670. // Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
  18671. // returns a Deferred to indicate when the set is complete.
  18672. paths: [],
  18673. // path: String[] or Item[]
  18674. // Backward compatible singular variant of paths.
  18675. path: [],
  18676. // selectedItems: [readonly] Item[]
  18677. // The currently selected items in this tree.
  18678. // This property can only be set (via set('selectedItems', ...)) when that item is already
  18679. // visible in the tree. (I.e. the tree has already been expanded to show that node.)
  18680. // Should generally use `paths` attribute to set the selected items instead.
  18681. selectedItems: null,
  18682. // selectedItem: [readonly] Item
  18683. // Backward compatible singular variant of selectedItems.
  18684. selectedItem: null,
  18685. // openOnClick: Boolean
  18686. // If true, clicking a folder node's label will open it, rather than calling onClick()
  18687. openOnClick: false,
  18688. // openOnDblClick: Boolean
  18689. // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
  18690. openOnDblClick: false,
  18691. templateString: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"),
  18692. // persist: Boolean
  18693. // Enables/disables use of cookies for state saving.
  18694. persist: true,
  18695. // autoExpand: Boolean
  18696. // Fully expand the tree on load. Overrides `persist`.
  18697. autoExpand: false,
  18698. // dndController: [protected] String
  18699. // Class name to use as as the dnd controller. Specifying this class enables DnD.
  18700. // Generally you should specify this as "dijit.tree.dndSource".
  18701. // Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
  18702. dndController: "dijit.tree._dndSelector",
  18703. // parameters to pull off of the tree and pass on to the dndController as its params
  18704. dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
  18705. //declare the above items so they can be pulled from the tree's markup
  18706. // onDndDrop: [protected] Function
  18707. // Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
  18708. // Generally this doesn't need to be set.
  18709. onDndDrop: null,
  18710. /*=====
  18711. itemCreator: function(nodes, target, source){
  18712. // summary:
  18713. // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
  18714. // dropped onto the tree. Developer must override this method to enable
  18715. // dropping from external sources onto this Tree, unless the Tree.model's items
  18716. // happen to look like {id: 123, name: "Apple" } with no other attributes.
  18717. // description:
  18718. // For each node in nodes[], which came from source, create a hash of name/value
  18719. // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
  18720. // nodes: DomNode[]
  18721. // The DOMNodes dragged from the source container
  18722. // target: DomNode
  18723. // The target TreeNode.rowNode
  18724. // source: dojo.dnd.Source
  18725. // The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
  18726. // returns: Object[]
  18727. // Array of name/value hashes for each new item to be added to the Tree, like:
  18728. // | [
  18729. // | { id: 123, label: "apple", foo: "bar" },
  18730. // | { id: 456, label: "pear", zaz: "bam" }
  18731. // | ]
  18732. // tags:
  18733. // extension
  18734. return [{}];
  18735. },
  18736. =====*/
  18737. itemCreator: null,
  18738. // onDndCancel: [protected] Function
  18739. // Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
  18740. // Generally this doesn't need to be set.
  18741. onDndCancel: null,
  18742. /*=====
  18743. checkAcceptance: function(source, nodes){
  18744. // summary:
  18745. // Checks if the Tree itself can accept nodes from this source
  18746. // source: dijit.tree._dndSource
  18747. // The source which provides items
  18748. // nodes: DOMNode[]
  18749. // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
  18750. // source is a dijit.Tree.
  18751. // tags:
  18752. // extension
  18753. return true; // Boolean
  18754. },
  18755. =====*/
  18756. checkAcceptance: null,
  18757. /*=====
  18758. checkItemAcceptance: function(target, source, position){
  18759. // summary:
  18760. // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
  18761. // description:
  18762. // In the base case, this is called to check if target can become a child of source.
  18763. // When betweenThreshold is set, position="before" or "after" means that we
  18764. // are asking if the source node can be dropped before/after the target node.
  18765. // target: DOMNode
  18766. // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
  18767. // Use dijit.getEnclosingWidget(target) to get the TreeNode.
  18768. // source: dijit.tree.dndSource
  18769. // The (set of) nodes we are dropping
  18770. // position: String
  18771. // "over", "before", or "after"
  18772. // tags:
  18773. // extension
  18774. return true; // Boolean
  18775. },
  18776. =====*/
  18777. checkItemAcceptance: null,
  18778. // dragThreshold: Integer
  18779. // Number of pixels mouse moves before it's considered the start of a drag operation
  18780. dragThreshold: 5,
  18781. // betweenThreshold: Integer
  18782. // Set to a positive value to allow drag and drop "between" nodes.
  18783. //
  18784. // If during DnD mouse is over a (target) node but less than betweenThreshold
  18785. // pixels from the bottom edge, dropping the the dragged node will make it
  18786. // the next sibling of the target node, rather than the child.
  18787. //
  18788. // Similarly, if mouse is over a target node but less that betweenThreshold
  18789. // pixels from the top edge, dropping the dragged node will make it
  18790. // the target node's previous sibling rather than the target node's child.
  18791. betweenThreshold: 0,
  18792. // _nodePixelIndent: Integer
  18793. // Number of pixels to indent tree nodes (relative to parent node).
  18794. // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
  18795. // and calling resize() or startup() on tree after it's in the DOM.
  18796. _nodePixelIndent: 19,
  18797. _publish: function(/*String*/ topicName, /*Object*/ message){
  18798. // summary:
  18799. // Publish a message for this widget/topic
  18800. dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
  18801. },
  18802. postMixInProperties: function(){
  18803. this.tree = this;
  18804. if(this.autoExpand){
  18805. // There's little point in saving opened/closed state of nodes for a Tree
  18806. // that initially opens all it's nodes.
  18807. this.persist = false;
  18808. }
  18809. this._itemNodesMap={};
  18810. if(!this.cookieName){
  18811. this.cookieName = this.id + "SaveStateCookie";
  18812. }
  18813. this._loadDeferred = new dojo.Deferred();
  18814. this.inherited(arguments);
  18815. },
  18816. postCreate: function(){
  18817. this._initState();
  18818. // Create glue between store and Tree, if not specified directly by user
  18819. if(!this.model){
  18820. this._store2model();
  18821. }
  18822. // monitor changes to items
  18823. this.connect(this.model, "onChange", "_onItemChange");
  18824. this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
  18825. this.connect(this.model, "onDelete", "_onItemDelete");
  18826. this._load();
  18827. this.inherited(arguments);
  18828. if(this.dndController){
  18829. if(dojo.isString(this.dndController)){
  18830. this.dndController = dojo.getObject(this.dndController);
  18831. }
  18832. var params={};
  18833. for(var i=0; i<this.dndParams.length;i++){
  18834. if(this[this.dndParams[i]]){
  18835. params[this.dndParams[i]] = this[this.dndParams[i]];
  18836. }
  18837. }
  18838. this.dndController = new this.dndController(this, params);
  18839. }
  18840. },
  18841. _store2model: function(){
  18842. // summary:
  18843. // User specified a store&query rather than model, so create model from store/query
  18844. this._v10Compat = true;
  18845. dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
  18846. var modelParams = {
  18847. id: this.id + "_ForestStoreModel",
  18848. store: this.store,
  18849. query: this.query,
  18850. childrenAttrs: this.childrenAttr
  18851. };
  18852. // Only override the model's mayHaveChildren() method if the user has specified an override
  18853. if(this.params.mayHaveChildren){
  18854. modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
  18855. }
  18856. if(this.params.getItemChildren){
  18857. modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
  18858. this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
  18859. });
  18860. }
  18861. this.model = new dijit.tree.ForestStoreModel(modelParams);
  18862. // For backwards compatibility, the visibility of the root node is controlled by
  18863. // whether or not the user has specified a label
  18864. this.showRoot = Boolean(this.label);
  18865. },
  18866. onLoad: function(){
  18867. // summary:
  18868. // Called when tree finishes loading and expanding.
  18869. // description:
  18870. // If persist == true the loading may encompass many levels of fetches
  18871. // from the data store, each asynchronous. Waits for all to finish.
  18872. // tags:
  18873. // callback
  18874. },
  18875. _load: function(){
  18876. // summary:
  18877. // Initial load of the tree.
  18878. // Load root node (possibly hidden) and it's children.
  18879. this.model.getRoot(
  18880. dojo.hitch(this, function(item){
  18881. var rn = (this.rootNode = this.tree._createTreeNode({
  18882. item: item,
  18883. tree: this,
  18884. isExpandable: true,
  18885. label: this.label || this.getLabel(item),
  18886. indent: this.showRoot ? 0 : -1
  18887. }));
  18888. if(!this.showRoot){
  18889. rn.rowNode.style.display="none";
  18890. // if root is not visible, move tree role to the invisible
  18891. // root node's containerNode, see #12135
  18892. dijit.setWaiRole(this.domNode, 'presentation');
  18893. dijit.setWaiRole(rn.labelNode, 'presentation');
  18894. dijit.setWaiRole(rn.containerNode, 'tree');
  18895. }
  18896. this.domNode.appendChild(rn.domNode);
  18897. var identity = this.model.getIdentity(item);
  18898. if(this._itemNodesMap[identity]){
  18899. this._itemNodesMap[identity].push(rn);
  18900. }else{
  18901. this._itemNodesMap[identity] = [rn];
  18902. }
  18903. rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
  18904. // load top level children and then fire onLoad() event
  18905. this._expandNode(rn).addCallback(dojo.hitch(this, function(){
  18906. this._loadDeferred.callback(true);
  18907. this.onLoad();
  18908. }));
  18909. }),
  18910. function(err){
  18911. console.error(this, ": error loading root: ", err);
  18912. }
  18913. );
  18914. },
  18915. getNodesByItem: function(/*dojo.data.Item or id*/ item){
  18916. // summary:
  18917. // Returns all tree nodes that refer to an item
  18918. // returns:
  18919. // Array of tree nodes that refer to passed item
  18920. if(!item){ return []; }
  18921. var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
  18922. // return a copy so widget don't get messed up by changes to returned array
  18923. return [].concat(this._itemNodesMap[identity]);
  18924. },
  18925. _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
  18926. this.set('selectedItems', [item]);
  18927. },
  18928. _setSelectedItemsAttr: function(/*dojo.data.Items or ids*/ items){
  18929. // summary:
  18930. // Select tree nodes related to passed items.
  18931. // WARNING: if model use multi-parented items or desired tree node isn't already loaded
  18932. // behavior is undefined. Use set('paths', ...) instead.
  18933. var tree = this;
  18934. this._loadDeferred.addCallback( dojo.hitch(this, function(){
  18935. var identities = dojo.map(items, function(item){
  18936. return (!item || dojo.isString(item)) ? item : tree.model.getIdentity(item);
  18937. });
  18938. var nodes = [];
  18939. dojo.forEach(identities, function(id){
  18940. nodes = nodes.concat(tree._itemNodesMap[id] || []);
  18941. });
  18942. this.set('selectedNodes', nodes);
  18943. }));
  18944. },
  18945. _setPathAttr: function(/*Item[] || String[]*/ path){
  18946. // summary:
  18947. // Singular variant of _setPathsAttr
  18948. if(path.length) {
  18949. return this.set("paths", [path]);
  18950. } else {
  18951. //Empty list is interpreted as "select nothing"
  18952. return this.set("paths", []);
  18953. }
  18954. },
  18955. _setPathsAttr: function(/*Item[][] || String[][]*/ paths){
  18956. // summary:
  18957. // Select the tree nodes identified by passed paths.
  18958. // paths:
  18959. // Array of arrays of items or item id's
  18960. // returns:
  18961. // Deferred to indicate when the set is complete
  18962. var tree = this;
  18963. // We may need to wait for some nodes to expand, so setting
  18964. // each path will involve a Deferred. We bring those deferreds
  18965. // together witha DeferredList.
  18966. return new dojo.DeferredList(dojo.map(paths, function(path){
  18967. var d = new dojo.Deferred();
  18968. // normalize path to use identity
  18969. path = dojo.map(path, function(item){
  18970. return dojo.isString(item) ? item : tree.model.getIdentity(item);
  18971. });
  18972. if(path.length){
  18973. // Wait for the tree to load, if it hasn't already.
  18974. tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
  18975. }else{
  18976. d.errback("Empty path");
  18977. }
  18978. return d;
  18979. })).addCallback(setNodes);
  18980. function selectPath(path, nodes, def){
  18981. // Traverse path; the next path component should be among "nodes".
  18982. var nextPath = path.shift();
  18983. var nextNode = dojo.filter(nodes, function(node){
  18984. return node.getIdentity() == nextPath;
  18985. })[0];
  18986. if(!!nextNode){
  18987. if(path.length){
  18988. tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
  18989. }else{
  18990. //Successfully reached the end of this path
  18991. def.callback(nextNode);
  18992. }
  18993. } else {
  18994. def.errback("Could not expand path at " + nextPath);
  18995. }
  18996. }
  18997. function setNodes(newNodes){
  18998. //After all expansion is finished, set the selection to
  18999. //the set of nodes successfully found.
  19000. tree.set("selectedNodes", dojo.map(
  19001. dojo.filter(newNodes,function(x){return x[0];}),
  19002. function(x){return x[1];}));
  19003. }
  19004. },
  19005. _setSelectedNodeAttr: function(node){
  19006. this.set('selectedNodes', [node]);
  19007. },
  19008. _setSelectedNodesAttr: function(nodes){
  19009. this._loadDeferred.addCallback( dojo.hitch(this, function(){
  19010. this.dndController.setSelection(nodes);
  19011. }));
  19012. },
  19013. ////////////// Data store related functions //////////////////////
  19014. // These just get passed to the model; they are here for back-compat
  19015. mayHaveChildren: function(/*dojo.data.Item*/ item){
  19016. // summary:
  19017. // Deprecated. This should be specified on the model itself.
  19018. //
  19019. // Overridable function to tell if an item has or may have children.
  19020. // Controls whether or not +/- expando icon is shown.
  19021. // (For efficiency reasons we may not want to check if an element actually
  19022. // has children until user clicks the expando node)
  19023. // tags:
  19024. // deprecated
  19025. },
  19026. getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
  19027. // summary:
  19028. // Deprecated. This should be specified on the model itself.
  19029. //
  19030. // Overridable function that return array of child items of given parent item,
  19031. // or if parentItem==null then return top items in tree
  19032. // tags:
  19033. // deprecated
  19034. },
  19035. ///////////////////////////////////////////////////////
  19036. // Functions for converting an item to a TreeNode
  19037. getLabel: function(/*dojo.data.Item*/ item){
  19038. // summary:
  19039. // Overridable function to get the label for a tree node (given the item)
  19040. // tags:
  19041. // extension
  19042. return this.model.getLabel(item); // String
  19043. },
  19044. getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19045. // summary:
  19046. // Overridable function to return CSS class name to display icon
  19047. // tags:
  19048. // extension
  19049. return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
  19050. },
  19051. getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19052. // summary:
  19053. // Overridable function to return CSS class name to display label
  19054. // tags:
  19055. // extension
  19056. },
  19057. getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19058. // summary:
  19059. // Overridable function to return CSS class name to display row
  19060. // tags:
  19061. // extension
  19062. },
  19063. getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19064. // summary:
  19065. // Overridable function to return CSS styles to display icon
  19066. // returns:
  19067. // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
  19068. // tags:
  19069. // extension
  19070. },
  19071. getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19072. // summary:
  19073. // Overridable function to return CSS styles to display label
  19074. // returns:
  19075. // Object suitable for input to dojo.style() like {color: "red", background: "green"}
  19076. // tags:
  19077. // extension
  19078. },
  19079. getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  19080. // summary:
  19081. // Overridable function to return CSS styles to display row
  19082. // returns:
  19083. // Object suitable for input to dojo.style() like {background-color: "#bbb"}
  19084. // tags:
  19085. // extension
  19086. },
  19087. getTooltip: function(/*dojo.data.Item*/ item){
  19088. // summary:
  19089. // Overridable function to get the tooltip for a tree node (given the item)
  19090. // tags:
  19091. // extension
  19092. return ""; // String
  19093. },
  19094. /////////// Keyboard and Mouse handlers ////////////////////
  19095. _onKeyPress: function(/*Event*/ e){
  19096. // summary:
  19097. // Translates keypress events into commands for the controller
  19098. if(e.altKey){ return; }
  19099. var dk = dojo.keys;
  19100. var treeNode = dijit.getEnclosingWidget(e.target);
  19101. if(!treeNode){ return; }
  19102. var key = e.charOrCode;
  19103. if(typeof key == "string" && key != " "){ // handle printables (letter navigation)
  19104. // Check for key navigation.
  19105. if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
  19106. this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
  19107. dojo.stopEvent(e);
  19108. }
  19109. }else{ // handle non-printables (arrow keys)
  19110. // clear record of recent printables (being saved for multi-char letter navigation),
  19111. // because "a", down-arrow, "b" shouldn't search for "ab"
  19112. if(this._curSearch){
  19113. clearTimeout(this._curSearch.timer);
  19114. delete this._curSearch;
  19115. }
  19116. var map = this._keyHandlerMap;
  19117. if(!map){
  19118. // setup table mapping keys to events
  19119. map = {};
  19120. map[dk.ENTER]="_onEnterKey";
  19121. //On WebKit based browsers, the combination ctrl-enter
  19122. //does not get passed through. To allow accessible
  19123. //multi-select on those browsers, the space key is
  19124. //also used for selection.
  19125. map[dk.SPACE]= map[" "] = "_onEnterKey";
  19126. map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
  19127. map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
  19128. map[dk.UP_ARROW]="_onUpArrow";
  19129. map[dk.DOWN_ARROW]="_onDownArrow";
  19130. map[dk.HOME]="_onHomeKey";
  19131. map[dk.END]="_onEndKey";
  19132. this._keyHandlerMap = map;
  19133. }
  19134. if(this._keyHandlerMap[key]){
  19135. this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
  19136. dojo.stopEvent(e);
  19137. }
  19138. }
  19139. },
  19140. _onEnterKey: function(/*Object*/ message){
  19141. this._publish("execute", { item: message.item, node: message.node } );
  19142. this.dndController.userSelect(message.node, dojo.isCopyKey( message.evt ), message.evt.shiftKey);
  19143. this.onClick(message.item, message.node, message.evt);
  19144. },
  19145. _onDownArrow: function(/*Object*/ message){
  19146. // summary:
  19147. // down arrow pressed; get next visible node, set focus there
  19148. var node = this._getNextNode(message.node);
  19149. if(node && node.isTreeNode){
  19150. this.focusNode(node);
  19151. }
  19152. },
  19153. _onUpArrow: function(/*Object*/ message){
  19154. // summary:
  19155. // Up arrow pressed; move to previous visible node
  19156. var node = message.node;
  19157. // if younger siblings
  19158. var previousSibling = node.getPreviousSibling();
  19159. if(previousSibling){
  19160. node = previousSibling;
  19161. // if the previous node is expanded, dive in deep
  19162. while(node.isExpandable && node.isExpanded && node.hasChildren()){
  19163. // move to the last child
  19164. var children = node.getChildren();
  19165. node = children[children.length-1];
  19166. }
  19167. }else{
  19168. // if this is the first child, return the parent
  19169. // unless the parent is the root of a tree with a hidden root
  19170. var parent = node.getParent();
  19171. if(!(!this.showRoot && parent === this.rootNode)){
  19172. node = parent;
  19173. }
  19174. }
  19175. if(node && node.isTreeNode){
  19176. this.focusNode(node);
  19177. }
  19178. },
  19179. _onRightArrow: function(/*Object*/ message){
  19180. // summary:
  19181. // Right arrow pressed; go to child node
  19182. var node = message.node;
  19183. // if not expanded, expand, else move to 1st child
  19184. if(node.isExpandable && !node.isExpanded){
  19185. this._expandNode(node);
  19186. }else if(node.hasChildren()){
  19187. node = node.getChildren()[0];
  19188. if(node && node.isTreeNode){
  19189. this.focusNode(node);
  19190. }
  19191. }
  19192. },
  19193. _onLeftArrow: function(/*Object*/ message){
  19194. // summary:
  19195. // Left arrow pressed.
  19196. // If not collapsed, collapse, else move to parent.
  19197. var node = message.node;
  19198. if(node.isExpandable && node.isExpanded){
  19199. this._collapseNode(node);
  19200. }else{
  19201. var parent = node.getParent();
  19202. if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
  19203. this.focusNode(parent);
  19204. }
  19205. }
  19206. },
  19207. _onHomeKey: function(){
  19208. // summary:
  19209. // Home key pressed; get first visible node, and set focus there
  19210. var node = this._getRootOrFirstNode();
  19211. if(node){
  19212. this.focusNode(node);
  19213. }
  19214. },
  19215. _onEndKey: function(/*Object*/ message){
  19216. // summary:
  19217. // End key pressed; go to last visible node.
  19218. var node = this.rootNode;
  19219. while(node.isExpanded){
  19220. var c = node.getChildren();
  19221. node = c[c.length - 1];
  19222. }
  19223. if(node && node.isTreeNode){
  19224. this.focusNode(node);
  19225. }
  19226. },
  19227. // multiCharSearchDuration: Number
  19228. // If multiple characters are typed where each keystroke happens within
  19229. // multiCharSearchDuration of the previous keystroke,
  19230. // search for nodes matching all the keystrokes.
  19231. //
  19232. // For example, typing "ab" will search for entries starting with
  19233. // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
  19234. multiCharSearchDuration: 250,
  19235. _onLetterKeyNav: function(message){
  19236. // summary:
  19237. // Called when user presses a prinatable key; search for node starting with recently typed letters.
  19238. // message: Object
  19239. // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
  19240. // Branch depending on whether this key starts a new search, or modifies an existing search
  19241. var cs = this._curSearch;
  19242. if(cs){
  19243. // We are continuing a search. Ex: user has pressed 'a', and now has pressed
  19244. // 'b', so we want to search for nodes starting w/"ab".
  19245. cs.pattern = cs.pattern + message.key;
  19246. clearTimeout(cs.timer);
  19247. }else{
  19248. // We are starting a new search
  19249. cs = this._curSearch = {
  19250. pattern: message.key,
  19251. startNode: message.node
  19252. };
  19253. }
  19254. // set/reset timer to forget recent keystrokes
  19255. var self = this;
  19256. cs.timer = setTimeout(function(){
  19257. delete self._curSearch;
  19258. }, this.multiCharSearchDuration);
  19259. // Navigate to TreeNode matching keystrokes [entered so far].
  19260. var node = cs.startNode;
  19261. do{
  19262. node = this._getNextNode(node);
  19263. //check for last node, jump to first node if necessary
  19264. if(!node){
  19265. node = this._getRootOrFirstNode();
  19266. }
  19267. }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
  19268. if(node && node.isTreeNode){
  19269. // no need to set focus if back where we started
  19270. if(node !== cs.startNode){
  19271. this.focusNode(node);
  19272. }
  19273. }
  19274. },
  19275. isExpandoNode: function(node, widget){
  19276. // summary:
  19277. // check whether a dom node is the expandoNode for a particular TreeNode widget
  19278. return dojo.isDescendant(node, widget.expandoNode);
  19279. },
  19280. _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
  19281. // summary:
  19282. // Translates click events into commands for the controller to process
  19283. var domElement = e.target,
  19284. isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
  19285. if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
  19286. // expando node was clicked, or label of a folder node was clicked; open it
  19287. if(nodeWidget.isExpandable){
  19288. this._onExpandoClick({node:nodeWidget});
  19289. }
  19290. }else{
  19291. this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
  19292. this.onClick(nodeWidget.item, nodeWidget, e);
  19293. this.focusNode(nodeWidget);
  19294. }
  19295. dojo.stopEvent(e);
  19296. },
  19297. _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
  19298. // summary:
  19299. // Translates double-click events into commands for the controller to process
  19300. var domElement = e.target,
  19301. isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
  19302. if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
  19303. // expando node was clicked, or label of a folder node was clicked; open it
  19304. if(nodeWidget.isExpandable){
  19305. this._onExpandoClick({node:nodeWidget});
  19306. }
  19307. }else{
  19308. this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
  19309. this.onDblClick(nodeWidget.item, nodeWidget, e);
  19310. this.focusNode(nodeWidget);
  19311. }
  19312. dojo.stopEvent(e);
  19313. },
  19314. _onExpandoClick: function(/*Object*/ message){
  19315. // summary:
  19316. // User clicked the +/- icon; expand or collapse my children.
  19317. var node = message.node;
  19318. // If we are collapsing, we might be hiding the currently focused node.
  19319. // Also, clicking the expando node might have erased focus from the current node.
  19320. // For simplicity's sake just focus on the node with the expando.
  19321. this.focusNode(node);
  19322. if(node.isExpanded){
  19323. this._collapseNode(node);
  19324. }else{
  19325. this._expandNode(node);
  19326. }
  19327. },
  19328. onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
  19329. // summary:
  19330. // Callback when a tree node is clicked
  19331. // tags:
  19332. // callback
  19333. },
  19334. onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
  19335. // summary:
  19336. // Callback when a tree node is double-clicked
  19337. // tags:
  19338. // callback
  19339. },
  19340. onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
  19341. // summary:
  19342. // Callback when a node is opened
  19343. // tags:
  19344. // callback
  19345. },
  19346. onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
  19347. // summary:
  19348. // Callback when a node is closed
  19349. // tags:
  19350. // callback
  19351. },
  19352. _getNextNode: function(node){
  19353. // summary:
  19354. // Get next visible node
  19355. if(node.isExpandable && node.isExpanded && node.hasChildren()){
  19356. // if this is an expanded node, get the first child
  19357. return node.getChildren()[0]; // _TreeNode
  19358. }else{
  19359. // find a parent node with a sibling
  19360. while(node && node.isTreeNode){
  19361. var returnNode = node.getNextSibling();
  19362. if(returnNode){
  19363. return returnNode; // _TreeNode
  19364. }
  19365. node = node.getParent();
  19366. }
  19367. return null;
  19368. }
  19369. },
  19370. _getRootOrFirstNode: function(){
  19371. // summary:
  19372. // Get first visible node
  19373. return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
  19374. },
  19375. _collapseNode: function(/*_TreeNode*/ node){
  19376. // summary:
  19377. // Called when the user has requested to collapse the node
  19378. if(node._expandNodeDeferred){
  19379. delete node._expandNodeDeferred;
  19380. }
  19381. if(node.isExpandable){
  19382. if(node.state == "LOADING"){
  19383. // ignore clicks while we are in the process of loading data
  19384. return;
  19385. }
  19386. node.collapse();
  19387. this.onClose(node.item, node);
  19388. if(node.item){
  19389. this._state(node.item,false);
  19390. this._saveState();
  19391. }
  19392. }
  19393. },
  19394. _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
  19395. // summary:
  19396. // Called when the user has requested to expand the node
  19397. // recursive:
  19398. // Internal flag used when _expandNode() calls itself, don't set.
  19399. // returns:
  19400. // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
  19401. // that were previously opened too
  19402. if(node._expandNodeDeferred && !recursive){
  19403. // there's already an expand in progress (or completed), so just return
  19404. return node._expandNodeDeferred; // dojo.Deferred
  19405. }
  19406. var model = this.model,
  19407. item = node.item,
  19408. _this = this;
  19409. switch(node.state){
  19410. case "UNCHECKED":
  19411. // need to load all the children, and then expand
  19412. node.markProcessing();
  19413. // Setup deferred to signal when the load and expand are finished.
  19414. // Save that deferred in this._expandDeferred as a flag that operation is in progress.
  19415. var def = (node._expandNodeDeferred = new dojo.Deferred());
  19416. // Get the children
  19417. model.getChildren(
  19418. item,
  19419. function(items){
  19420. node.unmarkProcessing();
  19421. // Display the children and also start expanding any children that were previously expanded
  19422. // (if this.persist == true). The returned Deferred will fire when those expansions finish.
  19423. var scid = node.setChildItems(items);
  19424. // Call _expandNode() again but this time it will just to do the animation (default branch).
  19425. // The returned Deferred will fire when the animation completes.
  19426. // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
  19427. var ed = _this._expandNode(node, true);
  19428. // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
  19429. // signal that I am done.
  19430. scid.addCallback(function(){
  19431. ed.addCallback(function(){
  19432. def.callback();
  19433. })
  19434. });
  19435. },
  19436. function(err){
  19437. console.error(_this, ": error loading root children: ", err);
  19438. }
  19439. );
  19440. break;
  19441. default: // "LOADED"
  19442. // data is already loaded; just expand node
  19443. def = (node._expandNodeDeferred = node.expand());
  19444. this.onOpen(node.item, node);
  19445. if(item){
  19446. this._state(item, true);
  19447. this._saveState();
  19448. }
  19449. }
  19450. return def; // dojo.Deferred
  19451. },
  19452. ////////////////// Miscellaneous functions ////////////////
  19453. focusNode: function(/* _tree.Node */ node){
  19454. // summary:
  19455. // Focus on the specified node (which must be visible)
  19456. // tags:
  19457. // protected
  19458. // set focus so that the label will be voiced using screen readers
  19459. dijit.focus(node.labelNode);
  19460. },
  19461. _onNodeFocus: function(/*dijit._Widget*/ node){
  19462. // summary:
  19463. // Called when a TreeNode gets focus, either by user clicking
  19464. // it, or programatically by arrow key handling code.
  19465. // description:
  19466. // It marks that the current node is the selected one, and the previously
  19467. // selected node no longer is.
  19468. if(node && node != this.lastFocused){
  19469. if(this.lastFocused && !this.lastFocused._destroyed){
  19470. // mark that the previously focsable node is no longer focusable
  19471. this.lastFocused.setFocusable(false);
  19472. }
  19473. // mark that the new node is the currently selected one
  19474. node.setFocusable(true);
  19475. this.lastFocused = node;
  19476. }
  19477. },
  19478. _onNodeMouseEnter: function(/*dijit._Widget*/ node){
  19479. // summary:
  19480. // Called when mouse is over a node (onmouseenter event),
  19481. // this is monitored by the DND code
  19482. },
  19483. _onNodeMouseLeave: function(/*dijit._Widget*/ node){
  19484. // summary:
  19485. // Called when mouse leaves a node (onmouseleave event),
  19486. // this is monitored by the DND code
  19487. },
  19488. //////////////// Events from the model //////////////////////////
  19489. _onItemChange: function(/*Item*/ item){
  19490. // summary:
  19491. // Processes notification of a change to an item's scalar values like label
  19492. var model = this.model,
  19493. identity = model.getIdentity(item),
  19494. nodes = this._itemNodesMap[identity];
  19495. if(nodes){
  19496. var label = this.getLabel(item),
  19497. tooltip = this.getTooltip(item);
  19498. dojo.forEach(nodes, function(node){
  19499. node.set({
  19500. item: item, // theoretically could be new JS Object representing same item
  19501. label: label,
  19502. tooltip: tooltip
  19503. });
  19504. node._updateItemClasses(item);
  19505. });
  19506. }
  19507. },
  19508. _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  19509. // summary:
  19510. // Processes notification of a change to an item's children
  19511. var model = this.model,
  19512. identity = model.getIdentity(parent),
  19513. parentNodes = this._itemNodesMap[identity];
  19514. if(parentNodes){
  19515. dojo.forEach(parentNodes,function(parentNode){
  19516. parentNode.setChildItems(newChildrenList);
  19517. });
  19518. }
  19519. },
  19520. _onItemDelete: function(/*Item*/ item){
  19521. // summary:
  19522. // Processes notification of a deletion of an item
  19523. var model = this.model,
  19524. identity = model.getIdentity(item),
  19525. nodes = this._itemNodesMap[identity];
  19526. if(nodes){
  19527. dojo.forEach(nodes,function(node){
  19528. // Remove node from set of selected nodes (if it's selected)
  19529. this.dndController.removeTreeNode(node);
  19530. var parent = node.getParent();
  19531. if(parent){
  19532. // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
  19533. parent.removeChild(node);
  19534. }
  19535. node.destroyRecursive();
  19536. }, this);
  19537. delete this._itemNodesMap[identity];
  19538. }
  19539. },
  19540. /////////////// Miscellaneous funcs
  19541. _initState: function(){
  19542. // summary:
  19543. // Load in which nodes should be opened automatically
  19544. if(this.persist){
  19545. var cookie = dojo.cookie(this.cookieName);
  19546. this._openedItemIds = {};
  19547. if(cookie){
  19548. dojo.forEach(cookie.split(','), function(item){
  19549. this._openedItemIds[item] = true;
  19550. }, this);
  19551. }
  19552. }
  19553. },
  19554. _state: function(item,expanded){
  19555. // summary:
  19556. // Query or set expanded state for an item,
  19557. if(!this.persist){
  19558. return false;
  19559. }
  19560. var id=this.model.getIdentity(item);
  19561. if(arguments.length === 1){
  19562. return this._openedItemIds[id];
  19563. }
  19564. if(expanded){
  19565. this._openedItemIds[id] = true;
  19566. }else{
  19567. delete this._openedItemIds[id];
  19568. }
  19569. },
  19570. _saveState: function(){
  19571. // summary:
  19572. // Create and save a cookie with the currently expanded nodes identifiers
  19573. if(!this.persist){
  19574. return;
  19575. }
  19576. var ary = [];
  19577. for(var id in this._openedItemIds){
  19578. ary.push(id);
  19579. }
  19580. dojo.cookie(this.cookieName, ary.join(","), {expires:365});
  19581. },
  19582. destroy: function(){
  19583. if(this._curSearch){
  19584. clearTimeout(this._curSearch.timer);
  19585. delete this._curSearch;
  19586. }
  19587. if(this.rootNode){
  19588. this.rootNode.destroyRecursive();
  19589. }
  19590. if(this.dndController && !dojo.isString(this.dndController)){
  19591. this.dndController.destroy();
  19592. }
  19593. this.rootNode = null;
  19594. this.inherited(arguments);
  19595. },
  19596. destroyRecursive: function(){
  19597. // A tree is treated as a leaf, not as a node with children (like a grid),
  19598. // but defining destroyRecursive for back-compat.
  19599. this.destroy();
  19600. },
  19601. resize: function(changeSize){
  19602. if(changeSize){
  19603. dojo.marginBox(this.domNode, changeSize);
  19604. }
  19605. // The only JS sizing involved w/tree is the indentation, which is specified
  19606. // in CSS and read in through this dummy indentDetector node (tree must be
  19607. // visible and attached to the DOM to read this)
  19608. this._nodePixelIndent = dojo._getMarginSize(this.tree.indentDetector).w;
  19609. if(this.tree.rootNode){
  19610. // If tree has already loaded, then reset indent for all the nodes
  19611. this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
  19612. }
  19613. },
  19614. _createTreeNode: function(/*Object*/ args){
  19615. // summary:
  19616. // creates a TreeNode
  19617. // description:
  19618. // Developers can override this method to define their own TreeNode class;
  19619. // However it will probably be removed in a future release in favor of a way
  19620. // of just specifying a widget for the label, rather than one that contains
  19621. // the children too.
  19622. return new dijit._TreeNode(args);
  19623. }
  19624. });
  19625. // For back-compat. TODO: remove in 2.0
  19626. }
  19627. if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19628. dojo._hasResource["dijit.InlineEditBox"] = true;
  19629. dojo.provide("dijit.InlineEditBox");
  19630. dojo.declare("dijit.InlineEditBox",
  19631. dijit._Widget,
  19632. {
  19633. // summary:
  19634. // An element with in-line edit capabilites
  19635. //
  19636. // description:
  19637. // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
  19638. // when you click it, an editor shows up in place of the original
  19639. // text. Optionally, Save and Cancel button are displayed below the edit widget.
  19640. // When Save is clicked, the text is pulled from the edit
  19641. // widget and redisplayed and the edit widget is again hidden.
  19642. // By default a plain Textarea widget is used as the editor (or for
  19643. // inline values a TextBox), but you can specify an editor such as
  19644. // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
  19645. // An edit widget must support the following API to be used:
  19646. // - displayedValue or value as initialization parameter,
  19647. // and available through set('displayedValue') / set('value')
  19648. // - void focus()
  19649. // - DOM-node focusNode = node containing editable text
  19650. // editing: [readonly] Boolean
  19651. // Is the node currently in edit mode?
  19652. editing: false,
  19653. // autoSave: Boolean
  19654. // Changing the value automatically saves it; don't have to push save button
  19655. // (and save button isn't even displayed)
  19656. autoSave: true,
  19657. // buttonSave: String
  19658. // Save button label
  19659. buttonSave: "",
  19660. // buttonCancel: String
  19661. // Cancel button label
  19662. buttonCancel: "",
  19663. // renderAsHtml: Boolean
  19664. // Set this to true if the specified Editor's value should be interpreted as HTML
  19665. // rather than plain text (ex: `dijit.Editor`)
  19666. renderAsHtml: false,
  19667. // editor: String|Function
  19668. // Class name (or reference to the Class) for Editor widget
  19669. editor: "dijit.form.TextBox",
  19670. // editorWrapper: String|Function
  19671. // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
  19672. // buttons.
  19673. editorWrapper: "dijit._InlineEditor",
  19674. // editorParams: Object
  19675. // Set of parameters for editor, like {required: true}
  19676. editorParams: {},
  19677. // disabled: Boolean
  19678. // If true, clicking the InlineEditBox to edit it will have no effect.
  19679. disabled: false,
  19680. onChange: function(value){
  19681. // summary:
  19682. // Set this handler to be notified of changes to value.
  19683. // tags:
  19684. // callback
  19685. },
  19686. onCancel: function(){
  19687. // summary:
  19688. // Set this handler to be notified when editing is cancelled.
  19689. // tags:
  19690. // callback
  19691. },
  19692. // width: String
  19693. // Width of editor. By default it's width=100% (ie, block mode).
  19694. width: "100%",
  19695. // value: String
  19696. // The display value of the widget in read-only mode
  19697. value: "",
  19698. // noValueIndicator: [const] String
  19699. // The text that gets displayed when there is no value (so that the user has a place to click to edit)
  19700. noValueIndicator: dojo.isIE <= 6 ? // font-family needed on IE6 but it messes up IE8
  19701. "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>" :
  19702. "<span style='text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
  19703. constructor: function(){
  19704. // summary:
  19705. // Sets up private arrays etc.
  19706. // tags:
  19707. // private
  19708. this.editorParams = {};
  19709. },
  19710. postMixInProperties: function(){
  19711. this.inherited(arguments);
  19712. // save pointer to original source node, since Widget nulls-out srcNodeRef
  19713. this.displayNode = this.srcNodeRef;
  19714. // connect handlers to the display node
  19715. var events = {
  19716. ondijitclick: "_onClick",
  19717. onmouseover: "_onMouseOver",
  19718. onmouseout: "_onMouseOut",
  19719. onfocus: "_onMouseOver",
  19720. onblur: "_onMouseOut"
  19721. };
  19722. for(var name in events){
  19723. this.connect(this.displayNode, name, events[name]);
  19724. }
  19725. dijit.setWaiRole(this.displayNode, "button");
  19726. if(!this.displayNode.getAttribute("tabIndex")){
  19727. this.displayNode.setAttribute("tabIndex", 0);
  19728. }
  19729. if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
  19730. this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML :
  19731. (this.displayNode.innerText||this.displayNode.textContent||""));
  19732. }
  19733. if(!this.value){
  19734. this.displayNode.innerHTML = this.noValueIndicator;
  19735. }
  19736. dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode');
  19737. },
  19738. setDisabled: function(/*Boolean*/ disabled){
  19739. // summary:
  19740. // Deprecated. Use set('disabled', ...) instead.
  19741. // tags:
  19742. // deprecated
  19743. dojo.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
  19744. this.set('disabled', disabled);
  19745. },
  19746. _setDisabledAttr: function(/*Boolean*/ disabled){
  19747. // summary:
  19748. // Hook to make set("disabled", ...) work.
  19749. // Set disabled state of widget.
  19750. dijit.setWaiState(this.domNode, "disabled", disabled);
  19751. if(disabled){
  19752. this.displayNode.removeAttribute("tabIndex");
  19753. }else{
  19754. this.displayNode.setAttribute("tabIndex", 0);
  19755. }
  19756. dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
  19757. this._set("disabled", disabled);
  19758. },
  19759. _onMouseOver: function(){
  19760. // summary:
  19761. // Handler for onmouseover and onfocus event.
  19762. // tags:
  19763. // private
  19764. if(!this.disabled){
  19765. dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
  19766. }
  19767. },
  19768. _onMouseOut: function(){
  19769. // summary:
  19770. // Handler for onmouseout and onblur event.
  19771. // tags:
  19772. // private
  19773. dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
  19774. },
  19775. _onClick: function(/*Event*/ e){
  19776. // summary:
  19777. // Handler for onclick event.
  19778. // tags:
  19779. // private
  19780. if(this.disabled){ return; }
  19781. if(e){ dojo.stopEvent(e); }
  19782. this._onMouseOut();
  19783. // Since FF gets upset if you move a node while in an event handler for that node...
  19784. setTimeout(dojo.hitch(this, "edit"), 0);
  19785. },
  19786. edit: function(){
  19787. // summary:
  19788. // Display the editor widget in place of the original (read only) markup.
  19789. // tags:
  19790. // private
  19791. if(this.disabled || this.editing){ return; }
  19792. this.editing = true;
  19793. // save some display node values that can be restored later
  19794. this._savedPosition = dojo.style(this.displayNode, "position") || "static";
  19795. this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1";
  19796. this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0";
  19797. if(this.wrapperWidget){
  19798. var ew = this.wrapperWidget.editWidget;
  19799. ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
  19800. }else{
  19801. // Placeholder for edit widget
  19802. // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
  19803. // when Calendar dropdown appears, which happens automatically on focus.
  19804. var placeholder = dojo.create("span", null, this.domNode, "before");
  19805. // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
  19806. var ewc = typeof this.editorWrapper == "string" ? dojo.getObject(this.editorWrapper) : this.editorWrapper;
  19807. this.wrapperWidget = new ewc({
  19808. value: this.value,
  19809. buttonSave: this.buttonSave,
  19810. buttonCancel: this.buttonCancel,
  19811. dir: this.dir,
  19812. lang: this.lang,
  19813. tabIndex: this._savedTabIndex,
  19814. editor: this.editor,
  19815. inlineEditBox: this,
  19816. sourceStyle: dojo.getComputedStyle(this.displayNode),
  19817. save: dojo.hitch(this, "save"),
  19818. cancel: dojo.hitch(this, "cancel")
  19819. }, placeholder);
  19820. if(!this._started){
  19821. this.startup();
  19822. }
  19823. }
  19824. var ww = this.wrapperWidget;
  19825. // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
  19826. // and then when it's finished rendering, we switch from display mode to editor
  19827. // position:absolute releases screen space allocated to the display node
  19828. // opacity:0 is the same as visibility:hidden but is still focusable
  19829. // visiblity:hidden removes focus outline
  19830. dojo.style(this.displayNode, { position: "absolute", opacity: "0" }); // makes display node invisible, display style used for focus-ability
  19831. dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
  19832. dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
  19833. // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
  19834. // focus can be shifted without incident. (browser may needs some time to render the editor.)
  19835. setTimeout(dojo.hitch(ww, function(){
  19836. this.focus(); // both nodes are showing, so we can switch focus safely
  19837. this._resetValue = this.getValue();
  19838. }), 0);
  19839. },
  19840. _onBlur: function(){
  19841. // summary:
  19842. // Called when focus moves outside the InlineEditBox.
  19843. // Performs garbage collection.
  19844. // tags:
  19845. // private
  19846. this.inherited(arguments);
  19847. if(!this.editing){
  19848. /* causes IE focus problems, see TooltipDialog_a11y.html...
  19849. setTimeout(dojo.hitch(this, function(){
  19850. if(this.wrapperWidget){
  19851. this.wrapperWidget.destroy();
  19852. delete this.wrapperWidget;
  19853. }
  19854. }), 0);
  19855. */
  19856. }
  19857. },
  19858. destroy: function(){
  19859. if(this.wrapperWidget && !this.wrapperWidget._destroyed){
  19860. this.wrapperWidget.destroy();
  19861. delete this.wrapperWidget;
  19862. }
  19863. this.inherited(arguments);
  19864. },
  19865. _showText: function(/*Boolean*/ focus){
  19866. // summary:
  19867. // Revert to display mode, and optionally focus on display node
  19868. // tags:
  19869. // private
  19870. var ww = this.wrapperWidget;
  19871. dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
  19872. dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity }); // make the original text visible
  19873. dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex);
  19874. if(focus){
  19875. dijit.focus(this.displayNode);
  19876. }
  19877. },
  19878. save: function(/*Boolean*/ focus){
  19879. // summary:
  19880. // Save the contents of the editor and revert to display mode.
  19881. // focus: Boolean
  19882. // Focus on the display mode text
  19883. // tags:
  19884. // private
  19885. if(this.disabled || !this.editing){ return; }
  19886. this.editing = false;
  19887. var ww = this.wrapperWidget;
  19888. var value = ww.getValue();
  19889. this.set('value', value); // display changed, formatted value
  19890. this._showText(focus); // set focus as needed
  19891. },
  19892. setValue: function(/*String*/ val){
  19893. // summary:
  19894. // Deprecated. Use set('value', ...) instead.
  19895. // tags:
  19896. // deprecated
  19897. dojo.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
  19898. return this.set("value", val);
  19899. },
  19900. _setValueAttr: function(/*String*/ val){
  19901. // summary:
  19902. // Hook to make set("value", ...) work.
  19903. // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
  19904. val = dojo.trim(val);
  19905. var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
  19906. this.displayNode.innerHTML = renderVal || this.noValueIndicator;
  19907. this._set("value", val);
  19908. if(this._started){
  19909. // tell the world that we have changed
  19910. setTimeout(dojo.hitch(this, "onChange", val), 0); // setTimeout prevents browser freeze for long-running event handlers
  19911. }
  19912. },
  19913. getValue: function(){
  19914. // summary:
  19915. // Deprecated. Use get('value') instead.
  19916. // tags:
  19917. // deprecated
  19918. dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
  19919. return this.get("value");
  19920. },
  19921. cancel: function(/*Boolean*/ focus){
  19922. // summary:
  19923. // Revert to display mode, discarding any changes made in the editor
  19924. // tags:
  19925. // private
  19926. if(this.disabled || !this.editing){ return; }
  19927. this.editing = false;
  19928. // tell the world that we have no changes
  19929. setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
  19930. this._showText(focus);
  19931. }
  19932. });
  19933. dojo.declare(
  19934. "dijit._InlineEditor",
  19935. [dijit._Widget, dijit._Templated],
  19936. {
  19937. // summary:
  19938. // Internal widget used by InlineEditBox, displayed when in editing mode
  19939. // to display the editor and maybe save/cancel buttons. Calling code should
  19940. // connect to save/cancel methods to detect when editing is finished
  19941. //
  19942. // Has mainly the same parameters as InlineEditBox, plus these values:
  19943. //
  19944. // style: Object
  19945. // Set of CSS attributes of display node, to replicate in editor
  19946. //
  19947. // value: String
  19948. // Value as an HTML string or plain text string, depending on renderAsHTML flag
  19949. templateString: dojo.cache("dijit", "templates/InlineEditBox.html", "<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"),
  19950. widgetsInTemplate: true,
  19951. postMixInProperties: function(){
  19952. this.inherited(arguments);
  19953. this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
  19954. dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
  19955. if(!this[prop]){ this[prop] = this.messages[prop]; }
  19956. }, this);
  19957. },
  19958. buildRendering: function(){
  19959. this.inherited(arguments);
  19960. // Create edit widget in place in the template
  19961. var cls = typeof this.editor == "string" ? dojo.getObject(this.editor) : this.editor;
  19962. // Copy the style from the source
  19963. // Don't copy ALL properties though, just the necessary/applicable ones.
  19964. // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
  19965. // is a relative value like 200%, rather than an absolute value like 24px, and
  19966. // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
  19967. var srcStyle = this.sourceStyle,
  19968. editStyle = "line-height:" + srcStyle.lineHeight + ";",
  19969. destStyle = dojo.getComputedStyle(this.domNode);
  19970. dojo.forEach(["Weight","Family","Size","Style"], function(prop){
  19971. var textStyle = srcStyle["font"+prop],
  19972. wrapperStyle = destStyle["font"+prop];
  19973. if(wrapperStyle != textStyle){
  19974. editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
  19975. }
  19976. }, this);
  19977. dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
  19978. this.domNode.style[prop] = srcStyle[prop];
  19979. }, this);
  19980. var width = this.inlineEditBox.width;
  19981. if(width == "100%"){
  19982. // block mode
  19983. editStyle += "width:100%;";
  19984. this.domNode.style.display = "block";
  19985. }else{
  19986. // inline-block mode
  19987. editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
  19988. }
  19989. var editorParams = dojo.delegate(this.inlineEditBox.editorParams, {
  19990. style: editStyle,
  19991. dir: this.dir,
  19992. lang: this.lang
  19993. });
  19994. editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
  19995. this.editWidget = new cls(editorParams, this.editorPlaceholder);
  19996. if(this.inlineEditBox.autoSave){
  19997. // Remove the save/cancel buttons since saving is done by simply tabbing away or
  19998. // selecting a value from the drop down list
  19999. dojo.destroy(this.buttonContainer);
  20000. }
  20001. },
  20002. postCreate: function(){
  20003. this.inherited(arguments);
  20004. var ew = this.editWidget;
  20005. if(this.inlineEditBox.autoSave){
  20006. // Selecting a value from a drop down list causes an onChange event and then we save
  20007. this.connect(ew, "onChange", "_onChange");
  20008. // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
  20009. // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
  20010. // so this is the only way we can see the key press event.
  20011. this.connect(ew, "onKeyPress", "_onKeyPress");
  20012. }else{
  20013. // If possible, enable/disable save button based on whether the user has changed the value
  20014. if("intermediateChanges" in ew){
  20015. ew.set("intermediateChanges", true);
  20016. this.connect(ew, "onChange", "_onIntermediateChange");
  20017. this.saveButton.set("disabled", true);
  20018. }
  20019. }
  20020. },
  20021. _onIntermediateChange: function(val){
  20022. // summary:
  20023. // Called for editor widgets that support the intermediateChanges=true flag as a way
  20024. // to detect when to enable/disabled the save button
  20025. this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
  20026. },
  20027. destroy: function(){
  20028. this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
  20029. this.inherited(arguments);
  20030. },
  20031. getValue: function(){
  20032. // summary:
  20033. // Return the [display] value of the edit widget
  20034. var ew = this.editWidget;
  20035. return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
  20036. },
  20037. _onKeyPress: function(e){
  20038. // summary:
  20039. // Handler for keypress in the edit box in autoSave mode.
  20040. // description:
  20041. // For autoSave widgets, if Esc/Enter, call cancel/save.
  20042. // tags:
  20043. // private
  20044. if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
  20045. if(e.altKey || e.ctrlKey){ return; }
  20046. // If Enter/Esc pressed, treat as save/cancel.
  20047. if(e.charOrCode == dojo.keys.ESCAPE){
  20048. dojo.stopEvent(e);
  20049. this.cancel(true); // sets editing=false which short-circuits _onBlur processing
  20050. }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){
  20051. dojo.stopEvent(e);
  20052. this._onChange(); // fire _onBlur and then save
  20053. }
  20054. // _onBlur will handle TAB automatically by allowing
  20055. // the TAB to change focus before we mess with the DOM: #6227
  20056. // Expounding by request:
  20057. // The current focus is on the edit widget input field.
  20058. // save() will hide and destroy this widget.
  20059. // We want the focus to jump from the currently hidden
  20060. // displayNode, but since it's hidden, it's impossible to
  20061. // unhide it, focus it, and then have the browser focus
  20062. // away from it to the next focusable element since each
  20063. // of these events is asynchronous and the focus-to-next-element
  20064. // is already queued.
  20065. // So we allow the browser time to unqueue the move-focus event
  20066. // before we do all the hide/show stuff.
  20067. }
  20068. },
  20069. _onBlur: function(){
  20070. // summary:
  20071. // Called when focus moves outside the editor
  20072. // tags:
  20073. // private
  20074. this.inherited(arguments);
  20075. if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
  20076. if(this.getValue() == this._resetValue){
  20077. this.cancel(false);
  20078. }else if(this.enableSave()){
  20079. this.save(false);
  20080. }
  20081. }
  20082. },
  20083. _onChange: function(){
  20084. // summary:
  20085. // Called when the underlying widget fires an onChange event,
  20086. // such as when the user selects a value from the drop down list of a ComboBox,
  20087. // which means that the user has finished entering the value and we should save.
  20088. // tags:
  20089. // private
  20090. if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
  20091. dijit.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
  20092. }
  20093. },
  20094. enableSave: function(){
  20095. // summary:
  20096. // User overridable function returning a Boolean to indicate
  20097. // if the Save button should be enabled or not - usually due to invalid conditions
  20098. // tags:
  20099. // extension
  20100. return (
  20101. this.editWidget.isValid
  20102. ? this.editWidget.isValid()
  20103. : true
  20104. );
  20105. },
  20106. focus: function(){
  20107. // summary:
  20108. // Focus the edit widget.
  20109. // tags:
  20110. // protected
  20111. this.editWidget.focus();
  20112. setTimeout(dojo.hitch(this, function(){
  20113. if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
  20114. dijit.selectInputText(this.editWidget.focusNode);
  20115. }
  20116. }), 0);
  20117. }
  20118. });
  20119. }
  20120. if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20121. dojo._hasResource["dijit.form.Form"] = true;
  20122. dojo.provide("dijit.form.Form");
  20123. dojo.declare(
  20124. "dijit.form.Form",
  20125. [dijit._Widget, dijit._Templated, dijit.form._FormMixin, dijit.layout._ContentPaneResizeMixin],
  20126. {
  20127. // summary:
  20128. // Widget corresponding to HTML form tag, for validation and serialization
  20129. //
  20130. // example:
  20131. // | <form dojoType="dijit.form.Form" id="myForm">
  20132. // | Name: <input type="text" name="name" />
  20133. // | </form>
  20134. // | myObj = {name: "John Doe"};
  20135. // | dijit.byId('myForm').set('value', myObj);
  20136. // |
  20137. // | myObj=dijit.byId('myForm').get('value');
  20138. // HTML <FORM> attributes
  20139. // name: String?
  20140. // Name of form for scripting.
  20141. name: "",
  20142. // action: String?
  20143. // Server-side form handler.
  20144. action: "",
  20145. // method: String?
  20146. // HTTP method used to submit the form, either "GET" or "POST".
  20147. method: "",
  20148. // encType: String?
  20149. // Encoding type for the form, ex: application/x-www-form-urlencoded.
  20150. encType: "",
  20151. // accept-charset: String?
  20152. // List of supported charsets.
  20153. "accept-charset": "",
  20154. // accept: String?
  20155. // List of MIME types for file upload.
  20156. accept: "",
  20157. // target: String?
  20158. // Target frame for the document to be opened in.
  20159. target: "",
  20160. templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
  20161. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  20162. action: "",
  20163. method: "",
  20164. encType: "",
  20165. "accept-charset": "",
  20166. accept: "",
  20167. target: ""
  20168. }),
  20169. postMixInProperties: function(){
  20170. // Setup name=foo string to be referenced from the template (but only if a name has been specified)
  20171. // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
  20172. this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
  20173. this.inherited(arguments);
  20174. },
  20175. execute: function(/*Object*/ formContents){
  20176. // summary:
  20177. // Deprecated: use submit()
  20178. // tags:
  20179. // deprecated
  20180. },
  20181. onExecute: function(){
  20182. // summary:
  20183. // Deprecated: use onSubmit()
  20184. // tags:
  20185. // deprecated
  20186. },
  20187. _setEncTypeAttr: function(/*String*/ value){
  20188. this.encType = value;
  20189. dojo.attr(this.domNode, "encType", value);
  20190. if(dojo.isIE){ this.domNode.encoding = value; }
  20191. },
  20192. postCreate: function(){
  20193. // IE tries to hide encType
  20194. // TODO: remove in 2.0, no longer necessary with data-dojo-params
  20195. if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
  20196. var item = this.srcNodeRef.attributes.getNamedItem('encType');
  20197. if(item && !item.specified && (typeof item.value == "string")){
  20198. this.set('encType', item.value);
  20199. }
  20200. }
  20201. this.inherited(arguments);
  20202. },
  20203. reset: function(/*Event?*/ e){
  20204. // summary:
  20205. // restores all widget values back to their init values,
  20206. // calls onReset() which can cancel the reset by returning false
  20207. // create fake event so we can know if preventDefault() is called
  20208. var faux = {
  20209. returnValue: true, // the IE way
  20210. preventDefault: function(){ // not IE
  20211. this.returnValue = false;
  20212. },
  20213. stopPropagation: function(){},
  20214. currentTarget: e ? e.target : this.domNode,
  20215. target: e ? e.target : this.domNode
  20216. };
  20217. // if return value is not exactly false, and haven't called preventDefault(), then reset
  20218. if(!(this.onReset(faux) === false) && faux.returnValue){
  20219. this.inherited(arguments, []);
  20220. }
  20221. },
  20222. onReset: function(/*Event?*/ e){
  20223. // summary:
  20224. // Callback when user resets the form. This method is intended
  20225. // to be over-ridden. When the `reset` method is called
  20226. // programmatically, the return value from `onReset` is used
  20227. // to compute whether or not resetting should proceed
  20228. // tags:
  20229. // callback
  20230. return true; // Boolean
  20231. },
  20232. _onReset: function(e){
  20233. this.reset(e);
  20234. dojo.stopEvent(e);
  20235. return false;
  20236. },
  20237. _onSubmit: function(e){
  20238. var fp = dijit.form.Form.prototype;
  20239. // TODO: remove this if statement beginning with 2.0
  20240. if(this.execute != fp.execute || this.onExecute != fp.onExecute){
  20241. dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
  20242. this.onExecute();
  20243. this.execute(this.getValues());
  20244. }
  20245. if(this.onSubmit(e) === false){ // only exactly false stops submit
  20246. dojo.stopEvent(e);
  20247. }
  20248. },
  20249. onSubmit: function(/*Event?*/ e){
  20250. // summary:
  20251. // Callback when user submits the form.
  20252. // description:
  20253. // This method is intended to be over-ridden, but by default it checks and
  20254. // returns the validity of form elements. When the `submit`
  20255. // method is called programmatically, the return value from
  20256. // `onSubmit` is used to compute whether or not submission
  20257. // should proceed
  20258. // tags:
  20259. // extension
  20260. return this.isValid(); // Boolean
  20261. },
  20262. submit: function(){
  20263. // summary:
  20264. // programmatically submit form if and only if the `onSubmit` returns true
  20265. if(!(this.onSubmit() === false)){
  20266. this.containerNode.submit();
  20267. }
  20268. }
  20269. }
  20270. );
  20271. }
  20272. if(!dojo._hasResource["dijit.form.ComboButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20273. dojo._hasResource["dijit.form.ComboButton"] = true;
  20274. dojo.provide("dijit.form.ComboButton");
  20275. }
  20276. if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20277. dojo._hasResource["dijit.form.ToggleButton"] = true;
  20278. dojo.provide("dijit.form.ToggleButton");
  20279. }
  20280. if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20281. dojo._hasResource["dijit.form.CheckBox"] = true;
  20282. dojo.provide("dijit.form.CheckBox");
  20283. dojo.declare(
  20284. "dijit.form.CheckBox",
  20285. dijit.form.ToggleButton,
  20286. {
  20287. // summary:
  20288. // Same as an HTML checkbox, but with fancy styling.
  20289. //
  20290. // description:
  20291. // User interacts with real html inputs.
  20292. // On onclick (which occurs by mouse click, space-bar, or
  20293. // using the arrow keys to switch the selected radio button),
  20294. // we update the state of the checkbox/radio.
  20295. //
  20296. // There are two modes:
  20297. // 1. High contrast mode
  20298. // 2. Normal mode
  20299. //
  20300. // In case 1, the regular html inputs are shown and used by the user.
  20301. // In case 2, the regular html inputs are invisible but still used by
  20302. // the user. They are turned quasi-invisible and overlay the background-image.
  20303. templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"focusNode\"\n\t \tdojoAttachEvent=\"onclick:_onClick\"\n/></div>\n"),
  20304. baseClass: "dijitCheckBox",
  20305. // type: [private] String
  20306. // type attribute on <input> node.
  20307. // Overrides `dijit.form.Button.type`. Users should not change this value.
  20308. type: "checkbox",
  20309. // value: String
  20310. // As an initialization parameter, equivalent to value field on normal checkbox
  20311. // (if checked, the value is passed as the value when form is submitted).
  20312. //
  20313. // However, get('value') will return either the string or false depending on
  20314. // whether or not the checkbox is checked.
  20315. //
  20316. // set('value', string) will check the checkbox and change the value to the
  20317. // specified string
  20318. //
  20319. // set('value', boolean) will change the checked state.
  20320. value: "on",
  20321. // readOnly: Boolean
  20322. // Should this widget respond to user input?
  20323. // In markup, this is specified as "readOnly".
  20324. // Similar to disabled except readOnly form values are submitted.
  20325. readOnly: false,
  20326. // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
  20327. // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
  20328. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  20329. readOnly: "focusNode"
  20330. }),
  20331. _setReadOnlyAttr: function(/*Boolean*/ value){
  20332. this._set("readOnly", value);
  20333. dojo.attr(this.focusNode, 'readOnly', value);
  20334. dijit.setWaiState(this.focusNode, "readonly", value);
  20335. },
  20336. _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
  20337. // summary:
  20338. // Handler for value= attribute to constructor, and also calls to
  20339. // set('value', val).
  20340. // description:
  20341. // During initialization, just saves as attribute to the <input type=checkbox>.
  20342. //
  20343. // After initialization,
  20344. // when passed a boolean, controls whether or not the CheckBox is checked.
  20345. // If passed a string, changes the value attribute of the CheckBox (the one
  20346. // specified as "value" when the CheckBox was constructed (ex: <input
  20347. // dojoType="dijit.CheckBox" value="chicken">)
  20348. if(typeof newValue == "string"){
  20349. this._set("value", newValue);
  20350. dojo.attr(this.focusNode, 'value', newValue);
  20351. newValue = true;
  20352. }
  20353. if(this._created){
  20354. this.set('checked', newValue, priorityChange);
  20355. }
  20356. },
  20357. _getValueAttr: function(){
  20358. // summary:
  20359. // Hook so get('value') works.
  20360. // description:
  20361. // If the CheckBox is checked, returns the value attribute.
  20362. // Otherwise returns false.
  20363. return (this.checked ? this.value : false);
  20364. },
  20365. // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
  20366. // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
  20367. _setLabelAttr: undefined,
  20368. postMixInProperties: function(){
  20369. if(this.value == ""){
  20370. this.value = "on";
  20371. }
  20372. // Need to set initial checked state as part of template, so that form submit works.
  20373. // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
  20374. // to <body>, see #8666
  20375. this.checkedAttrSetting = this.checked ? "checked" : "";
  20376. this.inherited(arguments);
  20377. },
  20378. _fillContent: function(/*DomNode*/ source){
  20379. // Override Button::_fillContent() since it doesn't make sense for CheckBox,
  20380. // since CheckBox doesn't even have a container
  20381. },
  20382. reset: function(){
  20383. // Override ToggleButton.reset()
  20384. this._hasBeenBlurred = false;
  20385. this.set('checked', this.params.checked || false);
  20386. // Handle unlikely event that the <input type=checkbox> value attribute has changed
  20387. this._set("value", this.params.value || "on");
  20388. dojo.attr(this.focusNode, 'value', this.value);
  20389. },
  20390. _onFocus: function(){
  20391. if(this.id){
  20392. dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
  20393. }
  20394. this.inherited(arguments);
  20395. },
  20396. _onBlur: function(){
  20397. if(this.id){
  20398. dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
  20399. }
  20400. this.inherited(arguments);
  20401. },
  20402. _onClick: function(/*Event*/ e){
  20403. // summary:
  20404. // Internal function to handle click actions - need to check
  20405. // readOnly, since button no longer does that check.
  20406. if(this.readOnly){
  20407. dojo.stopEvent(e);
  20408. return false;
  20409. }
  20410. return this.inherited(arguments);
  20411. }
  20412. }
  20413. );
  20414. dojo.declare(
  20415. "dijit.form.RadioButton",
  20416. dijit.form.CheckBox,
  20417. {
  20418. // summary:
  20419. // Same as an HTML radio, but with fancy styling.
  20420. type: "radio",
  20421. baseClass: "dijitRadio",
  20422. _setCheckedAttr: function(/*Boolean*/ value){
  20423. // If I am being checked then have to deselect currently checked radio button
  20424. this.inherited(arguments);
  20425. if(!this._created){ return; }
  20426. if(value){
  20427. var _this = this;
  20428. // search for radio buttons with the same name that need to be unchecked
  20429. dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
  20430. function(inputNode){
  20431. if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
  20432. var widget = dijit.getEnclosingWidget(inputNode);
  20433. if(widget && widget.checked){
  20434. widget.set('checked', false);
  20435. }
  20436. }
  20437. }
  20438. );
  20439. }
  20440. },
  20441. _clicked: function(/*Event*/ e){
  20442. if(!this.checked){
  20443. this.set('checked', true);
  20444. }
  20445. }
  20446. }
  20447. );
  20448. }
  20449. if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20450. dojo._hasResource["dijit.form.RadioButton"] = true;
  20451. dojo.provide("dijit.form.RadioButton");
  20452. // TODO: for 2.0, move the RadioButton code into this file
  20453. }
  20454. if(!dojo._hasResource["dojo.cldr.monetary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20455. dojo._hasResource["dojo.cldr.monetary"] = true;
  20456. dojo.provide("dojo.cldr.monetary");
  20457. dojo.getObject("cldr.monetary", true, dojo);
  20458. dojo.cldr.monetary.getData = function(/*String*/code){
  20459. // summary: A mapping of currency code to currency-specific formatting information. Returns a unique object with properties: places, round.
  20460. // code: an [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code
  20461. // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/currencyData/fractions
  20462. var placesData = {
  20463. ADP:0,AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,
  20464. COP:0,CRC:0,DJF:0,ESP:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,
  20465. IRR:3,ISK:0,ITL:0,JOD:3,JPY:0,KMF:0,KPW:0,KRW:0,KWD:3,
  20466. LAK:0,LBP:0,LUF:0,LYD:3,MGA:0,MGF:0,MMK:0,MNT:0,MRO:0,
  20467. MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,
  20468. SYP:0,TMM:0,TND:3,TRL:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,
  20469. XAF:0,XOF:0,XPF:0,YER:0,ZMK:0,ZWD:0
  20470. };
  20471. var roundingData = {CHF:5};
  20472. var places = placesData[code], round = roundingData[code];
  20473. if(typeof places == "undefined"){ places = 2; }
  20474. if(typeof round == "undefined"){ round = 0; }
  20475. return {places: places, round: round}; // Object
  20476. };
  20477. }
  20478. if(!dojo._hasResource["dojo.currency"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20479. dojo._hasResource["dojo.currency"] = true;
  20480. dojo.provide("dojo.currency");
  20481. dojo.getObject("currency", true, dojo);
  20482. /*=====
  20483. dojo.currency = {
  20484. // summary: localized formatting and parsing routines for currencies
  20485. //
  20486. // description: extends dojo.number to provide culturally-appropriate formatting of values
  20487. // in various world currencies, including use of a currency symbol. The currencies are specified
  20488. // by a three-letter international symbol in all uppercase, and support for the currencies is
  20489. // provided by the data in `dojo.cldr`. The scripts generating dojo.cldr specify which
  20490. // currency support is included. A fixed number of decimal places is determined based
  20491. // on the currency type and is not determined by the 'pattern' argument. The fractional
  20492. // portion is optional, by default, and variable length decimals are not supported.
  20493. }
  20494. =====*/
  20495. dojo.currency._mixInDefaults = function(options){
  20496. options = options || {};
  20497. options.type = "currency";
  20498. // Get locale-dependent currency data, like the symbol
  20499. var bundle = dojo.i18n.getLocalization("dojo.cldr", "currency", options.locale) || {};
  20500. // Mixin locale-independent currency data, like # of places
  20501. var iso = options.currency;
  20502. var data = dojo.cldr.monetary.getData(iso);
  20503. dojo.forEach(["displayName","symbol","group","decimal"], function(prop){
  20504. data[prop] = bundle[iso+"_"+prop];
  20505. });
  20506. data.fractional = [true, false];
  20507. // Mixin with provided options
  20508. return dojo.mixin(data, options);
  20509. };
  20510. /*=====
  20511. dojo.declare("dojo.currency.__FormatOptions", [dojo.number.__FormatOptions], {
  20512. // type: String?
  20513. // Should not be set. Value is assumed to be "currency".
  20514. // symbol: String?
  20515. // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
  20516. // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
  20517. // currency: String?
  20518. // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
  20519. // For use with dojo.currency only.
  20520. // places: Number?
  20521. // number of decimal places to show. Default is defined based on which currency is used.
  20522. type: "",
  20523. symbol: "",
  20524. currency: "",
  20525. places: ""
  20526. });
  20527. =====*/
  20528. dojo.currency.format = function(/*Number*/value, /*dojo.currency.__FormatOptions?*/options){
  20529. // summary:
  20530. // Format a Number as a currency, using locale-specific settings
  20531. //
  20532. // description:
  20533. // Create a string from a Number using a known, localized pattern.
  20534. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements)
  20535. // appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr)
  20536. // as well as the appropriate symbols and delimiters and number of decimal places.
  20537. //
  20538. // value:
  20539. // the number to be formatted.
  20540. return dojo.number.format(value, dojo.currency._mixInDefaults(options));
  20541. };
  20542. dojo.currency.regexp = function(/*dojo.number.__RegexpOptions?*/options){
  20543. //
  20544. // summary:
  20545. // Builds the regular needed to parse a currency value
  20546. //
  20547. // description:
  20548. // Returns regular expression with positive and negative match, group and decimal separators
  20549. // Note: the options.places default, the number of decimal places to accept, is defined by the currency type.
  20550. return dojo.number.regexp(dojo.currency._mixInDefaults(options)); // String
  20551. };
  20552. /*=====
  20553. dojo.declare("dojo.currency.__ParseOptions", [dojo.number.__ParseOptions], {
  20554. // type: String?
  20555. // Should not be set. Value is assumed to be currency.
  20556. // currency: String?
  20557. // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD".
  20558. // For use with dojo.currency only.
  20559. // symbol: String?
  20560. // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr`
  20561. // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found.
  20562. // places: Number?
  20563. // fixed number of decimal places to accept. The default is determined based on which currency is used.
  20564. // fractional: Boolean?|Array?
  20565. // Whether to include the fractional portion, where the number of decimal places are implied by the currency
  20566. // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
  20567. // By default for currencies, it the fractional portion is optional.
  20568. type: "",
  20569. currency: "",
  20570. symbol: "",
  20571. places: "",
  20572. fractional: ""
  20573. });
  20574. =====*/
  20575. dojo.currency.parse = function(/*String*/expression, /*dojo.currency.__ParseOptions?*/options){
  20576. //
  20577. // summary:
  20578. // Convert a properly formatted currency string to a primitive Number,
  20579. // using locale-specific settings.
  20580. //
  20581. // description:
  20582. // Create a Number from a string using a known, localized pattern.
  20583. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  20584. // are chosen appropriate to the locale, as well as the appropriate symbols and delimiters
  20585. // and number of decimal places.
  20586. //
  20587. // expression: A string representation of a currency value
  20588. return dojo.number.parse(expression, dojo.currency._mixInDefaults(options));
  20589. };
  20590. }
  20591. if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20592. dojo._hasResource["dijit.form.NumberTextBox"] = true;
  20593. dojo.provide("dijit.form.NumberTextBox");
  20594. /*=====
  20595. dojo.declare(
  20596. "dijit.form.NumberTextBox.__Constraints",
  20597. [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
  20598. // summary:
  20599. // Specifies both the rules on valid/invalid values (minimum, maximum,
  20600. // number of required decimal places), and also formatting options for
  20601. // displaying the value when the field is not focused.
  20602. // example:
  20603. // Minimum/maximum:
  20604. // To specify a field between 0 and 120:
  20605. // | {min:0,max:120}
  20606. // To specify a field that must be an integer:
  20607. // | {fractional:false}
  20608. // To specify a field where 0 to 3 decimal places are allowed on input:
  20609. // | {places:'0,3'}
  20610. });
  20611. =====*/
  20612. dojo.declare("dijit.form.NumberTextBoxMixin",
  20613. null,
  20614. {
  20615. // summary:
  20616. // A mixin for all number textboxes
  20617. // tags:
  20618. // protected
  20619. // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
  20620. // than a straight regexp to deal with locale (plus formatting options too?)
  20621. regExpGen: dojo.number.regexp,
  20622. /*=====
  20623. // constraints: dijit.form.NumberTextBox.__Constraints
  20624. // Despite the name, this parameter specifies both constraints on the input
  20625. // (including minimum/maximum allowed values) as well as
  20626. // formatting options like places (the number of digits to display after
  20627. // the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
  20628. constraints: {},
  20629. ======*/
  20630. // value: Number
  20631. // The value of this NumberTextBox as a Javascript Number (i.e., not a String).
  20632. // If the displayed value is blank, the value is NaN, and if the user types in
  20633. // an gibberish value (like "hello world"), the value is undefined
  20634. // (i.e. get('value') returns undefined).
  20635. //
  20636. // Symmetrically, set('value', NaN) will clear the displayed value,
  20637. // whereas set('value', undefined) will have no effect.
  20638. value: NaN,
  20639. // editOptions: [protected] Object
  20640. // Properties to mix into constraints when the value is being edited.
  20641. // This is here because we edit the number in the format "12345", which is
  20642. // different than the display value (ex: "12,345")
  20643. editOptions: { pattern: '#.######' },
  20644. /*=====
  20645. _formatter: function(value, options){
  20646. // summary:
  20647. // _formatter() is called by format(). It's the base routine for formatting a number,
  20648. // as a string, for example converting 12345 into "12,345".
  20649. // value: Number
  20650. // The number to be converted into a string.
  20651. // options: dojo.number.__FormatOptions?
  20652. // Formatting options
  20653. // tags:
  20654. // protected extension
  20655. return "12345"; // String
  20656. },
  20657. =====*/
  20658. _formatter: dojo.number.format,
  20659. _setConstraintsAttr: function(/*Object*/ constraints){
  20660. var places = typeof constraints.places == "number"? constraints.places : 0;
  20661. if(places){ places++; } // decimal rounding errors take away another digit of precision
  20662. if(typeof constraints.max != "number"){
  20663. constraints.max = 9 * Math.pow(10, 15-places);
  20664. }
  20665. if(typeof constraints.min != "number"){
  20666. constraints.min = -9 * Math.pow(10, 15-places);
  20667. }
  20668. this.inherited(arguments, [ constraints ]);
  20669. if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
  20670. this.set('value', this.value);
  20671. }
  20672. },
  20673. _onFocus: function(){
  20674. if(this.disabled){ return; }
  20675. var val = this.get('value');
  20676. if(typeof val == "number" && !isNaN(val)){
  20677. var formattedValue = this.format(val, this.constraints);
  20678. if(formattedValue !== undefined){
  20679. this.textbox.value = formattedValue;
  20680. }
  20681. }
  20682. this.inherited(arguments);
  20683. },
  20684. format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
  20685. // summary:
  20686. // Formats the value as a Number, according to constraints.
  20687. // tags:
  20688. // protected
  20689. var formattedValue = String(value);
  20690. if(typeof value != "number"){ return formattedValue; }
  20691. if(isNaN(value)){ return ""; }
  20692. // check for exponential notation that dojo.number.format chokes on
  20693. if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
  20694. return formattedValue;
  20695. }
  20696. if(this.editOptions && this._focused){
  20697. constraints = dojo.mixin({}, constraints, this.editOptions);
  20698. }
  20699. return this._formatter(value, constraints);
  20700. },
  20701. /*=====
  20702. _parser: function(value, constraints){
  20703. // summary:
  20704. // Parses the string value as a Number, according to constraints.
  20705. // value: String
  20706. // String representing a number
  20707. // constraints: dojo.number.__ParseOptions
  20708. // Formatting options
  20709. // tags:
  20710. // protected
  20711. return 123.45; // Number
  20712. },
  20713. =====*/
  20714. _parser: dojo.number.parse,
  20715. parse: function(/*String*/ value, /*dojo.number.__FormatOptions*/ constraints){
  20716. // summary:
  20717. // Replacable function to convert a formatted string to a number value
  20718. // tags:
  20719. // protected extension
  20720. var v = this._parser(value, dojo.mixin({}, constraints, (this.editOptions && this._focused) ? this.editOptions : {}));
  20721. if(this.editOptions && this._focused && isNaN(v)){
  20722. v = this._parser(value, constraints); // parse w/o editOptions: not technically needed but is nice for the user
  20723. }
  20724. return v;
  20725. },
  20726. _getDisplayedValueAttr: function(){
  20727. var v = this.inherited(arguments);
  20728. return isNaN(v) ? this.textbox.value : v;
  20729. },
  20730. filter: function(/*Number*/ value){
  20731. // summary:
  20732. // This is called with both the display value (string), and the actual value (a number).
  20733. // When called with the actual value it does corrections so that '' etc. are represented as NaN.
  20734. // Otherwise it dispatches to the superclass's filter() method.
  20735. //
  20736. // See `dijit.form.TextBox.filter` for more details.
  20737. return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // set('value', null||''||undefined) should fire onChange(NaN)
  20738. },
  20739. serialize: function(/*Number*/ value, /*Object?*/ options){
  20740. // summary:
  20741. // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
  20742. // tags:
  20743. // protected
  20744. return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
  20745. },
  20746. _setBlurValue: function(){
  20747. var val = dojo.hitch(dojo.mixin({}, this, { _focused: true }), "get")('value'); // parse with editOptions
  20748. this._setValueAttr(val, true);
  20749. },
  20750. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  20751. // summary:
  20752. // Hook so set('value', ...) works.
  20753. if(value !== undefined && formattedValue === undefined){
  20754. formattedValue = String(value);
  20755. if(typeof value == "number"){
  20756. if(isNaN(value)){ formattedValue = '' }
  20757. // check for exponential notation that dojo.number.format chokes on
  20758. else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
  20759. formattedValue = undefined; // lets format comnpute a real string value
  20760. }
  20761. }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
  20762. formattedValue = '';
  20763. value = NaN;
  20764. }else{ // non-numeric values
  20765. value = undefined;
  20766. }
  20767. }
  20768. this.inherited(arguments, [value, priorityChange, formattedValue]);
  20769. },
  20770. _getValueAttr: function(){
  20771. // summary:
  20772. // Hook so get('value') works.
  20773. // Returns Number, NaN for '', or undefined for unparsable text
  20774. var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
  20775. // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
  20776. // returns NaN; this if() branch converts the return value to undefined.
  20777. // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
  20778. // A blank displayed value is still returned as NaN.
  20779. if(isNaN(v) && this.textbox.value !== ''){
  20780. if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value) && (new RegExp("^"+dojo.number._realNumberRegexp(dojo.mixin({}, this.constraints))+"$").test(this.textbox.value))){ // check for exponential notation that parse() rejected (erroneously?)
  20781. var n = Number(this.textbox.value);
  20782. return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
  20783. }else{
  20784. return undefined; // gibberish
  20785. }
  20786. }else{
  20787. return v; // Number or NaN for ''
  20788. }
  20789. },
  20790. isValid: function(/*Boolean*/ isFocused){
  20791. // Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
  20792. // it may not be formatted according to the regExp vaidation rules
  20793. if(!this._focused || this._isEmpty(this.textbox.value)){
  20794. return this.inherited(arguments);
  20795. }else{
  20796. var v = this.get('value');
  20797. if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
  20798. if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
  20799. return true; // valid exponential number in range
  20800. }else{
  20801. return this.inherited(arguments);
  20802. }
  20803. }else{
  20804. return false;
  20805. }
  20806. }
  20807. }
  20808. }
  20809. );
  20810. dojo.declare("dijit.form.NumberTextBox",
  20811. [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
  20812. {
  20813. // summary:
  20814. // A TextBox for entering numbers, with formatting and range checking
  20815. // description:
  20816. // NumberTextBox is a textbox for entering and displaying numbers, supporting
  20817. // the following main features:
  20818. //
  20819. // 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
  20820. // a number rather than a random string)
  20821. // 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
  20822. // depending on locale).
  20823. // 3. Separate modes for editing the value and displaying it, specifically that
  20824. // the thousands separator character (typically comma) disappears when editing
  20825. // but reappears after the field is blurred.
  20826. // 4. Formatting and constraints regarding the number of places (digits after the decimal point)
  20827. // allowed on input, and number of places displayed when blurred (see `constraints` parameter).
  20828. baseClass: "dijitTextBox dijitNumberTextBox"
  20829. }
  20830. );
  20831. }
  20832. if(!dojo._hasResource["dijit.form.CurrencyTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20833. dojo._hasResource["dijit.form.CurrencyTextBox"] = true;
  20834. dojo.provide("dijit.form.CurrencyTextBox");
  20835. /*=====
  20836. dojo.declare(
  20837. "dijit.form.CurrencyTextBox.__Constraints",
  20838. [dijit.form.NumberTextBox.__Constraints, dojo.currency.__FormatOptions, dojo.currency.__ParseOptions], {
  20839. // summary:
  20840. // Specifies both the rules on valid/invalid values (minimum, maximum,
  20841. // number of required decimal places), and also formatting options for
  20842. // displaying the value when the field is not focused (currency symbol,
  20843. // etc.)
  20844. // description:
  20845. // Follows the pattern of `dijit.form.NumberTextBox.constraints`.
  20846. // In general developers won't need to set this parameter
  20847. // example:
  20848. // To ensure that the user types in the cents (for example, 1.00 instead of just 1):
  20849. // | {fractional:true}
  20850. });
  20851. =====*/
  20852. dojo.declare(
  20853. "dijit.form.CurrencyTextBox",
  20854. dijit.form.NumberTextBox,
  20855. {
  20856. // summary:
  20857. // A validating currency textbox
  20858. // description:
  20859. // CurrencyTextBox is similar to `dijit.form.NumberTextBox` but has a few
  20860. // extra features related to currency:
  20861. //
  20862. // 1. After specifying the currency type (american dollars, euros, etc.) it automatically
  20863. // sets parse/format options such as how many decimal places to show.
  20864. // 2. The currency mark (dollar sign, euro mark, etc.) is displayed when the field is blurred
  20865. // but erased during editing, so that the user can just enter a plain number.
  20866. // currency: [const] String
  20867. // the [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD"
  20868. currency: "",
  20869. /*=====
  20870. // constraints: dijit.form.CurrencyTextBox.__Constraints
  20871. // Despite the name, this parameter specifies both constraints on the input
  20872. // (including minimum/maximum allowed values) as well as
  20873. // formatting options. See `dijit.form.CurrencyTextBox.__Constraints` for details.
  20874. constraints: {},
  20875. ======*/
  20876. baseClass: "dijitTextBox dijitCurrencyTextBox",
  20877. // Override regExpGen ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
  20878. // than a straight regexp to deal with locale (plus formatting options too?)
  20879. regExpGen: function(constraints){
  20880. // if focused, accept either currency data or NumberTextBox format
  20881. return '(' + (this._focused? this.inherited(arguments, [ dojo.mixin({}, constraints, this.editOptions) ]) + '|' : '')
  20882. + dojo.currency.regexp(constraints) + ')';
  20883. },
  20884. // Override NumberTextBox._formatter to deal with currencies, ex: converts "123.45" to "$123.45"
  20885. _formatter: dojo.currency.format,
  20886. _parser: dojo.currency.parse,
  20887. parse: function(/*String*/ value, /*Object*/ constraints){
  20888. // summary:
  20889. // Parses string value as a Currency, according to the constraints object
  20890. // tags:
  20891. // protected extension
  20892. var v = this.inherited(arguments);
  20893. if(isNaN(v) && /\d+/.test(value)){ // currency parse failed, but it could be because they are using NumberTextBox format so try its parse
  20894. v = dojo.hitch(dojo.mixin({}, this, { _parser: dijit.form.NumberTextBox.prototype._parser }), "inherited")(arguments);
  20895. }
  20896. return v;
  20897. },
  20898. _setConstraintsAttr: function(/*Object*/ constraints){
  20899. if(!constraints.currency && this.currency){
  20900. constraints.currency = this.currency;
  20901. }
  20902. this.inherited(arguments, [ dojo.currency._mixInDefaults(dojo.mixin(constraints, { exponent: false })) ]); // get places
  20903. }
  20904. }
  20905. );
  20906. }
  20907. if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20908. dojo._hasResource["dojo.cldr.supplemental"] = true;
  20909. dojo.provide("dojo.cldr.supplemental");
  20910. dojo.getObject("cldr.supplemental", true, dojo);
  20911. dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
  20912. // summary: Returns a zero-based index for first day of the week
  20913. // description:
  20914. // Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
  20915. // e.g. Sunday (returns 0), or Monday (returns 1)
  20916. // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
  20917. var firstDay = {/*default is 1=Monday*/
  20918. mv:5,
  20919. ae:6,af:6,bh:6,dj:6,dz:6,eg:6,er:6,et:6,iq:6,ir:6,jo:6,ke:6,kw:6,
  20920. ly:6,ma:6,om:6,qa:6,sa:6,sd:6,so:6,sy:6,tn:6,ye:6,
  20921. ar:0,as:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,
  20922. il:0,'in':0,jm:0,jp:0,kg:0,kr:0,la:0,mh:0,mn:0,mo:0,mp:0,
  20923. mt:0,nz:0,ph:0,pk:0,sg:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,
  20924. vi:0,zw:0
  20925. // variant. do not use? gb:0,
  20926. };
  20927. var country = dojo.cldr.supplemental._region(locale);
  20928. var dow = firstDay[country];
  20929. return (dow === undefined) ? 1 : dow; /*Number*/
  20930. };
  20931. dojo.cldr.supplemental._region = function(/*String?*/locale){
  20932. locale = dojo.i18n.normalizeLocale(locale);
  20933. var tags = locale.split('-');
  20934. var region = tags[1];
  20935. if(!region){
  20936. // IE often gives language only (#2269)
  20937. // Arbitrary mappings of language-only locales to a country:
  20938. region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", he:"il", hu:"hu", it:"it",
  20939. ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
  20940. }else if(region.length == 4){
  20941. // The ISO 3166 country code is usually in the second position, unless a
  20942. // 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
  20943. region = tags[2];
  20944. }
  20945. return region;
  20946. };
  20947. dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
  20948. // summary: Returns a hash containing the start and end days of the weekend
  20949. // description:
  20950. // Returns a hash containing the start and end days of the weekend according to local custom using locale,
  20951. // or by default in the user's locale.
  20952. // e.g. {start:6, end:0}
  20953. // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
  20954. var weekendStart = {/*default is 6=Saturday*/
  20955. 'in':0,
  20956. af:4,dz:4,ir:4,om:4,sa:4,ye:4,
  20957. ae:5,bh:5,eg:5,il:5,iq:5,jo:5,kw:5,ly:5,ma:5,qa:5,sd:5,sy:5,tn:5
  20958. };
  20959. var weekendEnd = {/*default is 0=Sunday*/
  20960. af:5,dz:5,ir:5,om:5,sa:5,ye:5,
  20961. ae:6,bh:5,eg:6,il:6,iq:6,jo:6,kw:6,ly:6,ma:6,qa:6,sd:6,sy:6,tn:6
  20962. };
  20963. var country = dojo.cldr.supplemental._region(locale);
  20964. var start = weekendStart[country];
  20965. var end = weekendEnd[country];
  20966. if(start === undefined){start=6;}
  20967. if(end === undefined){end=0;}
  20968. return {start:start, end:end}; /*Object {start,end}*/
  20969. };
  20970. }
  20971. if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20972. dojo._hasResource["dojo.date"] = true;
  20973. dojo.provide("dojo.date");
  20974. dojo.getObject("date", true, dojo);
  20975. /*=====
  20976. dojo.date = {
  20977. // summary: Date manipulation utilities
  20978. }
  20979. =====*/
  20980. dojo.date.getDaysInMonth = function(/*Date*/dateObject){
  20981. // summary:
  20982. // Returns the number of days in the month used by dateObject
  20983. var month = dateObject.getMonth();
  20984. var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  20985. if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
  20986. return days[month]; // Number
  20987. };
  20988. dojo.date.isLeapYear = function(/*Date*/dateObject){
  20989. // summary:
  20990. // Determines if the year of the dateObject is a leap year
  20991. // description:
  20992. // Leap years are years with an additional day YYYY-02-29, where the
  20993. // year number is a multiple of four with the following exception: If
  20994. // a year is a multiple of 100, then it is only a leap year if it is
  20995. // also a multiple of 400. For example, 1900 was not a leap year, but
  20996. // 2000 is one.
  20997. var year = dateObject.getFullYear();
  20998. return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
  20999. };
  21000. // FIXME: This is not localized
  21001. dojo.date.getTimezoneName = function(/*Date*/dateObject){
  21002. // summary:
  21003. // Get the user's time zone as provided by the browser
  21004. // dateObject:
  21005. // Needed because the timezone may vary with time (daylight savings)
  21006. // description:
  21007. // Try to get time zone info from toString or toLocaleString method of
  21008. // the Date object -- UTC offset is not a time zone. See
  21009. // http://www.twinsun.com/tz/tz-link.htm Note: results may be
  21010. // inconsistent across browsers.
  21011. var str = dateObject.toString(); // Start looking in toString
  21012. var tz = ''; // The result -- return empty string if nothing found
  21013. var match;
  21014. // First look for something in parentheses -- fast lookup, no regex
  21015. var pos = str.indexOf('(');
  21016. if(pos > -1){
  21017. tz = str.substring(++pos, str.indexOf(')'));
  21018. }else{
  21019. // If at first you don't succeed ...
  21020. // If IE knows about the TZ, it appears before the year
  21021. // Capital letters or slash before a 4-digit year
  21022. // at the end of string
  21023. var pat = /([A-Z\/]+) \d{4}$/;
  21024. if((match = str.match(pat))){
  21025. tz = match[1];
  21026. }else{
  21027. // Some browsers (e.g. Safari) glue the TZ on the end
  21028. // of toLocaleString instead of putting it in toString
  21029. str = dateObject.toLocaleString();
  21030. // Capital letters or slash -- end of string,
  21031. // after space
  21032. pat = / ([A-Z\/]+)$/;
  21033. if((match = str.match(pat))){
  21034. tz = match[1];
  21035. }
  21036. }
  21037. }
  21038. // Make sure it doesn't somehow end up return AM or PM
  21039. return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
  21040. };
  21041. // Utility methods to do arithmetic calculations with Dates
  21042. dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
  21043. // summary:
  21044. // Compare two date objects by date, time, or both.
  21045. // description:
  21046. // Returns 0 if equal, positive if a > b, else negative.
  21047. // date1:
  21048. // Date object
  21049. // date2:
  21050. // Date object. If not specified, the current Date is used.
  21051. // portion:
  21052. // A string indicating the "date" or "time" portion of a Date object.
  21053. // Compares both "date" and "time" by default. One of the following:
  21054. // "date", "time", "datetime"
  21055. // Extra step required in copy for IE - see #3112
  21056. date1 = new Date(+date1);
  21057. date2 = new Date(+(date2 || new Date()));
  21058. if(portion == "date"){
  21059. // Ignore times and compare dates.
  21060. date1.setHours(0, 0, 0, 0);
  21061. date2.setHours(0, 0, 0, 0);
  21062. }else if(portion == "time"){
  21063. // Ignore dates and compare times.
  21064. date1.setFullYear(0, 0, 0);
  21065. date2.setFullYear(0, 0, 0);
  21066. }
  21067. if(date1 > date2){ return 1; } // int
  21068. if(date1 < date2){ return -1; } // int
  21069. return 0; // int
  21070. };
  21071. dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
  21072. // summary:
  21073. // Add to a Date in intervals of different size, from milliseconds to years
  21074. // date: Date
  21075. // Date object to start with
  21076. // interval:
  21077. // A string representing the interval. One of the following:
  21078. // "year", "month", "day", "hour", "minute", "second",
  21079. // "millisecond", "quarter", "week", "weekday"
  21080. // amount:
  21081. // How much to add to the date.
  21082. var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112)
  21083. var fixOvershoot = false;
  21084. var property = "Date";
  21085. switch(interval){
  21086. case "day":
  21087. break;
  21088. case "weekday":
  21089. //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo.cldr.supplemental
  21090. // Divide the increment time span into weekspans plus leftover days
  21091. // e.g., 8 days is one 5-day weekspan / and two leftover days
  21092. // Can't have zero leftover days, so numbers divisible by 5 get
  21093. // a days value of 5, and the remaining days make up the number of weeks
  21094. var days, weeks;
  21095. var mod = amount % 5;
  21096. if(!mod){
  21097. days = (amount > 0) ? 5 : -5;
  21098. weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
  21099. }else{
  21100. days = mod;
  21101. weeks = parseInt(amount/5);
  21102. }
  21103. // Get weekday value for orig date param
  21104. var strt = date.getDay();
  21105. // Orig date is Sat / positive incrementer
  21106. // Jump over Sun
  21107. var adj = 0;
  21108. if(strt == 6 && amount > 0){
  21109. adj = 1;
  21110. }else if(strt == 0 && amount < 0){
  21111. // Orig date is Sun / negative incrementer
  21112. // Jump back over Sat
  21113. adj = -1;
  21114. }
  21115. // Get weekday val for the new date
  21116. var trgt = strt + days;
  21117. // New date is on Sat or Sun
  21118. if(trgt == 0 || trgt == 6){
  21119. adj = (amount > 0) ? 2 : -2;
  21120. }
  21121. // Increment by number of weeks plus leftover days plus
  21122. // weekend adjustments
  21123. amount = (7 * weeks) + days + adj;
  21124. break;
  21125. case "year":
  21126. property = "FullYear";
  21127. // Keep increment/decrement from 2/29 out of March
  21128. fixOvershoot = true;
  21129. break;
  21130. case "week":
  21131. amount *= 7;
  21132. break;
  21133. case "quarter":
  21134. // Naive quarter is just three months
  21135. amount *= 3;
  21136. // fallthrough...
  21137. case "month":
  21138. // Reset to last day of month if you overshoot
  21139. fixOvershoot = true;
  21140. property = "Month";
  21141. break;
  21142. // case "hour":
  21143. // case "minute":
  21144. // case "second":
  21145. // case "millisecond":
  21146. default:
  21147. property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
  21148. }
  21149. if(property){
  21150. sum["set"+property](sum["get"+property]()+amount);
  21151. }
  21152. if(fixOvershoot && (sum.getDate() < date.getDate())){
  21153. sum.setDate(0);
  21154. }
  21155. return sum; // Date
  21156. };
  21157. dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
  21158. // summary:
  21159. // Get the difference in a specific unit of time (e.g., number of
  21160. // months, weeks, days, etc.) between two dates, rounded to the
  21161. // nearest integer.
  21162. // date1:
  21163. // Date object
  21164. // date2:
  21165. // Date object. If not specified, the current Date is used.
  21166. // interval:
  21167. // A string representing the interval. One of the following:
  21168. // "year", "month", "day", "hour", "minute", "second",
  21169. // "millisecond", "quarter", "week", "weekday"
  21170. // Defaults to "day".
  21171. date2 = date2 || new Date();
  21172. interval = interval || "day";
  21173. var yearDiff = date2.getFullYear() - date1.getFullYear();
  21174. var delta = 1; // Integer return value
  21175. switch(interval){
  21176. case "quarter":
  21177. var m1 = date1.getMonth();
  21178. var m2 = date2.getMonth();
  21179. // Figure out which quarter the months are in
  21180. var q1 = Math.floor(m1/3) + 1;
  21181. var q2 = Math.floor(m2/3) + 1;
  21182. // Add quarters for any year difference between the dates
  21183. q2 += (yearDiff * 4);
  21184. delta = q2 - q1;
  21185. break;
  21186. case "weekday":
  21187. var days = Math.round(dojo.date.difference(date1, date2, "day"));
  21188. var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
  21189. var mod = days % 7;
  21190. // Even number of weeks
  21191. if(mod == 0){
  21192. days = weeks*5;
  21193. }else{
  21194. // Weeks plus spare change (< 7 days)
  21195. var adj = 0;
  21196. var aDay = date1.getDay();
  21197. var bDay = date2.getDay();
  21198. weeks = parseInt(days/7);
  21199. mod = days % 7;
  21200. // Mark the date advanced by the number of
  21201. // round weeks (may be zero)
  21202. var dtMark = new Date(date1);
  21203. dtMark.setDate(dtMark.getDate()+(weeks*7));
  21204. var dayMark = dtMark.getDay();
  21205. // Spare change days -- 6 or less
  21206. if(days > 0){
  21207. switch(true){
  21208. // Range starts on Sat
  21209. case aDay == 6:
  21210. adj = -1;
  21211. break;
  21212. // Range starts on Sun
  21213. case aDay == 0:
  21214. adj = 0;
  21215. break;
  21216. // Range ends on Sat
  21217. case bDay == 6:
  21218. adj = -1;
  21219. break;
  21220. // Range ends on Sun
  21221. case bDay == 0:
  21222. adj = -2;
  21223. break;
  21224. // Range contains weekend
  21225. case (dayMark + mod) > 5:
  21226. adj = -2;
  21227. }
  21228. }else if(days < 0){
  21229. switch(true){
  21230. // Range starts on Sat
  21231. case aDay == 6:
  21232. adj = 0;
  21233. break;
  21234. // Range starts on Sun
  21235. case aDay == 0:
  21236. adj = 1;
  21237. break;
  21238. // Range ends on Sat
  21239. case bDay == 6:
  21240. adj = 2;
  21241. break;
  21242. // Range ends on Sun
  21243. case bDay == 0:
  21244. adj = 1;
  21245. break;
  21246. // Range contains weekend
  21247. case (dayMark + mod) < 0:
  21248. adj = 2;
  21249. }
  21250. }
  21251. days += adj;
  21252. days -= (weeks*2);
  21253. }
  21254. delta = days;
  21255. break;
  21256. case "year":
  21257. delta = yearDiff;
  21258. break;
  21259. case "month":
  21260. delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
  21261. break;
  21262. case "week":
  21263. // Truncate instead of rounding
  21264. // Don't use Math.floor -- value may be negative
  21265. delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
  21266. break;
  21267. case "day":
  21268. delta /= 24;
  21269. // fallthrough
  21270. case "hour":
  21271. delta /= 60;
  21272. // fallthrough
  21273. case "minute":
  21274. delta /= 60;
  21275. // fallthrough
  21276. case "second":
  21277. delta /= 1000;
  21278. // fallthrough
  21279. case "millisecond":
  21280. delta *= date2.getTime() - date1.getTime();
  21281. }
  21282. // Round for fractional values and DST leaps
  21283. return Math.round(delta); // Number (integer)
  21284. };
  21285. }
  21286. if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21287. dojo._hasResource["dojo.date.locale"] = true;
  21288. dojo.provide("dojo.date.locale");
  21289. dojo.getObject("date.locale", true, dojo);
  21290. // Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data.
  21291. // Load the bundles containing localization information for
  21292. // names and formats
  21293. //NOTE: Everything in this module assumes Gregorian calendars.
  21294. // Other calendars will be implemented in separate modules.
  21295. (function(){
  21296. // Format a pattern without literals
  21297. function formatPattern(dateObject, bundle, options, pattern){
  21298. return pattern.replace(/([a-z])\1*/ig, function(match){
  21299. var s, pad,
  21300. c = match.charAt(0),
  21301. l = match.length,
  21302. widthList = ["abbr", "wide", "narrow"];
  21303. switch(c){
  21304. case 'G':
  21305. s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
  21306. break;
  21307. case 'y':
  21308. s = dateObject.getFullYear();
  21309. switch(l){
  21310. case 1:
  21311. break;
  21312. case 2:
  21313. if(!options.fullYear){
  21314. s = String(s); s = s.substr(s.length - 2);
  21315. break;
  21316. }
  21317. // fallthrough
  21318. default:
  21319. pad = true;
  21320. }
  21321. break;
  21322. case 'Q':
  21323. case 'q':
  21324. s = Math.ceil((dateObject.getMonth()+1)/3);
  21325. // switch(l){
  21326. // case 1: case 2:
  21327. pad = true;
  21328. // break;
  21329. // case 3: case 4: // unimplemented
  21330. // }
  21331. break;
  21332. case 'M':
  21333. var m = dateObject.getMonth();
  21334. if(l<3){
  21335. s = m+1; pad = true;
  21336. }else{
  21337. var propM = ["months", "format", widthList[l-3]].join("-");
  21338. s = bundle[propM][m];
  21339. }
  21340. break;
  21341. case 'w':
  21342. var firstDay = 0;
  21343. s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
  21344. break;
  21345. case 'd':
  21346. s = dateObject.getDate(); pad = true;
  21347. break;
  21348. case 'D':
  21349. s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
  21350. break;
  21351. case 'E':
  21352. var d = dateObject.getDay();
  21353. if(l<3){
  21354. s = d+1; pad = true;
  21355. }else{
  21356. var propD = ["days", "format", widthList[l-3]].join("-");
  21357. s = bundle[propD][d];
  21358. }
  21359. break;
  21360. case 'a':
  21361. var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
  21362. s = options[timePeriod] || bundle['dayPeriods-format-wide-' + timePeriod];
  21363. break;
  21364. case 'h':
  21365. case 'H':
  21366. case 'K':
  21367. case 'k':
  21368. var h = dateObject.getHours();
  21369. // strange choices in the date format make it impossible to write this succinctly
  21370. switch (c){
  21371. case 'h': // 1-12
  21372. s = (h % 12) || 12;
  21373. break;
  21374. case 'H': // 0-23
  21375. s = h;
  21376. break;
  21377. case 'K': // 0-11
  21378. s = (h % 12);
  21379. break;
  21380. case 'k': // 1-24
  21381. s = h || 24;
  21382. break;
  21383. }
  21384. pad = true;
  21385. break;
  21386. case 'm':
  21387. s = dateObject.getMinutes(); pad = true;
  21388. break;
  21389. case 's':
  21390. s = dateObject.getSeconds(); pad = true;
  21391. break;
  21392. case 'S':
  21393. s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
  21394. break;
  21395. case 'v': // FIXME: don't know what this is. seems to be same as z?
  21396. case 'z':
  21397. // We only have one timezone to offer; the one from the browser
  21398. s = dojo.date.locale._getZone(dateObject, true, options);
  21399. if(s){break;}
  21400. l=4;
  21401. // fallthrough... use GMT if tz not available
  21402. case 'Z':
  21403. var offset = dojo.date.locale._getZone(dateObject, false, options);
  21404. var tz = [
  21405. (offset<=0 ? "+" : "-"),
  21406. dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
  21407. dojo.string.pad(Math.abs(offset)% 60, 2)
  21408. ];
  21409. if(l==4){
  21410. tz.splice(0, 0, "GMT");
  21411. tz.splice(3, 0, ":");
  21412. }
  21413. s = tz.join("");
  21414. break;
  21415. // case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
  21416. // console.log(match+" modifier unimplemented");
  21417. default:
  21418. throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
  21419. }
  21420. if(pad){ s = dojo.string.pad(s, l); }
  21421. return s;
  21422. });
  21423. }
  21424. /*=====
  21425. dojo.date.locale.__FormatOptions = function(){
  21426. // selector: String
  21427. // choice of 'time','date' (default: date and time)
  21428. // formatLength: String
  21429. // choice of long, short, medium or full (plus any custom additions). Defaults to 'short'
  21430. // datePattern:String
  21431. // override pattern with this string
  21432. // timePattern:String
  21433. // override pattern with this string
  21434. // am: String
  21435. // override strings for am in times
  21436. // pm: String
  21437. // override strings for pm in times
  21438. // locale: String
  21439. // override the locale used to determine formatting rules
  21440. // fullYear: Boolean
  21441. // (format only) use 4 digit years whenever 2 digit years are called for
  21442. // strict: Boolean
  21443. // (parse only) strict parsing, off by default
  21444. this.selector = selector;
  21445. this.formatLength = formatLength;
  21446. this.datePattern = datePattern;
  21447. this.timePattern = timePattern;
  21448. this.am = am;
  21449. this.pm = pm;
  21450. this.locale = locale;
  21451. this.fullYear = fullYear;
  21452. this.strict = strict;
  21453. }
  21454. =====*/
  21455. dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){
  21456. // summary:
  21457. // Returns the zone (or offset) for the given date and options. This
  21458. // is broken out into a separate function so that it can be overridden
  21459. // by timezone-aware code.
  21460. //
  21461. // dateObject:
  21462. // the date and/or time being formatted.
  21463. //
  21464. // getName:
  21465. // Whether to return the timezone string (if true), or the offset (if false)
  21466. //
  21467. // options:
  21468. // The options being used for formatting
  21469. if(getName){
  21470. return dojo.date.getTimezoneName(dateObject);
  21471. }else{
  21472. return dateObject.getTimezoneOffset();
  21473. }
  21474. };
  21475. dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
  21476. // summary:
  21477. // Format a Date object as a String, using locale-specific settings.
  21478. //
  21479. // description:
  21480. // Create a string from a Date object using a known localized pattern.
  21481. // By default, this method formats both date and time from dateObject.
  21482. // Formatting patterns are chosen appropriate to the locale. Different
  21483. // formatting lengths may be chosen, with "full" used by default.
  21484. // Custom patterns may be used or registered with translations using
  21485. // the dojo.date.locale.addCustomFormats method.
  21486. // Formatting patterns are implemented using [the syntax described at
  21487. // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
  21488. //
  21489. // dateObject:
  21490. // the date and/or time to be formatted. If a time only is formatted,
  21491. // the values in the year, month, and day fields are irrelevant. The
  21492. // opposite is true when formatting only dates.
  21493. options = options || {};
  21494. var locale = dojo.i18n.normalizeLocale(options.locale),
  21495. formatLength = options.formatLength || 'short',
  21496. bundle = dojo.date.locale._getGregorianBundle(locale),
  21497. str = [],
  21498. sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options);
  21499. if(options.selector == "year"){
  21500. return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce);
  21501. }
  21502. var pattern;
  21503. if(options.selector != "date"){
  21504. pattern = options.timePattern || bundle["timeFormat-"+formatLength];
  21505. if(pattern){str.push(_processPattern(pattern, sauce));}
  21506. }
  21507. if(options.selector != "time"){
  21508. pattern = options.datePattern || bundle["dateFormat-"+formatLength];
  21509. if(pattern){str.push(_processPattern(pattern, sauce));}
  21510. }
  21511. return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
  21512. function(match, key){ return str[key]; }); // String
  21513. };
  21514. dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
  21515. // summary:
  21516. // Builds the regular needed to parse a localized date
  21517. return dojo.date.locale._parseInfo(options).regexp; // String
  21518. };
  21519. dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
  21520. options = options || {};
  21521. var locale = dojo.i18n.normalizeLocale(options.locale),
  21522. bundle = dojo.date.locale._getGregorianBundle(locale),
  21523. formatLength = options.formatLength || 'short',
  21524. datePattern = options.datePattern || bundle["dateFormat-" + formatLength],
  21525. timePattern = options.timePattern || bundle["timeFormat-" + formatLength],
  21526. pattern;
  21527. if(options.selector == 'date'){
  21528. pattern = datePattern;
  21529. }else if(options.selector == 'time'){
  21530. pattern = timePattern;
  21531. }else{
  21532. pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
  21533. function(match, key){ return [timePattern, datePattern][key]; });
  21534. }
  21535. var tokens = [],
  21536. re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
  21537. return {regexp: re, tokens: tokens, bundle: bundle};
  21538. };
  21539. dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
  21540. // summary:
  21541. // Convert a properly formatted string to a primitive Date object,
  21542. // using locale-specific settings.
  21543. //
  21544. // description:
  21545. // Create a Date object from a string using a known localized pattern.
  21546. // By default, this method parses looking for both date and time in the string.
  21547. // Formatting patterns are chosen appropriate to the locale. Different
  21548. // formatting lengths may be chosen, with "full" used by default.
  21549. // Custom patterns may be used or registered with translations using
  21550. // the dojo.date.locale.addCustomFormats method.
  21551. //
  21552. // Formatting patterns are implemented using [the syntax described at
  21553. // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
  21554. // When two digit years are used, a century is chosen according to a sliding
  21555. // window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
  21556. // year < 100CE requires strict mode.
  21557. //
  21558. // value:
  21559. // A string representation of a date
  21560. // remove non-printing bidi control chars from input and pattern
  21561. var controlChars = /[\u200E\u200F\u202A\u202E]/g,
  21562. info = dojo.date.locale._parseInfo(options),
  21563. tokens = info.tokens, bundle = info.bundle,
  21564. re = new RegExp("^" + info.regexp.replace(controlChars, "") + "$",
  21565. info.strict ? "" : "i"),
  21566. match = re.exec(value && value.replace(controlChars, ""));
  21567. if(!match){ return null; } // null
  21568. var widthList = ['abbr', 'wide', 'narrow'],
  21569. result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end
  21570. amPm = "",
  21571. valid = dojo.every(match, function(v, i){
  21572. if(!i){return true;}
  21573. var token=tokens[i-1];
  21574. var l=token.length;
  21575. switch(token.charAt(0)){
  21576. case 'y':
  21577. if(l != 2 && options.strict){
  21578. //interpret year literally, so '5' would be 5 A.D.
  21579. result[0] = v;
  21580. }else{
  21581. if(v<100){
  21582. v = Number(v);
  21583. //choose century to apply, according to a sliding window
  21584. //of 80 years before and 20 years after present year
  21585. var year = '' + new Date().getFullYear(),
  21586. century = year.substring(0, 2) * 100,
  21587. cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99),
  21588. num = (v < cutoff) ? century + v : century - 100 + v;
  21589. result[0] = num;
  21590. }else{
  21591. //we expected 2 digits and got more...
  21592. if(options.strict){
  21593. return false;
  21594. }
  21595. //interpret literally, so '150' would be 150 A.D.
  21596. //also tolerate '1950', if 'yyyy' input passed to 'yy' format
  21597. result[0] = v;
  21598. }
  21599. }
  21600. break;
  21601. case 'M':
  21602. if(l>2){
  21603. var months = bundle['months-format-' + widthList[l-3]].concat();
  21604. if(!options.strict){
  21605. //Tolerate abbreviating period in month part
  21606. //Case-insensitive comparison
  21607. v = v.replace(".","").toLowerCase();
  21608. months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
  21609. }
  21610. v = dojo.indexOf(months, v);
  21611. if(v == -1){
  21612. // console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
  21613. return false;
  21614. }
  21615. }else{
  21616. v--;
  21617. }
  21618. result[1] = v;
  21619. break;
  21620. case 'E':
  21621. case 'e':
  21622. var days = bundle['days-format-' + widthList[l-3]].concat();
  21623. if(!options.strict){
  21624. //Case-insensitive comparison
  21625. v = v.toLowerCase();
  21626. days = dojo.map(days, function(d){return d.toLowerCase();});
  21627. }
  21628. v = dojo.indexOf(days, v);
  21629. if(v == -1){
  21630. // console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
  21631. return false;
  21632. }
  21633. //TODO: not sure what to actually do with this input,
  21634. //in terms of setting something on the Date obj...?
  21635. //without more context, can't affect the actual date
  21636. //TODO: just validate?
  21637. break;
  21638. case 'D':
  21639. result[1] = 0;
  21640. // fallthrough...
  21641. case 'd':
  21642. result[2] = v;
  21643. break;
  21644. case 'a': //am/pm
  21645. var am = options.am || bundle['dayPeriods-format-wide-am'],
  21646. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  21647. if(!options.strict){
  21648. var period = /\./g;
  21649. v = v.replace(period,'').toLowerCase();
  21650. am = am.replace(period,'').toLowerCase();
  21651. pm = pm.replace(period,'').toLowerCase();
  21652. }
  21653. if(options.strict && v != am && v != pm){
  21654. // console.log("dojo.date.locale.parse: Could not parse am/pm part.");
  21655. return false;
  21656. }
  21657. // we might not have seen the hours field yet, so store the state and apply hour change later
  21658. amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
  21659. break;
  21660. case 'K': //hour (1-24)
  21661. if(v == 24){ v = 0; }
  21662. // fallthrough...
  21663. case 'h': //hour (1-12)
  21664. case 'H': //hour (0-23)
  21665. case 'k': //hour (0-11)
  21666. //TODO: strict bounds checking, padding
  21667. if(v > 23){
  21668. // console.log("dojo.date.locale.parse: Illegal hours value");
  21669. return false;
  21670. }
  21671. //in the 12-hour case, adjusting for am/pm requires the 'a' part
  21672. //which could come before or after the hour, so we will adjust later
  21673. result[3] = v;
  21674. break;
  21675. case 'm': //minutes
  21676. result[4] = v;
  21677. break;
  21678. case 's': //seconds
  21679. result[5] = v;
  21680. break;
  21681. case 'S': //milliseconds
  21682. result[6] = v;
  21683. // break;
  21684. // case 'w':
  21685. //TODO var firstDay = 0;
  21686. // default:
  21687. //TODO: throw?
  21688. // console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
  21689. }
  21690. return true;
  21691. });
  21692. var hours = +result[3];
  21693. if(amPm === 'p' && hours < 12){
  21694. result[3] = hours + 12; //e.g., 3pm -> 15
  21695. }else if(amPm === 'a' && hours == 12){
  21696. result[3] = 0; //12am -> 0
  21697. }
  21698. //TODO: implement a getWeekday() method in order to test
  21699. //validity of input strings containing 'EEE' or 'EEEE'...
  21700. var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
  21701. if(options.strict){
  21702. dateObject.setFullYear(result[0]);
  21703. }
  21704. // Check for overflow. The Date() constructor normalizes things like April 32nd...
  21705. //TODO: why isn't this done for times as well?
  21706. var allTokens = tokens.join(""),
  21707. dateToken = allTokens.indexOf('d') != -1,
  21708. monthToken = allTokens.indexOf('M') != -1;
  21709. if(!valid ||
  21710. (monthToken && dateObject.getMonth() > result[1]) ||
  21711. (dateToken && dateObject.getDate() > result[2])){
  21712. return null;
  21713. }
  21714. // Check for underflow, due to DST shifts. See #9366
  21715. // This assumes a 1 hour dst shift correction at midnight
  21716. // We could compare the timezone offset after the shift and add the difference instead.
  21717. if((monthToken && dateObject.getMonth() < result[1]) ||
  21718. (dateToken && dateObject.getDate() < result[2])){
  21719. dateObject = dojo.date.add(dateObject, "hour", 1);
  21720. }
  21721. return dateObject; // Date
  21722. };
  21723. function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
  21724. //summary: Process a pattern with literals in it
  21725. // Break up on single quotes, treat every other one as a literal, except '' which becomes '
  21726. var identity = function(x){return x;};
  21727. applyPattern = applyPattern || identity;
  21728. applyLiteral = applyLiteral || identity;
  21729. applyAll = applyAll || identity;
  21730. //split on single quotes (which escape literals in date format strings)
  21731. //but preserve escaped single quotes (e.g., o''clock)
  21732. var chunks = pattern.match(/(''|[^'])+/g),
  21733. literal = pattern.charAt(0) == "'";
  21734. dojo.forEach(chunks, function(chunk, i){
  21735. if(!chunk){
  21736. chunks[i]='';
  21737. }else{
  21738. chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'"));
  21739. literal = !literal;
  21740. }
  21741. });
  21742. return applyAll(chunks.join(''));
  21743. }
  21744. function _buildDateTimeRE(tokens, bundle, options, pattern){
  21745. pattern = dojo.regexp.escapeString(pattern);
  21746. if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
  21747. return pattern.replace(/([a-z])\1*/ig, function(match){
  21748. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  21749. var s,
  21750. c = match.charAt(0),
  21751. l = match.length,
  21752. p2 = '', p3 = '';
  21753. if(options.strict){
  21754. if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
  21755. if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
  21756. }else{
  21757. p2 = '0?'; p3 = '0{0,2}';
  21758. }
  21759. switch(c){
  21760. case 'y':
  21761. s = '\\d{2,4}';
  21762. break;
  21763. case 'M':
  21764. s = (l>2) ? '\\S+?' : '1[0-2]|'+p2+'[1-9]';
  21765. break;
  21766. case 'D':
  21767. s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|'+p3+'[1-9][0-9]|'+p2+'[1-9]';
  21768. break;
  21769. case 'd':
  21770. s = '3[01]|[12]\\d|'+p2+'[1-9]';
  21771. break;
  21772. case 'w':
  21773. s = '[1-4][0-9]|5[0-3]|'+p2+'[1-9]';
  21774. break;
  21775. case 'E':
  21776. s = '\\S+';
  21777. break;
  21778. case 'h': //hour (1-12)
  21779. s = '1[0-2]|'+p2+'[1-9]';
  21780. break;
  21781. case 'k': //hour (0-11)
  21782. s = '1[01]|'+p2+'\\d';
  21783. break;
  21784. case 'H': //hour (0-23)
  21785. s = '1\\d|2[0-3]|'+p2+'\\d';
  21786. break;
  21787. case 'K': //hour (1-24)
  21788. s = '1\\d|2[0-4]|'+p2+'[1-9]';
  21789. break;
  21790. case 'm':
  21791. case 's':
  21792. s = '[0-5]\\d';
  21793. break;
  21794. case 'S':
  21795. s = '\\d{'+l+'}';
  21796. break;
  21797. case 'a':
  21798. var am = options.am || bundle['dayPeriods-format-wide-am'],
  21799. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  21800. s = am + '|' + pm;
  21801. if(!options.strict){
  21802. if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
  21803. if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
  21804. if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); }
  21805. }
  21806. s = s.replace(/\./g, "\\.");
  21807. break;
  21808. default:
  21809. // case 'v':
  21810. // case 'z':
  21811. // case 'Z':
  21812. s = ".*";
  21813. // console.log("parse of date format, pattern=" + pattern);
  21814. }
  21815. if(tokens){ tokens.push(match); }
  21816. return "(" + s + ")"; // add capture
  21817. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
  21818. }
  21819. })();
  21820. (function(){
  21821. var _customFormats = [];
  21822. dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
  21823. // summary:
  21824. // Add a reference to a bundle containing localized custom formats to be
  21825. // used by date/time formatting and parsing routines.
  21826. //
  21827. // description:
  21828. // The user may add custom localized formats where the bundle has properties following the
  21829. // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
  21830. // The pattern string should match the format used by the CLDR.
  21831. // See dojo.date.locale.format() for details.
  21832. // The resources must be loaded by dojo.requireLocalization() prior to use
  21833. _customFormats.push({pkg:packageName,name:bundleName});
  21834. };
  21835. dojo.date.locale._getGregorianBundle = function(/*String*/locale){
  21836. var gregorian = {};
  21837. dojo.forEach(_customFormats, function(desc){
  21838. var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
  21839. gregorian = dojo.mixin(gregorian, bundle);
  21840. }, this);
  21841. return gregorian; /*Object*/
  21842. };
  21843. })();
  21844. dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
  21845. dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale){
  21846. // summary:
  21847. // Used to get localized strings from dojo.cldr for day or month names.
  21848. //
  21849. // item:
  21850. // 'months' || 'days'
  21851. // type:
  21852. // 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
  21853. // context:
  21854. // 'standAlone' || 'format' (default)
  21855. // locale:
  21856. // override locale used to find the names
  21857. var label,
  21858. lookup = dojo.date.locale._getGregorianBundle(locale),
  21859. props = [item, context, type];
  21860. if(context == 'standAlone'){
  21861. var key = props.join('-');
  21862. label = lookup[key];
  21863. // Fall back to 'format' flavor of name
  21864. if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr
  21865. }
  21866. props[1] = 'format';
  21867. // return by copy so changes won't be made accidentally to the in-memory model
  21868. return (label || lookup[props.join('-')]).concat(); /*Array*/
  21869. };
  21870. dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
  21871. // summary:
  21872. // Determines if the date falls on a weekend, according to local custom.
  21873. var weekend = dojo.cldr.supplemental.getWeekend(locale),
  21874. day = (dateObject || new Date()).getDay();
  21875. if(weekend.end < weekend.start){
  21876. weekend.end += 7;
  21877. if(day < weekend.start){ day += 7; }
  21878. }
  21879. return day >= weekend.start && day <= weekend.end; // Boolean
  21880. };
  21881. // These are used only by format and strftime. Do they need to be public? Which module should they go in?
  21882. dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
  21883. // summary: gets the day of the year as represented by dateObject
  21884. return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number
  21885. };
  21886. dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
  21887. if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
  21888. var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(),
  21889. adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
  21890. week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
  21891. // if year starts on the specified day, start counting weeks at 1
  21892. if(firstDayOfYear == firstDayOfWeek){ week++; }
  21893. return week; // Number
  21894. };
  21895. }
  21896. if(!dojo._hasResource["dijit.Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21897. dojo._hasResource["dijit.Calendar"] = true;
  21898. dojo.provide("dijit.Calendar");
  21899. dojo.declare(
  21900. "dijit.Calendar",
  21901. [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  21902. {
  21903. // summary:
  21904. // A simple GUI for choosing a date in the context of a monthly calendar.
  21905. //
  21906. // description:
  21907. // A simple GUI for choosing a date in the context of a monthly calendar.
  21908. // This widget can't be used in a form because it doesn't serialize the date to an
  21909. // `<input>` field. For a form element, use dijit.form.DateTextBox instead.
  21910. //
  21911. // Note that the parser takes all dates attributes passed in the
  21912. // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
  21913. // so that they are serializable and locale-independent.
  21914. //
  21915. // example:
  21916. // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode"));
  21917. //
  21918. // example:
  21919. // | <div dojoType="dijit.Calendar"></div>
  21920. templateString: dojo.cache("dijit", "templates/Calendar.html", "<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\" role=\"grid\" dojoAttachEvent=\"onkeypress: _onKeyPress\" aria-labelledby=\"${id}_year\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset dijitCalendarArrow' dojoAttachPoint=\"decrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarDecrease\" role=\"presentation\"/>\n\t\t\t\t<span dojoAttachPoint=\"decreaseArrowNode\" class=\"dijitA11ySideArrow\">-</span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\">\n\t\t\t\t<div dojoType=\"dijit.form.DropDownButton\" dojoAttachPoint=\"monthDropDownButton\"\n\t\t\t\t\tid=\"${id}_mddb\" tabIndex=\"-1\">\n\t\t\t\t</div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset dijitCalendarArrow' dojoAttachPoint=\"incrementMonth\">\n\t\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitCalendarIncrementControl dijitCalendarIncrease\" role=\"presentation\"/>\n\t\t\t\t<span dojoAttachPoint=\"increaseArrowNode\" class=\"dijitA11ySideArrow\">+</span>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<th class=\"dijitReset dijitCalendarDayLabelTemplate\" role=\"columnheader\"><span class=\"dijitCalendarDayLabel\"></span></th>\n\t\t</tr>\n\t</thead>\n\t<tbody dojoAttachEvent=\"onclick: _onDayClick, onmouseover: _onDayMouseOver, onmouseout: _onDayMouseOut, onmousedown: _onDayMouseDown, onmouseup: _onDayMouseUp\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t<tr class=\"dijitReset dijitCalendarWeekTemplate\" role=\"row\">\n\t\t\t<td class=\"dijitReset dijitCalendarDateTemplate\" role=\"gridcell\"><span class=\"dijitCalendarDateLabel\"></span></td>\n\t\t</tr>\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\">\n\t\t\t\t<h3 class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span dojoAttachPoint=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\" id=\"${id}_year\"></span>\n\t\t\t\t\t<span dojoAttachPoint=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\"></span>\n\t\t\t\t</h3>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\n"),
  21921. widgetsInTemplate: true,
  21922. // value: Date
  21923. // The currently selected Date, initially set to invalid date to indicate no selection.
  21924. value: new Date(""),
  21925. // TODO: for 2.0 make this a string (ISO format) rather than a Date
  21926. // datePackage: String
  21927. // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines
  21928. // at dojo.date by default.
  21929. datePackage: "dojo.date",
  21930. // dayWidth: String
  21931. // How to represent the days of the week in the calendar header. See dojo.date.locale
  21932. dayWidth: "narrow",
  21933. // tabIndex: Integer
  21934. // Order fields are traversed when user hits the tab key
  21935. tabIndex: "0",
  21936. // currentFocus: Date
  21937. // Date object containing the currently focused date, or the date which would be focused
  21938. // if the calendar itself was focused. Also indicates which year and month to display,
  21939. // i.e. the current "page" the calendar is on.
  21940. currentFocus: new Date(),
  21941. baseClass:"dijitCalendar",
  21942. // Set node classes for various mouse events, see dijit._CssStateMixin for more details
  21943. cssStateNodes: {
  21944. "decrementMonth": "dijitCalendarArrow",
  21945. "incrementMonth": "dijitCalendarArrow",
  21946. "previousYearLabelNode": "dijitCalendarPreviousYear",
  21947. "nextYearLabelNode": "dijitCalendarNextYear"
  21948. },
  21949. _isValidDate: function(/*Date*/ value){
  21950. // summary:
  21951. // Runs various tests on the value, checking that it's a valid date, rather
  21952. // than blank or NaN.
  21953. // tags:
  21954. // private
  21955. return value && !isNaN(value) && typeof value == "object" &&
  21956. value.toString() != this.constructor.prototype.value.toString();
  21957. },
  21958. setValue: function(/*Date*/ value){
  21959. // summary:
  21960. // Deprecated. Use set('value', ...) instead.
  21961. // tags:
  21962. // deprecated
  21963. dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
  21964. this.set('value', value);
  21965. },
  21966. _getValueAttr: function(){
  21967. // summary:
  21968. // Support get('value')
  21969. // this.value is set to 1AM, but return midnight, local time for back-compat
  21970. var value = new this.dateClassObj(this.value);
  21971. value.setHours(0, 0, 0, 0);
  21972. // If daylight savings pushes midnight to the previous date, fix the Date
  21973. // object to point at 1am so it will represent the correct day. See #9366
  21974. if(value.getDate() < this.value.getDate()){
  21975. value = this.dateFuncObj.add(value, "hour", 1);
  21976. }
  21977. return value;
  21978. },
  21979. _setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){
  21980. // summary:
  21981. // Support set("value", ...)
  21982. // description:
  21983. // Set the current date and update the UI. If the date is disabled, the value will
  21984. // not change, but the display will change to the corresponding month.
  21985. // value:
  21986. // Either a Date or the number of seconds since 1970.
  21987. // tags:
  21988. // protected
  21989. if(value){
  21990. // convert from Number to Date, or make copy of Date object so that setHours() call below
  21991. // doesn't affect original value
  21992. value = new this.dateClassObj(value);
  21993. }
  21994. if(this._isValidDate(value)){
  21995. if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){
  21996. value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
  21997. if(!this.isDisabledDate(value, this.lang)){
  21998. this._set("value", value);
  21999. // Set focus cell to the new value. Arguably this should only happen when there isn't a current
  22000. // focus point. This will also repopulate the grid, showing the new selected value (and possibly
  22001. // new month/year).
  22002. this.set("currentFocus", value);
  22003. if(priorityChange || typeof priorityChange == "undefined"){
  22004. this.onChange(this.get('value'));
  22005. this.onValueSelected(this.get('value')); // remove in 2.0
  22006. }
  22007. }
  22008. }
  22009. }else{
  22010. // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus
  22011. this._set("value", null);
  22012. this.set("currentFocus", this.currentFocus);
  22013. }
  22014. },
  22015. _setText: function(node, text){
  22016. // summary:
  22017. // This just sets the content of node to the specified text.
  22018. // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434.
  22019. // tags:
  22020. // private
  22021. while(node.firstChild){
  22022. node.removeChild(node.firstChild);
  22023. }
  22024. node.appendChild(dojo.doc.createTextNode(text));
  22025. },
  22026. _populateGrid: function(){
  22027. // summary:
  22028. // Fills in the calendar grid with each day (1-31)
  22029. // tags:
  22030. // private
  22031. var month = new this.dateClassObj(this.currentFocus);
  22032. month.setDate(1);
  22033. var firstDay = month.getDay(),
  22034. daysInMonth = this.dateFuncObj.getDaysInMonth(month),
  22035. daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)),
  22036. today = new this.dateClassObj(),
  22037. dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
  22038. if(dayOffset > firstDay){ dayOffset -= 7; }
  22039. // Iterate through dates in the calendar and fill in date numbers and style info
  22040. dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
  22041. i += dayOffset;
  22042. var date = new this.dateClassObj(month),
  22043. number, clazz = "dijitCalendar", adj = 0;
  22044. if(i < firstDay){
  22045. number = daysInPreviousMonth - firstDay + i + 1;
  22046. adj = -1;
  22047. clazz += "Previous";
  22048. }else if(i >= (firstDay + daysInMonth)){
  22049. number = i - firstDay - daysInMonth + 1;
  22050. adj = 1;
  22051. clazz += "Next";
  22052. }else{
  22053. number = i - firstDay + 1;
  22054. clazz += "Current";
  22055. }
  22056. if(adj){
  22057. date = this.dateFuncObj.add(date, "month", adj);
  22058. }
  22059. date.setDate(number);
  22060. if(!this.dateFuncObj.compare(date, today, "date")){
  22061. clazz = "dijitCalendarCurrentDate " + clazz;
  22062. }
  22063. if(this._isSelectedDate(date, this.lang)){
  22064. clazz = "dijitCalendarSelectedDate " + clazz;
  22065. }
  22066. if(this.isDisabledDate(date, this.lang)){
  22067. clazz = "dijitCalendarDisabledDate " + clazz;
  22068. }
  22069. var clazz2 = this.getClassForDate(date, this.lang);
  22070. if(clazz2){
  22071. clazz = clazz2 + " " + clazz;
  22072. }
  22073. template.className = clazz + "Month dijitCalendarDateTemplate";
  22074. template.dijitDateValue = date.valueOf(); // original code
  22075. dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it
  22076. var label = dojo.query(".dijitCalendarDateLabel", template)[0],
  22077. text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate();
  22078. this._setText(label, text);
  22079. }, this);
  22080. // Repopulate month drop down list based on current year.
  22081. // Need to do this to hide leap months in Hebrew calendar.
  22082. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
  22083. this.monthDropDownButton.dropDown.set("months", monthNames);
  22084. // Set name of current month and also fill in spacer element with all the month names
  22085. // (invisible) so that the maximum width will affect layout. But not on IE6 because then
  22086. // the center <TH> overlaps the right <TH> (due to a browser bug).
  22087. this.monthDropDownButton.containerNode.innerHTML =
  22088. (dojo.isIE == 6 ? "" : "<div class='dijitSpacer'>" + this.monthDropDownButton.dropDown.domNode.innerHTML + "</div>") +
  22089. "<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" + monthNames[month.getMonth()] + "</div>";
  22090. // Fill in localized prev/current/next years
  22091. var y = month.getFullYear() - 1;
  22092. var d = new this.dateClassObj();
  22093. dojo.forEach(["previous", "current", "next"], function(name){
  22094. d.setFullYear(y++);
  22095. this._setText(this[name+"YearLabelNode"],
  22096. this.dateLocaleModule.format(d, {selector:'year', locale:this.lang}));
  22097. }, this);
  22098. },
  22099. goToToday: function(){
  22100. // summary:
  22101. // Sets calendar's value to today's date
  22102. this.set('value', new this.dateClassObj());
  22103. },
  22104. constructor: function(/*Object*/args){
  22105. var dateClass = (args.datePackage && (args.datePackage != "dojo.date"))? args.datePackage + ".Date" : "Date";
  22106. this.dateClassObj = dojo.getObject(dateClass, false);
  22107. this.datePackage = args.datePackage || this.datePackage;
  22108. this.dateFuncObj = dojo.getObject(this.datePackage, false);
  22109. this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
  22110. },
  22111. postMixInProperties: function(){
  22112. // Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead.
  22113. // TODO: remove this for 2.0 (thanks to #11511)
  22114. if(isNaN(this.value)){ delete this.value; }
  22115. this.inherited(arguments);
  22116. },
  22117. buildRendering: function(){
  22118. this.inherited(arguments);
  22119. dojo.setSelectable(this.domNode, false);
  22120. var cloneClass = dojo.hitch(this, function(clazz, n){
  22121. var template = dojo.query(clazz, this.domNode)[0];
  22122. for(var i=0; i<n; i++){
  22123. template.parentNode.appendChild(template.cloneNode(true));
  22124. }
  22125. });
  22126. // clone the day label and calendar day templates 6 times to make 7 columns
  22127. cloneClass(".dijitCalendarDayLabelTemplate", 6);
  22128. cloneClass(".dijitCalendarDateTemplate", 6);
  22129. // now make 6 week rows
  22130. cloneClass(".dijitCalendarWeekTemplate", 5);
  22131. // insert localized day names in the header
  22132. var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang);
  22133. var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
  22134. dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
  22135. this._setText(label, dayNames[(i + dayOffset) % 7]);
  22136. }, this);
  22137. var dateObj = new this.dateClassObj(this.currentFocus);
  22138. this.monthDropDownButton.dropDown = new dijit.Calendar._MonthDropDown({
  22139. id: this.id + "_mdd",
  22140. onChange: dojo.hitch(this, "_onMonthSelect")
  22141. });
  22142. this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
  22143. // Set up repeating mouse behavior for increment/decrement of months/years
  22144. var _this = this;
  22145. var typematic = function(nodeProp, dateProp, adj){
  22146. _this._connects.push(
  22147. dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
  22148. if(count >= 0){ _this._adjustDisplay(dateProp, adj); }
  22149. }, 0.8, 500)
  22150. );
  22151. };
  22152. typematic("incrementMonth", "month", 1);
  22153. typematic("decrementMonth", "month", -1);
  22154. typematic("nextYearLabelNode", "year", 1);
  22155. typematic("previousYearLabelNode", "year", -1);
  22156. },
  22157. _adjustDisplay: function(/*String*/ part, /*int*/ amount){
  22158. // summary:
  22159. // Moves calendar forwards or backwards by months or years
  22160. // part:
  22161. // "month" or "year"
  22162. // amount:
  22163. // Number of months or years
  22164. // tags:
  22165. // private
  22166. this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount));
  22167. },
  22168. _setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){
  22169. // summary:
  22170. // If the calendar currently has focus, then focuses specified date,
  22171. // changing the currently displayed month/year if necessary.
  22172. // If the calendar doesn't have focus, updates currently
  22173. // displayed month/year, and sets the cell that will get focus.
  22174. // forceFocus:
  22175. // If true, will focus() the cell even if calendar itself doesn't have focus
  22176. var oldFocus = this.currentFocus,
  22177. oldCell = oldFocus ? dojo.query("[dijitDateValue=" + oldFocus.valueOf() + "]", this.domNode)[0] : null;
  22178. // round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
  22179. date = new this.dateClassObj(date);
  22180. date.setHours(1, 0, 0, 0);
  22181. this._set("currentFocus", date);
  22182. // TODO: only re-populate grid when month/year has changed
  22183. this._populateGrid();
  22184. // set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
  22185. var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0];
  22186. newCell.setAttribute("tabIndex", this.tabIndex);
  22187. if(this._focused || forceFocus){
  22188. newCell.focus();
  22189. }
  22190. // set tabIndex=-1 on old focusable cell
  22191. if(oldCell && oldCell != newCell){
  22192. if(dojo.isWebKit){ // see #11064 about webkit bug
  22193. oldCell.setAttribute("tabIndex", "-1");
  22194. }else{
  22195. oldCell.removeAttribute("tabIndex");
  22196. }
  22197. }
  22198. },
  22199. focus: function(){
  22200. // summary:
  22201. // Focus the calendar by focusing one of the calendar cells
  22202. this._setCurrentFocusAttr(this.currentFocus, true);
  22203. },
  22204. _onMonthSelect: function(/*Number*/ newMonth){
  22205. // summary:
  22206. // Handler for when user selects a month from the drop down list
  22207. // tags:
  22208. // protected
  22209. // move to selected month, bounding by the number of days in the month
  22210. // (ex: dec 31 --> jan 28, not jan 31)
  22211. this.currentFocus = this.dateFuncObj.add(this.currentFocus, "month",
  22212. newMonth - this.currentFocus.getMonth());
  22213. this._populateGrid();
  22214. },
  22215. _onDayClick: function(/*Event*/ evt){
  22216. // summary:
  22217. // Handler for day clicks, selects the date if appropriate
  22218. // tags:
  22219. // protected
  22220. dojo.stopEvent(evt);
  22221. for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode);
  22222. if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){
  22223. this.set('value', node.dijitDateValue);
  22224. }
  22225. },
  22226. _onDayMouseOver: function(/*Event*/ evt){
  22227. // summary:
  22228. // Handler for mouse over events on days, sets hovered style
  22229. // tags:
  22230. // protected
  22231. // event can occur on <td> or the <span> inside the td,
  22232. // set node to the <td>.
  22233. var node =
  22234. dojo.hasClass(evt.target, "dijitCalendarDateLabel") ?
  22235. evt.target.parentNode :
  22236. evt.target;
  22237. if(node && (node.dijitDateValue || node == this.previousYearLabelNode || node == this.nextYearLabelNode) ){
  22238. dojo.addClass(node, "dijitCalendarHoveredDate");
  22239. this._currentNode = node;
  22240. }
  22241. },
  22242. _onDayMouseOut: function(/*Event*/ evt){
  22243. // summary:
  22244. // Handler for mouse out events on days, clears hovered style
  22245. // tags:
  22246. // protected
  22247. if(!this._currentNode){ return; }
  22248. // if mouse out occurs moving from <td> to <span> inside <td>, ignore it
  22249. if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; }
  22250. var cls = "dijitCalendarHoveredDate";
  22251. if(dojo.hasClass(this._currentNode, "dijitCalendarActiveDate")) {
  22252. cls += " dijitCalendarActiveDate";
  22253. }
  22254. dojo.removeClass(this._currentNode, cls);
  22255. this._currentNode = null;
  22256. },
  22257. _onDayMouseDown: function(/*Event*/ evt){
  22258. var node = evt.target.parentNode;
  22259. if(node && node.dijitDateValue){
  22260. dojo.addClass(node, "dijitCalendarActiveDate");
  22261. this._currentNode = node;
  22262. }
  22263. },
  22264. _onDayMouseUp: function(/*Event*/ evt){
  22265. var node = evt.target.parentNode;
  22266. if(node && node.dijitDateValue){
  22267. dojo.removeClass(node, "dijitCalendarActiveDate");
  22268. }
  22269. },
  22270. //TODO: use typematic
  22271. handleKey: function(/*Event*/ evt){
  22272. // summary:
  22273. // Provides keyboard navigation of calendar.
  22274. // description:
  22275. // Called from _onKeyPress() to handle keypress on a stand alone Calendar,
  22276. // and also from `dijit.form._DateTimeTextBox` to pass a keypress event
  22277. // from the `dijit.form.DateTextBox` to be handled in this widget
  22278. // returns:
  22279. // False if the key was recognized as a navigation key,
  22280. // to indicate that the event was handled by Calendar and shouldn't be propogated
  22281. // tags:
  22282. // protected
  22283. var dk = dojo.keys,
  22284. increment = -1,
  22285. interval,
  22286. newValue = this.currentFocus;
  22287. switch(evt.keyCode){
  22288. case dk.RIGHT_ARROW:
  22289. increment = 1;
  22290. //fallthrough...
  22291. case dk.LEFT_ARROW:
  22292. interval = "day";
  22293. if(!this.isLeftToRight()){ increment *= -1; }
  22294. break;
  22295. case dk.DOWN_ARROW:
  22296. increment = 1;
  22297. //fallthrough...
  22298. case dk.UP_ARROW:
  22299. interval = "week";
  22300. break;
  22301. case dk.PAGE_DOWN:
  22302. increment = 1;
  22303. //fallthrough...
  22304. case dk.PAGE_UP:
  22305. interval = evt.ctrlKey || evt.altKey ? "year" : "month";
  22306. break;
  22307. case dk.END:
  22308. // go to the next month
  22309. newValue = this.dateFuncObj.add(newValue, "month", 1);
  22310. // subtract a day from the result when we're done
  22311. interval = "day";
  22312. //fallthrough...
  22313. case dk.HOME:
  22314. newValue = new this.dateClassObj(newValue);
  22315. newValue.setDate(1);
  22316. break;
  22317. case dk.ENTER:
  22318. case dk.SPACE:
  22319. this.set("value", this.currentFocus);
  22320. break;
  22321. default:
  22322. return true;
  22323. }
  22324. if(interval){
  22325. newValue = this.dateFuncObj.add(newValue, interval, increment);
  22326. }
  22327. this._setCurrentFocusAttr(newValue);
  22328. return false;
  22329. },
  22330. _onKeyPress: function(/*Event*/ evt){
  22331. // summary:
  22332. // For handling keypress events on a stand alone calendar
  22333. if(!this.handleKey(evt)){
  22334. dojo.stopEvent(evt);
  22335. }
  22336. },
  22337. onValueSelected: function(/*Date*/ date){
  22338. // summary:
  22339. // Notification that a date cell was selected. It may be the same as the previous value.
  22340. // description:
  22341. // Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
  22342. // to get notification when the user has clicked a date. Now onExecute() (above) is used.
  22343. // tags:
  22344. // protected
  22345. },
  22346. onChange: function(/*Date*/ date){
  22347. // summary:
  22348. // Called only when the selected date has changed
  22349. },
  22350. _isSelectedDate: function(/*Date*/ dateObject, /*String?*/ locale){
  22351. // summary:
  22352. // Extension point so developers can subclass Calendar to
  22353. // support multiple (concurrently) selected dates
  22354. // tags:
  22355. // protected extension
  22356. return this._isValidDate(this.value) && !this.dateFuncObj.compare(dateObject, this.value, "date")
  22357. },
  22358. isDisabledDate: function(/*Date*/ dateObject, /*String?*/ locale){
  22359. // summary:
  22360. // May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
  22361. // tags:
  22362. // extension
  22363. /*=====
  22364. return false; // Boolean
  22365. =====*/
  22366. },
  22367. getClassForDate: function(/*Date*/ dateObject, /*String?*/ locale){
  22368. // summary:
  22369. // May be overridden to return CSS classes to associate with the date entry for the given dateObject,
  22370. // for example to indicate a holiday in specified locale.
  22371. // tags:
  22372. // extension
  22373. /*=====
  22374. return ""; // String
  22375. =====*/
  22376. }
  22377. }
  22378. );
  22379. dojo.declare("dijit.Calendar._MonthDropDown", [dijit._Widget, dijit._Templated], {
  22380. // summary:
  22381. // The month drop down
  22382. // months: String[]
  22383. // List of names of months, possibly w/some undefined entries for Hebrew leap months
  22384. // (ex: ["January", "February", undefined, "April", ...])
  22385. months: [],
  22386. templateString: "<div class='dijitCalendarMonthMenu dijitMenu' " +
  22387. "dojoAttachEvent='onclick:_onClick,onmouseover:_onMenuHover,onmouseout:_onMenuHover'></div>",
  22388. _setMonthsAttr: function(/*String[]*/ months){
  22389. this.domNode.innerHTML = dojo.map(months, function(month, idx){
  22390. return month ? "<div class='dijitCalendarMonthLabel' month='" + idx +"'>" + month + "</div>" : "";
  22391. }).join("");
  22392. },
  22393. _onClick: function(/*Event*/ evt){
  22394. this.onChange(dojo.attr(evt.target, "month"));
  22395. },
  22396. onChange: function(/*Number*/ month){
  22397. // summary:
  22398. // Callback when month is selected from drop down
  22399. },
  22400. _onMenuHover: function(evt){
  22401. dojo.toggleClass(evt.target, "dijitCalendarMonthLabelHover", evt.type == "mouseover");
  22402. }
  22403. });
  22404. }
  22405. if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22406. dojo._hasResource["dijit.form._DateTimeTextBox"] = true;
  22407. dojo.provide("dijit.form._DateTimeTextBox");
  22408. new Date("X"); // workaround for #11279, new Date("") == NaN
  22409. /*=====
  22410. dojo.declare(
  22411. "dijit.form._DateTimeTextBox.__Constraints",
  22412. [dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions], {
  22413. // summary:
  22414. // Specifies both the rules on valid/invalid values (first/last date/time allowed),
  22415. // and also formatting options for how the date/time is displayed.
  22416. // example:
  22417. // To restrict to dates within 2004, displayed in a long format like "December 25, 2005":
  22418. // | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'}
  22419. });
  22420. =====*/
  22421. dojo.declare(
  22422. "dijit.form._DateTimeTextBox",
  22423. [ dijit.form.RangeBoundTextBox, dijit._HasDropDown ],
  22424. {
  22425. // summary:
  22426. // Base class for validating, serializable, range-bound date or time text box.
  22427. templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"),
  22428. // hasDownArrow: [const] Boolean
  22429. // Set this textbox to display a down arrow button, to open the drop down list.
  22430. hasDownArrow: true,
  22431. // openOnClick: [const] Boolean
  22432. // Set to true to open drop down upon clicking anywhere on the textbox.
  22433. openOnClick: true,
  22434. /*=====
  22435. // constraints: dijit.form._DateTimeTextBox.__Constraints
  22436. // Despite the name, this parameter specifies both constraints on the input
  22437. // (including starting/ending dates/times allowed) as well as
  22438. // formatting options like whether the date is displayed in long (ex: December 25, 2005)
  22439. // or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details.
  22440. constraints: {},
  22441. ======*/
  22442. // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
  22443. // than a straight regexp to deal with locale (plus formatting options too?)
  22444. regExpGen: dojo.date.locale.regexp,
  22445. // datePackage: String
  22446. // JavaScript namespace to find calendar routines. Uses Gregorian calendar routines
  22447. // at dojo.date, by default.
  22448. datePackage: "dojo.date",
  22449. // Override _FormWidget.compare() to work for dates/times
  22450. compare: function(/*Date*/ val1, /*Date*/ val2){
  22451. var isInvalid1 = this._isInvalidDate(val1);
  22452. var isInvalid2 = this._isInvalidDate(val2);
  22453. return isInvalid1 ? (isInvalid2 ? 0 : -1) : (isInvalid2 ? 1 : dojo.date.compare(val1, val2, this._selector));
  22454. },
  22455. // flag to _HasDropDown to make drop down Calendar width == <input> width
  22456. forceWidth: true,
  22457. format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
  22458. // summary:
  22459. // Formats the value as a Date, according to specified locale (second argument)
  22460. // tags:
  22461. // protected
  22462. if(!value){ return ''; }
  22463. return this.dateLocaleModule.format(value, constraints);
  22464. },
  22465. "parse": function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
  22466. // summary:
  22467. // Parses as string as a Date, according to constraints
  22468. // tags:
  22469. // protected
  22470. return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date
  22471. },
  22472. // Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format.
  22473. serialize: function(/*anything*/ val, /*Object?*/ options){
  22474. if(val.toGregorian){
  22475. val = val.toGregorian();
  22476. }
  22477. return dojo.date.stamp.toISOString(val, options);
  22478. },
  22479. // dropDownDefaultValue: Date
  22480. // The default value to focus in the popupClass widget when the textbox value is empty.
  22481. dropDownDefaultValue : new Date(),
  22482. // value: Date
  22483. // The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate.
  22484. // When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
  22485. value: new Date(""), // value.toString()="NaN"
  22486. _blankValue: null, // used by filter() when the textbox is blank
  22487. // popupClass: [protected extension] String
  22488. // Name of the popup widget class used to select a date/time.
  22489. // Subclasses should specify this.
  22490. popupClass: "", // default is no popup = text only
  22491. // _selector: [protected extension] String
  22492. // Specifies constraints.selector passed to dojo.date functions, should be either
  22493. // "date" or "time".
  22494. // Subclass must specify this.
  22495. _selector: "",
  22496. constructor: function(/*Object*/ args){
  22497. var dateClass = args.datePackage ? args.datePackage + ".Date" : "Date";
  22498. this.dateClassObj = dojo.getObject(dateClass, false);
  22499. this.value = new this.dateClassObj("");
  22500. this.datePackage = args.datePackage || this.datePackage;
  22501. this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
  22502. this.regExpGen = this.dateLocaleModule.regexp;
  22503. this._invalidDate = dijit.form._DateTimeTextBox.prototype.value.toString();
  22504. },
  22505. buildRendering: function(){
  22506. this.inherited(arguments);
  22507. if(!this.hasDownArrow){
  22508. this._buttonNode.style.display = "none";
  22509. }
  22510. // If openOnClick is true, we basically just want to treat the whole widget as the
  22511. // button. We need to do that also if the actual drop down button will be hidden,
  22512. // so that there's a mouse method for opening the drop down.
  22513. if(this.openOnClick || !this.hasDownArrow){
  22514. this._buttonNode = this.domNode;
  22515. this.baseClass += " dijitComboBoxOpenOnClick";
  22516. }
  22517. },
  22518. _setConstraintsAttr: function(/*Object*/ constraints){
  22519. constraints.selector = this._selector;
  22520. constraints.fullYear = true; // see #5465 - always format with 4-digit years
  22521. var fromISO = dojo.date.stamp.fromISOString;
  22522. if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
  22523. if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
  22524. this.inherited(arguments);
  22525. },
  22526. _isInvalidDate: function(/*Date*/ value){
  22527. // summary:
  22528. // Runs various tests on the value, checking for invalid conditions
  22529. // tags:
  22530. // private
  22531. return !value || isNaN(value) || typeof value != "object" || value.toString() == this._invalidDate;
  22532. },
  22533. _setValueAttr: function(/*Date|String*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  22534. // summary:
  22535. // Sets the date on this textbox. Note: value can be a JavaScript Date literal or a string to be parsed.
  22536. if(value !== undefined){
  22537. if(typeof value == "string"){
  22538. value = dojo.date.stamp.fromISOString(value);
  22539. }
  22540. if(this._isInvalidDate(value)){
  22541. value = null;
  22542. }
  22543. if(value instanceof Date && !(this.dateClassObj instanceof Date)){
  22544. value = new this.dateClassObj(value);
  22545. }
  22546. }
  22547. this.inherited(arguments);
  22548. if(this.value instanceof Date){
  22549. this.filterString = "";
  22550. }
  22551. if(this.dropDown){
  22552. this.dropDown.set('value', value, false);
  22553. }
  22554. },
  22555. _set: function(attr, value){
  22556. // Avoid spurious watch() notifications when value is changed to new Date object w/the same value
  22557. if(attr == "value" && this.value instanceof Date && this.compare(value, this.value) == 0){
  22558. return;
  22559. }
  22560. this.inherited(arguments);
  22561. },
  22562. _setDropDownDefaultValueAttr: function(/*Date*/ val){
  22563. if(this._isInvalidDate(val)){
  22564. // convert null setting into today's date, since there needs to be *some* default at all times.
  22565. val = new this.dateClassObj();
  22566. }
  22567. this.dropDownDefaultValue = val;
  22568. },
  22569. openDropDown: function(/*Function*/ callback){
  22570. // rebuild drop down every time, so that constraints get copied (#6002)
  22571. if(this.dropDown){
  22572. this.dropDown.destroy();
  22573. }
  22574. var PopupProto = dojo.getObject(this.popupClass, false),
  22575. textBox = this,
  22576. value = this.get("value");
  22577. this.dropDown = new PopupProto({
  22578. onChange: function(value){
  22579. // this will cause InlineEditBox and other handlers to do stuff so make sure it's last
  22580. textBox.set('value', value, true);
  22581. },
  22582. id: this.id + "_popup",
  22583. dir: textBox.dir,
  22584. lang: textBox.lang,
  22585. value: value,
  22586. currentFocus: !this._isInvalidDate(value) ? value : this.dropDownDefaultValue,
  22587. constraints: textBox.constraints,
  22588. filterString: textBox.filterString, // for TimeTextBox, to filter times shown
  22589. datePackage: textBox.datePackage,
  22590. isDisabledDate: function(/*Date*/ date){
  22591. // summary:
  22592. // disables dates outside of the min/max of the _DateTimeTextBox
  22593. return !textBox.rangeCheck(date, textBox.constraints);
  22594. }
  22595. });
  22596. this.inherited(arguments);
  22597. },
  22598. _getDisplayedValueAttr: function(){
  22599. return this.textbox.value;
  22600. },
  22601. _setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
  22602. this._setValueAttr(this.parse(value, this.constraints), priorityChange, value);
  22603. }
  22604. }
  22605. );
  22606. }
  22607. if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22608. dojo._hasResource["dijit.form.DateTextBox"] = true;
  22609. dojo.provide("dijit.form.DateTextBox");
  22610. dojo.declare(
  22611. "dijit.form.DateTextBox",
  22612. dijit.form._DateTimeTextBox,
  22613. {
  22614. // summary:
  22615. // A validating, serializable, range-bound date text box with a drop down calendar
  22616. //
  22617. // Example:
  22618. // | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)})
  22619. //
  22620. // Example:
  22621. // | <input dojotype='dijit.form.DateTextBox' value='2009-01-20'>
  22622. baseClass: "dijitTextBox dijitComboBox dijitDateTextBox",
  22623. popupClass: "dijit.Calendar",
  22624. _selector: "date",
  22625. // value: Date
  22626. // The value of this widget as a JavaScript Date object, with only year/month/day specified.
  22627. // If specified in markup, use the format specified in `dojo.date.stamp.fromISOString`.
  22628. // set("value", ...) accepts either a Date object or a string.
  22629. value: new Date("") // value.toString()="NaN"
  22630. }
  22631. );
  22632. }
  22633. if(!dojo._hasResource["dijit.form._Spinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22634. dojo._hasResource["dijit.form._Spinner"] = true;
  22635. dojo.provide("dijit.form._Spinner");
  22636. dojo.declare(
  22637. "dijit.form._Spinner",
  22638. dijit.form.RangeBoundTextBox,
  22639. {
  22640. // summary:
  22641. // Mixin for validation widgets with a spinner.
  22642. // description:
  22643. // This class basically (conceptually) extends `dijit.form.ValidationTextBox`.
  22644. // It modifies the template to have up/down arrows, and provides related handling code.
  22645. // defaultTimeout: Number
  22646. // Number of milliseconds before a held arrow key or up/down button becomes typematic
  22647. defaultTimeout: 500,
  22648. // minimumTimeout: Number
  22649. // minimum number of milliseconds that typematic event fires when held key or button is held
  22650. minimumTimeout: 10,
  22651. // timeoutChangeRate: Number
  22652. // Fraction of time used to change the typematic timer between events.
  22653. // 1.0 means that each typematic event fires at defaultTimeout intervals.
  22654. // < 1.0 means that each typematic event fires at an increasing faster rate.
  22655. timeoutChangeRate: 0.90,
  22656. // smallDelta: Number
  22657. // Adjust the value by this much when spinning using the arrow keys/buttons
  22658. smallDelta: 1,
  22659. // largeDelta: Number
  22660. // Adjust the value by this much when spinning using the PgUp/Dn keys
  22661. largeDelta: 10,
  22662. templateString: dojo.cache("dijit.form", "templates/Spinner.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitButtonNode dijitSpinnerButtonContainer\"\n\t\t><input class=\"dijitReset dijitInputField dijitSpinnerButtonInner\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t/><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitUpArrowButton\"\n\t\t\tdojoAttachPoint=\"upArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"&#9650;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t\t><div class=\"dijitReset dijitLeft dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\tdojoAttachPoint=\"downArrowNode\"\n\t\t\t><div class=\"dijitArrowButtonInner\"\n\t\t\t\t><input class=\"dijitReset dijitInputField\" value=\"&#9660;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t\t${_buttonInputDisabled}\n\t\t\t/></div\n\t\t></div\n\t></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935;\" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' dojoAttachPoint=\"textbox,focusNode\" type=\"${type}\" dojoAttachEvent=\"onkeypress:_onKeyPress\"\n\t\t\trole=\"spinbutton\" autocomplete=\"off\" ${!nameAttrSetting}\n\t/></div\n></div>\n"),
  22663. baseClass: "dijitTextBox dijitSpinner",
  22664. // Set classes like dijitUpArrowButtonHover or dijitDownArrowButtonActive depending on
  22665. // mouse action over specified node
  22666. cssStateNodes: {
  22667. "upArrowNode": "dijitUpArrowButton",
  22668. "downArrowNode": "dijitDownArrowButton"
  22669. },
  22670. adjust: function(/*Object*/ val, /*Number*/ delta){
  22671. // summary:
  22672. // Overridable function used to adjust a primitive value(Number/Date/...) by the delta amount specified.
  22673. // The val is adjusted in a way that makes sense to the object type.
  22674. // tags:
  22675. // protected extension
  22676. return val;
  22677. },
  22678. _arrowPressed: function(/*Node*/ nodePressed, /*Number*/ direction, /*Number*/ increment){
  22679. // summary:
  22680. // Handler for arrow button or arrow key being pressed
  22681. if(this.disabled || this.readOnly){ return; }
  22682. this._setValueAttr(this.adjust(this.get('value'), direction*increment), false);
  22683. dijit.selectInputText(this.textbox, this.textbox.value.length);
  22684. },
  22685. _arrowReleased: function(/*Node*/ node){
  22686. // summary:
  22687. // Handler for arrow button or arrow key being released
  22688. this._wheelTimer = null;
  22689. if(this.disabled || this.readOnly){ return; }
  22690. },
  22691. _typematicCallback: function(/*Number*/ count, /*DOMNode*/ node, /*Event*/ evt){
  22692. var inc=this.smallDelta;
  22693. if(node == this.textbox){
  22694. var k=dojo.keys;
  22695. var key = evt.charOrCode;
  22696. inc = (key == k.PAGE_UP || key == k.PAGE_DOWN) ? this.largeDelta : this.smallDelta;
  22697. node = (key == k.UP_ARROW || key == k.PAGE_UP) ? this.upArrowNode : this.downArrowNode;
  22698. }
  22699. if(count == -1){ this._arrowReleased(node); }
  22700. else{ this._arrowPressed(node, (node == this.upArrowNode) ? 1 : -1, inc); }
  22701. },
  22702. _wheelTimer: null,
  22703. _mouseWheeled: function(/*Event*/ evt){
  22704. // summary:
  22705. // Mouse wheel listener where supported
  22706. dojo.stopEvent(evt);
  22707. // FIXME: Safari bubbles
  22708. // be nice to DOH and scroll as much as the event says to
  22709. var scrollAmount = evt.detail ? (evt.detail * -1) : (evt.wheelDelta / 120);
  22710. if(scrollAmount !== 0){
  22711. var node = this[(scrollAmount > 0 ? "upArrowNode" : "downArrowNode" )];
  22712. this._arrowPressed(node, scrollAmount, this.smallDelta);
  22713. if(!this._wheelTimer){
  22714. clearTimeout(this._wheelTimer);
  22715. }
  22716. this._wheelTimer = setTimeout(dojo.hitch(this,"_arrowReleased",node), 50);
  22717. }
  22718. },
  22719. postCreate: function(){
  22720. this.inherited(arguments);
  22721. // extra listeners
  22722. this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled");
  22723. this._connects.push(dijit.typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:dojo.keys.UP_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
  22724. this._connects.push(dijit.typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:dojo.keys.DOWN_ARROW,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
  22725. this._connects.push(dijit.typematic.addListener(this.upArrowNode, this.textbox, {charOrCode:dojo.keys.PAGE_UP,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
  22726. this._connects.push(dijit.typematic.addListener(this.downArrowNode, this.textbox, {charOrCode:dojo.keys.PAGE_DOWN,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false}, this, "_typematicCallback", this.timeoutChangeRate, this.defaultTimeout, this.minimumTimeout));
  22727. }
  22728. });
  22729. }
  22730. if(!dojo._hasResource["dijit.form.NumberSpinner"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22731. dojo._hasResource["dijit.form.NumberSpinner"] = true;
  22732. dojo.provide("dijit.form.NumberSpinner");
  22733. dojo.declare("dijit.form.NumberSpinner",
  22734. [dijit.form._Spinner, dijit.form.NumberTextBoxMixin],
  22735. {
  22736. // summary:
  22737. // Extends NumberTextBox to add up/down arrows and pageup/pagedown for incremental change to the value
  22738. //
  22739. // description:
  22740. // A `dijit.form.NumberTextBox` extension to provide keyboard accessible value selection
  22741. // as well as icons for spinning direction. When using the keyboard, the typematic rules
  22742. // apply, meaning holding the key will gradually increase or decrease the value and
  22743. // accelerate.
  22744. //
  22745. // example:
  22746. // | new dijit.form.NumberSpinner({ constraints:{ max:300, min:100 }}, "someInput");
  22747. adjust: function(/*Object*/ val, /*Number*/ delta){
  22748. // summary:
  22749. // Change Number val by the given amount
  22750. // tags:
  22751. // protected
  22752. var tc = this.constraints,
  22753. v = isNaN(val),
  22754. gotMax = !isNaN(tc.max),
  22755. gotMin = !isNaN(tc.min)
  22756. ;
  22757. if(v && delta != 0){ // blank or invalid value and they want to spin, so create defaults
  22758. val = (delta > 0) ?
  22759. gotMin ? tc.min : gotMax ? tc.max : 0 :
  22760. gotMax ? this.constraints.max : gotMin ? tc.min : 0
  22761. ;
  22762. }
  22763. var newval = val + delta;
  22764. if(v || isNaN(newval)){ return val; }
  22765. if(gotMax && (newval > tc.max)){
  22766. newval = tc.max;
  22767. }
  22768. if(gotMin && (newval < tc.min)){
  22769. newval = tc.min;
  22770. }
  22771. return newval;
  22772. },
  22773. _onKeyPress: function(e){
  22774. if((e.charOrCode == dojo.keys.HOME || e.charOrCode == dojo.keys.END) && !(e.ctrlKey || e.altKey || e.metaKey)
  22775. && typeof this.get('value') != 'undefined' /* gibberish, so HOME and END are default editing keys*/){
  22776. var value = this.constraints[(e.charOrCode == dojo.keys.HOME ? "min" : "max")];
  22777. if(typeof value == "number"){
  22778. this._setValueAttr(value, false);
  22779. }
  22780. // eat home or end key whether we change the value or not
  22781. dojo.stopEvent(e);
  22782. }
  22783. }
  22784. });
  22785. }
  22786. if(!dojo._hasResource["dijit.form.MultiSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22787. dojo._hasResource["dijit.form.MultiSelect"] = true;
  22788. dojo.provide("dijit.form.MultiSelect");
  22789. dojo.declare("dijit.form.MultiSelect", dijit.form._FormValueWidget, {
  22790. // summary:
  22791. // Widget version of a <select multiple=true> element,
  22792. // for selecting multiple options.
  22793. // size: Number
  22794. // Number of elements to display on a page
  22795. // NOTE: may be removed in version 2.0, since elements may have variable height;
  22796. // set the size via style="..." or CSS class names instead.
  22797. size: 7,
  22798. templateString: "<select multiple='true' ${!nameAttrSetting} dojoAttachPoint='containerNode,focusNode' dojoAttachEvent='onchange: _onChange'></select>",
  22799. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  22800. size: "focusNode"
  22801. }),
  22802. reset: function(){
  22803. // summary:
  22804. // Reset the widget's value to what it was at initialization time
  22805. // TODO: once we inherit from FormValueWidget this won't be needed
  22806. this._hasBeenBlurred = false;
  22807. this._setValueAttr(this._resetValue, true);
  22808. },
  22809. addSelected: function(/*dijit.form.MultiSelect*/ select){
  22810. // summary:
  22811. // Move the selected nodes of a passed Select widget
  22812. // instance to this Select widget.
  22813. //
  22814. // example:
  22815. // | // move all the selected values from "bar" to "foo"
  22816. // | dijit.byId("foo").addSelected(dijit.byId("bar"));
  22817. select.getSelected().forEach(function(n){
  22818. this.containerNode.appendChild(n);
  22819. // scroll to bottom to see item
  22820. // cannot use scrollIntoView since <option> tags don't support all attributes
  22821. // does not work on IE due to a bug where <select> always shows scrollTop = 0
  22822. this.domNode.scrollTop = this.domNode.offsetHeight; // overshoot will be ignored
  22823. // scrolling the source select is trickier esp. on safari who forgets to change the scrollbar size
  22824. var oldscroll = select.domNode.scrollTop;
  22825. select.domNode.scrollTop = 0;
  22826. select.domNode.scrollTop = oldscroll;
  22827. },this);
  22828. },
  22829. getSelected: function(){
  22830. // summary:
  22831. // Access the NodeList of the selected options directly
  22832. return dojo.query("option",this.containerNode).filter(function(n){
  22833. return n.selected; // Boolean
  22834. }); // dojo.NodeList
  22835. },
  22836. _getValueAttr: function(){
  22837. // summary:
  22838. // Hook so get('value') works.
  22839. // description:
  22840. // Returns an array of the selected options' values.
  22841. return this.getSelected().map(function(n){
  22842. return n.value;
  22843. });
  22844. },
  22845. multiple: true, // for Form
  22846. _setValueAttr: function(/*Array*/ values){
  22847. // summary:
  22848. // Hook so set('value', values) works.
  22849. // description:
  22850. // Set the value(s) of this Select based on passed values
  22851. dojo.query("option",this.containerNode).forEach(function(n){
  22852. n.selected = (dojo.indexOf(values,n.value) != -1);
  22853. });
  22854. },
  22855. invertSelection: function(onChange){
  22856. // summary:
  22857. // Invert the selection
  22858. // onChange: Boolean
  22859. // If null, onChange is not fired.
  22860. dojo.query("option",this.containerNode).forEach(function(n){
  22861. n.selected = !n.selected;
  22862. });
  22863. this._handleOnChange(this.get('value'), onChange == true);
  22864. },
  22865. _onChange: function(/*Event*/ e){
  22866. this._handleOnChange(this.get('value'), true);
  22867. },
  22868. // for layout widgets:
  22869. resize: function(/*Object*/ size){
  22870. if(size){
  22871. dojo.marginBox(this.domNode, size);
  22872. }
  22873. },
  22874. postCreate: function(){
  22875. this._onChange();
  22876. }
  22877. });
  22878. }
  22879. if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22880. dojo._hasResource["dijit.form.HorizontalSlider"] = true;
  22881. dojo.provide("dijit.form.HorizontalSlider");
  22882. dojo.declare(
  22883. "dijit.form.HorizontalSlider",
  22884. [dijit.form._FormValueWidget, dijit._Container],
  22885. {
  22886. // summary:
  22887. // A form widget that allows one to select a value with a horizontally draggable handle
  22888. templateString: dojo.cache("dijit.form", "templates/HorizontalSlider.html", "<table class=\"dijit dijitReset dijitSlider dijitSliderH\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" dojoAttachEvent=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\trole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"topDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationT dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderDecrementIconH\" style=\"display:none\" dojoAttachPoint=\"decrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderLeftBumper\" dojoAttachEvent=\"onmousedown:_onClkDecBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><div class=\"dijitReset dijitSliderBarContainerH\" role=\"presentation\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" dojoAttachEvent=\"onmousedown:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableH\"\n\t\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleH\" dojoAttachEvent=\"onmousedown:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" dojoAttachEvent=\"onmousedown:_onBarClick\"></div\n\t\t\t></div\n\t\t></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperH dijitSliderRightBumper\" dojoAttachEvent=\"onmousedown:_onClkIncBumper\"></div\n\t\t></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerH\"\n\t\t\t><div class=\"dijitSliderIncrementIconH\" style=\"display:none\" dojoAttachPoint=\"incrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t\t><td dojoAttachPoint=\"containerNode,bottomDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationB dijitSliderDecorationH\"></td\n\t\t><td class=\"dijitReset\" colspan=\"2\"></td\n\t></tr\n></table>\n"),
  22889. // Overrides FormValueWidget.value to indicate numeric value
  22890. value: 0,
  22891. // showButtons: [const] Boolean
  22892. // Show increment/decrement buttons at the ends of the slider?
  22893. showButtons: true,
  22894. // minimum:: [const] Integer
  22895. // The minimum value the slider can be set to.
  22896. minimum: 0,
  22897. // maximum: [const] Integer
  22898. // The maximum value the slider can be set to.
  22899. maximum: 100,
  22900. // discreteValues: Integer
  22901. // If specified, indicates that the slider handle has only 'discreteValues' possible positions,
  22902. // and that after dragging the handle, it will snap to the nearest possible position.
  22903. // Thus, the slider has only 'discreteValues' possible values.
  22904. //
  22905. // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
  22906. // three possible positions, representing values 10, 20, or 30.
  22907. //
  22908. // If discreteValues is not specified or if it's value is higher than the number of pixels
  22909. // in the slider bar, then the slider handle can be moved freely, and the slider's value will be
  22910. // computed/reported based on pixel position (in this case it will likely be fractional,
  22911. // such as 123.456789).
  22912. discreteValues: Infinity,
  22913. // pageIncrement: Integer
  22914. // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
  22915. // that the slider handle is moved via pageup/pagedown keys.
  22916. // If discreteValues is not specified, it indicates the number of pixels.
  22917. pageIncrement: 2,
  22918. // clickSelect: Boolean
  22919. // If clicking the slider bar changes the value or not
  22920. clickSelect: true,
  22921. // slideDuration: Number
  22922. // The time in ms to take to animate the slider handle from 0% to 100%,
  22923. // when clicking the slider bar to make the handle move.
  22924. slideDuration: dijit.defaultDuration,
  22925. // Flag to _Templated (TODO: why is this here? I see no widgets in the template.)
  22926. widgetsInTemplate: true,
  22927. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  22928. id: ""
  22929. }),
  22930. baseClass: "dijitSlider",
  22931. // Apply CSS classes to up/down arrows and handle per mouse state
  22932. cssStateNodes: {
  22933. incrementButton: "dijitSliderIncrementButton",
  22934. decrementButton: "dijitSliderDecrementButton",
  22935. focusNode: "dijitSliderThumb"
  22936. },
  22937. _mousePixelCoord: "pageX",
  22938. _pixelCount: "w",
  22939. _startingPixelCoord: "x",
  22940. _startingPixelCount: "l",
  22941. _handleOffsetCoord: "left",
  22942. _progressPixelSize: "width",
  22943. _onKeyUp: function(/*Event*/ e){
  22944. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  22945. this._setValueAttr(this.value, true);
  22946. },
  22947. _onKeyPress: function(/*Event*/ e){
  22948. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  22949. switch(e.charOrCode){
  22950. case dojo.keys.HOME:
  22951. this._setValueAttr(this.minimum, false);
  22952. break;
  22953. case dojo.keys.END:
  22954. this._setValueAttr(this.maximum, false);
  22955. break;
  22956. // this._descending === false: if ascending vertical (min on top)
  22957. // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
  22958. case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
  22959. case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
  22960. case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
  22961. this.increment(e);
  22962. break;
  22963. case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
  22964. case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
  22965. case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
  22966. this.decrement(e);
  22967. break;
  22968. default:
  22969. return;
  22970. }
  22971. dojo.stopEvent(e);
  22972. },
  22973. _onHandleClick: function(e){
  22974. if(this.disabled || this.readOnly){ return; }
  22975. if(!dojo.isIE){
  22976. // make sure you get focus when dragging the handle
  22977. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  22978. dijit.focus(this.sliderHandle);
  22979. }
  22980. dojo.stopEvent(e);
  22981. },
  22982. _isReversed: function(){
  22983. // summary:
  22984. // Returns true if direction is from right to left
  22985. // tags:
  22986. // protected extension
  22987. return !this.isLeftToRight();
  22988. },
  22989. _onBarClick: function(e){
  22990. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  22991. dijit.focus(this.sliderHandle);
  22992. dojo.stopEvent(e);
  22993. var abspos = dojo.position(this.sliderBarContainer, true);
  22994. var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
  22995. this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
  22996. this._movable.onMouseDown(e);
  22997. },
  22998. _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){
  22999. if(this.disabled || this.readOnly){ return; }
  23000. pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
  23001. var count = this.discreteValues;
  23002. if(count <= 1 || count == Infinity){ count = maxPixels; }
  23003. count--;
  23004. var pixelsPerValue = maxPixels / count;
  23005. var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
  23006. this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
  23007. },
  23008. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  23009. // summary:
  23010. // Hook so set('value', value) works.
  23011. this._set("value", value);
  23012. this.valueNode.value = value;
  23013. dijit.setWaiState(this.focusNode, "valuenow", value);
  23014. this.inherited(arguments);
  23015. var percent = (value - this.minimum) / (this.maximum - this.minimum);
  23016. var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
  23017. var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
  23018. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  23019. this._inProgressAnim.stop(true);
  23020. }
  23021. if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
  23022. // animate the slider
  23023. var _this = this;
  23024. var props = {};
  23025. var start = parseFloat(progressBar.style[this._progressPixelSize]);
  23026. var duration = this.slideDuration * (percent-start/100);
  23027. if(duration == 0){ return; }
  23028. if(duration < 0){ duration = 0 - duration; }
  23029. props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
  23030. this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration,
  23031. onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; },
  23032. onEnd: function(){ delete _this._inProgressAnim; },
  23033. properties: props
  23034. })
  23035. this._inProgressAnim.play();
  23036. }else{
  23037. progressBar.style[this._progressPixelSize] = (percent*100) + "%";
  23038. remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
  23039. }
  23040. },
  23041. _bumpValue: function(signedChange, /*Boolean?*/ priorityChange){
  23042. if(this.disabled || this.readOnly){ return; }
  23043. var s = dojo.getComputedStyle(this.sliderBarContainer);
  23044. var c = dojo._getContentBox(this.sliderBarContainer, s);
  23045. var count = this.discreteValues;
  23046. if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
  23047. count--;
  23048. var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
  23049. if(value < 0){ value = 0; }
  23050. if(value > count){ value = count; }
  23051. value = value * (this.maximum - this.minimum) / count + this.minimum;
  23052. this._setValueAttr(value, priorityChange);
  23053. },
  23054. _onClkBumper: function(val){
  23055. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  23056. this._setValueAttr(val, true);
  23057. },
  23058. _onClkIncBumper: function(){
  23059. this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
  23060. },
  23061. _onClkDecBumper: function(){
  23062. this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
  23063. },
  23064. decrement: function(/*Event*/ e){
  23065. // summary:
  23066. // Decrement slider
  23067. // tags:
  23068. // private
  23069. this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1);
  23070. },
  23071. increment: function(/*Event*/ e){
  23072. // summary:
  23073. // Increment slider
  23074. // tags:
  23075. // private
  23076. this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1);
  23077. },
  23078. _mouseWheeled: function(/*Event*/ evt){
  23079. // summary:
  23080. // Event handler for mousewheel where supported
  23081. dojo.stopEvent(evt);
  23082. var janky = !dojo.isMozilla;
  23083. var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
  23084. this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
  23085. },
  23086. startup: function(){
  23087. if(this._started){ return; }
  23088. dojo.forEach(this.getChildren(), function(child){
  23089. if(this[child.container] != this.containerNode){
  23090. this[child.container].appendChild(child.domNode);
  23091. }
  23092. }, this);
  23093. this.inherited(arguments);
  23094. },
  23095. _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
  23096. if(count == -1){
  23097. this._setValueAttr(this.value, true);
  23098. }else{
  23099. this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
  23100. }
  23101. },
  23102. buildRendering: function(){
  23103. this.inherited(arguments);
  23104. if(this.showButtons){
  23105. this.incrementButton.style.display="";
  23106. this.decrementButton.style.display="";
  23107. }
  23108. // find any associated label element and add to slider focusnode.
  23109. var label = dojo.query('label[for="'+this.id+'"]');
  23110. if(label.length){
  23111. label[0].id = (this.id+"_label");
  23112. dijit.setWaiState(this.focusNode, "labelledby", label[0].id);
  23113. }
  23114. dijit.setWaiState(this.focusNode, "valuemin", this.minimum);
  23115. dijit.setWaiState(this.focusNode, "valuemax", this.maximum);
  23116. },
  23117. postCreate: function(){
  23118. this.inherited(arguments);
  23119. if(this.showButtons){
  23120. this._connects.push(dijit.typematic.addMouseListener(
  23121. this.decrementButton, this, "_typematicCallback", 25, 500));
  23122. this._connects.push(dijit.typematic.addMouseListener(
  23123. this.incrementButton, this, "_typematicCallback", 25, 500));
  23124. }
  23125. this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
  23126. // define a custom constructor for a SliderMover that points back to me
  23127. var mover = dojo.declare(dijit.form._SliderMover, {
  23128. widget: this
  23129. });
  23130. this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
  23131. this._layoutHackIE7();
  23132. },
  23133. destroy: function(){
  23134. this._movable.destroy();
  23135. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  23136. this._inProgressAnim.stop(true);
  23137. }
  23138. this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
  23139. this.inherited(arguments);
  23140. }
  23141. });
  23142. dojo.declare("dijit.form._SliderMover",
  23143. dojo.dnd.Mover,
  23144. {
  23145. onMouseMove: function(e){
  23146. var widget = this.widget;
  23147. var abspos = widget._abspos;
  23148. if(!abspos){
  23149. abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true);
  23150. widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
  23151. widget._isReversed_ = widget._isReversed();
  23152. }
  23153. var coordEvent = e.touches ? e.touches[0] : e, // if multitouch take first touch for coords
  23154. pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
  23155. widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
  23156. },
  23157. destroy: function(e){
  23158. dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
  23159. var widget = this.widget;
  23160. widget._abspos = null;
  23161. widget._setValueAttr(widget.value, true);
  23162. }
  23163. });
  23164. }
  23165. if(!dojo._hasResource["dijit.form.VerticalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23166. dojo._hasResource["dijit.form.VerticalSlider"] = true;
  23167. dojo.provide("dijit.form.VerticalSlider");
  23168. dojo.declare(
  23169. "dijit.form.VerticalSlider",
  23170. dijit.form.HorizontalSlider,
  23171. {
  23172. // summary:
  23173. // A form widget that allows one to select a value with a vertically draggable handle
  23174. templateString: dojo.cache("dijit.form", "templates/VerticalSlider.html", "<table class=\"dijit dijitReset dijitSlider dijitSliderV\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" dojoAttachEvent=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\n\trole=\"presentation\"\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderIncrementIconV\" style=\"display:none\" dojoAttachPoint=\"decrementButton\"><span class=\"dijitSliderButtonInner\">+</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderTopBumper\" dojoAttachEvent=\"onmousedown:_onClkIncBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td dojoAttachPoint=\"leftDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationL dijitSliderDecorationV\"></td\n\t\t><td class=\"dijitReset dijitSliderDecorationC\" style=\"height:100%;\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><center class=\"dijitReset dijitSliderBarContainerV\" role=\"presentation\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderRemainingBar dijitSliderRemainingBarV\" dojoAttachEvent=\"onmousedown:_onBarClick\"><!--#5629--></div\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"progressBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderProgressBar dijitSliderProgressBarV\" dojoAttachEvent=\"onmousedown:_onBarClick\"\n\t\t\t\t\t><div class=\"dijitSliderMoveable dijitSliderMoveableV\" style=\"vertical-align:top;\"\n\t\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle,focusNode\" class=\"dijitSliderImageHandle dijitSliderImageHandleV\" dojoAttachEvent=\"onmousedown:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"></div\n\t\t\t\t\t></div\n\t\t\t\t></div\n\t\t\t></center\n\t\t></td\n\t\t><td dojoAttachPoint=\"containerNode,rightDecoration\" class=\"dijitReset dijitSliderDecoration dijitSliderDecorationR dijitSliderDecorationV\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset\"\n\t\t\t><center><div class=\"dijitSliderBar dijitSliderBumper dijitSliderBumperV dijitSliderBottomBumper\" dojoAttachEvent=\"onmousedown:_onClkDecBumper\"></div></center\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n\t><tr class=\"dijitReset\"\n\t\t><td class=\"dijitReset\"></td\n\t\t><td class=\"dijitReset dijitSliderButtonContainer dijitSliderButtonContainerV\"\n\t\t\t><div class=\"dijitSliderDecrementIconV\" style=\"display:none\" dojoAttachPoint=\"incrementButton\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></table>\n"),
  23175. _mousePixelCoord: "pageY",
  23176. _pixelCount: "h",
  23177. _startingPixelCoord: "y",
  23178. _startingPixelCount: "t",
  23179. _handleOffsetCoord: "top",
  23180. _progressPixelSize: "height",
  23181. // _descending: Boolean
  23182. // Specifies if the slider values go from high-on-top (true), or low-on-top (false)
  23183. // TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
  23184. _descending: true,
  23185. _isReversed: function(){
  23186. // summary:
  23187. // Overrides HorizontalSlider._isReversed.
  23188. // Indicates if values are high on top (with low numbers on the bottom).
  23189. return this._descending;
  23190. }
  23191. });
  23192. }
  23193. if(!dojo._hasResource["dijit.form.HorizontalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23194. dojo._hasResource["dijit.form.HorizontalRule"] = true;
  23195. dojo.provide("dijit.form.HorizontalRule");
  23196. dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
  23197. {
  23198. // summary:
  23199. // Hash marks for `dijit.form.HorizontalSlider`
  23200. templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
  23201. // count: Integer
  23202. // Number of hash marks to generate
  23203. count: 3,
  23204. // container: String
  23205. // For HorizontalSlider, this is either "topDecoration" or "bottomDecoration",
  23206. // and indicates whether this rule goes above or below the slider.
  23207. container: "containerNode",
  23208. // ruleStyle: String
  23209. // CSS style to apply to individual hash marks
  23210. ruleStyle: "",
  23211. _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:',
  23212. _positionSuffix: '%;',
  23213. _suffix: '"></div>',
  23214. _genHTML: function(pos, ndx){
  23215. return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
  23216. },
  23217. // _isHorizontal: [protected extension] Boolean
  23218. // VerticalRule will override this...
  23219. _isHorizontal: true,
  23220. buildRendering: function(){
  23221. this.inherited(arguments);
  23222. var innerHTML;
  23223. if(this.count == 1){
  23224. innerHTML = this._genHTML(50, 0);
  23225. }else{
  23226. var i;
  23227. var interval = 100 / (this.count-1);
  23228. if(!this._isHorizontal || this.isLeftToRight()){
  23229. innerHTML = this._genHTML(0, 0);
  23230. for(i=1; i < this.count-1; i++){
  23231. innerHTML += this._genHTML(interval*i, i);
  23232. }
  23233. innerHTML += this._genHTML(100, this.count-1);
  23234. }else{
  23235. innerHTML = this._genHTML(100, 0);
  23236. for(i=1; i < this.count-1; i++){
  23237. innerHTML += this._genHTML(100-interval*i, i);
  23238. }
  23239. innerHTML += this._genHTML(0, this.count-1);
  23240. }
  23241. }
  23242. this.domNode.innerHTML = innerHTML;
  23243. }
  23244. });
  23245. }
  23246. if(!dojo._hasResource["dijit.form.VerticalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23247. dojo._hasResource["dijit.form.VerticalRule"] = true;
  23248. dojo.provide("dijit.form.VerticalRule");
  23249. dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
  23250. {
  23251. // summary:
  23252. // Hash marks for the `dijit.form.VerticalSlider`
  23253. templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
  23254. _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:',
  23255. /*=====
  23256. // container: String
  23257. // This is either "leftDecoration" or "rightDecoration",
  23258. // to indicate whether this rule goes to the left or to the right of the slider.
  23259. // Note that on RTL system, "leftDecoration" would actually go to the right, and vice-versa.
  23260. container: "",
  23261. =====*/
  23262. // Overrides HorizontalRule._isHorizontal
  23263. _isHorizontal: false
  23264. });
  23265. }
  23266. if(!dojo._hasResource["dijit.form.HorizontalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23267. dojo._hasResource["dijit.form.HorizontalRuleLabels"] = true;
  23268. dojo.provide("dijit.form.HorizontalRuleLabels");
  23269. dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule,
  23270. {
  23271. // summary:
  23272. // Labels for `dijit.form.HorizontalSlider`
  23273. templateString: '<div class="dijitRuleContainer dijitRuleContainerH dijitRuleLabelsContainer dijitRuleLabelsContainerH"></div>',
  23274. // labelStyle: String
  23275. // CSS style to apply to individual text labels
  23276. labelStyle: "",
  23277. // labels: String[]?
  23278. // Array of text labels to render - evenly spaced from left-to-right or bottom-to-top.
  23279. // Alternately, minimum and maximum can be specified, to get numeric labels.
  23280. labels: [],
  23281. // numericMargin: Integer
  23282. // Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
  23283. numericMargin: 0,
  23284. // numericMinimum: Integer
  23285. // Leftmost label value for generated numeric labels when labels[] are not specified
  23286. minimum: 0,
  23287. // numericMaximum: Integer
  23288. // Rightmost label value for generated numeric labels when labels[] are not specified
  23289. maximum: 1,
  23290. // constraints: Object
  23291. // pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
  23292. constraints: {pattern:"#%"},
  23293. _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:',
  23294. _labelPrefix: '"><div class="dijitRuleLabel dijitRuleLabelH">',
  23295. _suffix: '</div></div>',
  23296. _calcPosition: function(pos){
  23297. // summary:
  23298. // Returns the value to be used in HTML for the label as part of the left: attribute
  23299. // tags:
  23300. // protected extension
  23301. return pos;
  23302. },
  23303. _genHTML: function(pos, ndx){
  23304. return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
  23305. },
  23306. getLabels: function(){
  23307. // summary:
  23308. // Overridable function to return array of labels to use for this slider.
  23309. // Can specify a getLabels() method instead of a labels[] array, or min/max attributes.
  23310. // tags:
  23311. // protected extension
  23312. // if the labels array was not specified directly, then see if <li> children were
  23313. var labels = this.labels;
  23314. if(!labels.length){
  23315. // for markup creation, labels are specified as child elements
  23316. labels = dojo.query("> li", this.srcNodeRef).map(function(node){
  23317. return String(node.innerHTML);
  23318. });
  23319. }
  23320. this.srcNodeRef.innerHTML = '';
  23321. // if the labels were not specified directly and not as <li> children, then calculate numeric labels
  23322. if(!labels.length && this.count > 1){
  23323. var start = this.minimum;
  23324. var inc = (this.maximum - start) / (this.count-1);
  23325. for(var i=0; i < this.count; i++){
  23326. labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints));
  23327. start += inc;
  23328. }
  23329. }
  23330. return labels;
  23331. },
  23332. postMixInProperties: function(){
  23333. this.inherited(arguments);
  23334. this.labels = this.getLabels();
  23335. this.count = this.labels.length;
  23336. }
  23337. });
  23338. }
  23339. if(!dojo._hasResource["dijit.form.VerticalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23340. dojo._hasResource["dijit.form.VerticalRuleLabels"] = true;
  23341. dojo.provide("dijit.form.VerticalRuleLabels");
  23342. dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
  23343. {
  23344. // summary:
  23345. // Labels for the `dijit.form.VerticalSlider`
  23346. templateString: '<div class="dijitRuleContainer dijitRuleContainerV dijitRuleLabelsContainer dijitRuleLabelsContainerV"></div>',
  23347. _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:',
  23348. _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">',
  23349. _calcPosition: function(pos){
  23350. // Overrides HorizontalRuleLabel._calcPosition()
  23351. return 100-pos;
  23352. },
  23353. // needed to prevent labels from being reversed in RTL mode
  23354. _isHorizontal: false
  23355. });
  23356. }
  23357. if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23358. dojo._hasResource["dijit.form.SimpleTextarea"] = true;
  23359. dojo.provide("dijit.form.SimpleTextarea");
  23360. dojo.declare("dijit.form.SimpleTextarea",
  23361. dijit.form.TextBox,
  23362. {
  23363. // summary:
  23364. // A simple textarea that degrades, and responds to
  23365. // minimal LayoutContainer usage, and works with dijit.form.Form.
  23366. // Doesn't automatically size according to input, like Textarea.
  23367. //
  23368. // example:
  23369. // | <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
  23370. //
  23371. // example:
  23372. // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
  23373. baseClass: "dijitTextBox dijitTextArea",
  23374. attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
  23375. rows:"textbox", cols: "textbox"
  23376. }),
  23377. // rows: Number
  23378. // The number of rows of text.
  23379. rows: "3",
  23380. // rows: Number
  23381. // The number of characters per line.
  23382. cols: "20",
  23383. templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
  23384. postMixInProperties: function(){
  23385. // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
  23386. // TODO: parser will handle this in 2.0
  23387. if(!this.value && this.srcNodeRef){
  23388. this.value = this.srcNodeRef.value;
  23389. }
  23390. this.inherited(arguments);
  23391. },
  23392. buildRendering: function(){
  23393. this.inherited(arguments);
  23394. if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
  23395. dojo.addClass(this.textbox, "dijitTextAreaCols");
  23396. }
  23397. },
  23398. filter: function(/*String*/ value){
  23399. // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
  23400. // as \r\n instead of just \n
  23401. if(value){
  23402. value = value.replace(/\r/g,"");
  23403. }
  23404. return this.inherited(arguments);
  23405. },
  23406. _previousValue: "",
  23407. _onInput: function(/*Event?*/ e){
  23408. // Override TextBox._onInput() to enforce maxLength restriction
  23409. if(this.maxLength){
  23410. var maxLength = parseInt(this.maxLength);
  23411. var value = this.textbox.value.replace(/\r/g,'');
  23412. var overflow = value.length - maxLength;
  23413. if(overflow > 0){
  23414. if(e){ dojo.stopEvent(e); }
  23415. var textarea = this.textbox;
  23416. if(textarea.selectionStart){
  23417. var pos = textarea.selectionStart;
  23418. var cr = 0;
  23419. if(dojo.isOpera){
  23420. cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
  23421. }
  23422. this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
  23423. textarea.setSelectionRange(pos-overflow, pos-overflow);
  23424. }else if(dojo.doc.selection){ //IE
  23425. textarea.focus();
  23426. var range = dojo.doc.selection.createRange();
  23427. // delete overflow characters
  23428. range.moveStart("character", -overflow);
  23429. range.text = '';
  23430. // show cursor
  23431. range.select();
  23432. }
  23433. }
  23434. this._previousValue = this.textbox.value;
  23435. }
  23436. this.inherited(arguments);
  23437. }
  23438. });
  23439. }
  23440. if(!dojo._hasResource["dijit.form._ExpandingTextAreaMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23441. dojo._hasResource["dijit.form._ExpandingTextAreaMixin"] = true;
  23442. dojo.provide("dijit.form._ExpandingTextAreaMixin");
  23443. // module:
  23444. // dijit/form/_ExpandingTextAreaMixin
  23445. // summary:
  23446. // Mixin for textarea widgets to add auto-expanding capability
  23447. // feature detection
  23448. var needsHelpShrinking;
  23449. dojo.declare("dijit.form._ExpandingTextAreaMixin", null, {
  23450. // summary:
  23451. // Mixin for textarea widgets to add auto-expanding capability
  23452. _setValueAttr: function(){
  23453. this.inherited(arguments);
  23454. this.resize();
  23455. },
  23456. postCreate: function(){
  23457. this.inherited(arguments);
  23458. var textarea = this.textbox;
  23459. if(needsHelpShrinking == undefined){
  23460. var te = dojo.create('textarea', {rows:"5", cols:"20", value: ' ', style: {zoom:1, fontSize:"12px", height:"96px", overflow:'hidden', visibility:'hidden', position:'absolute', border:"5px solid white", margin:"0", padding:"0", boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' }}, dojo.body(), "last");
  23461. needsHelpShrinking = te.scrollHeight >= te.clientHeight;
  23462. dojo.body().removeChild(te);
  23463. }
  23464. this.connect(textarea, "onresize", "_resizeLater");
  23465. this.connect(textarea, "onfocus", "_resizeLater");
  23466. textarea.style.overflowY = "hidden";
  23467. },
  23468. startup: function(){
  23469. this.inherited(arguments);
  23470. this._resizeLater();
  23471. },
  23472. _onInput: function(e){
  23473. this.inherited(arguments);
  23474. this.resize();
  23475. },
  23476. _estimateHeight: function(){
  23477. // summary:
  23478. // Approximate the height when the textarea is invisible with the number of lines in the text.
  23479. // Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
  23480. // In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
  23481. //
  23482. var textarea = this.textbox;
  23483. // #rows = #newlines+1
  23484. textarea.rows = (textarea.value.match(/\n/g) || []).length + 1;
  23485. },
  23486. _resizeLater: function(){
  23487. this.defer("resize");
  23488. },
  23489. resize: function(){
  23490. // summary:
  23491. // Resizes the textarea vertically (should be called after a style/value change)
  23492. var textarea = this.textbox;
  23493. function textareaScrollHeight(){
  23494. var empty = false;
  23495. if(textarea.value === ''){
  23496. textarea.value = ' ';
  23497. empty = true;
  23498. }
  23499. var sh = textarea.scrollHeight;
  23500. if(empty){ textarea.value = ''; }
  23501. return sh;
  23502. }
  23503. if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
  23504. if(this.busyResizing){ return; }
  23505. this.busyResizing = true;
  23506. if(textareaScrollHeight() || textarea.offsetHeight){
  23507. var newH = textareaScrollHeight() + Math.max(textarea.offsetHeight - textarea.clientHeight, 0);
  23508. var newHpx = newH + "px";
  23509. if(newHpx != textarea.style.height){
  23510. textarea.style.height = newHpx;
  23511. textarea.rows = 1; // rows can act like a minHeight if not cleared
  23512. }
  23513. if(needsHelpShrinking){
  23514. var origScrollHeight = textareaScrollHeight(),
  23515. newScrollHeight = origScrollHeight,
  23516. origMinHeight = textarea.style.minHeight,
  23517. decrement = 4, // not too fast, not too slow
  23518. thisScrollHeight,
  23519. origScrollTop = textarea.scrollTop;
  23520. textarea.style.minHeight = newHpx; // maintain current height
  23521. textarea.style.height = "auto"; // allow scrollHeight to change
  23522. while(newH > 0){
  23523. textarea.style.minHeight = Math.max(newH - decrement, 4) + "px";
  23524. thisScrollHeight = textareaScrollHeight();
  23525. var change = newScrollHeight - thisScrollHeight;
  23526. newH -= change;
  23527. if(change < decrement){
  23528. break; // scrollHeight didn't shrink
  23529. }
  23530. newScrollHeight = thisScrollHeight;
  23531. decrement <<= 1;
  23532. }
  23533. textarea.style.height = newH + "px";
  23534. textarea.style.minHeight = origMinHeight;
  23535. textarea.scrollTop = origScrollTop;
  23536. }
  23537. textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden";
  23538. if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
  23539. }else{
  23540. // hidden content of unknown size
  23541. this._estimateHeight();
  23542. }
  23543. this.busyResizing = false;
  23544. }
  23545. });
  23546. }
  23547. if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23548. dojo._hasResource["dijit.form.Textarea"] = true;
  23549. dojo.provide("dijit.form.Textarea");
  23550. /*=====
  23551. var _ExpandingTextAreaMixin = dijit.form._ExpandingTextAreaMixin;
  23552. var SimpleTextarea = dijit.form.SimpleTextarea;
  23553. =====*/
  23554. // module:
  23555. // dijit/form/Textarea
  23556. // summary:
  23557. // A textarea widget that adjusts it's height according to the amount of data.
  23558. dojo.declare("dijit.form.Textarea", [dijit.form.SimpleTextarea, dijit.form._ExpandingTextAreaMixin], {
  23559. // summary:
  23560. // A textarea widget that adjusts it's height according to the amount of data.
  23561. //
  23562. // description:
  23563. // A textarea that dynamically expands/contracts (changing it's height) as
  23564. // the user types, to display all the text without requiring a scroll bar.
  23565. //
  23566. // Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
  23567. // Rows is not supported since this widget adjusts the height.
  23568. //
  23569. // example:
  23570. // | <textarea data-dojo-type="dijit.form.TextArea">...</textarea>
  23571. // TODO: for 2.0, rename this to ExpandingTextArea, and rename SimpleTextarea to TextArea
  23572. baseClass: "dijitTextBox dijitTextArea dijitExpandingTextArea",
  23573. // Override SimpleTextArea.cols to default to width:100%, for backward compatibility
  23574. cols: "",
  23575. buildRendering: function(){
  23576. this.inherited(arguments);
  23577. // tweak textarea style to reduce browser differences
  23578. dojo.style(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' });
  23579. }
  23580. });
  23581. }
  23582. if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23583. dojo._hasResource["dijit.layout.StackController"] = true;
  23584. dojo.provide("dijit.layout.StackController");
  23585. dojo.declare(
  23586. "dijit.layout.StackController",
  23587. [dijit._Widget, dijit._Templated, dijit._Container],
  23588. {
  23589. // summary:
  23590. // Set of buttons to select a page in a page list.
  23591. // description:
  23592. // Monitors the specified StackContainer, and whenever a page is
  23593. // added, deleted, or selected, updates itself accordingly.
  23594. templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
  23595. // containerId: [const] String
  23596. // The id of the page container that I point to
  23597. containerId: "",
  23598. // buttonWidget: [const] String
  23599. // The name of the button widget to create to correspond to each page
  23600. buttonWidget: "dijit.layout._StackButton",
  23601. constructor: function(){
  23602. this.pane2button = {}; // mapping from pane id to buttons
  23603. this.pane2connects = {}; // mapping from pane id to this.connect() handles
  23604. this.pane2watches = {}; // mapping from pane id to watch() handles
  23605. },
  23606. buildRendering: function(){
  23607. this.inherited(arguments);
  23608. dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above.
  23609. },
  23610. postCreate: function(){
  23611. this.inherited(arguments);
  23612. // Listen to notifications from StackContainer
  23613. this.subscribe(this.containerId+"-startup", "onStartup");
  23614. this.subscribe(this.containerId+"-addChild", "onAddChild");
  23615. this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
  23616. this.subscribe(this.containerId+"-selectChild", "onSelectChild");
  23617. this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
  23618. },
  23619. onStartup: function(/*Object*/ info){
  23620. // summary:
  23621. // Called after StackContainer has finished initializing
  23622. // tags:
  23623. // private
  23624. dojo.forEach(info.children, this.onAddChild, this);
  23625. if(info.selected){
  23626. // Show button corresponding to selected pane (unless selected
  23627. // is null because there are no panes)
  23628. this.onSelectChild(info.selected);
  23629. }
  23630. },
  23631. destroy: function(){
  23632. for(var pane in this.pane2button){
  23633. this.onRemoveChild(dijit.byId(pane));
  23634. }
  23635. this.inherited(arguments);
  23636. },
  23637. onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
  23638. // summary:
  23639. // Called whenever a page is added to the container.
  23640. // Create button corresponding to the page.
  23641. // tags:
  23642. // private
  23643. // create an instance of the button widget
  23644. var cls = dojo.getObject(this.buttonWidget);
  23645. var button = new cls({
  23646. id: this.id + "_" + page.id,
  23647. label: page.title,
  23648. dir: page.dir,
  23649. lang: page.lang,
  23650. showLabel: page.showTitle,
  23651. iconClass: page.iconClass,
  23652. closeButton: page.closable,
  23653. title: page.tooltip
  23654. });
  23655. dijit.setWaiState(button.focusNode,"selected", "false");
  23656. // map from page attribute to corresponding tab button attribute
  23657. var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
  23658. buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
  23659. // watch() so events like page title changes are reflected in tab button
  23660. this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
  23661. return page.watch(pageAttr, function(name, oldVal, newVal){
  23662. button.set(buttonAttrList[idx], newVal);
  23663. });
  23664. });
  23665. // connections so that clicking a tab button selects the corresponding page
  23666. this.pane2connects[page.id] = [
  23667. this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
  23668. this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
  23669. ];
  23670. this.addChild(button, insertIndex);
  23671. this.pane2button[page.id] = button;
  23672. page.controlButton = button; // this value might be overwritten if two tabs point to same container
  23673. if(!this._currentChild){ // put the first child into the tab order
  23674. button.focusNode.setAttribute("tabIndex", "0");
  23675. dijit.setWaiState(button.focusNode, "selected", "true");
  23676. this._currentChild = page;
  23677. }
  23678. // make sure all tabs have the same length
  23679. if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
  23680. this._rectifyRtlTabList();
  23681. }
  23682. },
  23683. onRemoveChild: function(/*dijit._Widget*/ page){
  23684. // summary:
  23685. // Called whenever a page is removed from the container.
  23686. // Remove the button corresponding to the page.
  23687. // tags:
  23688. // private
  23689. if(this._currentChild === page){ this._currentChild = null; }
  23690. // disconnect/unwatch connections/watches related to page being removed
  23691. dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
  23692. delete this.pane2connects[page.id];
  23693. dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
  23694. delete this.pane2watches[page.id];
  23695. var button = this.pane2button[page.id];
  23696. if(button){
  23697. this.removeChild(button);
  23698. delete this.pane2button[page.id];
  23699. button.destroy();
  23700. }
  23701. delete page.controlButton;
  23702. },
  23703. onSelectChild: function(/*dijit._Widget*/ page){
  23704. // summary:
  23705. // Called when a page has been selected in the StackContainer, either by me or by another StackController
  23706. // tags:
  23707. // private
  23708. if(!page){ return; }
  23709. if(this._currentChild){
  23710. var oldButton=this.pane2button[this._currentChild.id];
  23711. oldButton.set('checked', false);
  23712. dijit.setWaiState(oldButton.focusNode, "selected", "false");
  23713. oldButton.focusNode.setAttribute("tabIndex", "-1");
  23714. }
  23715. var newButton=this.pane2button[page.id];
  23716. newButton.set('checked', true);
  23717. dijit.setWaiState(newButton.focusNode, "selected", "true");
  23718. this._currentChild = page;
  23719. newButton.focusNode.setAttribute("tabIndex", "0");
  23720. var container = dijit.byId(this.containerId);
  23721. dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
  23722. },
  23723. onButtonClick: function(/*dijit._Widget*/ page){
  23724. // summary:
  23725. // Called whenever one of my child buttons is pressed in an attempt to select a page
  23726. // tags:
  23727. // private
  23728. var container = dijit.byId(this.containerId);
  23729. container.selectChild(page);
  23730. },
  23731. onCloseButtonClick: function(/*dijit._Widget*/ page){
  23732. // summary:
  23733. // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
  23734. // tags:
  23735. // private
  23736. var container = dijit.byId(this.containerId);
  23737. container.closeChild(page);
  23738. if(this._currentChild){
  23739. var b = this.pane2button[this._currentChild.id];
  23740. if(b){
  23741. dijit.focus(b.focusNode || b.domNode);
  23742. }
  23743. }
  23744. },
  23745. // TODO: this is a bit redundant with forward, back api in StackContainer
  23746. adjacent: function(/*Boolean*/ forward){
  23747. // summary:
  23748. // Helper for onkeypress to find next/previous button
  23749. // tags:
  23750. // private
  23751. if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
  23752. // find currently focused button in children array
  23753. var children = this.getChildren();
  23754. var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
  23755. // pick next button to focus on
  23756. var offset = forward ? 1 : children.length - 1;
  23757. return children[ (current + offset) % children.length ]; // dijit._Widget
  23758. },
  23759. onkeypress: function(/*Event*/ e){
  23760. // summary:
  23761. // Handle keystrokes on the page list, for advancing to next/previous button
  23762. // and closing the current page if the page is closable.
  23763. // tags:
  23764. // private
  23765. if(this.disabled || e.altKey ){ return; }
  23766. var forward = null;
  23767. if(e.ctrlKey || !e._djpage){
  23768. var k = dojo.keys;
  23769. switch(e.charOrCode){
  23770. case k.LEFT_ARROW:
  23771. case k.UP_ARROW:
  23772. if(!e._djpage){ forward = false; }
  23773. break;
  23774. case k.PAGE_UP:
  23775. if(e.ctrlKey){ forward = false; }
  23776. break;
  23777. case k.RIGHT_ARROW:
  23778. case k.DOWN_ARROW:
  23779. if(!e._djpage){ forward = true; }
  23780. break;
  23781. case k.PAGE_DOWN:
  23782. if(e.ctrlKey){ forward = true; }
  23783. break;
  23784. case k.HOME:
  23785. case k.END:
  23786. var children = this.getChildren();
  23787. if(children && children.length){
  23788. children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
  23789. }
  23790. dojo.stopEvent(e);
  23791. break;
  23792. case k.DELETE:
  23793. if(this._currentChild.closable){
  23794. this.onCloseButtonClick(this._currentChild);
  23795. }
  23796. dojo.stopEvent(e);
  23797. break;
  23798. default:
  23799. if(e.ctrlKey){
  23800. if(e.charOrCode === k.TAB){
  23801. this.adjacent(!e.shiftKey).onClick();
  23802. dojo.stopEvent(e);
  23803. }else if(e.charOrCode == "w"){
  23804. if(this._currentChild.closable){
  23805. this.onCloseButtonClick(this._currentChild);
  23806. }
  23807. dojo.stopEvent(e); // avoid browser tab closing.
  23808. }
  23809. }
  23810. }
  23811. // handle next/previous page navigation (left/right arrow, etc.)
  23812. if(forward !== null){
  23813. this.adjacent(forward).onClick();
  23814. dojo.stopEvent(e);
  23815. }
  23816. }
  23817. },
  23818. onContainerKeyPress: function(/*Object*/ info){
  23819. // summary:
  23820. // Called when there was a keypress on the container
  23821. // tags:
  23822. // private
  23823. info.e._djpage = info.page;
  23824. this.onkeypress(info.e);
  23825. }
  23826. });
  23827. dojo.declare("dijit.layout._StackButton",
  23828. dijit.form.ToggleButton,
  23829. {
  23830. // summary:
  23831. // Internal widget used by StackContainer.
  23832. // description:
  23833. // The button-like or tab-like object you click to select or delete a page
  23834. // tags:
  23835. // private
  23836. // Override _FormWidget.tabIndex.
  23837. // StackContainer buttons are not in the tab order by default.
  23838. // Probably we should be calling this.startupKeyNavChildren() instead.
  23839. tabIndex: "-1",
  23840. buildRendering: function(/*Event*/ evt){
  23841. this.inherited(arguments);
  23842. dijit.setWaiRole((this.focusNode || this.domNode), "tab");
  23843. },
  23844. onClick: function(/*Event*/ evt){
  23845. // summary:
  23846. // This is for TabContainer where the tabs are <span> rather than button,
  23847. // so need to set focus explicitly (on some browsers)
  23848. // Note that you shouldn't override this method, but you can connect to it.
  23849. dijit.focus(this.focusNode);
  23850. // ... now let StackController catch the event and tell me what to do
  23851. },
  23852. onClickCloseButton: function(/*Event*/ evt){
  23853. // summary:
  23854. // StackContainer connects to this function; if your widget contains a close button
  23855. // then clicking it should call this function.
  23856. // Note that you shouldn't override this method, but you can connect to it.
  23857. evt.stopPropagation();
  23858. }
  23859. });
  23860. }
  23861. if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23862. dojo._hasResource["dijit.layout.StackContainer"] = true;
  23863. dojo.provide("dijit.layout.StackContainer");
  23864. dojo.declare(
  23865. "dijit.layout.StackContainer",
  23866. dijit.layout._LayoutWidget,
  23867. {
  23868. // summary:
  23869. // A container that has multiple children, but shows only
  23870. // one child at a time
  23871. //
  23872. // description:
  23873. // A container for widgets (ContentPanes, for example) That displays
  23874. // only one Widget at a time.
  23875. //
  23876. // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
  23877. //
  23878. // Can be base class for container, Wizard, Show, etc.
  23879. // doLayout: Boolean
  23880. // If true, change the size of my currently displayed child to match my size
  23881. doLayout: true,
  23882. // persist: Boolean
  23883. // Remembers the selected child across sessions
  23884. persist: false,
  23885. baseClass: "dijitStackContainer",
  23886. /*=====
  23887. // selectedChildWidget: [readonly] dijit._Widget
  23888. // References the currently selected child widget, if any.
  23889. // Adjust selected child with selectChild() method.
  23890. selectedChildWidget: null,
  23891. =====*/
  23892. buildRendering: function(){
  23893. this.inherited(arguments);
  23894. dojo.addClass(this.domNode, "dijitLayoutContainer");
  23895. dijit.setWaiRole(this.containerNode, "tabpanel");
  23896. },
  23897. postCreate: function(){
  23898. this.inherited(arguments);
  23899. this.connect(this.domNode, "onkeypress", this._onKeyPress);
  23900. },
  23901. startup: function(){
  23902. if(this._started){ return; }
  23903. var children = this.getChildren();
  23904. // Setup each page panel to be initially hidden
  23905. dojo.forEach(children, this._setupChild, this);
  23906. // Figure out which child to initially display, defaulting to first one
  23907. if(this.persist){
  23908. this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
  23909. }else{
  23910. dojo.some(children, function(child){
  23911. if(child.selected){
  23912. this.selectedChildWidget = child;
  23913. }
  23914. return child.selected;
  23915. }, this);
  23916. }
  23917. var selected = this.selectedChildWidget;
  23918. if(!selected && children[0]){
  23919. selected = this.selectedChildWidget = children[0];
  23920. selected.selected = true;
  23921. }
  23922. // Publish information about myself so any StackControllers can initialize.
  23923. // This needs to happen before this.inherited(arguments) so that for
  23924. // TabContainer, this._contentBox doesn't include the space for the tab labels.
  23925. dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
  23926. // Startup each child widget, and do initial layout like setting this._contentBox,
  23927. // then calls this.resize() which does the initial sizing on the selected child.
  23928. this.inherited(arguments);
  23929. },
  23930. resize: function(){
  23931. // Resize is called when we are first made visible (it's called from startup()
  23932. // if we are initially visible). If this is the first time we've been made
  23933. // visible then show our first child.
  23934. if(!this._hasBeenShown){
  23935. this._hasBeenShown = true;
  23936. var selected = this.selectedChildWidget;
  23937. if(selected){
  23938. this._showChild(selected);
  23939. }
  23940. }
  23941. this.inherited(arguments);
  23942. },
  23943. _setupChild: function(/*dijit._Widget*/ child){
  23944. // Overrides _LayoutWidget._setupChild()
  23945. this.inherited(arguments);
  23946. dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
  23947. // remove the title attribute so it doesn't show up when i hover
  23948. // over a node
  23949. child.domNode.title = "";
  23950. },
  23951. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  23952. // Overrides _Container.addChild() to do layout and publish events
  23953. this.inherited(arguments);
  23954. if(this._started){
  23955. dojo.publish(this.id+"-addChild", [child, insertIndex]);
  23956. // in case the tab titles have overflowed from one line to two lines
  23957. // (or, if this if first child, from zero lines to one line)
  23958. // TODO: w/ScrollingTabController this is no longer necessary, although
  23959. // ScrollTabController.resize() does need to get called to show/hide
  23960. // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
  23961. this.layout();
  23962. // if this is the first child, then select it
  23963. if(!this.selectedChildWidget){
  23964. this.selectChild(child);
  23965. }
  23966. }
  23967. },
  23968. removeChild: function(/*dijit._Widget*/ page){
  23969. // Overrides _Container.removeChild() to do layout and publish events
  23970. this.inherited(arguments);
  23971. if(this._started){
  23972. // this will notify any tablists to remove a button; do this first because it may affect sizing
  23973. dojo.publish(this.id + "-removeChild", [page]);
  23974. }
  23975. // If we are being destroyed than don't run the code below (to select another page), because we are deleting
  23976. // every page one by one
  23977. if(this._beingDestroyed){ return; }
  23978. // Select new page to display, also updating TabController to show the respective tab.
  23979. // Do this before layout call because it can affect the height of the TabController.
  23980. if(this.selectedChildWidget === page){
  23981. this.selectedChildWidget = undefined;
  23982. if(this._started){
  23983. var children = this.getChildren();
  23984. if(children.length){
  23985. this.selectChild(children[0]);
  23986. }
  23987. }
  23988. }
  23989. if(this._started){
  23990. // In case the tab titles now take up one line instead of two lines
  23991. // (note though that ScrollingTabController never overflows to multiple lines),
  23992. // or the height has changed slightly because of addition/removal of tab which close icon
  23993. this.layout();
  23994. }
  23995. },
  23996. selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
  23997. // summary:
  23998. // Show the given widget (which must be one of my children)
  23999. // page:
  24000. // Reference to child widget or id of child widget
  24001. page = dijit.byId(page);
  24002. if(this.selectedChildWidget != page){
  24003. // Deselect old page and select new one
  24004. var d = this._transition(page, this.selectedChildWidget, animate);
  24005. this._set("selectedChildWidget", page);
  24006. dojo.publish(this.id+"-selectChild", [page]);
  24007. if(this.persist){
  24008. dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
  24009. }
  24010. }
  24011. return d; // If child has an href, promise that fires when the child's href finishes loading
  24012. },
  24013. _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
  24014. // summary:
  24015. // Hide the old widget and display the new widget.
  24016. // Subclasses should override this.
  24017. // tags:
  24018. // protected extension
  24019. if(oldWidget){
  24020. this._hideChild(oldWidget);
  24021. }
  24022. var d = this._showChild(newWidget);
  24023. // Size the new widget, in case this is the first time it's being shown,
  24024. // or I have been resized since the last time it was shown.
  24025. // Note that page must be visible for resizing to work.
  24026. if(newWidget.resize){
  24027. if(this.doLayout){
  24028. newWidget.resize(this._containerContentBox || this._contentBox);
  24029. }else{
  24030. // the child should pick it's own size but we still need to call resize()
  24031. // (with no arguments) to let the widget lay itself out
  24032. newWidget.resize();
  24033. }
  24034. }
  24035. return d; // If child has an href, promise that fires when the child's href finishes loading
  24036. },
  24037. _adjacent: function(/*Boolean*/ forward){
  24038. // summary:
  24039. // Gets the next/previous child widget in this container from the current selection.
  24040. var children = this.getChildren();
  24041. var index = dojo.indexOf(children, this.selectedChildWidget);
  24042. index += forward ? 1 : children.length - 1;
  24043. return children[ index % children.length ]; // dijit._Widget
  24044. },
  24045. forward: function(){
  24046. // summary:
  24047. // Advance to next page.
  24048. return this.selectChild(this._adjacent(true), true);
  24049. },
  24050. back: function(){
  24051. // summary:
  24052. // Go back to previous page.
  24053. return this.selectChild(this._adjacent(false), true);
  24054. },
  24055. _onKeyPress: function(e){
  24056. dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
  24057. },
  24058. layout: function(){
  24059. // Implement _LayoutWidget.layout() virtual method.
  24060. if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
  24061. this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
  24062. }
  24063. },
  24064. _showChild: function(/*dijit._Widget*/ page){
  24065. // summary:
  24066. // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
  24067. // it can do any updates it needs regarding loading href's etc.
  24068. // returns:
  24069. // Promise that fires when page has finished showing, or true if there's no href
  24070. var children = this.getChildren();
  24071. page.isFirstChild = (page == children[0]);
  24072. page.isLastChild = (page == children[children.length-1]);
  24073. page._set("selected", true);
  24074. dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
  24075. return page._onShow() || true;
  24076. },
  24077. _hideChild: function(/*dijit._Widget*/ page){
  24078. // summary:
  24079. // Hide the specified child by changing it's CSS, and call _onHide() so
  24080. // it's notified.
  24081. page._set("selected", false);
  24082. dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
  24083. page.onHide();
  24084. },
  24085. closeChild: function(/*dijit._Widget*/ page){
  24086. // summary:
  24087. // Callback when user clicks the [X] to remove a page.
  24088. // If onClose() returns true then remove and destroy the child.
  24089. // tags:
  24090. // private
  24091. var remove = page.onClose(this, page);
  24092. if(remove){
  24093. this.removeChild(page);
  24094. // makes sure we can clean up executeScripts in ContentPane onUnLoad
  24095. page.destroyRecursive();
  24096. }
  24097. },
  24098. destroyDescendants: function(/*Boolean*/ preserveDom){
  24099. dojo.forEach(this.getChildren(), function(child){
  24100. this.removeChild(child);
  24101. child.destroyRecursive(preserveDom);
  24102. }, this);
  24103. }
  24104. });
  24105. // For back-compat, remove for 2.0
  24106. // These arguments can be specified for the children of a StackContainer.
  24107. // Since any widget can be specified as a StackContainer child, mix them
  24108. // into the base widget class. (This is a hack, but it's effective.)
  24109. dojo.extend(dijit._Widget, {
  24110. // selected: Boolean
  24111. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  24112. // Specifies that this widget should be the initially displayed pane.
  24113. // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
  24114. selected: false,
  24115. // closable: Boolean
  24116. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  24117. // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
  24118. closable: false,
  24119. // iconClass: String
  24120. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  24121. // CSS Class specifying icon to use in label associated with this pane.
  24122. iconClass: "",
  24123. // showTitle: Boolean
  24124. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  24125. // When true, display title of this widget as tab label etc., rather than just using
  24126. // icon specified in iconClass
  24127. showTitle: true
  24128. });
  24129. }
  24130. if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  24131. dojo._hasResource["dijit.layout.AccordionPane"] = true;
  24132. dojo.provide("dijit.layout.AccordionPane");
  24133. dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, {
  24134. // summary:
  24135. // Deprecated widget. Use `dijit.layout.ContentPane` instead.
  24136. // tags:
  24137. // deprecated
  24138. constructor: function(){
  24139. dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
  24140. },
  24141. onSelected: function(){
  24142. // summary:
  24143. // called when this pane is selected
  24144. }
  24145. });
  24146. }
  24147. if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  24148. dojo._hasResource["dijit.layout.AccordionContainer"] = true;
  24149. dojo.provide("dijit.layout.AccordionContainer");
  24150. //dojo.require("dijit.layout.AccordionPane "); // for back compat, remove for 2.0
  24151. // Design notes:
  24152. //
  24153. // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
  24154. // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
  24155. //
  24156. // The resulting markup will look like:
  24157. //
  24158. // <div class=dijitAccordionContainer>
  24159. // <div class=dijitAccordionInnerContainer> (one pane)
  24160. // <div class=dijitAccordionTitle> (title bar) ... </div>
  24161. // <div class=dijtAccordionChildWrapper> (content pane) </div>
  24162. // </div>
  24163. // </div>
  24164. //
  24165. // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
  24166. // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
  24167. // which on claro has a 1px border plus a 2px bottom margin.
  24168. //
  24169. // During animation there are two dijtAccordionChildWrapper's shown, so we need
  24170. // to compensate for that.
  24171. dojo.declare(
  24172. "dijit.layout.AccordionContainer",
  24173. dijit.layout.StackContainer,
  24174. {
  24175. // summary:
  24176. // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
  24177. // and switching between panes is visualized by sliding the other panes up/down.
  24178. // example:
  24179. // | <div dojoType="dijit.layout.AccordionContainer">
  24180. // | <div dojoType="dijit.layout.ContentPane" title="pane 1">
  24181. // | </div>
  24182. // | <div dojoType="dijit.layout.ContentPane" title="pane 2">
  24183. // | <p>This is some text</p>
  24184. // | </div>
  24185. // | </div>
  24186. // duration: Integer
  24187. // Amount of time (in ms) it takes to slide panes
  24188. duration: dijit.defaultDuration,
  24189. // buttonWidget: [const] String
  24190. // The name of the widget used to display the title of each pane
  24191. buttonWidget: "dijit.layout._AccordionButton",
  24192. /*=====
  24193. // _verticalSpace: Number
  24194. // Pixels of space available for the open pane
  24195. // (my content box size minus the cumulative size of all the title bars)
  24196. _verticalSpace: 0,
  24197. =====*/
  24198. baseClass: "dijitAccordionContainer",
  24199. buildRendering: function(){
  24200. this.inherited(arguments);
  24201. this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
  24202. dijit.setWaiRole(this.domNode, "tablist"); // TODO: put this in template
  24203. },
  24204. startup: function(){
  24205. if(this._started){ return; }
  24206. this.inherited(arguments);
  24207. if(this.selectedChildWidget){
  24208. var style = this.selectedChildWidget.containerNode.style;
  24209. style.display = "";
  24210. style.overflow = "auto";
  24211. this.selectedChildWidget._wrapperWidget.set("selected", true);
  24212. }
  24213. },
  24214. layout: function(){
  24215. // Implement _LayoutWidget.layout() virtual method.
  24216. // Set the height of the open pane based on what room remains.
  24217. var openPane = this.selectedChildWidget;
  24218. if(!openPane){ return;}
  24219. // space taken up by title, plus wrapper div (with border/margin) for open pane
  24220. var wrapperDomNode = openPane._wrapperWidget.domNode,
  24221. wrapperDomNodeMargin = dojo._getMarginExtents(wrapperDomNode),
  24222. wrapperDomNodePadBorder = dojo._getPadBorderExtents(wrapperDomNode),
  24223. wrapperContainerNode = openPane._wrapperWidget.containerNode,
  24224. wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode),
  24225. wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
  24226. mySize = this._contentBox;
  24227. // get cumulative height of all the unselected title bars
  24228. var totalCollapsedHeight = 0;
  24229. dojo.forEach(this.getChildren(), function(child){
  24230. if(child != openPane){
  24231. totalCollapsedHeight += dojo._getMarginSize(child._wrapperWidget.domNode).h;
  24232. }
  24233. });
  24234. this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
  24235. - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
  24236. - openPane._buttonWidget.getTitleHeight();
  24237. // Memo size to make displayed child
  24238. this._containerContentBox = {
  24239. h: this._verticalSpace,
  24240. w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
  24241. - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
  24242. };
  24243. if(openPane){
  24244. openPane.resize(this._containerContentBox);
  24245. }
  24246. },
  24247. _setupChild: function(child){
  24248. // Overrides _LayoutWidget._setupChild().
  24249. // Put wrapper widget around the child widget, showing title
  24250. child._wrapperWidget = new dijit.layout._AccordionInnerContainer({
  24251. contentWidget: child,
  24252. buttonWidget: this.buttonWidget,
  24253. id: child.id + "_wrapper",
  24254. dir: child.dir,
  24255. lang: child.lang,
  24256. parent: this
  24257. });
  24258. this.inherited(arguments);
  24259. },
  24260. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  24261. if(this._started){
  24262. // Adding a child to a started Accordion is complicated because children have
  24263. // wrapper widgets. Default code path (calling this.inherited()) would add
  24264. // the new child inside another child's wrapper.
  24265. // First add in child as a direct child of this AccordionContainer
  24266. dojo.place(child.domNode, this.containerNode, insertIndex);
  24267. if(!child._started){
  24268. child.startup();
  24269. }
  24270. // Then stick the wrapper widget around the child widget
  24271. this._setupChild(child);
  24272. // Code below copied from StackContainer
  24273. dojo.publish(this.id+"-addChild", [child, insertIndex]);
  24274. this.layout();
  24275. if(!this.selectedChildWidget){
  24276. this.selectChild(child);
  24277. }
  24278. }else{
  24279. // We haven't been started yet so just add in the child widget directly,
  24280. // and the wrapper will be created on startup()
  24281. this.inherited(arguments);
  24282. }
  24283. },
  24284. removeChild: function(child){
  24285. // Overrides _LayoutWidget.removeChild().
  24286. // Destroy wrapper widget first, before StackContainer.getChildren() call.
  24287. // Replace wrapper widget with true child widget (ContentPane etc.).
  24288. // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
  24289. if(child._wrapperWidget){
  24290. dojo.place(child.domNode, child._wrapperWidget.domNode, "after");
  24291. child._wrapperWidget.destroy();
  24292. delete child._wrapperWidget;
  24293. }
  24294. dojo.removeClass(child.domNode, "dijitHidden");
  24295. this.inherited(arguments);
  24296. },
  24297. getChildren: function(){
  24298. // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
  24299. return dojo.map(this.inherited(arguments), function(child){
  24300. return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
  24301. }, this);
  24302. },
  24303. destroy: function(){
  24304. if(this._animation){
  24305. this._animation.stop();
  24306. }
  24307. dojo.forEach(this.getChildren(), function(child){
  24308. // If AccordionContainer has been started, then each child has a wrapper widget which
  24309. // also needs to be destroyed.
  24310. if(child._wrapperWidget){
  24311. child._wrapperWidget.destroy();
  24312. }else{
  24313. child.destroyRecursive();
  24314. }
  24315. });
  24316. this.inherited(arguments);
  24317. },
  24318. _showChild: function(child){
  24319. // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
  24320. child._wrapperWidget.containerNode.style.display="block";
  24321. return this.inherited(arguments);
  24322. },
  24323. _hideChild: function(child){
  24324. // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
  24325. child._wrapperWidget.containerNode.style.display="none";
  24326. this.inherited(arguments);
  24327. },
  24328. _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){
  24329. // Overrides StackContainer._transition() to provide sliding of title bars etc.
  24330. if(dojo.isIE < 8){
  24331. // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
  24332. animate = false;
  24333. }
  24334. if(this._animation){
  24335. // there's an in-progress animation. speedily end it so we can do the newly requested one
  24336. this._animation.stop(true);
  24337. delete this._animation;
  24338. }
  24339. var self = this;
  24340. if(newWidget){
  24341. newWidget._wrapperWidget.set("selected", true);
  24342. var d = this._showChild(newWidget); // prepare widget to be slid in
  24343. // Size the new widget, in case this is the first time it's being shown,
  24344. // or I have been resized since the last time it was shown.
  24345. // Note that page must be visible for resizing to work.
  24346. if(this.doLayout && newWidget.resize){
  24347. newWidget.resize(this._containerContentBox);
  24348. }
  24349. }
  24350. if(oldWidget){
  24351. oldWidget._wrapperWidget.set("selected", false);
  24352. if(!animate){
  24353. this._hideChild(oldWidget);
  24354. }
  24355. }
  24356. if(animate){
  24357. var newContents = newWidget._wrapperWidget.containerNode,
  24358. oldContents = oldWidget._wrapperWidget.containerNode;
  24359. // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
  24360. // which on claro takes up 4px extra space (compared to stable AccordionContainer).
  24361. // Have to compensate for that by immediately shrinking the pane being closed.
  24362. var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
  24363. wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode),
  24364. wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
  24365. animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
  24366. oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
  24367. this._animation = new dojo.Animation({
  24368. node: newContents,
  24369. duration: this.duration,
  24370. curve: [1, this._verticalSpace - animationHeightOverhead - 1],
  24371. onAnimate: function(value){
  24372. value = Math.floor(value); // avoid fractional values
  24373. newContents.style.height = value + "px";
  24374. oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
  24375. },
  24376. onEnd: function(){
  24377. delete self._animation;
  24378. newContents.style.height = "auto";
  24379. oldWidget._wrapperWidget.containerNode.style.display = "none";
  24380. oldContents.style.height = "auto";
  24381. self._hideChild(oldWidget);
  24382. }
  24383. });
  24384. this._animation.onStop = this._animation.onEnd;
  24385. this._animation.play();
  24386. }
  24387. return d; // If child has an href, promise that fires when the widget has finished loading
  24388. },
  24389. // note: we are treating the container as controller here
  24390. _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
  24391. // summary:
  24392. // Handle keypress events
  24393. // description:
  24394. // This is called from a handler on AccordionContainer.domNode
  24395. // (setup in StackContainer), and is also called directly from
  24396. // the click handler for accordion labels
  24397. if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
  24398. return;
  24399. }
  24400. var k = dojo.keys,
  24401. c = e.charOrCode;
  24402. if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
  24403. (e.ctrlKey && c == k.PAGE_UP)){
  24404. this._adjacent(false)._buttonWidget._onTitleClick();
  24405. dojo.stopEvent(e);
  24406. }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
  24407. (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
  24408. this._adjacent(true)._buttonWidget._onTitleClick();
  24409. dojo.stopEvent(e);
  24410. }
  24411. }
  24412. }
  24413. );
  24414. dojo.declare("dijit.layout._AccordionInnerContainer",
  24415. [dijit._Widget, dijit._CssStateMixin], {
  24416. // summary:
  24417. // Internal widget placed as direct child of AccordionContainer.containerNode.
  24418. // When other widgets are added as children to an AccordionContainer they are wrapped in
  24419. // this widget.
  24420. /*=====
  24421. // buttonWidget: String
  24422. // Name of class to use to instantiate title
  24423. // (Wish we didn't have a separate widget for just the title but maintaining it
  24424. // for backwards compatibility, is it worth it?)
  24425. buttonWidget: null,
  24426. =====*/
  24427. /*=====
  24428. // contentWidget: dijit._Widget
  24429. // Pointer to the real child widget
  24430. contentWidget: null,
  24431. =====*/
  24432. baseClass: "dijitAccordionInnerContainer",
  24433. // tell nested layout widget that we will take care of sizing
  24434. isContainer: true,
  24435. isLayoutContainer: true,
  24436. buildRendering: function(){
  24437. // Builds a template like:
  24438. // <div class=dijitAccordionInnerContainer>
  24439. // Button
  24440. // <div class=dijitAccordionChildWrapper>
  24441. // ContentPane
  24442. // </div>
  24443. // </div>
  24444. // Create wrapper div, placed where the child is now
  24445. this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after");
  24446. // wrapper div's first child is the button widget (ie, the title bar)
  24447. var child = this.contentWidget,
  24448. cls = dojo.getObject(this.buttonWidget);
  24449. this.button = child._buttonWidget = (new cls({
  24450. contentWidget: child,
  24451. label: child.title,
  24452. title: child.tooltip,
  24453. dir: child.dir,
  24454. lang: child.lang,
  24455. iconClass: child.iconClass,
  24456. id: child.id + "_button",
  24457. parent: this.parent
  24458. })).placeAt(this.domNode);
  24459. // and then the actual content widget (changing it from prior-sibling to last-child),
  24460. // wrapped by a <div class=dijitAccordionChildWrapper>
  24461. this.containerNode = dojo.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
  24462. dojo.place(this.contentWidget.domNode, this.containerNode);
  24463. },
  24464. postCreate: function(){
  24465. this.inherited(arguments);
  24466. // Map changes in content widget's title etc. to changes in the button
  24467. var button = this.button;
  24468. this._contentWidgetWatches = [
  24469. this.contentWidget.watch('title', dojo.hitch(this, function(name, oldValue, newValue){
  24470. button.set("label", newValue);
  24471. })),
  24472. this.contentWidget.watch('tooltip', dojo.hitch(this, function(name, oldValue, newValue){
  24473. button.set("title", newValue);
  24474. })),
  24475. this.contentWidget.watch('iconClass', dojo.hitch(this, function(name, oldValue, newValue){
  24476. button.set("iconClass", newValue);
  24477. }))
  24478. ];
  24479. },
  24480. _setSelectedAttr: function(/*Boolean*/ isSelected){
  24481. this._set("selected", isSelected);
  24482. this.button.set("selected", isSelected);
  24483. if(isSelected){
  24484. var cw = this.contentWidget;
  24485. if(cw.onSelected){ cw.onSelected(); }
  24486. }
  24487. },
  24488. startup: function(){
  24489. // Called by _Container.addChild()
  24490. this.contentWidget.startup();
  24491. },
  24492. destroy: function(){
  24493. this.button.destroyRecursive();
  24494. dojo.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
  24495. delete this.contentWidget._buttonWidget;
  24496. delete this.contentWidget._wrapperWidget;
  24497. this.inherited(arguments);
  24498. },
  24499. destroyDescendants: function(){
  24500. // since getChildren isn't working for me, have to code this manually
  24501. this.contentWidget.destroyRecursive();
  24502. }
  24503. });
  24504. dojo.declare("dijit.layout._AccordionButton",
  24505. [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  24506. {
  24507. // summary:
  24508. // The title bar to click to open up an accordion pane.
  24509. // Internal widget used by AccordionContainer.
  24510. // tags:
  24511. // private
  24512. templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "<div dojoAttachEvent='onclick:_onTitleClick' class='dijitAccordionTitle'>\n\t<div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"),
  24513. attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
  24514. label: {node: "titleTextNode", type: "innerHTML" },
  24515. title: {node: "titleTextNode", type: "attribute", attribute: "title"},
  24516. iconClass: { node: "iconNode", type: "class" }
  24517. }),
  24518. baseClass: "dijitAccordionTitle",
  24519. getParent: function(){
  24520. // summary:
  24521. // Returns the AccordionContainer parent.
  24522. // tags:
  24523. // private
  24524. return this.parent;
  24525. },
  24526. buildRendering: function(){
  24527. this.inherited(arguments);
  24528. var titleTextNodeId = this.id.replace(' ','_');
  24529. dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
  24530. dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
  24531. dojo.setSelectable(this.domNode, false);
  24532. },
  24533. getTitleHeight: function(){
  24534. // summary:
  24535. // Returns the height of the title dom node.
  24536. return dojo._getMarginSize(this.domNode).h; // Integer
  24537. },
  24538. // TODO: maybe the parent should set these methods directly rather than forcing the code
  24539. // into the button widget?
  24540. _onTitleClick: function(){
  24541. // summary:
  24542. // Callback when someone clicks my title.
  24543. var parent = this.getParent();
  24544. parent.selectChild(this.contentWidget, true);
  24545. dijit.focus(this.focusNode);
  24546. },
  24547. _onTitleKeyPress: function(/*Event*/ evt){
  24548. return this.getParent()._onKeyPress(evt, this.contentWidget);
  24549. },
  24550. _setSelectedAttr: function(/*Boolean*/ isSelected){
  24551. this._set("selected", isSelected);
  24552. dijit.setWaiState(this.focusNode, "expanded", isSelected);
  24553. dijit.setWaiState(this.focusNode, "selected", isSelected);
  24554. this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
  24555. }
  24556. });
  24557. }
  24558. if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  24559. dojo._hasResource["dijit.layout.BorderContainer"] = true;
  24560. dojo.provide("dijit.layout.BorderContainer");
  24561. dojo.declare(
  24562. "dijit.layout.BorderContainer",
  24563. dijit.layout._LayoutWidget,
  24564. {
  24565. // summary:
  24566. // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
  24567. //
  24568. // description:
  24569. // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
  24570. // that contains a child widget marked region="center" and optionally children widgets marked
  24571. // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
  24572. // Children along the edges will be laid out according to width or height dimensions and may
  24573. // include optional splitters (splitter="true") to make them resizable by the user. The remaining
  24574. // space is designated for the center region.
  24575. //
  24576. // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
  24577. // and height for the top and bottom, respectively. No dimensions should be specified on the center;
  24578. // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
  24579. // "left" and "right" except that they will be reversed in right-to-left environments.
  24580. //
  24581. // For complex layouts, multiple children can be specified for a single region. In this case, the
  24582. // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
  24583. // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
  24584. // instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes.
  24585. // example:
  24586. // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
  24587. // | style="width: 400px; height: 300px;">
  24588. // | <div dojoType="dijit.layout.ContentPane" region="top">header text</div>
  24589. // | <div dojoType="dijit.layout.ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
  24590. // | <div dojoType="dijit.layout.ContentPane" region="center">client area</div>
  24591. // | </div>
  24592. // design: String
  24593. // Which design is used for the layout:
  24594. // - "headline" (default) where the top and bottom extend
  24595. // the full width of the container
  24596. // - "sidebar" where the left and right sides extend from top to bottom.
  24597. design: "headline",
  24598. // gutters: [const] Boolean
  24599. // Give each pane a border and margin.
  24600. // Margin determined by domNode.paddingLeft.
  24601. // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
  24602. gutters: true,
  24603. // liveSplitters: [const] Boolean
  24604. // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
  24605. liveSplitters: true,
  24606. // persist: Boolean
  24607. // Save splitter positions in a cookie.
  24608. persist: false,
  24609. baseClass: "dijitBorderContainer",
  24610. // _splitterClass: String
  24611. // Optional hook to override the default Splitter widget used by BorderContainer
  24612. _splitterClass: "dijit.layout._Splitter",
  24613. postMixInProperties: function(){
  24614. // change class name to indicate that BorderContainer is being used purely for
  24615. // layout (like LayoutContainer) rather than for pretty formatting.
  24616. if(!this.gutters){
  24617. this.baseClass += "NoGutter";
  24618. }
  24619. this.inherited(arguments);
  24620. },
  24621. startup: function(){
  24622. if(this._started){ return; }
  24623. dojo.forEach(this.getChildren(), this._setupChild, this);
  24624. this.inherited(arguments);
  24625. },
  24626. _setupChild: function(/*dijit._Widget*/ child){
  24627. // Override _LayoutWidget._setupChild().
  24628. var region = child.region;
  24629. if(region){
  24630. this.inherited(arguments);
  24631. dojo.addClass(child.domNode, this.baseClass+"Pane");
  24632. var ltr = this.isLeftToRight();
  24633. if(region == "leading"){ region = ltr ? "left" : "right"; }
  24634. if(region == "trailing"){ region = ltr ? "right" : "left"; }
  24635. // Create draggable splitter for resizing pane,
  24636. // or alternately if splitter=false but BorderContainer.gutters=true then
  24637. // insert dummy div just for spacing
  24638. if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
  24639. var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
  24640. var splitter = new _Splitter({
  24641. id: child.id + "_splitter",
  24642. container: this,
  24643. child: child,
  24644. region: region,
  24645. live: this.liveSplitters
  24646. });
  24647. splitter.isSplitter = true;
  24648. child._splitterWidget = splitter;
  24649. dojo.place(splitter.domNode, child.domNode, "after");
  24650. // Splitters aren't added as Contained children, so we need to call startup explicitly
  24651. splitter.startup();
  24652. }
  24653. child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
  24654. }
  24655. },
  24656. layout: function(){
  24657. // Implement _LayoutWidget.layout() virtual method.
  24658. this._layoutChildren();
  24659. },
  24660. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  24661. // Override _LayoutWidget.addChild().
  24662. this.inherited(arguments);
  24663. if(this._started){
  24664. this.layout(); //OPT
  24665. }
  24666. },
  24667. removeChild: function(/*dijit._Widget*/ child){
  24668. // Override _LayoutWidget.removeChild().
  24669. var region = child.region;
  24670. var splitter = child._splitterWidget
  24671. if(splitter){
  24672. splitter.destroy();
  24673. delete child._splitterWidget;
  24674. }
  24675. this.inherited(arguments);
  24676. if(this._started){
  24677. this._layoutChildren();
  24678. }
  24679. // Clean up whatever style changes we made to the child pane.
  24680. // Unclear how height and width should be handled.
  24681. dojo.removeClass(child.domNode, this.baseClass+"Pane");
  24682. dojo.style(child.domNode, {
  24683. top: "auto",
  24684. bottom: "auto",
  24685. left: "auto",
  24686. right: "auto",
  24687. position: "static"
  24688. });
  24689. dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
  24690. },
  24691. getChildren: function(){
  24692. // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
  24693. return dojo.filter(this.inherited(arguments), function(widget){
  24694. return !widget.isSplitter;
  24695. });
  24696. },
  24697. // TODO: remove in 2.0
  24698. getSplitter: function(/*String*/region){
  24699. // summary:
  24700. // Returns the widget responsible for rendering the splitter associated with region
  24701. // tags:
  24702. // deprecated
  24703. return dojo.filter(this.getChildren(), function(child){
  24704. return child.region == region;
  24705. })[0]._splitterWidget;
  24706. },
  24707. resize: function(newSize, currentSize){
  24708. // Overrides _LayoutWidget.resize().
  24709. // resetting potential padding to 0px to provide support for 100% width/height + padding
  24710. // TODO: this hack doesn't respect the box model and is a temporary fix
  24711. if(!this.cs || !this.pe){
  24712. var node = this.domNode;
  24713. this.cs = dojo.getComputedStyle(node);
  24714. this.pe = dojo._getPadExtents(node, this.cs);
  24715. this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
  24716. this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
  24717. dojo.style(node, "padding", "0px");
  24718. }
  24719. this.inherited(arguments);
  24720. },
  24721. _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
  24722. // summary:
  24723. // This is the main routine for setting size/position of each child.
  24724. // description:
  24725. // With no arguments, measures the height of top/bottom panes, the width
  24726. // of left/right panes, and then sizes all panes accordingly.
  24727. //
  24728. // With changedRegion specified (as "left", "top", "bottom", or "right"),
  24729. // it changes that region's width/height to changedRegionSize and
  24730. // then resizes other regions that were affected.
  24731. // changedChildId:
  24732. // Id of the child which should be resized because splitter was dragged.
  24733. // changedChildSize:
  24734. // The new width/height (in pixels) to make specified child
  24735. if(!this._borderBox || !this._borderBox.h){
  24736. // We are currently hidden, or we haven't been sized by our parent yet.
  24737. // Abort. Someone will resize us later.
  24738. return;
  24739. }
  24740. // Generate list of wrappers of my children in the order that I want layoutChildren()
  24741. // to process them (i.e. from the outside to the inside)
  24742. var wrappers = dojo.map(this.getChildren(), function(child, idx){
  24743. return {
  24744. pane: child,
  24745. weight: [
  24746. child.region == "center" ? Infinity : 0,
  24747. child.layoutPriority,
  24748. (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
  24749. idx
  24750. ]
  24751. };
  24752. }, this);
  24753. wrappers.sort(function(a, b){
  24754. var aw = a.weight, bw = b.weight;
  24755. for(var i=0; i<aw.length; i++){
  24756. if(aw[i] != bw[i]){
  24757. return aw[i] - bw[i];
  24758. }
  24759. }
  24760. return 0;
  24761. });
  24762. // Make new list, combining the externally specified children with splitters and gutters
  24763. var childrenAndSplitters = [];
  24764. dojo.forEach(wrappers, function(wrapper){
  24765. var pane = wrapper.pane;
  24766. childrenAndSplitters.push(pane);
  24767. if(pane._splitterWidget){
  24768. childrenAndSplitters.push(pane._splitterWidget);
  24769. }
  24770. });
  24771. // Compute the box in which to lay out my children
  24772. var dim = {
  24773. l: this.pe.l,
  24774. t: this.pe.t,
  24775. w: this._borderBox.w - this.pe.w,
  24776. h: this._borderBox.h - this.pe.h
  24777. };
  24778. // Layout the children, possibly changing size due to a splitter drag
  24779. dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters,
  24780. changedChildId, changedChildSize);
  24781. },
  24782. destroyRecursive: function(){
  24783. // Destroy splitters first, while getChildren() still works
  24784. dojo.forEach(this.getChildren(), function(child){
  24785. var splitter = child._splitterWidget;
  24786. if(splitter){
  24787. splitter.destroy();
  24788. }
  24789. delete child._splitterWidget;
  24790. });
  24791. // Then destroy the real children, and myself
  24792. this.inherited(arguments);
  24793. }
  24794. });
  24795. // This argument can be specified for the children of a BorderContainer.
  24796. // Since any widget can be specified as a LayoutContainer child, mix it
  24797. // into the base widget class. (This is a hack, but it's effective.)
  24798. dojo.extend(dijit._Widget, {
  24799. // region: [const] String
  24800. // Parameter for children of `dijit.layout.BorderContainer`.
  24801. // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
  24802. // See the `dijit.layout.BorderContainer` description for details.
  24803. region: '',
  24804. // layoutPriority: [const] Number
  24805. // Parameter for children of `dijit.layout.BorderContainer`.
  24806. // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
  24807. // between children with a lower layoutPriority.
  24808. layoutPriority: 0,
  24809. // splitter: [const] Boolean
  24810. // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
  24811. // If true, enables user to resize the widget by putting a draggable splitter between
  24812. // this widget and the region=center widget.
  24813. splitter: false,
  24814. // minSize: [const] Number
  24815. // Parameter for children of `dijit.layout.BorderContainer`.
  24816. // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
  24817. minSize: 0,
  24818. // maxSize: [const] Number
  24819. // Parameter for children of `dijit.layout.BorderContainer`.
  24820. // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
  24821. maxSize: Infinity
  24822. });
  24823. dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
  24824. {
  24825. // summary:
  24826. // A draggable spacer between two items in a `dijit.layout.BorderContainer`.
  24827. // description:
  24828. // This is instantiated by `dijit.layout.BorderContainer`. Users should not
  24829. // create it directly.
  24830. // tags:
  24831. // private
  24832. /*=====
  24833. // container: [const] dijit.layout.BorderContainer
  24834. // Pointer to the parent BorderContainer
  24835. container: null,
  24836. // child: [const] dijit.layout._LayoutWidget
  24837. // Pointer to the pane associated with this splitter
  24838. child: null,
  24839. // region: [const] String
  24840. // Region of pane associated with this splitter.
  24841. // "top", "bottom", "left", "right".
  24842. region: null,
  24843. =====*/
  24844. // live: [const] Boolean
  24845. // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
  24846. // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
  24847. live: true,
  24848. templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
  24849. postMixInProperties: function(){
  24850. this.inherited(arguments);
  24851. this.horizontal = /top|bottom/.test(this.region);
  24852. this._factor = /top|left/.test(this.region) ? 1 : -1;
  24853. this._cookieName = this.container.id + "_" + this.region;
  24854. },
  24855. buildRendering: function(){
  24856. this.inherited(arguments);
  24857. dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
  24858. if(this.container.persist){
  24859. // restore old size
  24860. var persistSize = dojo.cookie(this._cookieName);
  24861. if(persistSize){
  24862. this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
  24863. }
  24864. }
  24865. },
  24866. _computeMaxSize: function(){
  24867. // summary:
  24868. // Return the maximum size that my corresponding pane can be set to
  24869. var dim = this.horizontal ? 'h' : 'w',
  24870. childSize = dojo.marginBox(this.child.domNode)[dim],
  24871. center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
  24872. spaceAvailable = dojo.marginBox(center.domNode)[dim]; // can expand until center is crushed to 0
  24873. return Math.min(this.child.maxSize, childSize + spaceAvailable);
  24874. },
  24875. _startDrag: function(e){
  24876. if(!this.cover){
  24877. this.cover = dojo.doc.createElement('div');
  24878. dojo.addClass(this.cover, "dijitSplitterCover");
  24879. dojo.place(this.cover, this.child.domNode, "after");
  24880. }
  24881. dojo.addClass(this.cover, "dijitSplitterCoverActive");
  24882. // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
  24883. if(this.fake){ dojo.destroy(this.fake); }
  24884. if(!(this._resize = this.live)){ //TODO: disable live for IE6?
  24885. // create fake splitter to display at old position while we drag
  24886. (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
  24887. dojo.addClass(this.domNode, "dijitSplitterShadow");
  24888. dojo.place(this.fake, this.domNode, "after");
  24889. }
  24890. dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
  24891. if(this.fake){
  24892. dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
  24893. }
  24894. //Performance: load data info local vars for onmousevent function closure
  24895. var factor = this._factor,
  24896. isHorizontal = this.horizontal,
  24897. axis = isHorizontal ? "pageY" : "pageX",
  24898. pageStart = e[axis],
  24899. splitterStyle = this.domNode.style,
  24900. dim = isHorizontal ? 'h' : 'w',
  24901. childStart = dojo.marginBox(this.child.domNode)[dim],
  24902. max = this._computeMaxSize(),
  24903. min = this.child.minSize || 20,
  24904. region = this.region,
  24905. splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
  24906. splitterStart = parseInt(splitterStyle[splitterAttr], 10),
  24907. resize = this._resize,
  24908. layoutFunc = dojo.hitch(this.container, "_layoutChildren", this.child.id),
  24909. de = dojo.doc;
  24910. this._handlers = (this._handlers || []).concat([
  24911. dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
  24912. var delta = e[axis] - pageStart,
  24913. childSize = factor * delta + childStart,
  24914. boundChildSize = Math.max(Math.min(childSize, max), min);
  24915. if(resize || forceResize){
  24916. layoutFunc(boundChildSize);
  24917. }
  24918. // TODO: setting style directly (usually) sets content box size, need to set margin box size
  24919. splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
  24920. }),
  24921. dojo.connect(de, "ondragstart", dojo.stopEvent),
  24922. dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
  24923. dojo.connect(de, "onmouseup", this, "_stopDrag")
  24924. ]);
  24925. dojo.stopEvent(e);
  24926. },
  24927. _onMouse: function(e){
  24928. var o = (e.type == "mouseover" || e.type == "mouseenter");
  24929. dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
  24930. dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
  24931. },
  24932. _stopDrag: function(e){
  24933. try{
  24934. if(this.cover){
  24935. dojo.removeClass(this.cover, "dijitSplitterCoverActive");
  24936. }
  24937. if(this.fake){ dojo.destroy(this.fake); }
  24938. dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter"
  24939. + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
  24940. this._drag(e); //TODO: redundant with onmousemove?
  24941. this._drag(e, true);
  24942. }finally{
  24943. this._cleanupHandlers();
  24944. delete this._drag;
  24945. }
  24946. if(this.container.persist){
  24947. dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
  24948. }
  24949. },
  24950. _cleanupHandlers: function(){
  24951. dojo.forEach(this._handlers, dojo.disconnect);
  24952. delete this._handlers;
  24953. },
  24954. _onKeyPress: function(/*Event*/ e){
  24955. // should we apply typematic to this?
  24956. this._resize = true;
  24957. var horizontal = this.horizontal;
  24958. var tick = 1;
  24959. var dk = dojo.keys;
  24960. switch(e.charOrCode){
  24961. case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
  24962. tick *= -1;
  24963. // break;
  24964. case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
  24965. break;
  24966. default:
  24967. // this.inherited(arguments);
  24968. return;
  24969. }
  24970. var childSize = dojo._getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
  24971. this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
  24972. dojo.stopEvent(e);
  24973. },
  24974. destroy: function(){
  24975. this._cleanupHandlers();
  24976. delete this.child;
  24977. delete this.container;
  24978. delete this.cover;
  24979. delete this.fake;
  24980. this.inherited(arguments);
  24981. }
  24982. });
  24983. dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
  24984. {
  24985. // summary:
  24986. // Just a spacer div to separate side pane from center pane.
  24987. // Basically a trick to lookup the gutter/splitter width from the theme.
  24988. // description:
  24989. // Instantiated by `dijit.layout.BorderContainer`. Users should not
  24990. // create directly.
  24991. // tags:
  24992. // private
  24993. templateString: '<div class="dijitGutter" role="presentation"></div>',
  24994. postMixInProperties: function(){
  24995. this.inherited(arguments);
  24996. this.horizontal = /top|bottom/.test(this.region);
  24997. },
  24998. buildRendering: function(){
  24999. this.inherited(arguments);
  25000. dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
  25001. }
  25002. });
  25003. }
  25004. if(!dojo._hasResource["dijit.layout.LayoutContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25005. dojo._hasResource["dijit.layout.LayoutContainer"] = true;
  25006. dojo.provide("dijit.layout.LayoutContainer");
  25007. dojo.declare("dijit.layout.LayoutContainer",
  25008. dijit.layout._LayoutWidget,
  25009. {
  25010. // summary:
  25011. // Deprecated. Use `dijit.layout.BorderContainer` instead.
  25012. //
  25013. // description:
  25014. // Provides Delphi-style panel layout semantics.
  25015. //
  25016. // A LayoutContainer is a box with a specified size (like style="width: 500px; height: 500px;"),
  25017. // that contains children widgets marked with "layoutAlign" of "left", "right", "bottom", "top", and "client".
  25018. // It takes it's children marked as left/top/bottom/right, and lays them out along the edges of the box,
  25019. // and then it takes the child marked "client" and puts it into the remaining space in the middle.
  25020. //
  25021. // Left/right positioning is similar to CSS's "float: left" and "float: right",
  25022. // and top/bottom positioning would be similar to "float: top" and "float: bottom", if there were such
  25023. // CSS.
  25024. //
  25025. // Note that there can only be one client element, but there can be multiple left, right, top,
  25026. // or bottom elements.
  25027. //
  25028. // example:
  25029. // | <style>
  25030. // | html, body{ height: 100%; width: 100%; }
  25031. // | </style>
  25032. // | <div dojoType="dijit.layout.LayoutContainer" style="width: 100%; height: 100%">
  25033. // | <div dojoType="dijit.layout.ContentPane" layoutAlign="top">header text</div>
  25034. // | <div dojoType="dijit.layout.ContentPane" layoutAlign="left" style="width: 200px;">table of contents</div>
  25035. // | <div dojoType="dijit.layout.ContentPane" layoutAlign="client">client area</div>
  25036. // | </div>
  25037. //
  25038. // Lays out each child in the natural order the children occur in.
  25039. // Basically each child is laid out into the "remaining space", where "remaining space" is initially
  25040. // the content area of this widget, but is reduced to a smaller rectangle each time a child is added.
  25041. // tags:
  25042. // deprecated
  25043. baseClass: "dijitLayoutContainer",
  25044. constructor: function(){
  25045. dojo.deprecated("dijit.layout.LayoutContainer is deprecated", "use BorderContainer instead", 2.0);
  25046. },
  25047. layout: function(){
  25048. dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
  25049. },
  25050. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  25051. this.inherited(arguments);
  25052. if(this._started){
  25053. dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
  25054. }
  25055. },
  25056. removeChild: function(/*dijit._Widget*/ widget){
  25057. this.inherited(arguments);
  25058. if(this._started){
  25059. dijit.layout.layoutChildren(this.domNode, this._contentBox, this.getChildren());
  25060. }
  25061. }
  25062. });
  25063. // This argument can be specified for the children of a LayoutContainer.
  25064. // Since any widget can be specified as a LayoutContainer child, mix it
  25065. // into the base widget class. (This is a hack, but it's effective.)
  25066. dojo.extend(dijit._Widget, {
  25067. // layoutAlign: String
  25068. // "none", "left", "right", "bottom", "top", and "client".
  25069. // See the LayoutContainer description for details on this parameter.
  25070. layoutAlign: 'none'
  25071. });
  25072. }
  25073. if(!dojo._hasResource["dijit.layout.LinkPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25074. dojo._hasResource["dijit.layout.LinkPane"] = true;
  25075. dojo.provide("dijit.layout.LinkPane");
  25076. dojo.declare("dijit.layout.LinkPane",
  25077. [dijit.layout.ContentPane, dijit._Templated],
  25078. {
  25079. // summary:
  25080. // A ContentPane with an href where (when declared in markup)
  25081. // the title is specified as innerHTML rather than as a title attribute.
  25082. // description:
  25083. // LinkPane is just a ContentPane that is declared in markup similarly
  25084. // to an anchor. The anchor's body (the words between `<a>` and `</a>`)
  25085. // become the title of the widget (used for TabContainer, AccordionContainer, etc.)
  25086. // example:
  25087. // | <a href="foo.html">my title</a>
  25088. // I'm using a template because the user may specify the input as
  25089. // <a href="foo.html">title</a>, in which case we need to get rid of the
  25090. // <a> because we don't want a link.
  25091. templateString: '<div class="dijitLinkPane" dojoAttachPoint="containerNode"></div>',
  25092. postMixInProperties: function(){
  25093. // If user has specified node contents, they become the title
  25094. // (the link must be plain text)
  25095. if(this.srcNodeRef){
  25096. this.title += this.srcNodeRef.innerHTML;
  25097. }
  25098. this.inherited(arguments);
  25099. },
  25100. _fillContent: function(/*DomNode*/ source){
  25101. // Overrides _Templated._fillContent().
  25102. // _Templated._fillContent() relocates srcNodeRef innerHTML to templated container node,
  25103. // but in our case the srcNodeRef innerHTML is the title, so shouldn't be
  25104. // copied
  25105. }
  25106. });
  25107. }
  25108. if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25109. dojo._hasResource["dijit.layout.SplitContainer"] = true;
  25110. dojo.provide("dijit.layout.SplitContainer");
  25111. //
  25112. // FIXME: make it prettier
  25113. // FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
  25114. //
  25115. dojo.declare("dijit.layout.SplitContainer",
  25116. dijit.layout._LayoutWidget,
  25117. {
  25118. // summary:
  25119. // Deprecated. Use `dijit.layout.BorderContainer` instead.
  25120. // description:
  25121. // A Container widget with sizing handles in-between each child.
  25122. // Contains multiple children widgets, all of which are displayed side by side
  25123. // (either horizontally or vertically); there's a bar between each of the children,
  25124. // and you can adjust the relative size of each child by dragging the bars.
  25125. //
  25126. // You must specify a size (width and height) for the SplitContainer.
  25127. // tags:
  25128. // deprecated
  25129. constructor: function(){
  25130. dojo.deprecated("dijit.layout.SplitContainer is deprecated", "use BorderContainer with splitter instead", 2.0);
  25131. },
  25132. // activeSizing: Boolean
  25133. // If true, the children's size changes as you drag the bar;
  25134. // otherwise, the sizes don't change until you drop the bar (by mouse-up)
  25135. activeSizing: false,
  25136. // sizerWidth: Integer
  25137. // Size in pixels of the bar between each child
  25138. sizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)
  25139. // orientation: String
  25140. // either 'horizontal' or vertical; indicates whether the children are
  25141. // arranged side-by-side or up/down.
  25142. orientation: 'horizontal',
  25143. // persist: Boolean
  25144. // Save splitter positions in a cookie
  25145. persist: true,
  25146. baseClass: "dijitSplitContainer",
  25147. postMixInProperties: function(){
  25148. this.inherited("postMixInProperties",arguments);
  25149. this.isHorizontal = (this.orientation == 'horizontal');
  25150. },
  25151. postCreate: function(){
  25152. this.inherited(arguments);
  25153. this.sizers = [];
  25154. // overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)
  25155. // to keep other combined css classes from inadvertantly making the overflow visible
  25156. if(dojo.isMozilla){
  25157. this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work
  25158. }
  25159. // create the fake dragger
  25160. if(typeof this.sizerWidth == "object"){
  25161. try{ //FIXME: do this without a try/catch
  25162. this.sizerWidth = parseInt(this.sizerWidth.toString());
  25163. }catch(e){ this.sizerWidth = 7; }
  25164. }
  25165. var sizer = dojo.doc.createElement('div');
  25166. this.virtualSizer = sizer;
  25167. sizer.style.position = 'relative';
  25168. // #1681: work around the dreaded 'quirky percentages in IE' layout bug
  25169. // If the splitcontainer's dimensions are specified in percentages, it
  25170. // will be resized when the virtualsizer is displayed in _showSizingLine
  25171. // (typically expanding its bounds unnecessarily). This happens because
  25172. // we use position: relative for .dijitSplitContainer.
  25173. // The workaround: instead of changing the display style attribute,
  25174. // switch to changing the zIndex (bring to front/move to back)
  25175. sizer.style.zIndex = 10;
  25176. sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';
  25177. this.domNode.appendChild(sizer);
  25178. dojo.setSelectable(sizer, false);
  25179. },
  25180. destroy: function(){
  25181. delete this.virtualSizer;
  25182. dojo.forEach(this._ownconnects, dojo.disconnect);
  25183. this.inherited(arguments);
  25184. },
  25185. startup: function(){
  25186. if(this._started){ return; }
  25187. dojo.forEach(this.getChildren(), function(child, i, children){
  25188. // attach the children and create the draggers
  25189. this._setupChild(child);
  25190. if(i < children.length-1){
  25191. this._addSizer();
  25192. }
  25193. }, this);
  25194. if(this.persist){
  25195. this._restoreState();
  25196. }
  25197. this.inherited(arguments);
  25198. },
  25199. _setupChild: function(/*dijit._Widget*/ child){
  25200. this.inherited(arguments);
  25201. child.domNode.style.position = "absolute";
  25202. dojo.addClass(child.domNode, "dijitSplitPane");
  25203. },
  25204. _onSizerMouseDown: function(e){
  25205. if(e.target.id){
  25206. for(var i=0;i<this.sizers.length;i++){
  25207. if(this.sizers[i].id == e.target.id){
  25208. break;
  25209. }
  25210. }
  25211. if(i<this.sizers.length){
  25212. this.beginSizing(e,i);
  25213. }
  25214. }
  25215. },
  25216. _addSizer: function(index){
  25217. index = index === undefined ? this.sizers.length : index;
  25218. // TODO: use a template for this!!!
  25219. var sizer = dojo.doc.createElement('div');
  25220. sizer.id=dijit.getUniqueId('dijit_layout_SplitterContainer_Splitter');
  25221. this.sizers.splice(index,0,sizer);
  25222. this.domNode.appendChild(sizer);
  25223. sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';
  25224. // add the thumb div
  25225. var thumb = dojo.doc.createElement('div');
  25226. thumb.className = 'thumb';
  25227. sizer.appendChild(thumb);
  25228. // FIXME: are you serious? why aren't we using mover start/stop combo?
  25229. this.connect(sizer, "onmousedown", '_onSizerMouseDown');
  25230. dojo.setSelectable(sizer, false);
  25231. },
  25232. removeChild: function(widget){
  25233. // summary:
  25234. // Remove sizer, but only if widget is really our child and
  25235. // we have at least one sizer to throw away
  25236. if(this.sizers.length){
  25237. var i=dojo.indexOf(this.getChildren(), widget)
  25238. if(i != -1){
  25239. if(i == this.sizers.length){
  25240. i--;
  25241. }
  25242. dojo.destroy(this.sizers[i]);
  25243. this.sizers.splice(i,1);
  25244. }
  25245. }
  25246. // Remove widget and repaint
  25247. this.inherited(arguments);
  25248. if(this._started){
  25249. this.layout();
  25250. }
  25251. },
  25252. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  25253. // summary:
  25254. // Add a child widget to the container
  25255. // child:
  25256. // a widget to add
  25257. // insertIndex:
  25258. // postion in the "stack" to add the child widget
  25259. this.inherited(arguments);
  25260. if(this._started){
  25261. // Do the stuff that startup() does for each widget
  25262. var children = this.getChildren();
  25263. if(children.length > 1){
  25264. this._addSizer(insertIndex);
  25265. }
  25266. // and then reposition (ie, shrink) every pane to make room for the new guy
  25267. this.layout();
  25268. }
  25269. },
  25270. layout: function(){
  25271. // summary:
  25272. // Do layout of panels
  25273. // base class defines this._contentBox on initial creation and also
  25274. // on resize
  25275. this.paneWidth = this._contentBox.w;
  25276. this.paneHeight = this._contentBox.h;
  25277. var children = this.getChildren();
  25278. if(!children.length){ return; }
  25279. //
  25280. // calculate space
  25281. //
  25282. var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
  25283. if(children.length > 1){
  25284. space -= this.sizerWidth * (children.length - 1);
  25285. }
  25286. //
  25287. // calculate total of SizeShare values
  25288. //
  25289. var outOf = 0;
  25290. dojo.forEach(children, function(child){
  25291. outOf += child.sizeShare;
  25292. });
  25293. //
  25294. // work out actual pixels per sizeshare unit
  25295. //
  25296. var pixPerUnit = space / outOf;
  25297. //
  25298. // set the SizeActual member of each pane
  25299. //
  25300. var totalSize = 0;
  25301. dojo.forEach(children.slice(0, children.length - 1), function(child){
  25302. var size = Math.round(pixPerUnit * child.sizeShare);
  25303. child.sizeActual = size;
  25304. totalSize += size;
  25305. });
  25306. children[children.length-1].sizeActual = space - totalSize;
  25307. //
  25308. // make sure the sizes are ok
  25309. //
  25310. this._checkSizes();
  25311. //
  25312. // now loop, positioning each pane and letting children resize themselves
  25313. //
  25314. var pos = 0;
  25315. var size = children[0].sizeActual;
  25316. this._movePanel(children[0], pos, size);
  25317. children[0].position = pos;
  25318. pos += size;
  25319. // if we don't have any sizers, our layout method hasn't been called yet
  25320. // so bail until we are called..TODO: REVISIT: need to change the startup
  25321. // algorithm to guaranteed the ordering of calls to layout method
  25322. if(!this.sizers){
  25323. return;
  25324. }
  25325. dojo.some(children.slice(1), function(child, i){
  25326. // error-checking
  25327. if(!this.sizers[i]){
  25328. return true;
  25329. }
  25330. // first we position the sizing handle before this pane
  25331. this._moveSlider(this.sizers[i], pos, this.sizerWidth);
  25332. this.sizers[i].position = pos;
  25333. pos += this.sizerWidth;
  25334. size = child.sizeActual;
  25335. this._movePanel(child, pos, size);
  25336. child.position = pos;
  25337. pos += size;
  25338. }, this);
  25339. },
  25340. _movePanel: function(panel, pos, size){
  25341. if(this.isHorizontal){
  25342. panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manually
  25343. panel.domNode.style.top = 0;
  25344. var box = {w: size, h: this.paneHeight};
  25345. if(panel.resize){
  25346. panel.resize(box);
  25347. }else{
  25348. dojo.marginBox(panel.domNode, box);
  25349. }
  25350. }else{
  25351. panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manually
  25352. panel.domNode.style.top = pos + 'px';
  25353. var box = {w: this.paneWidth, h: size};
  25354. if(panel.resize){
  25355. panel.resize(box);
  25356. }else{
  25357. dojo.marginBox(panel.domNode, box);
  25358. }
  25359. }
  25360. },
  25361. _moveSlider: function(slider, pos, size){
  25362. if(this.isHorizontal){
  25363. slider.style.left = pos + 'px';
  25364. slider.style.top = 0;
  25365. dojo.marginBox(slider, { w: size, h: this.paneHeight });
  25366. }else{
  25367. slider.style.left = 0;
  25368. slider.style.top = pos + 'px';
  25369. dojo.marginBox(slider, { w: this.paneWidth, h: size });
  25370. }
  25371. },
  25372. _growPane: function(growth, pane){
  25373. if(growth > 0){
  25374. if(pane.sizeActual > pane.sizeMin){
  25375. if((pane.sizeActual - pane.sizeMin) > growth){
  25376. // stick all the growth in this pane
  25377. pane.sizeActual = pane.sizeActual - growth;
  25378. growth = 0;
  25379. }else{
  25380. // put as much growth in here as we can
  25381. growth -= pane.sizeActual - pane.sizeMin;
  25382. pane.sizeActual = pane.sizeMin;
  25383. }
  25384. }
  25385. }
  25386. return growth;
  25387. },
  25388. _checkSizes: function(){
  25389. var totalMinSize = 0;
  25390. var totalSize = 0;
  25391. var children = this.getChildren();
  25392. dojo.forEach(children, function(child){
  25393. totalSize += child.sizeActual;
  25394. totalMinSize += child.sizeMin;
  25395. });
  25396. // only make adjustments if we have enough space for all the minimums
  25397. if(totalMinSize <= totalSize){
  25398. var growth = 0;
  25399. dojo.forEach(children, function(child){
  25400. if(child.sizeActual < child.sizeMin){
  25401. growth += child.sizeMin - child.sizeActual;
  25402. child.sizeActual = child.sizeMin;
  25403. }
  25404. });
  25405. if(growth > 0){
  25406. var list = this.isDraggingLeft ? children.reverse() : children;
  25407. dojo.forEach(list, function(child){
  25408. growth = this._growPane(growth, child);
  25409. }, this);
  25410. }
  25411. }else{
  25412. dojo.forEach(children, function(child){
  25413. child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));
  25414. });
  25415. }
  25416. },
  25417. beginSizing: function(e, i){
  25418. var children = this.getChildren();
  25419. this.paneBefore = children[i];
  25420. this.paneAfter = children[i+1];
  25421. this.isSizing = true;
  25422. this.sizingSplitter = this.sizers[i];
  25423. if(!this.cover){
  25424. this.cover = dojo.create('div', {
  25425. style: {
  25426. position:'absolute',
  25427. zIndex:5,
  25428. top: 0,
  25429. left: 0,
  25430. width: "100%",
  25431. height: "100%"
  25432. }
  25433. }, this.domNode);
  25434. }else{
  25435. this.cover.style.zIndex = 5;
  25436. }
  25437. this.sizingSplitter.style.zIndex = 6;
  25438. // TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.)
  25439. this.originPos = dojo.position(children[0].domNode, true);
  25440. if(this.isHorizontal){
  25441. var client = e.layerX || e.offsetX || 0;
  25442. var screen = e.pageX;
  25443. this.originPos = this.originPos.x;
  25444. }else{
  25445. var client = e.layerY || e.offsetY || 0;
  25446. var screen = e.pageY;
  25447. this.originPos = this.originPos.y;
  25448. }
  25449. this.startPoint = this.lastPoint = screen;
  25450. this.screenToClientOffset = screen - client;
  25451. this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;
  25452. if(!this.activeSizing){
  25453. this._showSizingLine();
  25454. }
  25455. //
  25456. // attach mouse events
  25457. //
  25458. this._ownconnects = [];
  25459. this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmousemove", this, "changeSizing"));
  25460. this._ownconnects.push(dojo.connect(dojo.doc.documentElement, "onmouseup", this, "endSizing"));
  25461. dojo.stopEvent(e);
  25462. },
  25463. changeSizing: function(e){
  25464. if(!this.isSizing){ return; }
  25465. this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;
  25466. this.movePoint();
  25467. if(this.activeSizing){
  25468. this._updateSize();
  25469. }else{
  25470. this._moveSizingLine();
  25471. }
  25472. dojo.stopEvent(e);
  25473. },
  25474. endSizing: function(e){
  25475. if(!this.isSizing){ return; }
  25476. if(this.cover){
  25477. this.cover.style.zIndex = -1;
  25478. }
  25479. if(!this.activeSizing){
  25480. this._hideSizingLine();
  25481. }
  25482. this._updateSize();
  25483. this.isSizing = false;
  25484. if(this.persist){
  25485. this._saveState(this);
  25486. }
  25487. dojo.forEach(this._ownconnects, dojo.disconnect);
  25488. },
  25489. movePoint: function(){
  25490. // make sure lastPoint is a legal point to drag to
  25491. var p = this.lastPoint - this.screenToClientOffset;
  25492. var a = p - this.dragOffset;
  25493. a = this.legaliseSplitPoint(a);
  25494. p = a + this.dragOffset;
  25495. this.lastPoint = p + this.screenToClientOffset;
  25496. },
  25497. legaliseSplitPoint: function(a){
  25498. a += this.sizingSplitter.position;
  25499. this.isDraggingLeft = !!(a > 0);
  25500. if(!this.activeSizing){
  25501. var min = this.paneBefore.position + this.paneBefore.sizeMin;
  25502. if(a < min){
  25503. a = min;
  25504. }
  25505. var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
  25506. if(a > max){
  25507. a = max;
  25508. }
  25509. }
  25510. a -= this.sizingSplitter.position;
  25511. this._checkSizes();
  25512. return a;
  25513. },
  25514. _updateSize: function(){
  25515. //FIXME: sometimes this.lastPoint is NaN
  25516. var pos = this.lastPoint - this.dragOffset - this.originPos;
  25517. var start_region = this.paneBefore.position;
  25518. var end_region = this.paneAfter.position + this.paneAfter.sizeActual;
  25519. this.paneBefore.sizeActual = pos - start_region;
  25520. this.paneAfter.position = pos + this.sizerWidth;
  25521. this.paneAfter.sizeActual = end_region - this.paneAfter.position;
  25522. dojo.forEach(this.getChildren(), function(child){
  25523. child.sizeShare = child.sizeActual;
  25524. });
  25525. if(this._started){
  25526. this.layout();
  25527. }
  25528. },
  25529. _showSizingLine: function(){
  25530. this._moveSizingLine();
  25531. dojo.marginBox(this.virtualSizer,
  25532. this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });
  25533. this.virtualSizer.style.display = 'block';
  25534. },
  25535. _hideSizingLine: function(){
  25536. this.virtualSizer.style.display = 'none';
  25537. },
  25538. _moveSizingLine: function(){
  25539. var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;
  25540. dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");
  25541. // this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better
  25542. },
  25543. _getCookieName: function(i){
  25544. return this.id + "_" + i;
  25545. },
  25546. _restoreState: function(){
  25547. dojo.forEach(this.getChildren(), function(child, i){
  25548. var cookieName = this._getCookieName(i);
  25549. var cookieValue = dojo.cookie(cookieName);
  25550. if(cookieValue){
  25551. var pos = parseInt(cookieValue);
  25552. if(typeof pos == "number"){
  25553. child.sizeShare = pos;
  25554. }
  25555. }
  25556. }, this);
  25557. },
  25558. _saveState: function(){
  25559. if(!this.persist){
  25560. return;
  25561. }
  25562. dojo.forEach(this.getChildren(), function(child, i){
  25563. dojo.cookie(this._getCookieName(i), child.sizeShare, {expires:365});
  25564. }, this);
  25565. }
  25566. });
  25567. // These arguments can be specified for the children of a SplitContainer.
  25568. // Since any widget can be specified as a SplitContainer child, mix them
  25569. // into the base widget class. (This is a hack, but it's effective.)
  25570. dojo.extend(dijit._Widget, {
  25571. // sizeMin: [deprecated] Integer
  25572. // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
  25573. // Minimum size (width or height) of a child of a SplitContainer.
  25574. // The value is relative to other children's sizeShare properties.
  25575. sizeMin: 10,
  25576. // sizeShare: [deprecated] Integer
  25577. // Deprecated. Parameter for children of `dijit.layout.SplitContainer`.
  25578. // Size (width or height) of a child of a SplitContainer.
  25579. // The value is relative to other children's sizeShare properties.
  25580. // For example, if there are two children and each has sizeShare=10, then
  25581. // each takes up 50% of the available space.
  25582. sizeShare: 10
  25583. });
  25584. }
  25585. if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25586. dojo._hasResource["dijit.layout._TabContainerBase"] = true;
  25587. dojo.provide("dijit.layout._TabContainerBase");
  25588. dojo.declare("dijit.layout._TabContainerBase",
  25589. [dijit.layout.StackContainer, dijit._Templated],
  25590. {
  25591. // summary:
  25592. // Abstract base class for TabContainer. Must define _makeController() to instantiate
  25593. // and return the widget that displays the tab labels
  25594. // description:
  25595. // A TabContainer is a container that has multiple panes, but shows only
  25596. // one pane at a time. There are a set of tabs corresponding to each pane,
  25597. // where each tab has the name (aka title) of the pane, and optionally a close button.
  25598. // tabPosition: String
  25599. // Defines where tabs go relative to tab content.
  25600. // "top", "bottom", "left-h", "right-h"
  25601. tabPosition: "top",
  25602. baseClass: "dijitTabContainer",
  25603. // tabStrip: [const] Boolean
  25604. // Defines whether the tablist gets an extra class for layouting, putting a border/shading
  25605. // around the set of tabs. Not supported by claro theme.
  25606. tabStrip: false,
  25607. // nested: [const] Boolean
  25608. // If true, use styling for a TabContainer nested inside another TabContainer.
  25609. // For tundra etc., makes tabs look like links, and hides the outer
  25610. // border since the outer TabContainer already has a border.
  25611. nested: false,
  25612. templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" dojoAttachPoint=\"tablistNode\"></div>\n\t<div dojoAttachPoint=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n"),
  25613. postMixInProperties: function(){
  25614. // set class name according to tab position, ex: dijitTabContainerTop
  25615. this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
  25616. this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
  25617. this.inherited(arguments);
  25618. },
  25619. buildRendering: function(){
  25620. this.inherited(arguments);
  25621. // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
  25622. this.tablist = this._makeController(this.tablistNode);
  25623. if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
  25624. if(this.nested){
  25625. /* workaround IE's lack of support for "a > b" selectors by
  25626. * tagging each node in the template.
  25627. */
  25628. dojo.addClass(this.domNode, "dijitTabContainerNested");
  25629. dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
  25630. dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
  25631. dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
  25632. }else{
  25633. dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
  25634. }
  25635. },
  25636. _setupChild: function(/*dijit._Widget*/ tab){
  25637. // Overrides StackContainer._setupChild().
  25638. dojo.addClass(tab.domNode, "dijitTabPane");
  25639. this.inherited(arguments);
  25640. },
  25641. startup: function(){
  25642. if(this._started){ return; }
  25643. // wire up the tablist and its tabs
  25644. this.tablist.startup();
  25645. this.inherited(arguments);
  25646. },
  25647. layout: function(){
  25648. // Overrides StackContainer.layout().
  25649. // Configure the content pane to take up all the space except for where the tabs are
  25650. if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
  25651. var sc = this.selectedChildWidget;
  25652. if(this.doLayout){
  25653. // position and size the titles and the container node
  25654. var titleAlign = this.tabPosition.replace(/-h/, "");
  25655. this.tablist.layoutAlign = titleAlign;
  25656. var children = [this.tablist, {
  25657. domNode: this.tablistSpacer,
  25658. layoutAlign: titleAlign
  25659. }, {
  25660. domNode: this.containerNode,
  25661. layoutAlign: "client"
  25662. }];
  25663. dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
  25664. // Compute size to make each of my children.
  25665. // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
  25666. this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
  25667. if(sc && sc.resize){
  25668. sc.resize(this._containerContentBox);
  25669. }
  25670. }else{
  25671. // just layout the tab controller, so it can position left/right buttons etc.
  25672. if(this.tablist.resize){
  25673. //make the tabs zero width so that they don't interfere with width calc, then reset
  25674. var s = this.tablist.domNode.style;
  25675. s.width="0";
  25676. var width = dojo.contentBox(this.domNode).w;
  25677. s.width="";
  25678. this.tablist.resize({w: width});
  25679. }
  25680. // and call resize() on the selected pane just to tell it that it's been made visible
  25681. if(sc && sc.resize){
  25682. sc.resize();
  25683. }
  25684. }
  25685. },
  25686. destroy: function(){
  25687. if(this.tablist){
  25688. this.tablist.destroy();
  25689. }
  25690. this.inherited(arguments);
  25691. }
  25692. });
  25693. }
  25694. if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25695. dojo._hasResource["dijit.layout.TabController"] = true;
  25696. dojo.provide("dijit.layout.TabController");
  25697. // Menu is used for an accessible close button, would be nice to have a lighter-weight solution
  25698. dojo.declare("dijit.layout.TabController",
  25699. dijit.layout.StackController,
  25700. {
  25701. // summary:
  25702. // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
  25703. // Used internally by `dijit.layout.TabContainer`.
  25704. // description:
  25705. // Lets the user select the currently shown pane in a TabContainer or StackContainer.
  25706. // TabController also monitors the TabContainer, and whenever a pane is
  25707. // added or deleted updates itself accordingly.
  25708. // tags:
  25709. // private
  25710. templateString: "<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
  25711. // tabPosition: String
  25712. // Defines where tabs go relative to the content.
  25713. // "top", "bottom", "left-h", "right-h"
  25714. tabPosition: "top",
  25715. // buttonWidget: String
  25716. // The name of the tab widget to create to correspond to each page
  25717. buttonWidget: "dijit.layout._TabButton",
  25718. _rectifyRtlTabList: function(){
  25719. // summary:
  25720. // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
  25721. if(0 >= this.tabPosition.indexOf('-h')){ return; }
  25722. if(!this.pane2button){ return; }
  25723. var maxWidth = 0;
  25724. for(var pane in this.pane2button){
  25725. var ow = this.pane2button[pane].innerDiv.scrollWidth;
  25726. maxWidth = Math.max(maxWidth, ow);
  25727. }
  25728. //unify the length of all the tabs
  25729. for(pane in this.pane2button){
  25730. this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
  25731. }
  25732. }
  25733. });
  25734. dojo.declare("dijit.layout._TabButton",
  25735. dijit.layout._StackButton,
  25736. {
  25737. // summary:
  25738. // A tab (the thing you click to select a pane).
  25739. // description:
  25740. // Contains the title of the pane, and optionally a close-button to destroy the pane.
  25741. // This is an internal widget and should not be instantiated directly.
  25742. // tags:
  25743. // private
  25744. // baseClass: String
  25745. // The CSS class applied to the domNode.
  25746. baseClass: "dijitTab",
  25747. // Apply dijitTabCloseButtonHover when close button is hovered
  25748. cssStateNodes: {
  25749. closeNode: "dijitTabCloseButton"
  25750. },
  25751. templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div role=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n \t<div role=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" dojoAttachPoint='iconNode' />\n\t\t <span dojoAttachPoint='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" dojoAttachPoint='closeNode'\n\t\t \t\tdojoAttachEvent='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span dojoAttachPoint='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"),
  25752. // Override _FormWidget.scrollOnFocus.
  25753. // Don't scroll the whole tab container into view when the button is focused.
  25754. scrollOnFocus: false,
  25755. buildRendering: function(){
  25756. this.inherited(arguments);
  25757. dojo.setSelectable(this.containerNode, false);
  25758. },
  25759. startup: function(){
  25760. this.inherited(arguments);
  25761. var n = this.domNode;
  25762. // Required to give IE6 a kick, as it initially hides the
  25763. // tabs until they are focused on.
  25764. setTimeout(function(){
  25765. n.className = n.className;
  25766. }, 1);
  25767. },
  25768. _setCloseButtonAttr: function(/*Boolean*/ disp){
  25769. // summary:
  25770. // Hide/show close button
  25771. this._set("closeButton", disp);
  25772. dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
  25773. this.closeNode.style.display = disp ? "" : "none";
  25774. if(disp){
  25775. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  25776. if(this.closeNode){
  25777. dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
  25778. }
  25779. // add context menu onto title button
  25780. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  25781. this._closeMenu = new dijit.Menu({
  25782. id: this.id+"_Menu",
  25783. dir: this.dir,
  25784. lang: this.lang,
  25785. targetNodeIds: [this.domNode]
  25786. });
  25787. this._closeMenu.addChild(new dijit.MenuItem({
  25788. label: _nlsResources.itemClose,
  25789. dir: this.dir,
  25790. lang: this.lang,
  25791. onClick: dojo.hitch(this, "onClickCloseButton")
  25792. }));
  25793. }else{
  25794. if(this._closeMenu){
  25795. this._closeMenu.destroyRecursive();
  25796. delete this._closeMenu;
  25797. }
  25798. }
  25799. },
  25800. _setLabelAttr: function(/*String*/ content){
  25801. // summary:
  25802. // Hook for set('label', ...) to work.
  25803. // description:
  25804. // takes an HTML string.
  25805. // Inherited ToggleButton implementation will Set the label (text) of the button;
  25806. // Need to set the alt attribute of icon on tab buttons if no label displayed
  25807. this.inherited(arguments);
  25808. if(this.showLabel == false && !this.params.title){
  25809. this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
  25810. }
  25811. },
  25812. destroy: function(){
  25813. if(this._closeMenu){
  25814. this._closeMenu.destroyRecursive();
  25815. delete this._closeMenu;
  25816. }
  25817. this.inherited(arguments);
  25818. }
  25819. });
  25820. }
  25821. if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25822. dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
  25823. dojo.provide("dijit.layout.ScrollingTabController");
  25824. dojo.declare("dijit.layout.ScrollingTabController",
  25825. dijit.layout.TabController,
  25826. {
  25827. // summary:
  25828. // Set of tabs with left/right arrow keys and a menu to switch between tabs not
  25829. // all fitting on a single row.
  25830. // Works only for horizontal tabs (either above or below the content, not to the left
  25831. // or right).
  25832. // tags:
  25833. // private
  25834. templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" containerId=\"${containerId}\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdropDownPosition=\"below-alt, above-alt\"\n\t\t\tdojoAttachPoint=\"_menuBtn\" showLabel=\"false\">&#9660;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\" iconClass=\"dijitTabStripSlideLeftIcon\"\n\t\t\tdojoAttachPoint=\"_leftBtn\" dojoAttachEvent=\"onClick: doSlideLeft\" showLabel=\"false\">&#9664;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\" iconClass=\"dijitTabStripSlideRightIcon\"\n\t\t\tdojoAttachPoint=\"_rightBtn\" dojoAttachEvent=\"onClick: doSlideRight\" showLabel=\"false\">&#9654;</div>\n\t<div class='dijitTabListWrapper' dojoAttachPoint='tablistWrapper'>\n\t\t<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"),
  25835. // useMenu: [const] Boolean
  25836. // True if a menu should be used to select tabs when they are too
  25837. // wide to fit the TabContainer, false otherwise.
  25838. useMenu: true,
  25839. // useSlider: [const] Boolean
  25840. // True if a slider should be used to select tabs when they are too
  25841. // wide to fit the TabContainer, false otherwise.
  25842. useSlider: true,
  25843. // tabStripClass: [const] String
  25844. // The css class to apply to the tab strip, if it is visible.
  25845. tabStripClass: "",
  25846. widgetsInTemplate: true,
  25847. // _minScroll: Number
  25848. // The distance in pixels from the edge of the tab strip which,
  25849. // if a scroll animation is less than, forces the scroll to
  25850. // go all the way to the left/right.
  25851. _minScroll: 5,
  25852. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  25853. "class": "containerNode"
  25854. }),
  25855. buildRendering: function(){
  25856. this.inherited(arguments);
  25857. var n = this.domNode;
  25858. this.scrollNode = this.tablistWrapper;
  25859. this._initButtons();
  25860. if(!this.tabStripClass){
  25861. this.tabStripClass = "dijitTabContainer" +
  25862. this.tabPosition.charAt(0).toUpperCase() +
  25863. this.tabPosition.substr(1).replace(/-.*/, "") +
  25864. "None";
  25865. dojo.addClass(n, "tabStrip-disabled")
  25866. }
  25867. dojo.addClass(this.tablistWrapper, this.tabStripClass);
  25868. },
  25869. onStartup: function(){
  25870. this.inherited(arguments);
  25871. // Do not show the TabController until the related
  25872. // StackController has added it's children. This gives
  25873. // a less visually jumpy instantiation.
  25874. dojo.style(this.domNode, "visibility", "visible");
  25875. this._postStartup = true;
  25876. },
  25877. onAddChild: function(page, insertIndex){
  25878. this.inherited(arguments);
  25879. // changes to the tab button label or iconClass will have changed the width of the
  25880. // buttons, so do a resize
  25881. dojo.forEach(["label", "iconClass"], function(attr){
  25882. this.pane2watches[page.id].push(
  25883. this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){
  25884. if(this._postStartup && this._dim){
  25885. this.resize(this._dim);
  25886. }
  25887. }))
  25888. );
  25889. }, this);
  25890. // Increment the width of the wrapper when a tab is added
  25891. // This makes sure that the buttons never wrap.
  25892. // The value 200 is chosen as it should be bigger than most
  25893. // Tab button widths.
  25894. dojo.style(this.containerNode, "width",
  25895. (dojo.style(this.containerNode, "width") + 200) + "px");
  25896. },
  25897. onRemoveChild: function(page, insertIndex){
  25898. // null out _selectedTab because we are about to delete that dom node
  25899. var button = this.pane2button[page.id];
  25900. if(this._selectedTab === button.domNode){
  25901. this._selectedTab = null;
  25902. }
  25903. this.inherited(arguments);
  25904. },
  25905. _initButtons: function(){
  25906. // summary:
  25907. // Creates the buttons used to scroll to view tabs that
  25908. // may not be visible if the TabContainer is too narrow.
  25909. // Make a list of the buttons to display when the tab labels become
  25910. // wider than the TabContainer, and hide the other buttons.
  25911. // Also gets the total width of the displayed buttons.
  25912. this._btnWidth = 0;
  25913. this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
  25914. if((this.useMenu && btn == this._menuBtn.domNode) ||
  25915. (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
  25916. this._btnWidth += dojo._getMarginSize(btn).w;
  25917. return true;
  25918. }else{
  25919. dojo.style(btn, "display", "none");
  25920. return false;
  25921. }
  25922. }, this);
  25923. },
  25924. _getTabsWidth: function(){
  25925. var children = this.getChildren();
  25926. if(children.length){
  25927. var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
  25928. rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
  25929. return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
  25930. }else{
  25931. return 0;
  25932. }
  25933. },
  25934. _enableBtn: function(width){
  25935. // summary:
  25936. // Determines if the tabs are wider than the width of the TabContainer, and
  25937. // thus that we need to display left/right/menu navigation buttons.
  25938. var tabsWidth = this._getTabsWidth();
  25939. width = width || dojo.style(this.scrollNode, "width");
  25940. return tabsWidth > 0 && width < tabsWidth;
  25941. },
  25942. resize: function(dim){
  25943. // summary:
  25944. // Hides or displays the buttons used to scroll the tab list and launch the menu
  25945. // that selects tabs.
  25946. if(this.domNode.offsetWidth == 0){
  25947. return;
  25948. }
  25949. // Save the dimensions to be used when a child is renamed.
  25950. this._dim = dim;
  25951. // Set my height to be my natural height (tall enough for one row of tab labels),
  25952. // and my content-box width based on margin-box width specified in dim parameter.
  25953. // But first reset scrollNode.height in case it was set by layoutChildren() call
  25954. // in a previous run of this method.
  25955. this.scrollNode.style.height = "auto";
  25956. this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
  25957. this._contentBox.h = this.scrollNode.offsetHeight;
  25958. dojo.contentBox(this.domNode, this._contentBox);
  25959. // Show/hide the left/right/menu navigation buttons depending on whether or not they
  25960. // are needed.
  25961. var enable = this._enableBtn(this._contentBox.w);
  25962. this._buttons.style("display", enable ? "" : "none");
  25963. // Position and size the navigation buttons and the tablist
  25964. this._leftBtn.layoutAlign = "left";
  25965. this._rightBtn.layoutAlign = "right";
  25966. this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
  25967. dijit.layout.layoutChildren(this.domNode, this._contentBox,
  25968. [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
  25969. // set proper scroll so that selected tab is visible
  25970. if(this._selectedTab){
  25971. if(this._anim && this._anim.status() == "playing"){
  25972. this._anim.stop();
  25973. }
  25974. var w = this.scrollNode,
  25975. sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
  25976. w.scrollLeft = sl;
  25977. }
  25978. // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
  25979. this._setButtonClass(this._getScroll());
  25980. this._postResize = true;
  25981. // Return my size so layoutChildren() can use it.
  25982. // Also avoids IE9 layout glitch on browser resize when scroll buttons present
  25983. return {h: this._contentBox.h, w: dim.w};
  25984. },
  25985. _getScroll: function(){
  25986. // summary:
  25987. // Returns the current scroll of the tabs where 0 means
  25988. // "scrolled all the way to the left" and some positive number, based on #
  25989. // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
  25990. var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
  25991. dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
  25992. + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
  25993. return sl;
  25994. },
  25995. _convertToScrollLeft: function(val){
  25996. // summary:
  25997. // Given a scroll value where 0 means "scrolled all the way to the left"
  25998. // and some positive number, based on # of pixels of possible scroll (ex: 1000)
  25999. // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
  26000. // to achieve that scroll.
  26001. //
  26002. // This method is to adjust for RTL funniness in various browsers and versions.
  26003. if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
  26004. return val;
  26005. }else{
  26006. var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
  26007. return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
  26008. }
  26009. },
  26010. onSelectChild: function(/*dijit._Widget*/ page){
  26011. // summary:
  26012. // Smoothly scrolls to a tab when it is selected.
  26013. var tab = this.pane2button[page.id];
  26014. if(!tab || !page){return;}
  26015. // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
  26016. var node = tab.domNode;
  26017. if(this._postResize && node != this._selectedTab){
  26018. this._selectedTab = node;
  26019. var sl = this._getScroll();
  26020. if(sl > node.offsetLeft ||
  26021. sl + dojo.style(this.scrollNode, "width") <
  26022. node.offsetLeft + dojo.style(node, "width")){
  26023. this.createSmoothScroll().play();
  26024. }
  26025. }
  26026. this.inherited(arguments);
  26027. },
  26028. _getScrollBounds: function(){
  26029. // summary:
  26030. // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
  26031. // tabs (respectively)
  26032. var children = this.getChildren(),
  26033. scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
  26034. containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
  26035. maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
  26036. tabsWidth = this._getTabsWidth();
  26037. if(children.length && tabsWidth > scrollNodeWidth){
  26038. // Scrolling should happen
  26039. return {
  26040. min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
  26041. max: this.isLeftToRight() ?
  26042. (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
  26043. maxPossibleScroll
  26044. };
  26045. }else{
  26046. // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
  26047. var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
  26048. return {
  26049. min: onlyScrollPosition,
  26050. max: onlyScrollPosition
  26051. };
  26052. }
  26053. },
  26054. _getScrollForSelectedTab: function(){
  26055. // summary:
  26056. // Returns the scroll value setting so that the selected tab
  26057. // will appear in the center
  26058. var w = this.scrollNode,
  26059. n = this._selectedTab,
  26060. scrollNodeWidth = dojo.style(this.scrollNode, "width"),
  26061. scrollBounds = this._getScrollBounds();
  26062. // TODO: scroll minimal amount (to either right or left) so that
  26063. // selected tab is fully visible, and just return if it's already visible?
  26064. var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
  26065. pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
  26066. // TODO:
  26067. // If scrolling close to the left side or right side, scroll
  26068. // all the way to the left or right. See this._minScroll.
  26069. // (But need to make sure that doesn't scroll the tab out of view...)
  26070. return pos;
  26071. },
  26072. createSmoothScroll: function(x){
  26073. // summary:
  26074. // Creates a dojo._Animation object that smoothly scrolls the tab list
  26075. // either to a fixed horizontal pixel value, or to the selected tab.
  26076. // description:
  26077. // If an number argument is passed to the function, that horizontal
  26078. // pixel position is scrolled to. Otherwise the currently selected
  26079. // tab is scrolled to.
  26080. // x: Integer?
  26081. // An optional pixel value to scroll to, indicating distance from left.
  26082. // Calculate position to scroll to
  26083. if(arguments.length > 0){
  26084. // position specified by caller, just make sure it's within bounds
  26085. var scrollBounds = this._getScrollBounds();
  26086. x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
  26087. }else{
  26088. // scroll to center the current tab
  26089. x = this._getScrollForSelectedTab();
  26090. }
  26091. if(this._anim && this._anim.status() == "playing"){
  26092. this._anim.stop();
  26093. }
  26094. var self = this,
  26095. w = this.scrollNode,
  26096. anim = new dojo._Animation({
  26097. beforeBegin: function(){
  26098. if(this.curve){ delete this.curve; }
  26099. var oldS = w.scrollLeft,
  26100. newS = self._convertToScrollLeft(x);
  26101. anim.curve = new dojo._Line(oldS, newS);
  26102. },
  26103. onAnimate: function(val){
  26104. w.scrollLeft = val;
  26105. }
  26106. });
  26107. this._anim = anim;
  26108. // Disable/enable left/right buttons according to new scroll position
  26109. this._setButtonClass(x);
  26110. return anim; // dojo._Animation
  26111. },
  26112. _getBtnNode: function(/*Event*/ e){
  26113. // summary:
  26114. // Gets a button DOM node from a mouse click event.
  26115. // e:
  26116. // The mouse click event.
  26117. var n = e.target;
  26118. while(n && !dojo.hasClass(n, "tabStripButton")){
  26119. n = n.parentNode;
  26120. }
  26121. return n;
  26122. },
  26123. doSlideRight: function(/*Event*/ e){
  26124. // summary:
  26125. // Scrolls the menu to the right.
  26126. // e:
  26127. // The mouse click event.
  26128. this.doSlide(1, this._getBtnNode(e));
  26129. },
  26130. doSlideLeft: function(/*Event*/ e){
  26131. // summary:
  26132. // Scrolls the menu to the left.
  26133. // e:
  26134. // The mouse click event.
  26135. this.doSlide(-1,this._getBtnNode(e));
  26136. },
  26137. doSlide: function(/*Number*/ direction, /*DomNode*/ node){
  26138. // summary:
  26139. // Scrolls the tab list to the left or right by 75% of the widget width.
  26140. // direction:
  26141. // If the direction is 1, the widget scrolls to the right, if it is
  26142. // -1, it scrolls to the left.
  26143. if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
  26144. var sWidth = dojo.style(this.scrollNode, "width");
  26145. var d = (sWidth * 0.75) * direction;
  26146. var to = this._getScroll() + d;
  26147. this._setButtonClass(to);
  26148. this.createSmoothScroll(to).play();
  26149. },
  26150. _setButtonClass: function(/*Number*/ scroll){
  26151. // summary:
  26152. // Disables the left scroll button if the tabs are scrolled all the way to the left,
  26153. // or the right scroll button in the opposite case.
  26154. // scroll: Integer
  26155. // amount of horizontal scroll
  26156. var scrollBounds = this._getScrollBounds();
  26157. this._leftBtn.set("disabled", scroll <= scrollBounds.min);
  26158. this._rightBtn.set("disabled", scroll >= scrollBounds.max);
  26159. }
  26160. });
  26161. dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
  26162. baseClass: "dijitTab tabStripButton",
  26163. templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t\t<span dojoAttachPoint=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>\n"),
  26164. // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
  26165. // able to tab to the left/right/menu buttons
  26166. tabIndex: "",
  26167. // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
  26168. // either (this override avoids focus() call in FormWidget.js)
  26169. isFocusable: function(){ return false; }
  26170. });
  26171. dojo.declare("dijit.layout._ScrollingTabControllerButton",
  26172. [dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]);
  26173. dojo.declare(
  26174. "dijit.layout._ScrollingTabControllerMenuButton",
  26175. [dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin],
  26176. {
  26177. // id of the TabContainer itself
  26178. containerId: "",
  26179. // -1 so user can't tab into the button, but so that button can still be focused programatically.
  26180. // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
  26181. tabIndex: "-1",
  26182. isLoaded: function(){
  26183. // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
  26184. return false;
  26185. },
  26186. loadDropDown: function(callback){
  26187. this.dropDown = new dijit.Menu({
  26188. id: this.containerId + "_menu",
  26189. dir: this.dir,
  26190. lang: this.lang
  26191. });
  26192. var container = dijit.byId(this.containerId);
  26193. dojo.forEach(container.getChildren(), function(page){
  26194. var menuItem = new dijit.MenuItem({
  26195. id: page.id + "_stcMi",
  26196. label: page.title,
  26197. iconClass: page.iconClass,
  26198. dir: page.dir,
  26199. lang: page.lang,
  26200. onClick: function(){
  26201. container.selectChild(page);
  26202. }
  26203. });
  26204. this.dropDown.addChild(menuItem);
  26205. }, this);
  26206. callback();
  26207. },
  26208. closeDropDown: function(/*Boolean*/ focus){
  26209. this.inherited(arguments);
  26210. if(this.dropDown){
  26211. this.dropDown.destroyRecursive();
  26212. delete this.dropDown;
  26213. }
  26214. }
  26215. });
  26216. }
  26217. if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  26218. dojo._hasResource["dijit.layout.TabContainer"] = true;
  26219. dojo.provide("dijit.layout.TabContainer");
  26220. dojo.declare("dijit.layout.TabContainer",
  26221. dijit.layout._TabContainerBase,
  26222. {
  26223. // summary:
  26224. // A Container with tabs to select each child (only one of which is displayed at a time).
  26225. // description:
  26226. // A TabContainer is a container that has multiple panes, but shows only
  26227. // one pane at a time. There are a set of tabs corresponding to each pane,
  26228. // where each tab has the name (aka title) of the pane, and optionally a close button.
  26229. // useMenu: [const] Boolean
  26230. // True if a menu should be used to select tabs when they are too
  26231. // wide to fit the TabContainer, false otherwise.
  26232. useMenu: true,
  26233. // useSlider: [const] Boolean
  26234. // True if a slider should be used to select tabs when they are too
  26235. // wide to fit the TabContainer, false otherwise.
  26236. useSlider: true,
  26237. // controllerWidget: String
  26238. // An optional parameter to override the widget used to display the tab labels
  26239. controllerWidget: "",
  26240. _makeController: function(/*DomNode*/ srcNode){
  26241. // summary:
  26242. // Instantiate tablist controller widget and return reference to it.
  26243. // Callback from _TabContainerBase.postCreate().
  26244. // tags:
  26245. // protected extension
  26246. var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
  26247. TabController = dojo.getObject(this.controllerWidget);
  26248. return new TabController({
  26249. id: this.id + "_tablist",
  26250. dir: this.dir,
  26251. lang: this.lang,
  26252. tabPosition: this.tabPosition,
  26253. doLayout: this.doLayout,
  26254. containerId: this.id,
  26255. "class": cls,
  26256. nested: this.nested,
  26257. useMenu: this.useMenu,
  26258. useSlider: this.useSlider,
  26259. tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
  26260. }, srcNode);
  26261. },
  26262. postMixInProperties: function(){
  26263. this.inherited(arguments);
  26264. // Scrolling controller only works for horizontal non-nested tabs
  26265. if(!this.controllerWidget){
  26266. this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
  26267. "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
  26268. }
  26269. }
  26270. });
  26271. }
  26272. if(!dojo._hasResource["dijit.dijit-all"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  26273. dojo._hasResource["dijit.dijit-all"] = true;
  26274. dojo.provide("dijit.dijit-all");
  26275. console.warn("dijit-all may include much more code than your application actually requires. We strongly recommend that you investigate a custom build or the web build tool");
  26276. /*=====
  26277. dijit["dijit-all"] = {
  26278. // summary:
  26279. // A rollup that includes every dijit. You probably don't need this.
  26280. };
  26281. =====*/
  26282. }
  26283. dojo.i18n._preloadLocalizations("dijit.nls.dijit-all", ["ROOT","ar","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hu","it","it-it","ja","ja-jp","ko","ko-kr","nb","nl","nl-nl","pl","pt","pt-br","pt-pt","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]);