buxdojo.js.uncompressed.js 1.4 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610
  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.back"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12. dojo._hasResource["dojo.back"] = true;
  13. dojo.provide("dojo.back");
  14. dojo.getObject("back", true, dojo);
  15. /*=====
  16. dojo.back = {
  17. // summary: Browser history management resources
  18. }
  19. =====*/
  20. (function(){
  21. var back = dojo.back,
  22. // everyone deals with encoding the hash slightly differently
  23. getHash= back.getHash= function(){
  24. var h = window.location.hash;
  25. if(h.charAt(0) == "#"){ h = h.substring(1); }
  26. return dojo.isMozilla ? h : decodeURIComponent(h);
  27. },
  28. setHash= back.setHash= function(h){
  29. if(!h){ h = ""; }
  30. window.location.hash = encodeURIComponent(h);
  31. historyCounter = history.length;
  32. };
  33. var initialHref = (typeof(window) !== "undefined") ? window.location.href : "";
  34. var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
  35. var initialState = null;
  36. var locationTimer = null;
  37. var bookmarkAnchor = null;
  38. var historyIframe = null;
  39. var forwardStack = [];
  40. var historyStack = [];
  41. var moveForward = false;
  42. var changingUrl = false;
  43. var historyCounter;
  44. function handleBackButton(){
  45. //summary: private method. Do not call this directly.
  46. //The "current" page is always at the top of the history stack.
  47. var current = historyStack.pop();
  48. if(!current){ return; }
  49. var last = historyStack[historyStack.length-1];
  50. if(!last && historyStack.length == 0){
  51. last = initialState;
  52. }
  53. if(last){
  54. if(last.kwArgs["back"]){
  55. last.kwArgs["back"]();
  56. }else if(last.kwArgs["backButton"]){
  57. last.kwArgs["backButton"]();
  58. }else if(last.kwArgs["handle"]){
  59. last.kwArgs.handle("back");
  60. }
  61. }
  62. forwardStack.push(current);
  63. }
  64. back.goBack = handleBackButton;
  65. function handleForwardButton(){
  66. //summary: private method. Do not call this directly.
  67. var last = forwardStack.pop();
  68. if(!last){ return; }
  69. if(last.kwArgs["forward"]){
  70. last.kwArgs.forward();
  71. }else if(last.kwArgs["forwardButton"]){
  72. last.kwArgs.forwardButton();
  73. }else if(last.kwArgs["handle"]){
  74. last.kwArgs.handle("forward");
  75. }
  76. historyStack.push(last);
  77. }
  78. back.goForward = handleForwardButton;
  79. function createState(url, args, hash){
  80. //summary: private method. Do not call this directly.
  81. return {"url": url, "kwArgs": args, "urlHash": hash}; //Object
  82. }
  83. function getUrlQuery(url){
  84. //summary: private method. Do not call this directly.
  85. var segments = url.split("?");
  86. if(segments.length < 2){
  87. return null; //null
  88. }
  89. else{
  90. return segments[1]; //String
  91. }
  92. }
  93. function loadIframeHistory(){
  94. //summary: private method. Do not call this directly.
  95. var url = (dojo.config["dojoIframeHistoryUrl"] || dojo.moduleUrl("dojo", "resources/iframe_history.html")) + "?" + (new Date()).getTime();
  96. moveForward = true;
  97. if(historyIframe){
  98. dojo.isWebKit ? historyIframe.location = url : window.frames[historyIframe.name].location = url;
  99. }else{
  100. //console.warn("dojo.back: Not initialised. You need to call dojo.back.init() from a <script> block that lives inside the <body> tag.");
  101. }
  102. return url; //String
  103. }
  104. function checkLocation(){
  105. if(!changingUrl){
  106. var hsl = historyStack.length;
  107. var hash = getHash();
  108. if((hash === initialHash||window.location.href == initialHref)&&(hsl == 1)){
  109. // FIXME: could this ever be a forward button?
  110. // we can't clear it because we still need to check for forwards. Ugg.
  111. // clearInterval(this.locationTimer);
  112. handleBackButton();
  113. return;
  114. }
  115. // first check to see if we could have gone forward. We always halt on
  116. // a no-hash item.
  117. if(forwardStack.length > 0){
  118. if(forwardStack[forwardStack.length-1].urlHash === hash){
  119. handleForwardButton();
  120. return;
  121. }
  122. }
  123. // ok, that didn't work, try someplace back in the history stack
  124. if((hsl >= 2)&&(historyStack[hsl-2])){
  125. if(historyStack[hsl-2].urlHash === hash){
  126. handleBackButton();
  127. return;
  128. }
  129. }
  130. }
  131. };
  132. back.init = function(){
  133. //summary: Initializes the undo stack. This must be called from a <script>
  134. // block that lives inside the <body> tag to prevent bugs on IE.
  135. // description:
  136. // Only call this method before the page's DOM is finished loading. Otherwise
  137. // it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios,
  138. // in order for this method to work, dojo.back will need to be part of a build layer.
  139. if(dojo.byId("dj_history")){ return; } // prevent reinit
  140. var src = dojo.config["dojoIframeHistoryUrl"] || dojo.moduleUrl("dojo", "resources/iframe_history.html");
  141. if (dojo._postLoad) {
  142. console.error("dojo.back.init() must be called before the DOM has loaded. "
  143. + "If using xdomain loading or djConfig.debugAtAllCosts, include dojo.back "
  144. + "in a build layer.");
  145. } else {
  146. document.write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="' + src + '"></iframe>');
  147. }
  148. };
  149. back.setInitialState = function(/*Object*/args){
  150. //summary:
  151. // Sets the state object and back callback for the very first page
  152. // that is loaded.
  153. //description:
  154. // It is recommended that you call this method as part of an event
  155. // listener that is registered via dojo.addOnLoad().
  156. //args: Object
  157. // See the addToHistory() function for the list of valid args properties.
  158. initialState = createState(initialHref, args, initialHash);
  159. };
  160. //FIXME: Make these doc comments not be awful. At least they're not wrong.
  161. //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
  162. //FIXME: is there a slight race condition in moz using change URL with the timer check and when
  163. // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
  164. /*=====
  165. dojo.__backArgs = function(kwArgs){
  166. // back: Function?
  167. // A function to be called when this state is reached via the user
  168. // clicking the back button.
  169. // forward: Function?
  170. // Upon return to this state from the "back, forward" combination
  171. // of navigation steps, this function will be called. Somewhat
  172. // analgous to the semantic of an "onRedo" event handler.
  173. // changeUrl: Boolean?|String?
  174. // Boolean indicating whether or not to create a unique hash for
  175. // this state. If a string is passed instead, it is used as the
  176. // hash.
  177. }
  178. =====*/
  179. back.addToHistory = function(/*dojo.__backArgs*/ args){
  180. // summary:
  181. // adds a state object (args) to the history list.
  182. // description:
  183. // To support getting back button notifications, the object
  184. // argument should implement a function called either "back",
  185. // "backButton", or "handle". The string "back" will be passed as
  186. // the first and only argument to this callback.
  187. //
  188. // To support getting forward button notifications, the object
  189. // argument should implement a function called either "forward",
  190. // "forwardButton", or "handle". The string "forward" will be
  191. // passed as the first and only argument to this callback.
  192. //
  193. // If you want the browser location string to change, define "changeUrl" on the object. If the
  194. // value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
  195. // identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
  196. // not evaluate to false, that value will be used as the fragment identifier. For example,
  197. // if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
  198. //
  199. // There are problems with using dojo.back with semantically-named fragment identifiers
  200. // ("hash values" on an URL). In most browsers it will be hard for dojo.back to know
  201. // distinguish a back from a forward event in those cases. For back/forward support to
  202. // work best, the fragment ID should always be a unique value (something using new Date().getTime()
  203. // for example). If you want to detect hash changes using semantic fragment IDs, then
  204. // consider using dojo.hash instead (in Dojo 1.4+).
  205. //
  206. // example:
  207. // | dojo.back.addToHistory({
  208. // | back: function(){ console.log('back pressed'); },
  209. // | forward: function(){ console.log('forward pressed'); },
  210. // | changeUrl: true
  211. // | });
  212. // BROWSER NOTES:
  213. // Safari 1.2:
  214. // back button "works" fine, however it's not possible to actually
  215. // DETECT that you've moved backwards by inspecting window.location.
  216. // Unless there is some other means of locating.
  217. // FIXME: perhaps we can poll on history.length?
  218. // Safari 2.0.3+ (and probably 1.3.2+):
  219. // works fine, except when changeUrl is used. When changeUrl is used,
  220. // Safari jumps all the way back to whatever page was shown before
  221. // the page that uses dojo.undo.browser support.
  222. // IE 5.5 SP2:
  223. // back button behavior is macro. It does not move back to the
  224. // previous hash value, but to the last full page load. This suggests
  225. // that the iframe is the correct way to capture the back button in
  226. // these cases.
  227. // Don't test this page using local disk for MSIE. MSIE will not create
  228. // a history list for iframe_history.html if served from a file: URL.
  229. // The XML served back from the XHR tests will also not be properly
  230. // created if served from local disk. Serve the test pages from a web
  231. // server to test in that browser.
  232. // IE 6.0:
  233. // same behavior as IE 5.5 SP2
  234. // Firefox 1.0+:
  235. // the back button will return us to the previous hash on the same
  236. // page, thereby not requiring an iframe hack, although we do then
  237. // need to run a timer to detect inter-page movement.
  238. //If addToHistory is called, then that means we prune the
  239. //forward stack -- the user went back, then wanted to
  240. //start a new forward path.
  241. forwardStack = [];
  242. var hash = null;
  243. var url = null;
  244. if(!historyIframe){
  245. if(dojo.config["useXDomain"] && !dojo.config["dojoIframeHistoryUrl"]){
  246. console.warn("dojo.back: When using cross-domain Dojo builds,"
  247. + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
  248. + " to the path on your domain to iframe_history.html");
  249. }
  250. historyIframe = window.frames["dj_history"];
  251. }
  252. if(!bookmarkAnchor){
  253. bookmarkAnchor = dojo.create("a", {style: {display: "none"}}, dojo.body());
  254. }
  255. if(args["changeUrl"]){
  256. hash = ""+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
  257. //If the current hash matches the new one, just replace the history object with
  258. //this new one. It doesn't make sense to track different state objects for the same
  259. //logical URL. This matches the browser behavior of only putting in one history
  260. //item no matter how many times you click on the same #hash link, at least in Firefox
  261. //and Safari, and there is no reliable way in those browsers to know if a #hash link
  262. //has been clicked on multiple times. So making this the standard behavior in all browsers
  263. //so that dojo.back's behavior is the same in all browsers.
  264. if(historyStack.length == 0 && initialState.urlHash == hash){
  265. initialState = createState(url, args, hash);
  266. return;
  267. }else if(historyStack.length > 0 && historyStack[historyStack.length - 1].urlHash == hash){
  268. historyStack[historyStack.length - 1] = createState(url, args, hash);
  269. return;
  270. }
  271. changingUrl = true;
  272. setTimeout(function() {
  273. setHash(hash);
  274. changingUrl = false;
  275. }, 1);
  276. bookmarkAnchor.href = hash;
  277. if(dojo.isIE){
  278. url = loadIframeHistory();
  279. var oldCB = args["back"]||args["backButton"]||args["handle"];
  280. //The function takes handleName as a parameter, in case the
  281. //callback we are overriding was "handle". In that case,
  282. //we will need to pass the handle name to handle.
  283. var tcb = function(handleName){
  284. if(getHash() != ""){
  285. setTimeout(function() { setHash(hash); }, 1);
  286. }
  287. //Use apply to set "this" to args, and to try to avoid memory leaks.
  288. oldCB.apply(this, [handleName]);
  289. };
  290. //Set interceptor function in the right place.
  291. if(args["back"]){
  292. args.back = tcb;
  293. }else if(args["backButton"]){
  294. args.backButton = tcb;
  295. }else if(args["handle"]){
  296. args.handle = tcb;
  297. }
  298. var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
  299. //The function takes handleName as a parameter, in case the
  300. //callback we are overriding was "handle". In that case,
  301. //we will need to pass the handle name to handle.
  302. var tfw = function(handleName){
  303. if(getHash() != ""){
  304. setHash(hash);
  305. }
  306. if(oldFW){ // we might not actually have one
  307. //Use apply to set "this" to args, and to try to avoid memory leaks.
  308. oldFW.apply(this, [handleName]);
  309. }
  310. };
  311. //Set interceptor function in the right place.
  312. if(args["forward"]){
  313. args.forward = tfw;
  314. }else if(args["forwardButton"]){
  315. args.forwardButton = tfw;
  316. }else if(args["handle"]){
  317. args.handle = tfw;
  318. }
  319. }else if(!dojo.isIE){
  320. // start the timer
  321. if(!locationTimer){
  322. locationTimer = setInterval(checkLocation, 200);
  323. }
  324. }
  325. }else{
  326. url = loadIframeHistory();
  327. }
  328. historyStack.push(createState(url, args, hash));
  329. };
  330. back._iframeLoaded = function(evt, ifrLoc){
  331. //summary:
  332. // private method. Do not call this directly.
  333. var query = getUrlQuery(ifrLoc.href);
  334. if(query == null){
  335. // alert("iframeLoaded");
  336. // we hit the end of the history, so we should go back
  337. if(historyStack.length == 1){
  338. handleBackButton();
  339. }
  340. return;
  341. }
  342. if(moveForward){
  343. // we were expecting it, so it's not either a forward or backward movement
  344. moveForward = false;
  345. return;
  346. }
  347. //Check the back stack first, since it is more likely.
  348. //Note that only one step back or forward is supported.
  349. if(historyStack.length >= 2 && query == getUrlQuery(historyStack[historyStack.length-2].url)){
  350. handleBackButton();
  351. }else if(forwardStack.length > 0 && query == getUrlQuery(forwardStack[forwardStack.length-1].url)){
  352. handleForwardButton();
  353. }
  354. };
  355. })();
  356. }
  357. if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  358. dojo._hasResource["dojo.regexp"] = true;
  359. dojo.provide("dojo.regexp");
  360. dojo.getObject("regexp", true, dojo);
  361. /*=====
  362. dojo.regexp = {
  363. // summary: Regular expressions and Builder resources
  364. };
  365. =====*/
  366. dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
  367. // summary:
  368. // Adds escape sequences for special characters in regular expressions
  369. // except:
  370. // a String with special characters to be left unescaped
  371. return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
  372. if(except && except.indexOf(ch) != -1){
  373. return ch;
  374. }
  375. return "\\" + ch;
  376. }); // String
  377. };
  378. dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
  379. // summary:
  380. // Builds a regular expression that groups subexpressions
  381. // description:
  382. // A utility function used by some of the RE generators. The
  383. // subexpressions are constructed by the function, re, in the second
  384. // parameter. re builds one subexpression for each elem in the array
  385. // a, in the first parameter. Returns a string for a regular
  386. // expression that groups all the subexpressions.
  387. // arr:
  388. // A single value or an array of values.
  389. // re:
  390. // A function. Takes one parameter and converts it to a regular
  391. // expression.
  392. // nonCapture:
  393. // If true, uses non-capturing match, otherwise matches are retained
  394. // by regular expression. Defaults to false
  395. // case 1: a is a single value.
  396. if(!(arr instanceof Array)){
  397. return re(arr); // String
  398. }
  399. // case 2: a is an array
  400. var b = [];
  401. for(var i = 0; i < arr.length; i++){
  402. // convert each elem to a RE
  403. b.push(re(arr[i]));
  404. }
  405. // join the REs as alternatives in a RE group.
  406. return dojo.regexp.group(b.join("|"), nonCapture); // String
  407. };
  408. dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
  409. // summary:
  410. // adds group match to expression
  411. // nonCapture:
  412. // If true, uses non-capturing match, otherwise matches are retained
  413. // by regular expression.
  414. return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
  415. };
  416. }
  417. if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  418. dojo._hasResource["dojo.cookie"] = true;
  419. dojo.provide("dojo.cookie");
  420. /*=====
  421. dojo.__cookieProps = function(){
  422. // expires: Date|String|Number?
  423. // If a number, the number of days from today at which the cookie
  424. // will expire. If a date, the date past which the cookie will expire.
  425. // If expires is in the past, the cookie will be deleted.
  426. // If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
  427. // path: String?
  428. // The path to use for the cookie.
  429. // domain: String?
  430. // The domain to use for the cookie.
  431. // secure: Boolean?
  432. // Whether to only send the cookie on secure connections
  433. this.expires = expires;
  434. this.path = path;
  435. this.domain = domain;
  436. this.secure = secure;
  437. }
  438. =====*/
  439. dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
  440. // summary:
  441. // Get or set a cookie.
  442. // description:
  443. // If one argument is passed, returns the value of the cookie
  444. // For two or more arguments, acts as a setter.
  445. // name:
  446. // Name of the cookie
  447. // value:
  448. // Value for the cookie
  449. // props:
  450. // Properties for the cookie
  451. // example:
  452. // set a cookie with the JSON-serialized contents of an object which
  453. // will expire 5 days from now:
  454. // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
  455. //
  456. // example:
  457. // de-serialize a cookie back into a JavaScript object:
  458. // | var config = dojo.fromJson(dojo.cookie("configObj"));
  459. //
  460. // example:
  461. // delete a cookie:
  462. // | dojo.cookie("configObj", null, {expires: -1});
  463. var c = document.cookie;
  464. if(arguments.length == 1){
  465. var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
  466. return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
  467. }else{
  468. props = props || {};
  469. // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
  470. var exp = props.expires;
  471. if(typeof exp == "number"){
  472. var d = new Date();
  473. d.setTime(d.getTime() + exp*24*60*60*1000);
  474. exp = props.expires = d;
  475. }
  476. if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
  477. value = encodeURIComponent(value);
  478. var updatedCookie = name + "=" + value, propName;
  479. for(propName in props){
  480. updatedCookie += "; " + propName;
  481. var propValue = props[propName];
  482. if(propValue !== true){ updatedCookie += "=" + propValue; }
  483. }
  484. document.cookie = updatedCookie;
  485. }
  486. };
  487. dojo.cookie.isSupported = function(){
  488. // summary:
  489. // Use to determine if the current browser supports cookies or not.
  490. //
  491. // Returns true if user allows cookies.
  492. // Returns false if user doesn't allow cookies.
  493. if(!("cookieEnabled" in navigator)){
  494. this("__djCookieTest__", "CookiesAllowed");
  495. navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
  496. if(navigator.cookieEnabled){
  497. this("__djCookieTest__", "", {expires: -1});
  498. }
  499. }
  500. return navigator.cookieEnabled;
  501. };
  502. }
  503. if(!dojo._hasResource["dojo.date"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  504. dojo._hasResource["dojo.date"] = true;
  505. dojo.provide("dojo.date");
  506. dojo.getObject("date", true, dojo);
  507. /*=====
  508. dojo.date = {
  509. // summary: Date manipulation utilities
  510. }
  511. =====*/
  512. dojo.date.getDaysInMonth = function(/*Date*/dateObject){
  513. // summary:
  514. // Returns the number of days in the month used by dateObject
  515. var month = dateObject.getMonth();
  516. var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  517. if(month == 1 && dojo.date.isLeapYear(dateObject)){ return 29; } // Number
  518. return days[month]; // Number
  519. };
  520. dojo.date.isLeapYear = function(/*Date*/dateObject){
  521. // summary:
  522. // Determines if the year of the dateObject is a leap year
  523. // description:
  524. // Leap years are years with an additional day YYYY-02-29, where the
  525. // year number is a multiple of four with the following exception: If
  526. // a year is a multiple of 100, then it is only a leap year if it is
  527. // also a multiple of 400. For example, 1900 was not a leap year, but
  528. // 2000 is one.
  529. var year = dateObject.getFullYear();
  530. return !(year%400) || (!(year%4) && !!(year%100)); // Boolean
  531. };
  532. // FIXME: This is not localized
  533. dojo.date.getTimezoneName = function(/*Date*/dateObject){
  534. // summary:
  535. // Get the user's time zone as provided by the browser
  536. // dateObject:
  537. // Needed because the timezone may vary with time (daylight savings)
  538. // description:
  539. // Try to get time zone info from toString or toLocaleString method of
  540. // the Date object -- UTC offset is not a time zone. See
  541. // http://www.twinsun.com/tz/tz-link.htm Note: results may be
  542. // inconsistent across browsers.
  543. var str = dateObject.toString(); // Start looking in toString
  544. var tz = ''; // The result -- return empty string if nothing found
  545. var match;
  546. // First look for something in parentheses -- fast lookup, no regex
  547. var pos = str.indexOf('(');
  548. if(pos > -1){
  549. tz = str.substring(++pos, str.indexOf(')'));
  550. }else{
  551. // If at first you don't succeed ...
  552. // If IE knows about the TZ, it appears before the year
  553. // Capital letters or slash before a 4-digit year
  554. // at the end of string
  555. var pat = /([A-Z\/]+) \d{4}$/;
  556. if((match = str.match(pat))){
  557. tz = match[1];
  558. }else{
  559. // Some browsers (e.g. Safari) glue the TZ on the end
  560. // of toLocaleString instead of putting it in toString
  561. str = dateObject.toLocaleString();
  562. // Capital letters or slash -- end of string,
  563. // after space
  564. pat = / ([A-Z\/]+)$/;
  565. if((match = str.match(pat))){
  566. tz = match[1];
  567. }
  568. }
  569. }
  570. // Make sure it doesn't somehow end up return AM or PM
  571. return (tz == 'AM' || tz == 'PM') ? '' : tz; // String
  572. };
  573. // Utility methods to do arithmetic calculations with Dates
  574. dojo.date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){
  575. // summary:
  576. // Compare two date objects by date, time, or both.
  577. // description:
  578. // Returns 0 if equal, positive if a > b, else negative.
  579. // date1:
  580. // Date object
  581. // date2:
  582. // Date object. If not specified, the current Date is used.
  583. // portion:
  584. // A string indicating the "date" or "time" portion of a Date object.
  585. // Compares both "date" and "time" by default. One of the following:
  586. // "date", "time", "datetime"
  587. // Extra step required in copy for IE - see #3112
  588. date1 = new Date(+date1);
  589. date2 = new Date(+(date2 || new Date()));
  590. if(portion == "date"){
  591. // Ignore times and compare dates.
  592. date1.setHours(0, 0, 0, 0);
  593. date2.setHours(0, 0, 0, 0);
  594. }else if(portion == "time"){
  595. // Ignore dates and compare times.
  596. date1.setFullYear(0, 0, 0);
  597. date2.setFullYear(0, 0, 0);
  598. }
  599. if(date1 > date2){ return 1; } // int
  600. if(date1 < date2){ return -1; } // int
  601. return 0; // int
  602. };
  603. dojo.date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){
  604. // summary:
  605. // Add to a Date in intervals of different size, from milliseconds to years
  606. // date: Date
  607. // Date object to start with
  608. // interval:
  609. // A string representing the interval. One of the following:
  610. // "year", "month", "day", "hour", "minute", "second",
  611. // "millisecond", "quarter", "week", "weekday"
  612. // amount:
  613. // How much to add to the date.
  614. var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112)
  615. var fixOvershoot = false;
  616. var property = "Date";
  617. switch(interval){
  618. case "day":
  619. break;
  620. case "weekday":
  621. //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo.cldr.supplemental
  622. // Divide the increment time span into weekspans plus leftover days
  623. // e.g., 8 days is one 5-day weekspan / and two leftover days
  624. // Can't have zero leftover days, so numbers divisible by 5 get
  625. // a days value of 5, and the remaining days make up the number of weeks
  626. var days, weeks;
  627. var mod = amount % 5;
  628. if(!mod){
  629. days = (amount > 0) ? 5 : -5;
  630. weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
  631. }else{
  632. days = mod;
  633. weeks = parseInt(amount/5);
  634. }
  635. // Get weekday value for orig date param
  636. var strt = date.getDay();
  637. // Orig date is Sat / positive incrementer
  638. // Jump over Sun
  639. var adj = 0;
  640. if(strt == 6 && amount > 0){
  641. adj = 1;
  642. }else if(strt == 0 && amount < 0){
  643. // Orig date is Sun / negative incrementer
  644. // Jump back over Sat
  645. adj = -1;
  646. }
  647. // Get weekday val for the new date
  648. var trgt = strt + days;
  649. // New date is on Sat or Sun
  650. if(trgt == 0 || trgt == 6){
  651. adj = (amount > 0) ? 2 : -2;
  652. }
  653. // Increment by number of weeks plus leftover days plus
  654. // weekend adjustments
  655. amount = (7 * weeks) + days + adj;
  656. break;
  657. case "year":
  658. property = "FullYear";
  659. // Keep increment/decrement from 2/29 out of March
  660. fixOvershoot = true;
  661. break;
  662. case "week":
  663. amount *= 7;
  664. break;
  665. case "quarter":
  666. // Naive quarter is just three months
  667. amount *= 3;
  668. // fallthrough...
  669. case "month":
  670. // Reset to last day of month if you overshoot
  671. fixOvershoot = true;
  672. property = "Month";
  673. break;
  674. // case "hour":
  675. // case "minute":
  676. // case "second":
  677. // case "millisecond":
  678. default:
  679. property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
  680. }
  681. if(property){
  682. sum["set"+property](sum["get"+property]()+amount);
  683. }
  684. if(fixOvershoot && (sum.getDate() < date.getDate())){
  685. sum.setDate(0);
  686. }
  687. return sum; // Date
  688. };
  689. dojo.date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){
  690. // summary:
  691. // Get the difference in a specific unit of time (e.g., number of
  692. // months, weeks, days, etc.) between two dates, rounded to the
  693. // nearest integer.
  694. // date1:
  695. // Date object
  696. // date2:
  697. // Date object. If not specified, the current Date is used.
  698. // interval:
  699. // A string representing the interval. One of the following:
  700. // "year", "month", "day", "hour", "minute", "second",
  701. // "millisecond", "quarter", "week", "weekday"
  702. // Defaults to "day".
  703. date2 = date2 || new Date();
  704. interval = interval || "day";
  705. var yearDiff = date2.getFullYear() - date1.getFullYear();
  706. var delta = 1; // Integer return value
  707. switch(interval){
  708. case "quarter":
  709. var m1 = date1.getMonth();
  710. var m2 = date2.getMonth();
  711. // Figure out which quarter the months are in
  712. var q1 = Math.floor(m1/3) + 1;
  713. var q2 = Math.floor(m2/3) + 1;
  714. // Add quarters for any year difference between the dates
  715. q2 += (yearDiff * 4);
  716. delta = q2 - q1;
  717. break;
  718. case "weekday":
  719. var days = Math.round(dojo.date.difference(date1, date2, "day"));
  720. var weeks = parseInt(dojo.date.difference(date1, date2, "week"));
  721. var mod = days % 7;
  722. // Even number of weeks
  723. if(mod == 0){
  724. days = weeks*5;
  725. }else{
  726. // Weeks plus spare change (< 7 days)
  727. var adj = 0;
  728. var aDay = date1.getDay();
  729. var bDay = date2.getDay();
  730. weeks = parseInt(days/7);
  731. mod = days % 7;
  732. // Mark the date advanced by the number of
  733. // round weeks (may be zero)
  734. var dtMark = new Date(date1);
  735. dtMark.setDate(dtMark.getDate()+(weeks*7));
  736. var dayMark = dtMark.getDay();
  737. // Spare change days -- 6 or less
  738. if(days > 0){
  739. switch(true){
  740. // Range starts on Sat
  741. case aDay == 6:
  742. adj = -1;
  743. break;
  744. // Range starts on Sun
  745. case aDay == 0:
  746. adj = 0;
  747. break;
  748. // Range ends on Sat
  749. case bDay == 6:
  750. adj = -1;
  751. break;
  752. // Range ends on Sun
  753. case bDay == 0:
  754. adj = -2;
  755. break;
  756. // Range contains weekend
  757. case (dayMark + mod) > 5:
  758. adj = -2;
  759. }
  760. }else if(days < 0){
  761. switch(true){
  762. // Range starts on Sat
  763. case aDay == 6:
  764. adj = 0;
  765. break;
  766. // Range starts on Sun
  767. case aDay == 0:
  768. adj = 1;
  769. break;
  770. // Range ends on Sat
  771. case bDay == 6:
  772. adj = 2;
  773. break;
  774. // Range ends on Sun
  775. case bDay == 0:
  776. adj = 1;
  777. break;
  778. // Range contains weekend
  779. case (dayMark + mod) < 0:
  780. adj = 2;
  781. }
  782. }
  783. days += adj;
  784. days -= (weeks*2);
  785. }
  786. delta = days;
  787. break;
  788. case "year":
  789. delta = yearDiff;
  790. break;
  791. case "month":
  792. delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12);
  793. break;
  794. case "week":
  795. // Truncate instead of rounding
  796. // Don't use Math.floor -- value may be negative
  797. delta = parseInt(dojo.date.difference(date1, date2, "day")/7);
  798. break;
  799. case "day":
  800. delta /= 24;
  801. // fallthrough
  802. case "hour":
  803. delta /= 60;
  804. // fallthrough
  805. case "minute":
  806. delta /= 60;
  807. // fallthrough
  808. case "second":
  809. delta /= 1000;
  810. // fallthrough
  811. case "millisecond":
  812. delta *= date2.getTime() - date1.getTime();
  813. }
  814. // Round for fractional values and DST leaps
  815. return Math.round(delta); // Number (integer)
  816. };
  817. }
  818. if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  819. dojo._hasResource["dojo.i18n"] = true;
  820. dojo.provide("dojo.i18n");
  821. dojo.getObject("i18n", true, dojo);
  822. /*=====
  823. dojo.i18n = {
  824. // summary: Utility classes to enable loading of resources for internationalization (i18n)
  825. };
  826. =====*/
  827. // when using a real AMD loader, dojo.i18n.getLocalization is already defined by dojo/lib/backCompat
  828. dojo.i18n.getLocalization = dojo.i18n.getLocalization || function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
  829. // summary:
  830. // Returns an Object containing the localization for a given resource
  831. // bundle in a package, matching the specified locale.
  832. // description:
  833. // Returns a hash containing name/value pairs in its prototypesuch
  834. // that values can be easily overridden. Throws an exception if the
  835. // bundle is not found. Bundle must have already been loaded by
  836. // `dojo.requireLocalization()` or by a build optimization step. NOTE:
  837. // try not to call this method as part of an object property
  838. // definition (`var foo = { bar: dojo.i18n.getLocalization() }`). In
  839. // some loading situations, the bundle may not be available in time
  840. // for the object definition. Instead, call this method inside a
  841. // function that is run after all modules load or the page loads (like
  842. // in `dojo.addOnLoad()`), or in a widget lifecycle method.
  843. // packageName:
  844. // package which is associated with this resource
  845. // bundleName:
  846. // the base filename of the resource bundle (without the ".js" suffix)
  847. // locale:
  848. // the variant to load (optional). By default, the locale defined by
  849. // the host environment: dojo.locale
  850. locale = dojo.i18n.normalizeLocale(locale);
  851. // look for nearest locale match
  852. var elements = locale.split('-');
  853. var module = [packageName,"nls",bundleName].join('.');
  854. var bundle = dojo._loadedModules[module];
  855. if(bundle){
  856. var localization;
  857. for(var i = elements.length; i > 0; i--){
  858. var loc = elements.slice(0, i).join('_');
  859. if(bundle[loc]){
  860. localization = bundle[loc];
  861. break;
  862. }
  863. }
  864. if(!localization){
  865. localization = bundle.ROOT;
  866. }
  867. // make a singleton prototype so that the caller won't accidentally change the values globally
  868. if(localization){
  869. var clazz = function(){};
  870. clazz.prototype = localization;
  871. return new clazz(); // Object
  872. }
  873. }
  874. throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
  875. };
  876. dojo.i18n.normalizeLocale = function(/*String?*/locale){
  877. // summary:
  878. // Returns canonical form of locale, as used by Dojo.
  879. //
  880. // description:
  881. // All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
  882. // If no locale is specified, the dojo.locale is returned. dojo.locale is defined by
  883. // the user agent's locale unless overridden by djConfig.
  884. var result = locale ? locale.toLowerCase() : dojo.locale;
  885. if(result == "root"){
  886. result = "ROOT";
  887. }
  888. return result; // String
  889. };
  890. dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
  891. // summary:
  892. // See dojo.requireLocalization()
  893. // description:
  894. // Called by the bootstrap, but factored out so that it is only
  895. // included in the build when needed.
  896. var targetLocale = dojo.i18n.normalizeLocale(locale);
  897. var bundlePackage = [moduleName, "nls", bundleName].join(".");
  898. // NOTE:
  899. // When loading these resources, the packaging does not match what is
  900. // on disk. This is an implementation detail, as this is just a
  901. // private data structure to hold the loaded resources. e.g.
  902. // `tests/hello/nls/en-us/salutations.js` is loaded as the object
  903. // `tests.hello.nls.salutations.en_us={...}` The structure on disk is
  904. // intended to be most convenient for developers and translators, but
  905. // in memory it is more logical and efficient to store in a different
  906. // order. Locales cannot use dashes, since the resulting path will
  907. // not evaluate as valid JS, so we translate them to underscores.
  908. //Find the best-match locale to load if we have available flat locales.
  909. var bestLocale = "";
  910. if(availableFlatLocales){
  911. var flatLocales = availableFlatLocales.split(",");
  912. for(var i = 0; i < flatLocales.length; i++){
  913. //Locale must match from start of string.
  914. //Using ["indexOf"] so customBase builds do not see
  915. //this as a dojo._base.array dependency.
  916. if(targetLocale["indexOf"](flatLocales[i]) == 0){
  917. if(flatLocales[i].length > bestLocale.length){
  918. bestLocale = flatLocales[i];
  919. }
  920. }
  921. }
  922. if(!bestLocale){
  923. bestLocale = "ROOT";
  924. }
  925. }
  926. //See if the desired locale is already loaded.
  927. var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
  928. var bundle = dojo._loadedModules[bundlePackage];
  929. var localizedBundle = null;
  930. if(bundle){
  931. if(dojo.config.localizationComplete && bundle._built){return;}
  932. var jsLoc = tempLocale.replace(/-/g, '_');
  933. var translationPackage = bundlePackage+"."+jsLoc;
  934. localizedBundle = dojo._loadedModules[translationPackage];
  935. }
  936. if(!localizedBundle){
  937. bundle = dojo["provide"](bundlePackage);
  938. var syms = dojo._getModuleSymbols(moduleName);
  939. var modpath = syms.concat("nls").join("/");
  940. var parent;
  941. dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
  942. var jsLoc = loc.replace(/-/g, '_');
  943. var translationPackage = bundlePackage + "." + jsLoc;
  944. var loaded = false;
  945. if(!dojo._loadedModules[translationPackage]){
  946. // Mark loaded whether it's found or not, so that further load attempts will not be made
  947. dojo["provide"](translationPackage);
  948. var module = [modpath];
  949. if(loc != "ROOT"){module.push(loc);}
  950. module.push(bundleName);
  951. var filespec = module.join("/") + '.js';
  952. loaded = dojo._loadPath(filespec, null, function(hash){
  953. hash = hash.root || hash;
  954. // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
  955. var clazz = function(){};
  956. clazz.prototype = parent;
  957. bundle[jsLoc] = new clazz();
  958. for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
  959. });
  960. }else{
  961. loaded = true;
  962. }
  963. if(loaded && bundle[jsLoc]){
  964. parent = bundle[jsLoc];
  965. }else{
  966. bundle[jsLoc] = parent;
  967. }
  968. if(availableFlatLocales){
  969. //Stop the locale path searching if we know the availableFlatLocales, since
  970. //the first call to this function will load the only bundle that is needed.
  971. return true;
  972. }
  973. });
  974. }
  975. //Save the best locale bundle as the target locale bundle when we know the
  976. //the available bundles.
  977. if(availableFlatLocales && targetLocale != bestLocale){
  978. bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
  979. }
  980. };
  981. (function(){
  982. // If other locales are used, dojo.requireLocalization should load them as
  983. // well, by default.
  984. //
  985. // Override dojo.requireLocalization to do load the default bundle, then
  986. // iterate through the extraLocale list and load those translations as
  987. // well, unless a particular locale was requested.
  988. var extra = dojo.config.extraLocale;
  989. if(extra){
  990. if(!extra instanceof Array){
  991. extra = [extra];
  992. }
  993. var req = dojo.i18n._requireLocalization;
  994. dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
  995. req(m,b,locale, availableFlatLocales);
  996. if(locale){return;}
  997. for(var i=0; i<extra.length; i++){
  998. req(m,b,extra[i], availableFlatLocales);
  999. }
  1000. };
  1001. }
  1002. })();
  1003. dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
  1004. // summary:
  1005. // A helper method to assist in searching for locale-based resources.
  1006. // Will iterate through the variants of a particular locale, either up
  1007. // or down, executing a callback function. For example, "en-us" and
  1008. // true will try "en-us" followed by "en" and finally "ROOT".
  1009. locale = dojo.i18n.normalizeLocale(locale);
  1010. var elements = locale.split('-');
  1011. var searchlist = [];
  1012. for(var i = elements.length; i > 0; i--){
  1013. searchlist.push(elements.slice(0, i).join('-'));
  1014. }
  1015. searchlist.push(false);
  1016. if(down){searchlist.reverse();}
  1017. for(var j = searchlist.length - 1; j >= 0; j--){
  1018. var loc = searchlist[j] || "ROOT";
  1019. var stop = searchFunc(loc);
  1020. if(stop){ break; }
  1021. }
  1022. };
  1023. dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
  1024. // summary:
  1025. // Load built, flattened resource bundles, if available for all
  1026. // locales used in the page. Only called by built layer files.
  1027. function preload(locale){
  1028. locale = dojo.i18n.normalizeLocale(locale);
  1029. dojo.i18n._searchLocalePath(locale, true, function(loc){
  1030. for(var i=0; i<localesGenerated.length;i++){
  1031. if(localesGenerated[i] == loc){
  1032. dojo["require"](bundlePrefix+"_"+loc);
  1033. return true; // Boolean
  1034. }
  1035. }
  1036. return false; // Boolean
  1037. });
  1038. }
  1039. preload();
  1040. var extra = dojo.config.extraLocale||[];
  1041. for(var i=0; i<extra.length; i++){
  1042. preload(extra[i]);
  1043. }
  1044. };
  1045. }
  1046. if(!dojo._hasResource["dojo.cldr.supplemental"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1047. dojo._hasResource["dojo.cldr.supplemental"] = true;
  1048. dojo.provide("dojo.cldr.supplemental");
  1049. dojo.getObject("cldr.supplemental", true, dojo);
  1050. dojo.cldr.supplemental.getFirstDayOfWeek = function(/*String?*/locale){
  1051. // summary: Returns a zero-based index for first day of the week
  1052. // description:
  1053. // Returns a zero-based index for first day of the week, as used by the local (Gregorian) calendar.
  1054. // e.g. Sunday (returns 0), or Monday (returns 1)
  1055. // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/firstDay
  1056. var firstDay = {/*default is 1=Monday*/
  1057. mv:5,
  1058. 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,
  1059. ly:6,ma:6,om:6,qa:6,sa:6,sd:6,so:6,sy:6,tn:6,ye:6,
  1060. ar:0,as:0,az:0,bw:0,ca:0,cn:0,fo:0,ge:0,gl:0,gu:0,hk:0,
  1061. il:0,'in':0,jm:0,jp:0,kg:0,kr:0,la:0,mh:0,mn:0,mo:0,mp:0,
  1062. mt:0,nz:0,ph:0,pk:0,sg:0,th:0,tt:0,tw:0,um:0,us:0,uz:0,
  1063. vi:0,zw:0
  1064. // variant. do not use? gb:0,
  1065. };
  1066. var country = dojo.cldr.supplemental._region(locale);
  1067. var dow = firstDay[country];
  1068. return (dow === undefined) ? 1 : dow; /*Number*/
  1069. };
  1070. dojo.cldr.supplemental._region = function(/*String?*/locale){
  1071. locale = dojo.i18n.normalizeLocale(locale);
  1072. var tags = locale.split('-');
  1073. var region = tags[1];
  1074. if(!region){
  1075. // IE often gives language only (#2269)
  1076. // Arbitrary mappings of language-only locales to a country:
  1077. region = {de:"de", en:"us", es:"es", fi:"fi", fr:"fr", he:"il", hu:"hu", it:"it",
  1078. ja:"jp", ko:"kr", nl:"nl", pt:"br", sv:"se", zh:"cn"}[tags[0]];
  1079. }else if(region.length == 4){
  1080. // The ISO 3166 country code is usually in the second position, unless a
  1081. // 4-letter script is given. See http://www.ietf.org/rfc/rfc4646.txt
  1082. region = tags[2];
  1083. }
  1084. return region;
  1085. };
  1086. dojo.cldr.supplemental.getWeekend = function(/*String?*/locale){
  1087. // summary: Returns a hash containing the start and end days of the weekend
  1088. // description:
  1089. // Returns a hash containing the start and end days of the weekend according to local custom using locale,
  1090. // or by default in the user's locale.
  1091. // e.g. {start:6, end:0}
  1092. // from http://www.unicode.org/cldr/data/common/supplemental/supplementalData.xml:supplementalData/weekData/weekend{Start,End}
  1093. var weekendStart = {/*default is 6=Saturday*/
  1094. 'in':0,
  1095. af:4,dz:4,ir:4,om:4,sa:4,ye:4,
  1096. 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
  1097. };
  1098. var weekendEnd = {/*default is 0=Sunday*/
  1099. af:5,dz:5,ir:5,om:5,sa:5,ye:5,
  1100. 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
  1101. };
  1102. var country = dojo.cldr.supplemental._region(locale);
  1103. var start = weekendStart[country];
  1104. var end = weekendEnd[country];
  1105. if(start === undefined){start=6;}
  1106. if(end === undefined){end=0;}
  1107. return {start:start, end:end}; /*Object {start,end}*/
  1108. };
  1109. }
  1110. if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1111. dojo._hasResource["dojo.string"] = true;
  1112. dojo.provide("dojo.string");
  1113. dojo.getObject("string", true, dojo);
  1114. /*=====
  1115. dojo.string = {
  1116. // summary: String utilities for Dojo
  1117. };
  1118. =====*/
  1119. dojo.string.rep = function(/*String*/str, /*Integer*/num){
  1120. // summary:
  1121. // Efficiently replicate a string `n` times.
  1122. // str:
  1123. // the string to replicate
  1124. // num:
  1125. // number of times to replicate the string
  1126. if(num <= 0 || !str){ return ""; }
  1127. var buf = [];
  1128. for(;;){
  1129. if(num & 1){
  1130. buf.push(str);
  1131. }
  1132. if(!(num >>= 1)){ break; }
  1133. str += str;
  1134. }
  1135. return buf.join(""); // String
  1136. };
  1137. dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
  1138. // summary:
  1139. // Pad a string to guarantee that it is at least `size` length by
  1140. // filling with the character `ch` at either the start or end of the
  1141. // string. Pads at the start, by default.
  1142. // text:
  1143. // the string to pad
  1144. // size:
  1145. // length to provide padding
  1146. // ch:
  1147. // character to pad, defaults to '0'
  1148. // end:
  1149. // adds padding at the end if true, otherwise pads at start
  1150. // example:
  1151. // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
  1152. // | dojo.string.pad("Dojo", 10, "+", true);
  1153. if(!ch){
  1154. ch = '0';
  1155. }
  1156. var out = String(text),
  1157. pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
  1158. return end ? out + pad : pad + out; // String
  1159. };
  1160. dojo.string.substitute = function( /*String*/ template,
  1161. /*Object|Array*/map,
  1162. /*Function?*/ transform,
  1163. /*Object?*/ thisObject){
  1164. // summary:
  1165. // Performs parameterized substitutions on a string. Throws an
  1166. // exception if any parameter is unmatched.
  1167. // template:
  1168. // a string with expressions in the form `${key}` to be replaced or
  1169. // `${key:format}` which specifies a format function. keys are case-sensitive.
  1170. // map:
  1171. // hash to search for substitutions
  1172. // transform:
  1173. // a function to process all parameters before substitution takes
  1174. // place, e.g. mylib.encodeXML
  1175. // thisObject:
  1176. // where to look for optional format function; default to the global
  1177. // namespace
  1178. // example:
  1179. // Substitutes two expressions in a string from an Array or Object
  1180. // | // returns "File 'foo.html' is not found in directory '/temp'."
  1181. // | // by providing substitution data in an Array
  1182. // | dojo.string.substitute(
  1183. // | "File '${0}' is not found in directory '${1}'.",
  1184. // | ["foo.html","/temp"]
  1185. // | );
  1186. // |
  1187. // | // also returns "File 'foo.html' is not found in directory '/temp'."
  1188. // | // but provides substitution data in an Object structure. Dotted
  1189. // | // notation may be used to traverse the structure.
  1190. // | dojo.string.substitute(
  1191. // | "File '${name}' is not found in directory '${info.dir}'.",
  1192. // | { name: "foo.html", info: { dir: "/temp" } }
  1193. // | );
  1194. // example:
  1195. // Use a transform function to modify the values:
  1196. // | // returns "file 'foo.html' is not found in directory '/temp'."
  1197. // | dojo.string.substitute(
  1198. // | "${0} is not found in ${1}.",
  1199. // | ["foo.html","/temp"],
  1200. // | function(str){
  1201. // | // try to figure out the type
  1202. // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
  1203. // | return prefix + " '" + str + "'";
  1204. // | }
  1205. // | );
  1206. // example:
  1207. // Use a formatter
  1208. // | // returns "thinger -- howdy"
  1209. // | dojo.string.substitute(
  1210. // | "${0:postfix}", ["thinger"], null, {
  1211. // | postfix: function(value, key){
  1212. // | return value + " -- howdy";
  1213. // | }
  1214. // | }
  1215. // | );
  1216. thisObject = thisObject || dojo.global;
  1217. transform = transform ?
  1218. dojo.hitch(thisObject, transform) : function(v){ return v; };
  1219. return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
  1220. function(match, key, format){
  1221. var value = dojo.getObject(key, false, map);
  1222. if(format){
  1223. value = dojo.getObject(format, false, thisObject).call(thisObject, value, key);
  1224. }
  1225. return transform(value, key).toString();
  1226. }); // String
  1227. };
  1228. /*=====
  1229. dojo.string.trim = function(str){
  1230. // summary:
  1231. // Trims whitespace from both sides of the string
  1232. // str: String
  1233. // String to be trimmed
  1234. // returns: String
  1235. // Returns the trimmed string
  1236. // description:
  1237. // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
  1238. // The short yet performant version of this function is dojo.trim(),
  1239. // which is part of Dojo base. Uses String.prototype.trim instead, if available.
  1240. return ""; // String
  1241. }
  1242. =====*/
  1243. dojo.string.trim = String.prototype.trim ?
  1244. dojo.trim : // aliasing to the native function
  1245. function(str){
  1246. str = str.replace(/^\s+/, '');
  1247. for(var i = str.length - 1; i >= 0; i--){
  1248. if(/\S/.test(str.charAt(i))){
  1249. str = str.substring(0, i + 1);
  1250. break;
  1251. }
  1252. }
  1253. return str;
  1254. };
  1255. }
  1256. if(!dojo._hasResource["dojo.date.locale"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1257. dojo._hasResource["dojo.date.locale"] = true;
  1258. dojo.provide("dojo.date.locale");
  1259. dojo.getObject("date.locale", true, dojo);
  1260. // Localization methods for Date. Honor local customs using locale-dependent dojo.cldr data.
  1261. // Load the bundles containing localization information for
  1262. // names and formats
  1263. //NOTE: Everything in this module assumes Gregorian calendars.
  1264. // Other calendars will be implemented in separate modules.
  1265. (function(){
  1266. // Format a pattern without literals
  1267. function formatPattern(dateObject, bundle, options, pattern){
  1268. return pattern.replace(/([a-z])\1*/ig, function(match){
  1269. var s, pad,
  1270. c = match.charAt(0),
  1271. l = match.length,
  1272. widthList = ["abbr", "wide", "narrow"];
  1273. switch(c){
  1274. case 'G':
  1275. s = bundle[(l < 4) ? "eraAbbr" : "eraNames"][dateObject.getFullYear() < 0 ? 0 : 1];
  1276. break;
  1277. case 'y':
  1278. s = dateObject.getFullYear();
  1279. switch(l){
  1280. case 1:
  1281. break;
  1282. case 2:
  1283. if(!options.fullYear){
  1284. s = String(s); s = s.substr(s.length - 2);
  1285. break;
  1286. }
  1287. // fallthrough
  1288. default:
  1289. pad = true;
  1290. }
  1291. break;
  1292. case 'Q':
  1293. case 'q':
  1294. s = Math.ceil((dateObject.getMonth()+1)/3);
  1295. // switch(l){
  1296. // case 1: case 2:
  1297. pad = true;
  1298. // break;
  1299. // case 3: case 4: // unimplemented
  1300. // }
  1301. break;
  1302. case 'M':
  1303. var m = dateObject.getMonth();
  1304. if(l<3){
  1305. s = m+1; pad = true;
  1306. }else{
  1307. var propM = ["months", "format", widthList[l-3]].join("-");
  1308. s = bundle[propM][m];
  1309. }
  1310. break;
  1311. case 'w':
  1312. var firstDay = 0;
  1313. s = dojo.date.locale._getWeekOfYear(dateObject, firstDay); pad = true;
  1314. break;
  1315. case 'd':
  1316. s = dateObject.getDate(); pad = true;
  1317. break;
  1318. case 'D':
  1319. s = dojo.date.locale._getDayOfYear(dateObject); pad = true;
  1320. break;
  1321. case 'E':
  1322. var d = dateObject.getDay();
  1323. if(l<3){
  1324. s = d+1; pad = true;
  1325. }else{
  1326. var propD = ["days", "format", widthList[l-3]].join("-");
  1327. s = bundle[propD][d];
  1328. }
  1329. break;
  1330. case 'a':
  1331. var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
  1332. s = options[timePeriod] || bundle['dayPeriods-format-wide-' + timePeriod];
  1333. break;
  1334. case 'h':
  1335. case 'H':
  1336. case 'K':
  1337. case 'k':
  1338. var h = dateObject.getHours();
  1339. // strange choices in the date format make it impossible to write this succinctly
  1340. switch (c){
  1341. case 'h': // 1-12
  1342. s = (h % 12) || 12;
  1343. break;
  1344. case 'H': // 0-23
  1345. s = h;
  1346. break;
  1347. case 'K': // 0-11
  1348. s = (h % 12);
  1349. break;
  1350. case 'k': // 1-24
  1351. s = h || 24;
  1352. break;
  1353. }
  1354. pad = true;
  1355. break;
  1356. case 'm':
  1357. s = dateObject.getMinutes(); pad = true;
  1358. break;
  1359. case 's':
  1360. s = dateObject.getSeconds(); pad = true;
  1361. break;
  1362. case 'S':
  1363. s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
  1364. break;
  1365. case 'v': // FIXME: don't know what this is. seems to be same as z?
  1366. case 'z':
  1367. // We only have one timezone to offer; the one from the browser
  1368. s = dojo.date.locale._getZone(dateObject, true, options);
  1369. if(s){break;}
  1370. l=4;
  1371. // fallthrough... use GMT if tz not available
  1372. case 'Z':
  1373. var offset = dojo.date.locale._getZone(dateObject, false, options);
  1374. var tz = [
  1375. (offset<=0 ? "+" : "-"),
  1376. dojo.string.pad(Math.floor(Math.abs(offset)/60), 2),
  1377. dojo.string.pad(Math.abs(offset)% 60, 2)
  1378. ];
  1379. if(l==4){
  1380. tz.splice(0, 0, "GMT");
  1381. tz.splice(3, 0, ":");
  1382. }
  1383. s = tz.join("");
  1384. break;
  1385. // case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
  1386. // console.log(match+" modifier unimplemented");
  1387. default:
  1388. throw new Error("dojo.date.locale.format: invalid pattern char: "+pattern);
  1389. }
  1390. if(pad){ s = dojo.string.pad(s, l); }
  1391. return s;
  1392. });
  1393. }
  1394. /*=====
  1395. dojo.date.locale.__FormatOptions = function(){
  1396. // selector: String
  1397. // choice of 'time','date' (default: date and time)
  1398. // formatLength: String
  1399. // choice of long, short, medium or full (plus any custom additions). Defaults to 'short'
  1400. // datePattern:String
  1401. // override pattern with this string
  1402. // timePattern:String
  1403. // override pattern with this string
  1404. // am: String
  1405. // override strings for am in times
  1406. // pm: String
  1407. // override strings for pm in times
  1408. // locale: String
  1409. // override the locale used to determine formatting rules
  1410. // fullYear: Boolean
  1411. // (format only) use 4 digit years whenever 2 digit years are called for
  1412. // strict: Boolean
  1413. // (parse only) strict parsing, off by default
  1414. this.selector = selector;
  1415. this.formatLength = formatLength;
  1416. this.datePattern = datePattern;
  1417. this.timePattern = timePattern;
  1418. this.am = am;
  1419. this.pm = pm;
  1420. this.locale = locale;
  1421. this.fullYear = fullYear;
  1422. this.strict = strict;
  1423. }
  1424. =====*/
  1425. dojo.date.locale._getZone = function(/*Date*/dateObject, /*boolean*/getName, /*dojo.date.locale.__FormatOptions?*/options){
  1426. // summary:
  1427. // Returns the zone (or offset) for the given date and options. This
  1428. // is broken out into a separate function so that it can be overridden
  1429. // by timezone-aware code.
  1430. //
  1431. // dateObject:
  1432. // the date and/or time being formatted.
  1433. //
  1434. // getName:
  1435. // Whether to return the timezone string (if true), or the offset (if false)
  1436. //
  1437. // options:
  1438. // The options being used for formatting
  1439. if(getName){
  1440. return dojo.date.getTimezoneName(dateObject);
  1441. }else{
  1442. return dateObject.getTimezoneOffset();
  1443. }
  1444. };
  1445. dojo.date.locale.format = function(/*Date*/dateObject, /*dojo.date.locale.__FormatOptions?*/options){
  1446. // summary:
  1447. // Format a Date object as a String, using locale-specific settings.
  1448. //
  1449. // description:
  1450. // Create a string from a Date object using a known localized pattern.
  1451. // By default, this method formats both date and time from dateObject.
  1452. // Formatting patterns are chosen appropriate to the locale. Different
  1453. // formatting lengths may be chosen, with "full" used by default.
  1454. // Custom patterns may be used or registered with translations using
  1455. // the dojo.date.locale.addCustomFormats method.
  1456. // Formatting patterns are implemented using [the syntax described at
  1457. // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
  1458. //
  1459. // dateObject:
  1460. // the date and/or time to be formatted. If a time only is formatted,
  1461. // the values in the year, month, and day fields are irrelevant. The
  1462. // opposite is true when formatting only dates.
  1463. options = options || {};
  1464. var locale = dojo.i18n.normalizeLocale(options.locale),
  1465. formatLength = options.formatLength || 'short',
  1466. bundle = dojo.date.locale._getGregorianBundle(locale),
  1467. str = [],
  1468. sauce = dojo.hitch(this, formatPattern, dateObject, bundle, options);
  1469. if(options.selector == "year"){
  1470. return _processPattern(bundle["dateFormatItem-yyyy"] || "yyyy", sauce);
  1471. }
  1472. var pattern;
  1473. if(options.selector != "date"){
  1474. pattern = options.timePattern || bundle["timeFormat-"+formatLength];
  1475. if(pattern){str.push(_processPattern(pattern, sauce));}
  1476. }
  1477. if(options.selector != "time"){
  1478. pattern = options.datePattern || bundle["dateFormat-"+formatLength];
  1479. if(pattern){str.push(_processPattern(pattern, sauce));}
  1480. }
  1481. return str.length == 1 ? str[0] : bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
  1482. function(match, key){ return str[key]; }); // String
  1483. };
  1484. dojo.date.locale.regexp = function(/*dojo.date.locale.__FormatOptions?*/options){
  1485. // summary:
  1486. // Builds the regular needed to parse a localized date
  1487. return dojo.date.locale._parseInfo(options).regexp; // String
  1488. };
  1489. dojo.date.locale._parseInfo = function(/*dojo.date.locale.__FormatOptions?*/options){
  1490. options = options || {};
  1491. var locale = dojo.i18n.normalizeLocale(options.locale),
  1492. bundle = dojo.date.locale._getGregorianBundle(locale),
  1493. formatLength = options.formatLength || 'short',
  1494. datePattern = options.datePattern || bundle["dateFormat-" + formatLength],
  1495. timePattern = options.timePattern || bundle["timeFormat-" + formatLength],
  1496. pattern;
  1497. if(options.selector == 'date'){
  1498. pattern = datePattern;
  1499. }else if(options.selector == 'time'){
  1500. pattern = timePattern;
  1501. }else{
  1502. pattern = bundle["dateTimeFormat-"+formatLength].replace(/\{(\d+)\}/g,
  1503. function(match, key){ return [timePattern, datePattern][key]; });
  1504. }
  1505. var tokens = [],
  1506. re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
  1507. return {regexp: re, tokens: tokens, bundle: bundle};
  1508. };
  1509. dojo.date.locale.parse = function(/*String*/value, /*dojo.date.locale.__FormatOptions?*/options){
  1510. // summary:
  1511. // Convert a properly formatted string to a primitive Date object,
  1512. // using locale-specific settings.
  1513. //
  1514. // description:
  1515. // Create a Date object from a string using a known localized pattern.
  1516. // By default, this method parses looking for both date and time in the string.
  1517. // Formatting patterns are chosen appropriate to the locale. Different
  1518. // formatting lengths may be chosen, with "full" used by default.
  1519. // Custom patterns may be used or registered with translations using
  1520. // the dojo.date.locale.addCustomFormats method.
  1521. //
  1522. // Formatting patterns are implemented using [the syntax described at
  1523. // unicode.org](http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns)
  1524. // When two digit years are used, a century is chosen according to a sliding
  1525. // window of 80 years before and 20 years after present year, for both `yy` and `yyyy` patterns.
  1526. // year < 100CE requires strict mode.
  1527. //
  1528. // value:
  1529. // A string representation of a date
  1530. // remove non-printing bidi control chars from input and pattern
  1531. var controlChars = /[\u200E\u200F\u202A\u202E]/g,
  1532. info = dojo.date.locale._parseInfo(options),
  1533. tokens = info.tokens, bundle = info.bundle,
  1534. re = new RegExp("^" + info.regexp.replace(controlChars, "") + "$",
  1535. info.strict ? "" : "i"),
  1536. match = re.exec(value && value.replace(controlChars, ""));
  1537. if(!match){ return null; } // null
  1538. var widthList = ['abbr', 'wide', 'narrow'],
  1539. result = [1970,0,1,0,0,0,0], // will get converted to a Date at the end
  1540. amPm = "",
  1541. valid = dojo.every(match, function(v, i){
  1542. if(!i){return true;}
  1543. var token=tokens[i-1];
  1544. var l=token.length;
  1545. switch(token.charAt(0)){
  1546. case 'y':
  1547. if(l != 2 && options.strict){
  1548. //interpret year literally, so '5' would be 5 A.D.
  1549. result[0] = v;
  1550. }else{
  1551. if(v<100){
  1552. v = Number(v);
  1553. //choose century to apply, according to a sliding window
  1554. //of 80 years before and 20 years after present year
  1555. var year = '' + new Date().getFullYear(),
  1556. century = year.substring(0, 2) * 100,
  1557. cutoff = Math.min(Number(year.substring(2, 4)) + 20, 99),
  1558. num = (v < cutoff) ? century + v : century - 100 + v;
  1559. result[0] = num;
  1560. }else{
  1561. //we expected 2 digits and got more...
  1562. if(options.strict){
  1563. return false;
  1564. }
  1565. //interpret literally, so '150' would be 150 A.D.
  1566. //also tolerate '1950', if 'yyyy' input passed to 'yy' format
  1567. result[0] = v;
  1568. }
  1569. }
  1570. break;
  1571. case 'M':
  1572. if(l>2){
  1573. var months = bundle['months-format-' + widthList[l-3]].concat();
  1574. if(!options.strict){
  1575. //Tolerate abbreviating period in month part
  1576. //Case-insensitive comparison
  1577. v = v.replace(".","").toLowerCase();
  1578. months = dojo.map(months, function(s){ return s.replace(".","").toLowerCase(); } );
  1579. }
  1580. v = dojo.indexOf(months, v);
  1581. if(v == -1){
  1582. // console.log("dojo.date.locale.parse: Could not parse month name: '" + v + "'.");
  1583. return false;
  1584. }
  1585. }else{
  1586. v--;
  1587. }
  1588. result[1] = v;
  1589. break;
  1590. case 'E':
  1591. case 'e':
  1592. var days = bundle['days-format-' + widthList[l-3]].concat();
  1593. if(!options.strict){
  1594. //Case-insensitive comparison
  1595. v = v.toLowerCase();
  1596. days = dojo.map(days, function(d){return d.toLowerCase();});
  1597. }
  1598. v = dojo.indexOf(days, v);
  1599. if(v == -1){
  1600. // console.log("dojo.date.locale.parse: Could not parse weekday name: '" + v + "'.");
  1601. return false;
  1602. }
  1603. //TODO: not sure what to actually do with this input,
  1604. //in terms of setting something on the Date obj...?
  1605. //without more context, can't affect the actual date
  1606. //TODO: just validate?
  1607. break;
  1608. case 'D':
  1609. result[1] = 0;
  1610. // fallthrough...
  1611. case 'd':
  1612. result[2] = v;
  1613. break;
  1614. case 'a': //am/pm
  1615. var am = options.am || bundle['dayPeriods-format-wide-am'],
  1616. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  1617. if(!options.strict){
  1618. var period = /\./g;
  1619. v = v.replace(period,'').toLowerCase();
  1620. am = am.replace(period,'').toLowerCase();
  1621. pm = pm.replace(period,'').toLowerCase();
  1622. }
  1623. if(options.strict && v != am && v != pm){
  1624. // console.log("dojo.date.locale.parse: Could not parse am/pm part.");
  1625. return false;
  1626. }
  1627. // we might not have seen the hours field yet, so store the state and apply hour change later
  1628. amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
  1629. break;
  1630. case 'K': //hour (1-24)
  1631. if(v == 24){ v = 0; }
  1632. // fallthrough...
  1633. case 'h': //hour (1-12)
  1634. case 'H': //hour (0-23)
  1635. case 'k': //hour (0-11)
  1636. //TODO: strict bounds checking, padding
  1637. if(v > 23){
  1638. // console.log("dojo.date.locale.parse: Illegal hours value");
  1639. return false;
  1640. }
  1641. //in the 12-hour case, adjusting for am/pm requires the 'a' part
  1642. //which could come before or after the hour, so we will adjust later
  1643. result[3] = v;
  1644. break;
  1645. case 'm': //minutes
  1646. result[4] = v;
  1647. break;
  1648. case 's': //seconds
  1649. result[5] = v;
  1650. break;
  1651. case 'S': //milliseconds
  1652. result[6] = v;
  1653. // break;
  1654. // case 'w':
  1655. //TODO var firstDay = 0;
  1656. // default:
  1657. //TODO: throw?
  1658. // console.log("dojo.date.locale.parse: unsupported pattern char=" + token.charAt(0));
  1659. }
  1660. return true;
  1661. });
  1662. var hours = +result[3];
  1663. if(amPm === 'p' && hours < 12){
  1664. result[3] = hours + 12; //e.g., 3pm -> 15
  1665. }else if(amPm === 'a' && hours == 12){
  1666. result[3] = 0; //12am -> 0
  1667. }
  1668. //TODO: implement a getWeekday() method in order to test
  1669. //validity of input strings containing 'EEE' or 'EEEE'...
  1670. var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
  1671. if(options.strict){
  1672. dateObject.setFullYear(result[0]);
  1673. }
  1674. // Check for overflow. The Date() constructor normalizes things like April 32nd...
  1675. //TODO: why isn't this done for times as well?
  1676. var allTokens = tokens.join(""),
  1677. dateToken = allTokens.indexOf('d') != -1,
  1678. monthToken = allTokens.indexOf('M') != -1;
  1679. if(!valid ||
  1680. (monthToken && dateObject.getMonth() > result[1]) ||
  1681. (dateToken && dateObject.getDate() > result[2])){
  1682. return null;
  1683. }
  1684. // Check for underflow, due to DST shifts. See #9366
  1685. // This assumes a 1 hour dst shift correction at midnight
  1686. // We could compare the timezone offset after the shift and add the difference instead.
  1687. if((monthToken && dateObject.getMonth() < result[1]) ||
  1688. (dateToken && dateObject.getDate() < result[2])){
  1689. dateObject = dojo.date.add(dateObject, "hour", 1);
  1690. }
  1691. return dateObject; // Date
  1692. };
  1693. function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
  1694. //summary: Process a pattern with literals in it
  1695. // Break up on single quotes, treat every other one as a literal, except '' which becomes '
  1696. var identity = function(x){return x;};
  1697. applyPattern = applyPattern || identity;
  1698. applyLiteral = applyLiteral || identity;
  1699. applyAll = applyAll || identity;
  1700. //split on single quotes (which escape literals in date format strings)
  1701. //but preserve escaped single quotes (e.g., o''clock)
  1702. var chunks = pattern.match(/(''|[^'])+/g),
  1703. literal = pattern.charAt(0) == "'";
  1704. dojo.forEach(chunks, function(chunk, i){
  1705. if(!chunk){
  1706. chunks[i]='';
  1707. }else{
  1708. chunks[i]=(literal ? applyLiteral : applyPattern)(chunk.replace(/''/g, "'"));
  1709. literal = !literal;
  1710. }
  1711. });
  1712. return applyAll(chunks.join(''));
  1713. }
  1714. function _buildDateTimeRE(tokens, bundle, options, pattern){
  1715. pattern = dojo.regexp.escapeString(pattern);
  1716. if(!options.strict){ pattern = pattern.replace(" a", " ?a"); } // kludge to tolerate no space before am/pm
  1717. return pattern.replace(/([a-z])\1*/ig, function(match){
  1718. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  1719. var s,
  1720. c = match.charAt(0),
  1721. l = match.length,
  1722. p2 = '', p3 = '';
  1723. if(options.strict){
  1724. if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
  1725. if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
  1726. }else{
  1727. p2 = '0?'; p3 = '0{0,2}';
  1728. }
  1729. switch(c){
  1730. case 'y':
  1731. s = '\\d{2,4}';
  1732. break;
  1733. case 'M':
  1734. s = (l>2) ? '\\S+?' : '1[0-2]|'+p2+'[1-9]';
  1735. break;
  1736. case 'D':
  1737. s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|'+p3+'[1-9][0-9]|'+p2+'[1-9]';
  1738. break;
  1739. case 'd':
  1740. s = '3[01]|[12]\\d|'+p2+'[1-9]';
  1741. break;
  1742. case 'w':
  1743. s = '[1-4][0-9]|5[0-3]|'+p2+'[1-9]';
  1744. break;
  1745. case 'E':
  1746. s = '\\S+';
  1747. break;
  1748. case 'h': //hour (1-12)
  1749. s = '1[0-2]|'+p2+'[1-9]';
  1750. break;
  1751. case 'k': //hour (0-11)
  1752. s = '1[01]|'+p2+'\\d';
  1753. break;
  1754. case 'H': //hour (0-23)
  1755. s = '1\\d|2[0-3]|'+p2+'\\d';
  1756. break;
  1757. case 'K': //hour (1-24)
  1758. s = '1\\d|2[0-4]|'+p2+'[1-9]';
  1759. break;
  1760. case 'm':
  1761. case 's':
  1762. s = '[0-5]\\d';
  1763. break;
  1764. case 'S':
  1765. s = '\\d{'+l+'}';
  1766. break;
  1767. case 'a':
  1768. var am = options.am || bundle['dayPeriods-format-wide-am'],
  1769. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  1770. s = am + '|' + pm;
  1771. if(!options.strict){
  1772. if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
  1773. if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
  1774. if(s.indexOf('.') != -1){ s += '|' + s.replace(/\./g, ""); }
  1775. }
  1776. s = s.replace(/\./g, "\\.");
  1777. break;
  1778. default:
  1779. // case 'v':
  1780. // case 'z':
  1781. // case 'Z':
  1782. s = ".*";
  1783. // console.log("parse of date format, pattern=" + pattern);
  1784. }
  1785. if(tokens){ tokens.push(match); }
  1786. return "(" + s + ")"; // add capture
  1787. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
  1788. }
  1789. })();
  1790. (function(){
  1791. var _customFormats = [];
  1792. dojo.date.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
  1793. // summary:
  1794. // Add a reference to a bundle containing localized custom formats to be
  1795. // used by date/time formatting and parsing routines.
  1796. //
  1797. // description:
  1798. // The user may add custom localized formats where the bundle has properties following the
  1799. // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
  1800. // The pattern string should match the format used by the CLDR.
  1801. // See dojo.date.locale.format() for details.
  1802. // The resources must be loaded by dojo.requireLocalization() prior to use
  1803. _customFormats.push({pkg:packageName,name:bundleName});
  1804. };
  1805. dojo.date.locale._getGregorianBundle = function(/*String*/locale){
  1806. var gregorian = {};
  1807. dojo.forEach(_customFormats, function(desc){
  1808. var bundle = dojo.i18n.getLocalization(desc.pkg, desc.name, locale);
  1809. gregorian = dojo.mixin(gregorian, bundle);
  1810. }, this);
  1811. return gregorian; /*Object*/
  1812. };
  1813. })();
  1814. dojo.date.locale.addCustomFormats("dojo.cldr","gregorian");
  1815. dojo.date.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale){
  1816. // summary:
  1817. // Used to get localized strings from dojo.cldr for day or month names.
  1818. //
  1819. // item:
  1820. // 'months' || 'days'
  1821. // type:
  1822. // 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
  1823. // context:
  1824. // 'standAlone' || 'format' (default)
  1825. // locale:
  1826. // override locale used to find the names
  1827. var label,
  1828. lookup = dojo.date.locale._getGregorianBundle(locale),
  1829. props = [item, context, type];
  1830. if(context == 'standAlone'){
  1831. var key = props.join('-');
  1832. label = lookup[key];
  1833. // Fall back to 'format' flavor of name
  1834. if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr
  1835. }
  1836. props[1] = 'format';
  1837. // return by copy so changes won't be made accidentally to the in-memory model
  1838. return (label || lookup[props.join('-')]).concat(); /*Array*/
  1839. };
  1840. dojo.date.locale.isWeekend = function(/*Date?*/dateObject, /*String?*/locale){
  1841. // summary:
  1842. // Determines if the date falls on a weekend, according to local custom.
  1843. var weekend = dojo.cldr.supplemental.getWeekend(locale),
  1844. day = (dateObject || new Date()).getDay();
  1845. if(weekend.end < weekend.start){
  1846. weekend.end += 7;
  1847. if(day < weekend.start){ day += 7; }
  1848. }
  1849. return day >= weekend.start && day <= weekend.end; // Boolean
  1850. };
  1851. // These are used only by format and strftime. Do they need to be public? Which module should they go in?
  1852. dojo.date.locale._getDayOfYear = function(/*Date*/dateObject){
  1853. // summary: gets the day of the year as represented by dateObject
  1854. return dojo.date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject) + 1; // Number
  1855. };
  1856. dojo.date.locale._getWeekOfYear = function(/*Date*/dateObject, /*Number*/firstDayOfWeek){
  1857. if(arguments.length == 1){ firstDayOfWeek = 0; } // Sunday
  1858. var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1).getDay(),
  1859. adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
  1860. week = Math.floor((dojo.date.locale._getDayOfYear(dateObject) + adj - 1) / 7);
  1861. // if year starts on the specified day, start counting weeks at 1
  1862. if(firstDayOfYear == firstDayOfWeek){ week++; }
  1863. return week; // Number
  1864. };
  1865. }
  1866. if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1867. dojo._hasResource["dojo.dnd.common"] = true;
  1868. dojo.provide("dojo.dnd.common");
  1869. dojo.getObject("dnd", true, dojo);
  1870. dojo.dnd.getCopyKeyState = dojo.isCopyKey;
  1871. dojo.dnd._uniqueId = 0;
  1872. dojo.dnd.getUniqueId = function(){
  1873. // summary:
  1874. // returns a unique string for use with any DOM element
  1875. var id;
  1876. do{
  1877. id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
  1878. }while(dojo.byId(id));
  1879. return id;
  1880. };
  1881. dojo.dnd._empty = {};
  1882. dojo.dnd.isFormElement = function(/*Event*/ e){
  1883. // summary:
  1884. // returns true if user clicked on a form element
  1885. var t = e.target;
  1886. if(t.nodeType == 3 /*TEXT_NODE*/){
  1887. t = t.parentNode;
  1888. }
  1889. return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
  1890. };
  1891. }
  1892. if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  1893. dojo._hasResource["dojo.window"] = true;
  1894. dojo.provide("dojo.window");
  1895. dojo.getObject("window", true, dojo);
  1896. dojo.window.getBox = function(){
  1897. // summary:
  1898. // Returns the dimensions and scroll position of the viewable area of a browser window
  1899. var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement;
  1900. // get scroll position
  1901. var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work
  1902. return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
  1903. };
  1904. dojo.window.get = function(doc){
  1905. // summary:
  1906. // Get window object associated with document doc
  1907. // In some IE versions (at least 6.0), document.parentWindow does not return a
  1908. // reference to the real window object (maybe a copy), so we must fix it as well
  1909. // We use IE specific execScript to attach the real window reference to
  1910. // document._parentWindow for later use
  1911. if(dojo.isIE && window !== document.parentWindow){
  1912. /*
  1913. In IE 6, only the variable "window" can be used to connect events (others
  1914. may be only copies).
  1915. */
  1916. doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
  1917. //to prevent memory leak, unset it after use
  1918. //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
  1919. var win = doc._parentWindow;
  1920. doc._parentWindow = null;
  1921. return win; // Window
  1922. }
  1923. return doc.parentWindow || doc.defaultView; // Window
  1924. };
  1925. dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
  1926. // summary:
  1927. // Scroll the passed node into view using minimal movement, if it is not already.
  1928. // Don't rely on node.scrollIntoView working just because the function is there since
  1929. // it forces the node to the page's bottom or top (and left or right in IE) without consideration for the minimal movement.
  1930. // WebKit's node.scrollIntoViewIfNeeded doesn't work either for inner scrollbars in right-to-left mode
  1931. // and when there's a fixed position scrollable element
  1932. node = dojo.byId(node);
  1933. var body, doc = node.ownerDocument || dojo.doc;
  1934. // has() functions but without has() support
  1935. if(!("rtl_adjust_position_for_verticalScrollBar" in dojo.window)){
  1936. body = dojo.body();
  1937. var scrollable = dojo.create('div', {
  1938. style: {overflow:'scroll', overflowX:'visible', direction:'rtl', visibility:'hidden', position:'absolute', left:'0', top:'0', width:'64px', height:'64px'}
  1939. }, body, "last"),
  1940. div = dojo.create('div', {
  1941. style: {overflow:'hidden', direction:'ltr'}
  1942. }, scrollable, "last");
  1943. dojo.window.rtl_adjust_position_for_verticalScrollBar = dojo.position(div).x != 0;
  1944. scrollable.removeChild(div);
  1945. body.removeChild(scrollable);
  1946. }
  1947. if(!("position_fixed_support" in dojo.window)){
  1948. // IE6, IE7+quirks, and some older mobile browsers don't support position:fixed
  1949. body = dojo.body();
  1950. var outer = dojo.create('span', {
  1951. style: {visibility:'hidden', position:'fixed', left:'1px', top:'1px'}
  1952. }, body, "last"),
  1953. inner = dojo.create('span', {
  1954. style: {position:'fixed', left:'0', top:'0'}
  1955. }, outer, "last");
  1956. dojo.window.position_fixed_support = dojo.position(inner).x != dojo.position(outer).x;
  1957. outer.removeChild(inner);
  1958. body.removeChild(outer);
  1959. }
  1960. try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
  1961. body = doc.body || doc.getElementsByTagName("body")[0];
  1962. var html = doc.documentElement || body.parentNode,
  1963. isIE = dojo.isIE,
  1964. isWK = dojo.isWebKit;
  1965. // if an untested browser, then use the native method
  1966. if(node == body || node == html){ return; }
  1967. if(!(dojo.isMozilla || isIE || isWK || dojo.isOpera) && ("scrollIntoView" in node)){
  1968. node.scrollIntoView(false); // short-circuit to native if possible
  1969. return;
  1970. }
  1971. var backCompat = doc.compatMode == 'BackCompat',
  1972. rootWidth = Math.min(body.clientWidth || html.clientWidth, html.clientWidth || body.clientWidth),
  1973. rootHeight = Math.min(body.clientHeight || html.clientHeight, html.clientHeight || body.clientHeight),
  1974. scrollRoot = (isWK || backCompat) ? body : html,
  1975. nodePos = pos || dojo.position(node),
  1976. el = node.parentNode,
  1977. isFixed = function(el){
  1978. return (isIE <= 6 || (isIE == 7 && backCompat))
  1979. ? false
  1980. : (dojo.window.position_fixed_support && (dojo.style(el, 'position').toLowerCase() == "fixed"));
  1981. };
  1982. if(isFixed(node)){ return; } // nothing to do
  1983. while(el){
  1984. if(el == body){ el = scrollRoot; }
  1985. var elPos = dojo.position(el),
  1986. fixedPos = isFixed(el),
  1987. rtl = dojo.getComputedStyle(el).direction.toLowerCase() == "rtl";
  1988. if(el == scrollRoot){
  1989. elPos.w = rootWidth; elPos.h = rootHeight;
  1990. if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
  1991. if(elPos.x < 0 || !isIE || isIE >= 9){ elPos.x = 0; } // older IE can have values > 0
  1992. if(elPos.y < 0 || !isIE || isIE >= 9){ elPos.y = 0; }
  1993. }else{
  1994. var pb = dojo._getPadBorderExtents(el);
  1995. elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
  1996. var clientSize = el.clientWidth,
  1997. scrollBarSize = elPos.w - clientSize;
  1998. if(clientSize > 0 && scrollBarSize > 0){
  1999. if(rtl && dojo.window.rtl_adjust_position_for_verticalScrollBar){
  2000. elPos.x += scrollBarSize;
  2001. }
  2002. elPos.w = clientSize;
  2003. }
  2004. clientSize = el.clientHeight;
  2005. scrollBarSize = elPos.h - clientSize;
  2006. if(clientSize > 0 && scrollBarSize > 0){
  2007. elPos.h = clientSize;
  2008. }
  2009. }
  2010. if(fixedPos){ // bounded by viewport, not parents
  2011. if(elPos.y < 0){
  2012. elPos.h += elPos.y; elPos.y = 0;
  2013. }
  2014. if(elPos.x < 0){
  2015. elPos.w += elPos.x; elPos.x = 0;
  2016. }
  2017. if(elPos.y + elPos.h > rootHeight){
  2018. elPos.h = rootHeight - elPos.y;
  2019. }
  2020. if(elPos.x + elPos.w > rootWidth){
  2021. elPos.w = rootWidth - elPos.x;
  2022. }
  2023. }
  2024. // calculate overflow in all 4 directions
  2025. var l = nodePos.x - elPos.x, // beyond left: < 0
  2026. // t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
  2027. t = nodePos.y - elPos.y, // beyond top: < 0
  2028. r = l + nodePos.w - elPos.w, // beyond right: > 0
  2029. bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
  2030. var s, old;
  2031. if(r * l > 0 && (!!el.scrollLeft || el == scrollRoot || el.scrollWidth > el.offsetHeight)){
  2032. s = Math[l < 0? "max" : "min"](l, r);
  2033. if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
  2034. old = el.scrollLeft;
  2035. el.scrollLeft += s;
  2036. s = el.scrollLeft - old;
  2037. nodePos.x -= s;
  2038. }
  2039. if(bot * t > 0 && (!!el.scrollTop || el == scrollRoot || el.scrollHeight > el.offsetHeight)){
  2040. s = Math.ceil(Math[t < 0? "max" : "min"](t, bot));
  2041. old = el.scrollTop;
  2042. el.scrollTop += s;
  2043. s = el.scrollTop - old;
  2044. nodePos.y -= s;
  2045. }
  2046. el = (el != scrollRoot) && !fixedPos && el.parentNode;
  2047. }
  2048. }catch(error){
  2049. console.error('scrollIntoView: ' + error);
  2050. node.scrollIntoView(false);
  2051. }
  2052. };
  2053. }
  2054. if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2055. dojo._hasResource["dojo.dnd.autoscroll"] = true;
  2056. dojo.provide("dojo.dnd.autoscroll");
  2057. dojo.getObject("dnd", true, dojo);
  2058. dojo.dnd.getViewport = dojo.window.getBox;
  2059. dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
  2060. dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
  2061. dojo.dnd.V_AUTOSCROLL_VALUE = 16;
  2062. dojo.dnd.H_AUTOSCROLL_VALUE = 16;
  2063. dojo.dnd.autoScroll = function(e){
  2064. // summary:
  2065. // a handler for onmousemove event, which scrolls the window, if
  2066. // necesary
  2067. // e: Event
  2068. // onmousemove event
  2069. // FIXME: needs more docs!
  2070. var v = dojo.window.getBox(), dx = 0, dy = 0;
  2071. if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
  2072. dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
  2073. }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
  2074. dx = dojo.dnd.H_AUTOSCROLL_VALUE;
  2075. }
  2076. if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
  2077. dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
  2078. }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
  2079. dy = dojo.dnd.V_AUTOSCROLL_VALUE;
  2080. }
  2081. window.scrollBy(dx, dy);
  2082. };
  2083. dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
  2084. dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
  2085. dojo.dnd.autoScrollNodes = function(e){
  2086. // summary:
  2087. // a handler for onmousemove event, which scrolls the first avaialble
  2088. // Dom element, it falls back to dojo.dnd.autoScroll()
  2089. // e: Event
  2090. // onmousemove event
  2091. // FIXME: needs more docs!
  2092. for(var n = e.target; n;){
  2093. if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
  2094. var s = dojo.getComputedStyle(n);
  2095. if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
  2096. var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
  2097. //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
  2098. var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
  2099. h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
  2100. rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
  2101. if(dojo.isWebKit || dojo.isOpera){
  2102. // FIXME: this code should not be here, it should be taken into account
  2103. // either by the event fixing code, or the dojo.position()
  2104. // FIXME: this code doesn't work on Opera 9.5 Beta
  2105. rx += dojo.body().scrollLeft;
  2106. ry += dojo.body().scrollTop;
  2107. }
  2108. if(rx > 0 && rx < b.w){
  2109. if(rx < w){
  2110. dx = -w;
  2111. }else if(rx > b.w - w){
  2112. dx = w;
  2113. }
  2114. }
  2115. //console.log("ry =", ry, "b.h =", b.h, "h =", h);
  2116. if(ry > 0 && ry < b.h){
  2117. if(ry < h){
  2118. dy = -h;
  2119. }else if(ry > b.h - h){
  2120. dy = h;
  2121. }
  2122. }
  2123. var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
  2124. n.scrollLeft = n.scrollLeft + dx;
  2125. n.scrollTop = n.scrollTop + dy;
  2126. if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
  2127. }
  2128. }
  2129. try{
  2130. n = n.parentNode;
  2131. }catch(x){
  2132. n = null;
  2133. }
  2134. }
  2135. dojo.dnd.autoScroll(e);
  2136. };
  2137. }
  2138. if(!dojo._hasResource["dojo.dnd.Avatar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2139. dojo._hasResource["dojo.dnd.Avatar"] = true;
  2140. dojo.provide("dojo.dnd.Avatar");
  2141. dojo.declare("dojo.dnd.Avatar", null, {
  2142. // summary:
  2143. // Object that represents transferred DnD items visually
  2144. // manager: Object
  2145. // a DnD manager object
  2146. constructor: function(manager){
  2147. this.manager = manager;
  2148. this.construct();
  2149. },
  2150. // methods
  2151. construct: function(){
  2152. // summary:
  2153. // constructor function;
  2154. // it is separate so it can be (dynamically) overwritten in case of need
  2155. this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
  2156. var a = dojo.create("table", {
  2157. "class": "dojoDndAvatar",
  2158. style: {
  2159. position: "absolute",
  2160. zIndex: "1999",
  2161. margin: "0px"
  2162. }
  2163. }),
  2164. source = this.manager.source, node,
  2165. b = dojo.create("tbody", null, a),
  2166. tr = dojo.create("tr", null, b),
  2167. td = dojo.create("td", null, tr),
  2168. icon = this.isA11y ? dojo.create("span", {
  2169. id : "a11yIcon",
  2170. innerHTML : this.manager.copy ? '+' : "<"
  2171. }, td) : null,
  2172. span = dojo.create("span", {
  2173. innerHTML: source.generateText ? this._generateText() : ""
  2174. }, td),
  2175. k = Math.min(5, this.manager.nodes.length), i = 0;
  2176. // we have to set the opacity on IE only after the node is live
  2177. dojo.attr(tr, {
  2178. "class": "dojoDndAvatarHeader",
  2179. style: {opacity: 0.9}
  2180. });
  2181. for(; i < k; ++i){
  2182. if(source.creator){
  2183. // create an avatar representation of the node
  2184. node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
  2185. }else{
  2186. // or just clone the node and hope it works
  2187. node = this.manager.nodes[i].cloneNode(true);
  2188. if(node.tagName.toLowerCase() == "tr"){
  2189. // insert extra table nodes
  2190. var table = dojo.create("table"),
  2191. tbody = dojo.create("tbody", null, table);
  2192. tbody.appendChild(node);
  2193. node = table;
  2194. }
  2195. }
  2196. node.id = "";
  2197. tr = dojo.create("tr", null, b);
  2198. td = dojo.create("td", null, tr);
  2199. td.appendChild(node);
  2200. dojo.attr(tr, {
  2201. "class": "dojoDndAvatarItem",
  2202. style: {opacity: (9 - i) / 10}
  2203. });
  2204. }
  2205. this.node = a;
  2206. },
  2207. destroy: function(){
  2208. // summary:
  2209. // destructor for the avatar; called to remove all references so it can be garbage-collected
  2210. dojo.destroy(this.node);
  2211. this.node = false;
  2212. },
  2213. update: function(){
  2214. // summary:
  2215. // updates the avatar to reflect the current DnD state
  2216. dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
  2217. if (this.isA11y){
  2218. var icon = dojo.byId("a11yIcon");
  2219. var text = '+'; // assume canDrop && copy
  2220. if (this.manager.canDropFlag && !this.manager.copy) {
  2221. text = '< '; // canDrop && move
  2222. }else if (!this.manager.canDropFlag && !this.manager.copy) {
  2223. text = "o"; //!canDrop && move
  2224. }else if(!this.manager.canDropFlag){
  2225. text = 'x'; // !canDrop && copy
  2226. }
  2227. icon.innerHTML=text;
  2228. }
  2229. // replace text
  2230. dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
  2231. function(node){
  2232. node.innerHTML = this._generateText();
  2233. }, this);
  2234. },
  2235. _generateText: function(){
  2236. // summary: generates a proper text to reflect copying or moving of items
  2237. return this.manager.nodes.length.toString();
  2238. }
  2239. });
  2240. }
  2241. if(!dojo._hasResource["dojo.dnd.Manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2242. dojo._hasResource["dojo.dnd.Manager"] = true;
  2243. dojo.provide("dojo.dnd.Manager");
  2244. dojo.declare("dojo.dnd.Manager", null, {
  2245. // summary:
  2246. // the manager of DnD operations (usually a singleton)
  2247. constructor: function(){
  2248. this.avatar = null;
  2249. this.source = null;
  2250. this.nodes = [];
  2251. this.copy = true;
  2252. this.target = null;
  2253. this.canDropFlag = false;
  2254. this.events = [];
  2255. },
  2256. // avatar's offset from the mouse
  2257. OFFSET_X: 16,
  2258. OFFSET_Y: 16,
  2259. // methods
  2260. overSource: function(source){
  2261. // summary:
  2262. // called when a source detected a mouse-over condition
  2263. // source: Object
  2264. // the reporter
  2265. if(this.avatar){
  2266. this.target = (source && source.targetState != "Disabled") ? source : null;
  2267. this.canDropFlag = Boolean(this.target);
  2268. this.avatar.update();
  2269. }
  2270. dojo.publish("/dnd/source/over", [source]);
  2271. },
  2272. outSource: function(source){
  2273. // summary:
  2274. // called when a source detected a mouse-out condition
  2275. // source: Object
  2276. // the reporter
  2277. if(this.avatar){
  2278. if(this.target == source){
  2279. this.target = null;
  2280. this.canDropFlag = false;
  2281. this.avatar.update();
  2282. dojo.publish("/dnd/source/over", [null]);
  2283. }
  2284. }else{
  2285. dojo.publish("/dnd/source/over", [null]);
  2286. }
  2287. },
  2288. startDrag: function(source, nodes, copy){
  2289. // summary:
  2290. // called to initiate the DnD operation
  2291. // source: Object
  2292. // the source which provides items
  2293. // nodes: Array
  2294. // the list of transferred items
  2295. // copy: Boolean
  2296. // copy items, if true, move items otherwise
  2297. this.source = source;
  2298. this.nodes = nodes;
  2299. this.copy = Boolean(copy); // normalizing to true boolean
  2300. this.avatar = this.makeAvatar();
  2301. dojo.body().appendChild(this.avatar.node);
  2302. dojo.publish("/dnd/start", [source, nodes, this.copy]);
  2303. this.events = [
  2304. dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
  2305. dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"),
  2306. dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"),
  2307. dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"),
  2308. // cancel text selection and text dragging
  2309. dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent),
  2310. dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
  2311. ];
  2312. var c = "dojoDnd" + (copy ? "Copy" : "Move");
  2313. dojo.addClass(dojo.body(), c);
  2314. },
  2315. canDrop: function(flag){
  2316. // summary:
  2317. // called to notify if the current target can accept items
  2318. var canDropFlag = Boolean(this.target && flag);
  2319. if(this.canDropFlag != canDropFlag){
  2320. this.canDropFlag = canDropFlag;
  2321. this.avatar.update();
  2322. }
  2323. },
  2324. stopDrag: function(){
  2325. // summary:
  2326. // stop the DnD in progress
  2327. dojo.removeClass(dojo.body(), ["dojoDndCopy", "dojoDndMove"]);
  2328. dojo.forEach(this.events, dojo.disconnect);
  2329. this.events = [];
  2330. this.avatar.destroy();
  2331. this.avatar = null;
  2332. this.source = this.target = null;
  2333. this.nodes = [];
  2334. },
  2335. makeAvatar: function(){
  2336. // summary:
  2337. // makes the avatar; it is separate to be overwritten dynamically, if needed
  2338. return new dojo.dnd.Avatar(this);
  2339. },
  2340. updateAvatar: function(){
  2341. // summary:
  2342. // updates the avatar; it is separate to be overwritten dynamically, if needed
  2343. this.avatar.update();
  2344. },
  2345. // mouse event processors
  2346. onMouseMove: function(e){
  2347. // summary:
  2348. // event processor for onmousemove
  2349. // e: Event
  2350. // mouse event
  2351. var a = this.avatar;
  2352. if(a){
  2353. dojo.dnd.autoScrollNodes(e);
  2354. //dojo.dnd.autoScroll(e);
  2355. var s = a.node.style;
  2356. s.left = (e.pageX + this.OFFSET_X) + "px";
  2357. s.top = (e.pageY + this.OFFSET_Y) + "px";
  2358. var copy = Boolean(this.source.copyState(dojo.isCopyKey(e)));
  2359. if(this.copy != copy){
  2360. this._setCopyStatus(copy);
  2361. }
  2362. }
  2363. },
  2364. onMouseUp: function(e){
  2365. // summary:
  2366. // event processor for onmouseup
  2367. // e: Event
  2368. // mouse event
  2369. if(this.avatar){
  2370. if(this.target && this.canDropFlag){
  2371. var copy = Boolean(this.source.copyState(dojo.isCopyKey(e))),
  2372. params = [this.source, this.nodes, copy, this.target, e];
  2373. dojo.publish("/dnd/drop/before", params);
  2374. dojo.publish("/dnd/drop", params);
  2375. }else{
  2376. dojo.publish("/dnd/cancel");
  2377. }
  2378. this.stopDrag();
  2379. }
  2380. },
  2381. // keyboard event processors
  2382. onKeyDown: function(e){
  2383. // summary:
  2384. // event processor for onkeydown:
  2385. // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
  2386. // e: Event
  2387. // keyboard event
  2388. if(this.avatar){
  2389. switch(e.keyCode){
  2390. case dojo.keys.CTRL:
  2391. var copy = Boolean(this.source.copyState(true));
  2392. if(this.copy != copy){
  2393. this._setCopyStatus(copy);
  2394. }
  2395. break;
  2396. case dojo.keys.ESCAPE:
  2397. dojo.publish("/dnd/cancel");
  2398. this.stopDrag();
  2399. break;
  2400. }
  2401. }
  2402. },
  2403. onKeyUp: function(e){
  2404. // summary:
  2405. // event processor for onkeyup, watching for CTRL for copy/move status
  2406. // e: Event
  2407. // keyboard event
  2408. if(this.avatar && e.keyCode == dojo.keys.CTRL){
  2409. var copy = Boolean(this.source.copyState(false));
  2410. if(this.copy != copy){
  2411. this._setCopyStatus(copy);
  2412. }
  2413. }
  2414. },
  2415. // utilities
  2416. _setCopyStatus: function(copy){
  2417. // summary:
  2418. // changes the copy status
  2419. // copy: Boolean
  2420. // the copy status
  2421. this.copy = copy;
  2422. this.source._markDndStatus(this.copy);
  2423. this.updateAvatar();
  2424. dojo.replaceClass(dojo.body(),
  2425. "dojoDnd" + (this.copy ? "Copy" : "Move"),
  2426. "dojoDnd" + (this.copy ? "Move" : "Copy"));
  2427. }
  2428. });
  2429. // dojo.dnd._manager:
  2430. // The manager singleton variable. Can be overwritten if needed.
  2431. dojo.dnd._manager = null;
  2432. dojo.dnd.manager = function(){
  2433. // summary:
  2434. // Returns the current DnD manager. Creates one if it is not created yet.
  2435. if(!dojo.dnd._manager){
  2436. dojo.dnd._manager = new dojo.dnd.Manager();
  2437. }
  2438. return dojo.dnd._manager; // Object
  2439. };
  2440. }
  2441. if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2442. dojo._hasResource["dojo.dnd.Mover"] = true;
  2443. dojo.provide("dojo.dnd.Mover");
  2444. dojo.declare("dojo.dnd.Mover", null, {
  2445. constructor: function(node, e, host){
  2446. // summary:
  2447. // an object which makes a node follow the mouse, or touch-drag on touch devices.
  2448. // Used as a default mover, and as a base class for custom movers.
  2449. // node: Node
  2450. // a node (or node's id) to be moved
  2451. // e: Event
  2452. // a mouse event, which started the move;
  2453. // only pageX and pageY properties are used
  2454. // host: Object?
  2455. // object which implements the functionality of the move,
  2456. // and defines proper events (onMoveStart and onMoveStop)
  2457. this.node = dojo.byId(node);
  2458. var pos = e.touches ? e.touches[0] : e;
  2459. this.marginBox = {l: pos.pageX, t: pos.pageY};
  2460. this.mouseButton = e.button;
  2461. var h = (this.host = host), d = node.ownerDocument;
  2462. this.events = [
  2463. // At the start of a drag, onFirstMove is called, and then the following two
  2464. // connects are disconnected
  2465. dojo.connect(d, "onmousemove", this, "onFirstMove"),
  2466. dojo.connect(d, "ontouchmove", this, "onFirstMove"),
  2467. // These are called continually during the drag
  2468. dojo.connect(d, "onmousemove", this, "onMouseMove"),
  2469. dojo.connect(d, "ontouchmove", this, "onMouseMove"),
  2470. // And these are called at the end of the drag
  2471. dojo.connect(d, "onmouseup", this, "onMouseUp"),
  2472. dojo.connect(d, "ontouchend", this, "onMouseUp"),
  2473. // cancel text selection and text dragging
  2474. dojo.connect(d, "ondragstart", dojo.stopEvent),
  2475. dojo.connect(d.body, "onselectstart", dojo.stopEvent)
  2476. ];
  2477. // notify that the move has started
  2478. if(h && h.onMoveStart){
  2479. h.onMoveStart(this);
  2480. }
  2481. },
  2482. // mouse event processors
  2483. onMouseMove: function(e){
  2484. // summary:
  2485. // event processor for onmousemove/ontouchmove
  2486. // e: Event
  2487. // mouse/touch event
  2488. dojo.dnd.autoScroll(e);
  2489. var m = this.marginBox,
  2490. pos = e.touches ? e.touches[0] : e;
  2491. this.host.onMove(this, {l: m.l + pos.pageX, t: m.t + pos.pageY}, e);
  2492. dojo.stopEvent(e);
  2493. },
  2494. onMouseUp: function(e){
  2495. if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
  2496. e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
  2497. this.destroy();
  2498. }
  2499. dojo.stopEvent(e);
  2500. },
  2501. // utilities
  2502. onFirstMove: function(e){
  2503. // summary:
  2504. // makes the node absolute; it is meant to be called only once.
  2505. // relative and absolutely positioned nodes are assumed to use pixel units
  2506. var s = this.node.style, l, t, h = this.host;
  2507. switch(s.position){
  2508. case "relative":
  2509. case "absolute":
  2510. // assume that left and top values are in pixels already
  2511. l = Math.round(parseFloat(s.left)) || 0;
  2512. t = Math.round(parseFloat(s.top)) || 0;
  2513. break;
  2514. default:
  2515. s.position = "absolute"; // enforcing the absolute mode
  2516. var m = dojo.marginBox(this.node);
  2517. // event.pageX/pageY (which we used to generate the initial
  2518. // margin box) includes padding and margin set on the body.
  2519. // However, setting the node's position to absolute and then
  2520. // doing dojo.marginBox on it *doesn't* take that additional
  2521. // space into account - so we need to subtract the combined
  2522. // padding and margin. We use getComputedStyle and
  2523. // _getMarginBox/_getContentBox to avoid the extra lookup of
  2524. // the computed style.
  2525. var b = dojo.doc.body;
  2526. var bs = dojo.getComputedStyle(b);
  2527. var bm = dojo._getMarginBox(b, bs);
  2528. var bc = dojo._getContentBox(b, bs);
  2529. l = m.l - (bc.l - bm.l);
  2530. t = m.t - (bc.t - bm.t);
  2531. break;
  2532. }
  2533. this.marginBox.l = l - this.marginBox.l;
  2534. this.marginBox.t = t - this.marginBox.t;
  2535. if(h && h.onFirstMove){
  2536. h.onFirstMove(this, e);
  2537. }
  2538. // Disconnect onmousemove and ontouchmove events that call this function
  2539. dojo.disconnect(this.events.shift());
  2540. dojo.disconnect(this.events.shift());
  2541. },
  2542. destroy: function(){
  2543. // summary:
  2544. // stops the move, deletes all references, so the object can be garbage-collected
  2545. dojo.forEach(this.events, dojo.disconnect);
  2546. // undo global settings
  2547. var h = this.host;
  2548. if(h && h.onMoveStop){
  2549. h.onMoveStop(this);
  2550. }
  2551. // destroy objects
  2552. this.events = this.node = this.host = null;
  2553. }
  2554. });
  2555. }
  2556. if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2557. dojo._hasResource["dojo.dnd.Moveable"] = true;
  2558. dojo.provide("dojo.dnd.Moveable");
  2559. /*=====
  2560. dojo.declare("dojo.dnd.__MoveableArgs", [], {
  2561. // handle: Node||String
  2562. // A node (or node's id), which is used as a mouse handle.
  2563. // If omitted, the node itself is used as a handle.
  2564. handle: null,
  2565. // delay: Number
  2566. // delay move by this number of pixels
  2567. delay: 0,
  2568. // skip: Boolean
  2569. // skip move of form elements
  2570. skip: false,
  2571. // mover: Object
  2572. // a constructor of custom Mover
  2573. mover: dojo.dnd.Mover
  2574. });
  2575. =====*/
  2576. dojo.declare("dojo.dnd.Moveable", null, {
  2577. // object attributes (for markup)
  2578. handle: "",
  2579. delay: 0,
  2580. skip: false,
  2581. constructor: function(node, params){
  2582. // summary:
  2583. // an object, which makes a node moveable
  2584. // node: Node
  2585. // a node (or node's id) to be moved
  2586. // params: dojo.dnd.__MoveableArgs?
  2587. // optional parameters
  2588. this.node = dojo.byId(node);
  2589. if(!params){ params = {}; }
  2590. this.handle = params.handle ? dojo.byId(params.handle) : null;
  2591. if(!this.handle){ this.handle = this.node; }
  2592. this.delay = params.delay > 0 ? params.delay : 0;
  2593. this.skip = params.skip;
  2594. this.mover = params.mover ? params.mover : dojo.dnd.Mover;
  2595. this.events = [
  2596. dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
  2597. dojo.connect(this.handle, "ontouchstart", this, "onMouseDown"),
  2598. // cancel text selection and text dragging
  2599. dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
  2600. dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
  2601. ];
  2602. },
  2603. // markup methods
  2604. markupFactory: function(params, node){
  2605. return new dojo.dnd.Moveable(node, params);
  2606. },
  2607. // methods
  2608. destroy: function(){
  2609. // summary:
  2610. // stops watching for possible move, deletes all references, so the object can be garbage-collected
  2611. dojo.forEach(this.events, dojo.disconnect);
  2612. this.events = this.node = this.handle = null;
  2613. },
  2614. // mouse event processors
  2615. onMouseDown: function(e){
  2616. // summary:
  2617. // event processor for onmousedown/ontouchstart, creates a Mover for the node
  2618. // e: Event
  2619. // mouse/touch event
  2620. if(this.skip && dojo.dnd.isFormElement(e)){ return; }
  2621. if(this.delay){
  2622. this.events.push(
  2623. dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
  2624. dojo.connect(this.handle, "ontouchmove", this, "onMouseMove"),
  2625. dojo.connect(this.handle, "onmouseup", this, "onMouseUp"),
  2626. dojo.connect(this.handle, "ontouchend", this, "onMouseUp")
  2627. );
  2628. var pos = e.touches ? e.touches[0] : e;
  2629. this._lastX = pos.pageX;
  2630. this._lastY = pos.pageY;
  2631. }else{
  2632. this.onDragDetected(e);
  2633. }
  2634. dojo.stopEvent(e);
  2635. },
  2636. onMouseMove: function(e){
  2637. // summary:
  2638. // event processor for onmousemove/ontouchmove, used only for delayed drags
  2639. // e: Event
  2640. // mouse/touch event
  2641. var pos = e.touches ? e.touches[0] : e;
  2642. if(Math.abs(pos.pageX - this._lastX) > this.delay || Math.abs(pos.pageY - this._lastY) > this.delay){
  2643. this.onMouseUp(e);
  2644. this.onDragDetected(e);
  2645. }
  2646. dojo.stopEvent(e);
  2647. },
  2648. onMouseUp: function(e){
  2649. // summary:
  2650. // event processor for onmouseup, used only for delayed drags
  2651. // e: Event
  2652. // mouse event
  2653. for(var i = 0; i < 2; ++i){
  2654. dojo.disconnect(this.events.pop());
  2655. }
  2656. dojo.stopEvent(e);
  2657. },
  2658. onSelectStart: function(e){
  2659. // summary:
  2660. // event processor for onselectevent and ondragevent
  2661. // e: Event
  2662. // mouse event
  2663. if(!this.skip || !dojo.dnd.isFormElement(e)){
  2664. dojo.stopEvent(e);
  2665. }
  2666. },
  2667. // local events
  2668. onDragDetected: function(/* Event */ e){
  2669. // summary:
  2670. // called when the drag is detected;
  2671. // responsible for creation of the mover
  2672. new this.mover(this.node, e, this);
  2673. },
  2674. onMoveStart: function(/* dojo.dnd.Mover */ mover){
  2675. // summary:
  2676. // called before every move operation
  2677. dojo.publish("/dnd/move/start", [mover]);
  2678. dojo.addClass(dojo.body(), "dojoMove");
  2679. dojo.addClass(this.node, "dojoMoveItem");
  2680. },
  2681. onMoveStop: function(/* dojo.dnd.Mover */ mover){
  2682. // summary:
  2683. // called after every move operation
  2684. dojo.publish("/dnd/move/stop", [mover]);
  2685. dojo.removeClass(dojo.body(), "dojoMove");
  2686. dojo.removeClass(this.node, "dojoMoveItem");
  2687. },
  2688. onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
  2689. // summary:
  2690. // called during the very first move notification;
  2691. // can be used to initialize coordinates, can be overwritten.
  2692. // default implementation does nothing
  2693. },
  2694. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
  2695. // summary:
  2696. // called during every move notification;
  2697. // should actually move the node; can be overwritten.
  2698. this.onMoving(mover, leftTop);
  2699. var s = mover.node.style;
  2700. s.left = leftTop.l + "px";
  2701. s.top = leftTop.t + "px";
  2702. this.onMoved(mover, leftTop);
  2703. },
  2704. onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  2705. // summary:
  2706. // called before every incremental move; can be overwritten.
  2707. // default implementation does nothing
  2708. },
  2709. onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  2710. // summary:
  2711. // called after every incremental move; can be overwritten.
  2712. // default implementation does nothing
  2713. }
  2714. });
  2715. }
  2716. if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2717. dojo._hasResource["dojo.dnd.move"] = true;
  2718. dojo.provide("dojo.dnd.move");
  2719. /*=====
  2720. dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
  2721. // constraints: Function
  2722. // Calculates a constraint box.
  2723. // It is called in a context of the moveable object.
  2724. constraints: function(){},
  2725. // within: Boolean
  2726. // restrict move within boundaries.
  2727. within: false
  2728. });
  2729. =====*/
  2730. dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
  2731. // object attributes (for markup)
  2732. constraints: function(){},
  2733. within: false,
  2734. // markup methods
  2735. markupFactory: function(params, node){
  2736. return new dojo.dnd.move.constrainedMoveable(node, params);
  2737. },
  2738. constructor: function(node, params){
  2739. // summary:
  2740. // an object that makes a node moveable
  2741. // node: Node
  2742. // a node (or node's id) to be moved
  2743. // params: dojo.dnd.move.__constrainedMoveableArgs?
  2744. // an optional object with additional parameters;
  2745. // the rest is passed to the base class
  2746. if(!params){ params = {}; }
  2747. this.constraints = params.constraints;
  2748. this.within = params.within;
  2749. },
  2750. onFirstMove: function(/* dojo.dnd.Mover */ mover){
  2751. // summary:
  2752. // called during the very first move notification;
  2753. // can be used to initialize coordinates, can be overwritten.
  2754. var c = this.constraintBox = this.constraints.call(this, mover);
  2755. c.r = c.l + c.w;
  2756. c.b = c.t + c.h;
  2757. if(this.within){
  2758. var mb = dojo._getMarginSize(mover.node);
  2759. c.r -= mb.w;
  2760. c.b -= mb.h;
  2761. }
  2762. },
  2763. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  2764. // summary:
  2765. // called during every move notification;
  2766. // should actually move the node; can be overwritten.
  2767. var c = this.constraintBox, s = mover.node.style;
  2768. this.onMoving(mover, leftTop);
  2769. leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
  2770. leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
  2771. s.left = leftTop.l + "px";
  2772. s.top = leftTop.t + "px";
  2773. this.onMoved(mover, leftTop);
  2774. }
  2775. });
  2776. /*=====
  2777. dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
  2778. // box: Object
  2779. // a constraint box
  2780. box: {}
  2781. });
  2782. =====*/
  2783. dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
  2784. // box:
  2785. // object attributes (for markup)
  2786. box: {},
  2787. // markup methods
  2788. markupFactory: function(params, node){
  2789. return new dojo.dnd.move.boxConstrainedMoveable(node, params);
  2790. },
  2791. constructor: function(node, params){
  2792. // summary:
  2793. // an object, which makes a node moveable
  2794. // node: Node
  2795. // a node (or node's id) to be moved
  2796. // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
  2797. // an optional object with parameters
  2798. var box = params && params.box;
  2799. this.constraints = function(){ return box; };
  2800. }
  2801. });
  2802. /*=====
  2803. dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
  2804. // area: String
  2805. // A parent's area to restrict the move.
  2806. // Can be "margin", "border", "padding", or "content".
  2807. area: ""
  2808. });
  2809. =====*/
  2810. dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
  2811. // area:
  2812. // object attributes (for markup)
  2813. area: "content",
  2814. // markup methods
  2815. markupFactory: function(params, node){
  2816. return new dojo.dnd.move.parentConstrainedMoveable(node, params);
  2817. },
  2818. constructor: function(node, params){
  2819. // summary:
  2820. // an object, which makes a node moveable
  2821. // node: Node
  2822. // a node (or node's id) to be moved
  2823. // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
  2824. // an optional object with parameters
  2825. var area = params && params.area;
  2826. this.constraints = function(){
  2827. var n = this.node.parentNode,
  2828. s = dojo.getComputedStyle(n),
  2829. mb = dojo._getMarginBox(n, s);
  2830. if(area == "margin"){
  2831. return mb; // Object
  2832. }
  2833. var t = dojo._getMarginExtents(n, s);
  2834. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  2835. if(area == "border"){
  2836. return mb; // Object
  2837. }
  2838. t = dojo._getBorderExtents(n, s);
  2839. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  2840. if(area == "padding"){
  2841. return mb; // Object
  2842. }
  2843. t = dojo._getPadExtents(n, s);
  2844. mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
  2845. return mb; // Object
  2846. };
  2847. }
  2848. });
  2849. // patching functions one level up for compatibility
  2850. dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
  2851. dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
  2852. dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
  2853. }
  2854. if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2855. dojo._hasResource["dojo.date.stamp"] = true;
  2856. dojo.provide("dojo.date.stamp");
  2857. dojo.getObject("date.stamp", true, dojo);
  2858. // Methods to convert dates to or from a wire (string) format using well-known conventions
  2859. dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
  2860. // summary:
  2861. // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
  2862. //
  2863. // description:
  2864. // Accepts a string formatted according to a profile of ISO8601 as defined by
  2865. // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
  2866. // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
  2867. // The following combinations are valid:
  2868. //
  2869. // * dates only
  2870. // | * yyyy
  2871. // | * yyyy-MM
  2872. // | * yyyy-MM-dd
  2873. // * times only, with an optional time zone appended
  2874. // | * THH:mm
  2875. // | * THH:mm:ss
  2876. // | * THH:mm:ss.SSS
  2877. // * and "datetimes" which could be any combination of the above
  2878. //
  2879. // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
  2880. // Assumes the local time zone if not specified. Does not validate. Improperly formatted
  2881. // input may return null. Arguments which are out of bounds will be handled
  2882. // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
  2883. // Only years between 100 and 9999 are supported.
  2884. //
  2885. // formattedString:
  2886. // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
  2887. //
  2888. // defaultTime:
  2889. // Used for defaults for fields omitted in the formattedString.
  2890. // Uses 1970-01-01T00:00:00.0Z by default.
  2891. if(!dojo.date.stamp._isoRegExp){
  2892. dojo.date.stamp._isoRegExp =
  2893. //TODO: could be more restrictive and check for 00-59, etc.
  2894. /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
  2895. }
  2896. var match = dojo.date.stamp._isoRegExp.exec(formattedString),
  2897. result = null;
  2898. if(match){
  2899. match.shift();
  2900. if(match[1]){match[1]--;} // Javascript Date months are 0-based
  2901. if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
  2902. if(defaultTime){
  2903. // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
  2904. defaultTime = new Date(defaultTime);
  2905. dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
  2906. return defaultTime["get" + prop]();
  2907. }), function(value, index){
  2908. match[index] = match[index] || value;
  2909. });
  2910. }
  2911. result = new Date(match[0]||1970, match[1]||0, match[2]||1, match[3]||0, match[4]||0, match[5]||0, match[6]||0); //TODO: UTC defaults
  2912. if(match[0] < 100){
  2913. result.setFullYear(match[0] || 1970);
  2914. }
  2915. var offset = 0,
  2916. zoneSign = match[7] && match[7].charAt(0);
  2917. if(zoneSign != 'Z'){
  2918. offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
  2919. if(zoneSign != '-'){ offset *= -1; }
  2920. }
  2921. if(zoneSign){
  2922. offset -= result.getTimezoneOffset();
  2923. }
  2924. if(offset){
  2925. result.setTime(result.getTime() + offset * 60000);
  2926. }
  2927. }
  2928. return result; // Date or null
  2929. };
  2930. /*=====
  2931. dojo.date.stamp.__Options = function(){
  2932. // selector: String
  2933. // "date" or "time" for partial formatting of the Date object.
  2934. // Both date and time will be formatted by default.
  2935. // zulu: Boolean
  2936. // if true, UTC/GMT is used for a timezone
  2937. // milliseconds: Boolean
  2938. // if true, output milliseconds
  2939. this.selector = selector;
  2940. this.zulu = zulu;
  2941. this.milliseconds = milliseconds;
  2942. }
  2943. =====*/
  2944. dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
  2945. // summary:
  2946. // Format a Date object as a string according a subset of the ISO-8601 standard
  2947. //
  2948. // description:
  2949. // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
  2950. // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
  2951. // Does not check bounds. Only years between 100 and 9999 are supported.
  2952. //
  2953. // dateObject:
  2954. // A Date object
  2955. var _ = function(n){ return (n < 10) ? "0" + n : n; };
  2956. options = options || {};
  2957. var formattedDate = [],
  2958. getter = options.zulu ? "getUTC" : "get",
  2959. date = "";
  2960. if(options.selector != "time"){
  2961. var year = dateObject[getter+"FullYear"]();
  2962. date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
  2963. }
  2964. formattedDate.push(date);
  2965. if(options.selector != "date"){
  2966. var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
  2967. var millis = dateObject[getter+"Milliseconds"]();
  2968. if(options.milliseconds){
  2969. time += "."+ (millis < 100 ? "0" : "") + _(millis);
  2970. }
  2971. if(options.zulu){
  2972. time += "Z";
  2973. }else if(options.selector != "time"){
  2974. var timezoneOffset = dateObject.getTimezoneOffset();
  2975. var absOffset = Math.abs(timezoneOffset);
  2976. time += (timezoneOffset > 0 ? "-" : "+") +
  2977. _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
  2978. }
  2979. formattedDate.push(time);
  2980. }
  2981. return formattedDate.join('T'); // String
  2982. };
  2983. }
  2984. if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  2985. dojo._hasResource["dojo.parser"] = true;
  2986. dojo.provide("dojo.parser");
  2987. new Date("X"); // workaround for #11279, new Date("") == NaN
  2988. dojo.parser = new function(){
  2989. // summary:
  2990. // The Dom/Widget parsing package
  2991. var d = dojo;
  2992. function val2type(/*Object*/ value){
  2993. // summary:
  2994. // Returns name of type of given value.
  2995. if(d.isString(value)){ return "string"; }
  2996. if(typeof value == "number"){ return "number"; }
  2997. if(typeof value == "boolean"){ return "boolean"; }
  2998. if(d.isFunction(value)){ return "function"; }
  2999. if(d.isArray(value)){ return "array"; } // typeof [] == "object"
  3000. if(value instanceof Date) { return "date"; } // assume timestamp
  3001. if(value instanceof d._Url){ return "url"; }
  3002. return "object";
  3003. }
  3004. function str2obj(/*String*/ value, /*String*/ type){
  3005. // summary:
  3006. // Convert given string value to given type
  3007. switch(type){
  3008. case "string":
  3009. return value;
  3010. case "number":
  3011. return value.length ? Number(value) : NaN;
  3012. case "boolean":
  3013. // for checked/disabled value might be "" or "checked". interpret as true.
  3014. return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
  3015. case "function":
  3016. if(d.isFunction(value)){
  3017. // IE gives us a function, even when we say something like onClick="foo"
  3018. // (in which case it gives us an invalid function "function(){ foo }").
  3019. // Therefore, convert to string
  3020. value=value.toString();
  3021. value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
  3022. }
  3023. try{
  3024. if(value === "" || value.search(/[^\w\.]+/i) != -1){
  3025. // The user has specified some text for a function like "return x+5"
  3026. return new Function(value);
  3027. }else{
  3028. // The user has specified the name of a function like "myOnClick"
  3029. // or a single word function "return"
  3030. return d.getObject(value, false) || new Function(value);
  3031. }
  3032. }catch(e){ return new Function(); }
  3033. case "array":
  3034. return value ? value.split(/\s*,\s*/) : [];
  3035. case "date":
  3036. switch(value){
  3037. case "": return new Date(""); // the NaN of dates
  3038. case "now": return new Date(); // current date
  3039. default: return d.date.stamp.fromISOString(value);
  3040. }
  3041. case "url":
  3042. return d.baseUrl + value;
  3043. default:
  3044. return d.fromJson(value);
  3045. }
  3046. }
  3047. var dummyClass = {}, instanceClasses = {
  3048. // map from fully qualified name (like "dijit.Button") to structure like
  3049. // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
  3050. };
  3051. // Widgets like BorderContainer add properties to _Widget via dojo.extend().
  3052. // If BorderContainer is loaded after _Widget's parameter list has been cached,
  3053. // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
  3054. // TODO: remove this in 2.0, when we stop caching parameters.
  3055. d.connect(d, "extend", function(){
  3056. instanceClasses = {};
  3057. });
  3058. function getProtoInfo(cls, params){
  3059. // cls: A prototype
  3060. // The prototype of the class to check props on
  3061. // params: Object
  3062. // The parameters object to mix found parameters onto.
  3063. for(var name in cls){
  3064. if(name.charAt(0)=="_"){ continue; } // skip internal properties
  3065. if(name in dummyClass){ continue; } // skip "constructor" and "toString"
  3066. params[name] = val2type(cls[name]);
  3067. }
  3068. return params;
  3069. }
  3070. function getClassInfo(/*String*/ className, /*Boolean*/ skipParamsLookup){
  3071. // summary:
  3072. // Maps a widget name string like "dijit.form.Button" to the widget constructor itself,
  3073. // and a list of that widget's parameters and their types
  3074. // className:
  3075. // fully qualified name (like "dijit.form.Button")
  3076. // returns:
  3077. // structure like
  3078. // {
  3079. // cls: dijit.Button,
  3080. // params: { label: "string", disabled: "boolean"}
  3081. // }
  3082. var c = instanceClasses[className];
  3083. if(!c){
  3084. // get pointer to widget class
  3085. var cls = d.getObject(className), params = null;
  3086. if(!cls){ return null; } // class not defined [yet]
  3087. if(!skipParamsLookup){ // from fastpath, we don't need to lookup the attrs on the proto because they are explicit
  3088. params = getProtoInfo(cls.prototype, {})
  3089. }
  3090. c = { cls: cls, params: params };
  3091. }else if(!skipParamsLookup && !c.params){
  3092. // if we're calling getClassInfo and have a cls proto, but no params info, scan that cls for params now
  3093. // and update the pointer in instanceClasses[className]. This happens when a widget appears in another
  3094. // widget's template which still uses dojoType, but an instance of the widget appears prior with a data-dojo-type,
  3095. // skipping this lookup the first time.
  3096. c.params = getProtoInfo(c.cls.prototype, {});
  3097. }
  3098. return c;
  3099. }
  3100. this._functionFromScript = function(script, attrData){
  3101. // summary:
  3102. // Convert a <script type="dojo/method" args="a, b, c"> ... </script>
  3103. // into a function
  3104. // script: DOMNode
  3105. // The <script> DOMNode
  3106. // attrData: String
  3107. // For HTML5 compliance, searches for attrData + "args" (typically
  3108. // "data-dojo-args") instead of "args"
  3109. var preamble = "";
  3110. var suffix = "";
  3111. var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args"));
  3112. if(argsStr){
  3113. d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
  3114. preamble += "var "+part+" = arguments["+idx+"]; ";
  3115. });
  3116. }
  3117. var withStr = script.getAttribute("with");
  3118. if(withStr && withStr.length){
  3119. d.forEach(withStr.split(/\s*,\s*/), function(part){
  3120. preamble += "with("+part+"){";
  3121. suffix += "}";
  3122. });
  3123. }
  3124. return new Function(preamble+script.innerHTML+suffix);
  3125. };
  3126. this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){
  3127. // summary:
  3128. // Takes array of nodes, and turns them into class instances and
  3129. // potentially calls a startup method to allow them to connect with
  3130. // any children.
  3131. // nodes: Array
  3132. // Array of nodes or objects like
  3133. // | {
  3134. // | type: "dijit.form.Button",
  3135. // | node: DOMNode,
  3136. // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
  3137. // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
  3138. // | }
  3139. // mixin: Object?
  3140. // An object that will be mixed in with each node in the array.
  3141. // Values in the mixin will override values in the node, if they
  3142. // exist.
  3143. // args: Object?
  3144. // An object used to hold kwArgs for instantiation.
  3145. // See parse.args argument for details.
  3146. var thelist = [],
  3147. mixin = mixin||{};
  3148. args = args||{};
  3149. // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
  3150. var attrName = (args.scope || d._scopeName) + "Type", // typically "dojoType"
  3151. attrData = "data-" + (args.scope || d._scopeName) + "-"; // typically "data-dojo-"
  3152. d.forEach(nodes, function(obj){
  3153. if(!obj){ return; }
  3154. // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.
  3155. var node, type, clsInfo, clazz, scripts, fastpath;
  3156. if(obj.node){
  3157. // new format of nodes[] array, object w/lots of properties pre-computed for me
  3158. node = obj.node;
  3159. type = obj.type;
  3160. fastpath = obj.fastpath;
  3161. clsInfo = obj.clsInfo || (type && getClassInfo(type, fastpath));
  3162. clazz = clsInfo && clsInfo.cls;
  3163. scripts = obj.scripts;
  3164. }else{
  3165. // old (backwards compatible) format of nodes[] array, simple array of DOMNodes. no fastpath/data-dojo-type support here.
  3166. node = obj;
  3167. type = attrName in mixin ? mixin[attrName] : node.getAttribute(attrName);
  3168. clsInfo = type && getClassInfo(type);
  3169. clazz = clsInfo && clsInfo.cls;
  3170. scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] :
  3171. d.query("> script[type^='dojo/']", node));
  3172. }
  3173. if(!clsInfo){
  3174. throw new Error("Could not load class '" + type);
  3175. }
  3176. // Setup hash to hold parameter settings for this widget. Start with the parameter
  3177. // settings inherited from ancestors ("dir" and "lang").
  3178. // Inherited setting may later be overridden by explicit settings on node itself.
  3179. var params = {};
  3180. if(args.defaults){
  3181. // settings for the document itself (or whatever subtree is being parsed)
  3182. d._mixin(params, args.defaults);
  3183. }
  3184. if(obj.inherited){
  3185. // settings from dir=rtl or lang=... on a node above this node
  3186. d._mixin(params, obj.inherited);
  3187. }
  3188. // mix things found in data-dojo-props into the params
  3189. if(fastpath){
  3190. var extra = node.getAttribute(attrData + "props");
  3191. if(extra && extra.length){
  3192. try{
  3193. extra = d.fromJson.call(args.propsThis, "{" + extra + "}");
  3194. d._mixin(params, extra);
  3195. }catch(e){
  3196. // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
  3197. throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
  3198. }
  3199. }
  3200. // For the benefit of _Templated, check if node has data-dojo-attach-point/data-dojo-attach-event
  3201. // and mix those in as though they were parameters
  3202. var attachPoint = node.getAttribute(attrData + "attach-point");
  3203. if(attachPoint){
  3204. params.dojoAttachPoint = attachPoint;
  3205. }
  3206. var attachEvent = node.getAttribute(attrData + "attach-event");
  3207. if(attachEvent){
  3208. params.dojoAttachEvent = attachEvent;
  3209. }
  3210. dojo.mixin(params, mixin);
  3211. }else{
  3212. // FIXME: we need something like "deprecateOnce()" to throw dojo.deprecation for something.
  3213. // remove this logic in 2.0
  3214. // read parameters (ie, attributes) specified on DOMNode
  3215. var attributes = node.attributes;
  3216. // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
  3217. for(var name in clsInfo.params){
  3218. var item = name in mixin ? { value:mixin[name], specified:true } : attributes.getNamedItem(name);
  3219. if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
  3220. var value = item.value;
  3221. // Deal with IE quirks for 'class' and 'style'
  3222. switch(name){
  3223. case "class":
  3224. value = "className" in mixin ? mixin.className : node.className;
  3225. break;
  3226. case "style":
  3227. value = "style" in mixin ? mixin.style : (node.style && node.style.cssText); // FIXME: Opera?
  3228. }
  3229. var _type = clsInfo.params[name];
  3230. if(typeof value == "string"){
  3231. params[name] = str2obj(value, _type);
  3232. }else{
  3233. params[name] = value;
  3234. }
  3235. }
  3236. }
  3237. // Process <script type="dojo/*"> script tags
  3238. // <script type="dojo/method" event="foo"> tags are added to params, and passed to
  3239. // the widget on instantiation.
  3240. // <script type="dojo/method"> tags (with no event) are executed after instantiation
  3241. // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
  3242. // note: dojo/* script tags cannot exist in self closing widgets, like <input />
  3243. var connects = [], // functions to connect after instantiation
  3244. calls = []; // functions to call after instantiation
  3245. d.forEach(scripts, function(script){
  3246. node.removeChild(script);
  3247. // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
  3248. var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
  3249. type = script.getAttribute("type"),
  3250. nf = d.parser._functionFromScript(script, attrData);
  3251. if(event){
  3252. if(type == "dojo/connect"){
  3253. connects.push({event: event, func: nf});
  3254. }else{
  3255. params[event] = nf;
  3256. }
  3257. }else{
  3258. calls.push(nf);
  3259. }
  3260. });
  3261. var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory;
  3262. // create the instance
  3263. var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
  3264. thelist.push(instance);
  3265. // map it to the JS namespace if that makes sense
  3266. // FIXME: in 2.0, drop jsId support. use data-dojo-id instead
  3267. var jsname = (node.getAttribute(attrData + "id") || node.getAttribute("jsId"));
  3268. if(jsname){
  3269. d.setObject(jsname, instance);
  3270. }
  3271. // process connections and startup functions
  3272. d.forEach(connects, function(connect){
  3273. d.connect(instance, connect.event, null, connect.func);
  3274. });
  3275. d.forEach(calls, function(func){
  3276. func.call(instance);
  3277. });
  3278. });
  3279. // Call startup on each top level instance if it makes sense (as for
  3280. // widgets). Parent widgets will recursively call startup on their
  3281. // (non-top level) children
  3282. if(!mixin._started){
  3283. // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
  3284. // relationships in the nodes[] array so that no getParent() call is needed.
  3285. // Note that will require a parse() call from ContentPane setting a param that the
  3286. // ContentPane is the parent widget (so that the parse doesn't call startup() on the
  3287. // ContentPane's children)
  3288. d.forEach(thelist, function(instance){
  3289. if( !args.noStart && instance &&
  3290. dojo.isFunction(instance.startup) &&
  3291. !instance._started &&
  3292. (!instance.getParent || !instance.getParent())
  3293. ){
  3294. instance.startup();
  3295. }
  3296. });
  3297. }
  3298. return thelist;
  3299. };
  3300. this.parse = function(rootNode, args){
  3301. // summary:
  3302. // Scan the DOM for class instances, and instantiate them.
  3303. //
  3304. // description:
  3305. // Search specified node (or root node) recursively for class instances,
  3306. // and instantiate them. Searches for either data-dojo-type="Class" or
  3307. // dojoType="Class" where "Class" is a a fully qualified class name,
  3308. // like `dijit.form.Button`
  3309. //
  3310. // Using `data-dojo-type`:
  3311. // Attributes using can be mixed into the parameters used to instantitate the
  3312. // Class by using a `data-dojo-props` attribute on the node being converted.
  3313. // `data-dojo-props` should be a string attribute to be converted from JSON.
  3314. //
  3315. // Using `dojoType`:
  3316. // Attributes are read from the original domNode and converted to appropriate
  3317. // types by looking up the Class prototype values. This is the default behavior
  3318. // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
  3319. // go away in Dojo 2.0.
  3320. //
  3321. // rootNode: DomNode?
  3322. // A default starting root node from which to start the parsing. Can be
  3323. // omitted, defaulting to the entire document. If omitted, the `args`
  3324. // object can be passed in this place. If the `args` object has a
  3325. // `rootNode` member, that is used.
  3326. //
  3327. // args: Object
  3328. // a kwArgs object passed along to instantiate()
  3329. //
  3330. // * noStart: Boolean?
  3331. // when set will prevent the parser from calling .startup()
  3332. // when locating the nodes.
  3333. // * rootNode: DomNode?
  3334. // identical to the function's `rootNode` argument, though
  3335. // allowed to be passed in via this `args object.
  3336. // * template: Boolean
  3337. // If true, ignores ContentPane's stopParser flag and parses contents inside of
  3338. // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
  3339. // nested inside the ContentPane to work.
  3340. // * inherited: Object
  3341. // Hash possibly containing dir and lang settings to be applied to
  3342. // parsed widgets, unless there's another setting on a sub-node that overrides
  3343. // * scope: String
  3344. // Root for attribute names to search for. If scopeName is dojo,
  3345. // will search for data-dojo-type (or dojoType). For backwards compatibility
  3346. // reasons defaults to dojo._scopeName (which is "dojo" except when
  3347. // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  3348. // * propsThis: Object
  3349. // If specified, "this" referenced from data-dojo-props will refer to propsThis.
  3350. // Intended for use from the widgets-in-template feature of `dijit._Templated`
  3351. //
  3352. // example:
  3353. // Parse all widgets on a page:
  3354. // | dojo.parser.parse();
  3355. //
  3356. // example:
  3357. // Parse all classes within the node with id="foo"
  3358. // | dojo.parser.parse(dojo.byId('foo'));
  3359. //
  3360. // example:
  3361. // Parse all classes in a page, but do not call .startup() on any
  3362. // child
  3363. // | dojo.parser.parse({ noStart: true })
  3364. //
  3365. // example:
  3366. // Parse all classes in a node, but do not call .startup()
  3367. // | dojo.parser.parse(someNode, { noStart:true });
  3368. // | // or
  3369. // | dojo.parser.parse({ noStart:true, rootNode: someNode });
  3370. // determine the root node based on the passed arguments.
  3371. var root;
  3372. if(!args && rootNode && rootNode.rootNode){
  3373. args = rootNode;
  3374. root = args.rootNode;
  3375. }else{
  3376. root = rootNode;
  3377. }
  3378. root = root ? dojo.byId(root) : dojo.body();
  3379. args = args || {};
  3380. var attrName = (args.scope || d._scopeName) + "Type", // typically "dojoType"
  3381. attrData = "data-" + (args.scope || d._scopeName) + "-"; // typically "data-dojo-"
  3382. function scan(parent, list){
  3383. // summary:
  3384. // Parent is an Object representing a DOMNode, with or without a dojoType specified.
  3385. // Scan parent's children looking for nodes with dojoType specified, storing in list[].
  3386. // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
  3387. // parent: Object
  3388. // Object representing the parent node, like
  3389. // | {
  3390. // | node: DomNode, // scan children of this node
  3391. // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node
  3392. // |
  3393. // | // attributes only set if node has dojoType specified
  3394. // | scripts: [], // empty array, put <script type=dojo/*> in here
  3395. // | clsInfo: { cls: dijit.form.Button, ...}
  3396. // | }
  3397. // list: DomNode[]
  3398. // Output array of objects (same format as parent) representing nodes to be turned into widgets
  3399. // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
  3400. var inherited = dojo.clone(parent.inherited);
  3401. dojo.forEach(["dir", "lang"], function(name){
  3402. // TODO: what if this is a widget and dir/lang are declared in data-dojo-props?
  3403. var val = parent.node.getAttribute(name);
  3404. if(val){
  3405. inherited[name] = val;
  3406. }
  3407. });
  3408. // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
  3409. var scripts = parent.clsInfo && !parent.clsInfo.cls.prototype._noScript ? parent.scripts : null;
  3410. // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
  3411. var recurse = (!parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser) || (args && args.template);
  3412. // scan parent's children looking for dojoType and <script type=dojo/*>
  3413. for(var child = parent.node.firstChild; child; child = child.nextSibling){
  3414. if(child.nodeType == 1){
  3415. // FIXME: desupport dojoType in 2.0. use data-dojo-type instead
  3416. var type, html5 = recurse && child.getAttribute(attrData + "type");
  3417. if(html5){
  3418. type = html5;
  3419. }else{
  3420. // fallback to backward compatible mode, using dojoType. remove in 2.0
  3421. type = recurse && child.getAttribute(attrName);
  3422. }
  3423. var fastpath = html5 == type;
  3424. if(type){
  3425. // if dojoType/data-dojo-type specified, add to output array of nodes to instantiate
  3426. var params = {
  3427. "type": type,
  3428. fastpath: fastpath,
  3429. clsInfo: getClassInfo(type, fastpath), // note: won't find classes declared via dojo.Declaration
  3430. node: child,
  3431. scripts: [], // <script> nodes that are parent's children
  3432. inherited: inherited // dir & lang attributes inherited from parent
  3433. };
  3434. list.push(params);
  3435. // Recurse, collecting <script type="dojo/..."> children, and also looking for
  3436. // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
  3437. scan(params, list);
  3438. }else if(scripts && child.nodeName.toLowerCase() == "script"){
  3439. // if <script type="dojo/...">, save in scripts[]
  3440. type = child.getAttribute("type");
  3441. if (type && /^dojo\/\w/i.test(type)) {
  3442. scripts.push(child);
  3443. }
  3444. }else if(recurse){
  3445. // Recurse, looking for grandchild nodes with dojoType specified
  3446. scan({
  3447. node: child,
  3448. inherited: inherited
  3449. }, list);
  3450. }
  3451. }
  3452. }
  3453. }
  3454. // Ignore bogus entries in inherited hash like {dir: ""}
  3455. var inherited = {};
  3456. if(args && args.inherited){
  3457. for(var key in args.inherited){
  3458. if(args.inherited[key]){ inherited[key] = args.inherited[key]; }
  3459. }
  3460. }
  3461. // Make list of all nodes on page w/dojoType specified
  3462. var list = [];
  3463. scan({
  3464. node: root,
  3465. inherited: inherited
  3466. }, list);
  3467. // go build the object instances
  3468. var mixin = args && args.template ? {template: true} : null;
  3469. return this.instantiate(list, mixin, args); // Array
  3470. };
  3471. }();
  3472. //Register the parser callback. It should be the first callback
  3473. //after the a11y test.
  3474. (function(){
  3475. var parseRunner = function(){
  3476. if(dojo.config.parseOnLoad){
  3477. dojo.parser.parse();
  3478. }
  3479. };
  3480. // FIXME: need to clobber cross-dependency!!
  3481. if(dojo.getObject("dijit.wai.onload") === dojo._loaders[0]){
  3482. dojo._loaders.splice(1, 0, parseRunner);
  3483. }else{
  3484. dojo._loaders.unshift(parseRunner);
  3485. }
  3486. })();
  3487. }
  3488. if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  3489. dojo._hasResource["dojo.dnd.Container"] = true;
  3490. dojo.provide("dojo.dnd.Container");
  3491. /*
  3492. Container states:
  3493. "" - normal state
  3494. "Over" - mouse over a container
  3495. Container item states:
  3496. "" - normal state
  3497. "Over" - mouse over a container item
  3498. */
  3499. /*=====
  3500. dojo.declare("dojo.dnd.__ContainerArgs", [], {
  3501. creator: function(){
  3502. // summary:
  3503. // a creator function, which takes a data item, and returns an object like that:
  3504. // {node: newNode, data: usedData, type: arrayOfStrings}
  3505. },
  3506. // skipForm: Boolean
  3507. // don't start the drag operation, if clicked on form elements
  3508. skipForm: false,
  3509. // dropParent: Node||String
  3510. // node or node's id to use as the parent node for dropped items
  3511. // (must be underneath the 'node' parameter in the DOM)
  3512. dropParent: null,
  3513. // _skipStartup: Boolean
  3514. // skip startup(), which collects children, for deferred initialization
  3515. // (this is used in the markup mode)
  3516. _skipStartup: false
  3517. });
  3518. dojo.dnd.Item = function(){
  3519. // summary:
  3520. // Represents (one of) the source node(s) being dragged.
  3521. // Contains (at least) the "type" and "data" attributes.
  3522. // type: String[]
  3523. // Type(s) of this item, by default this is ["text"]
  3524. // data: Object
  3525. // Logical representation of the object being dragged.
  3526. // If the drag object's type is "text" then data is a String,
  3527. // if it's another type then data could be a different Object,
  3528. // perhaps a name/value hash.
  3529. this.type = type;
  3530. this.data = data;
  3531. }
  3532. =====*/
  3533. dojo.declare("dojo.dnd.Container", null, {
  3534. // summary:
  3535. // a Container object, which knows when mouse hovers over it,
  3536. // and over which element it hovers
  3537. // object attributes (for markup)
  3538. skipForm: false,
  3539. /*=====
  3540. // current: DomNode
  3541. // The DOM node the mouse is currently hovered over
  3542. current: null,
  3543. // map: Hash<String, dojo.dnd.Item>
  3544. // Map from an item's id (which is also the DOMNode's id) to
  3545. // the dojo.dnd.Item itself.
  3546. map: {},
  3547. =====*/
  3548. constructor: function(node, params){
  3549. // summary:
  3550. // a constructor of the Container
  3551. // node: Node
  3552. // node or node's id to build the container on
  3553. // params: dojo.dnd.__ContainerArgs
  3554. // a dictionary of parameters
  3555. this.node = dojo.byId(node);
  3556. if(!params){ params = {}; }
  3557. this.creator = params.creator || null;
  3558. this.skipForm = params.skipForm;
  3559. this.parent = params.dropParent && dojo.byId(params.dropParent);
  3560. // class-specific variables
  3561. this.map = {};
  3562. this.current = null;
  3563. // states
  3564. this.containerState = "";
  3565. dojo.addClass(this.node, "dojoDndContainer");
  3566. // mark up children
  3567. if(!(params && params._skipStartup)){
  3568. this.startup();
  3569. }
  3570. // set up events
  3571. this.events = [
  3572. dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
  3573. dojo.connect(this.node, "onmouseout", this, "onMouseOut"),
  3574. // cancel text selection and text dragging
  3575. dojo.connect(this.node, "ondragstart", this, "onSelectStart"),
  3576. dojo.connect(this.node, "onselectstart", this, "onSelectStart")
  3577. ];
  3578. },
  3579. // object attributes (for markup)
  3580. creator: function(){
  3581. // summary:
  3582. // creator function, dummy at the moment
  3583. },
  3584. // abstract access to the map
  3585. getItem: function(/*String*/ key){
  3586. // summary:
  3587. // returns a data item by its key (id)
  3588. return this.map[key]; // dojo.dnd.Item
  3589. },
  3590. setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
  3591. // summary:
  3592. // associates a data item with its key (id)
  3593. this.map[key] = data;
  3594. },
  3595. delItem: function(/*String*/ key){
  3596. // summary:
  3597. // removes a data item from the map by its key (id)
  3598. delete this.map[key];
  3599. },
  3600. forInItems: function(/*Function*/ f, /*Object?*/ o){
  3601. // summary:
  3602. // iterates over a data map skipping members that
  3603. // are present in the empty object (IE and/or 3rd-party libraries).
  3604. o = o || dojo.global;
  3605. var m = this.map, e = dojo.dnd._empty;
  3606. for(var i in m){
  3607. if(i in e){ continue; }
  3608. f.call(o, m[i], i, this);
  3609. }
  3610. return o; // Object
  3611. },
  3612. clearItems: function(){
  3613. // summary:
  3614. // removes all data items from the map
  3615. this.map = {};
  3616. },
  3617. // methods
  3618. getAllNodes: function(){
  3619. // summary:
  3620. // returns a list (an array) of all valid child nodes
  3621. return dojo.query("> .dojoDndItem", this.parent); // NodeList
  3622. },
  3623. sync: function(){
  3624. // summary:
  3625. // sync up the node list with the data map
  3626. var map = {};
  3627. this.getAllNodes().forEach(function(node){
  3628. if(node.id){
  3629. var item = this.getItem(node.id);
  3630. if(item){
  3631. map[node.id] = item;
  3632. return;
  3633. }
  3634. }else{
  3635. node.id = dojo.dnd.getUniqueId();
  3636. }
  3637. var type = node.getAttribute("dndType"),
  3638. data = node.getAttribute("dndData");
  3639. map[node.id] = {
  3640. data: data || node.innerHTML,
  3641. type: type ? type.split(/\s*,\s*/) : ["text"]
  3642. };
  3643. }, this);
  3644. this.map = map;
  3645. return this; // self
  3646. },
  3647. insertNodes: function(data, before, anchor){
  3648. // summary:
  3649. // inserts an array of new nodes before/after an anchor node
  3650. // data: Array
  3651. // a list of data items, which should be processed by the creator function
  3652. // before: Boolean
  3653. // insert before the anchor, if true, and after the anchor otherwise
  3654. // anchor: Node
  3655. // the anchor node to be used as a point of insertion
  3656. if(!this.parent.firstChild){
  3657. anchor = null;
  3658. }else if(before){
  3659. if(!anchor){
  3660. anchor = this.parent.firstChild;
  3661. }
  3662. }else{
  3663. if(anchor){
  3664. anchor = anchor.nextSibling;
  3665. }
  3666. }
  3667. if(anchor){
  3668. for(var i = 0; i < data.length; ++i){
  3669. var t = this._normalizedCreator(data[i]);
  3670. this.setItem(t.node.id, {data: t.data, type: t.type});
  3671. this.parent.insertBefore(t.node, anchor);
  3672. }
  3673. }else{
  3674. for(var i = 0; i < data.length; ++i){
  3675. var t = this._normalizedCreator(data[i]);
  3676. this.setItem(t.node.id, {data: t.data, type: t.type});
  3677. this.parent.appendChild(t.node);
  3678. }
  3679. }
  3680. return this; // self
  3681. },
  3682. destroy: function(){
  3683. // summary:
  3684. // prepares this object to be garbage-collected
  3685. dojo.forEach(this.events, dojo.disconnect);
  3686. this.clearItems();
  3687. this.node = this.parent = this.current = null;
  3688. },
  3689. // markup methods
  3690. markupFactory: function(params, node){
  3691. params._skipStartup = true;
  3692. return new dojo.dnd.Container(node, params);
  3693. },
  3694. startup: function(){
  3695. // summary:
  3696. // collects valid child items and populate the map
  3697. // set up the real parent node
  3698. if(!this.parent){
  3699. // use the standard algorithm, if not assigned
  3700. this.parent = this.node;
  3701. if(this.parent.tagName.toLowerCase() == "table"){
  3702. var c = this.parent.getElementsByTagName("tbody");
  3703. if(c && c.length){ this.parent = c[0]; }
  3704. }
  3705. }
  3706. this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
  3707. // process specially marked children
  3708. this.sync();
  3709. },
  3710. // mouse events
  3711. onMouseOver: function(e){
  3712. // summary:
  3713. // event processor for onmouseover
  3714. // e: Event
  3715. // mouse event
  3716. var n = e.relatedTarget;
  3717. while(n){
  3718. if(n == this.node){ break; }
  3719. try{
  3720. n = n.parentNode;
  3721. }catch(x){
  3722. n = null;
  3723. }
  3724. }
  3725. if(!n){
  3726. this._changeState("Container", "Over");
  3727. this.onOverEvent();
  3728. }
  3729. n = this._getChildByEvent(e);
  3730. if(this.current == n){ return; }
  3731. if(this.current){ this._removeItemClass(this.current, "Over"); }
  3732. if(n){ this._addItemClass(n, "Over"); }
  3733. this.current = n;
  3734. },
  3735. onMouseOut: function(e){
  3736. // summary:
  3737. // event processor for onmouseout
  3738. // e: Event
  3739. // mouse event
  3740. for(var n = e.relatedTarget; n;){
  3741. if(n == this.node){ return; }
  3742. try{
  3743. n = n.parentNode;
  3744. }catch(x){
  3745. n = null;
  3746. }
  3747. }
  3748. if(this.current){
  3749. this._removeItemClass(this.current, "Over");
  3750. this.current = null;
  3751. }
  3752. this._changeState("Container", "");
  3753. this.onOutEvent();
  3754. },
  3755. onSelectStart: function(e){
  3756. // summary:
  3757. // event processor for onselectevent and ondragevent
  3758. // e: Event
  3759. // mouse event
  3760. if(!this.skipForm || !dojo.dnd.isFormElement(e)){
  3761. dojo.stopEvent(e);
  3762. }
  3763. },
  3764. // utilities
  3765. onOverEvent: function(){
  3766. // summary:
  3767. // this function is called once, when mouse is over our container
  3768. },
  3769. onOutEvent: function(){
  3770. // summary:
  3771. // this function is called once, when mouse is out of our container
  3772. },
  3773. _changeState: function(type, newState){
  3774. // summary:
  3775. // changes a named state to new state value
  3776. // type: String
  3777. // a name of the state to change
  3778. // newState: String
  3779. // new state
  3780. var prefix = "dojoDnd" + type;
  3781. var state = type.toLowerCase() + "State";
  3782. //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  3783. dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  3784. this[state] = newState;
  3785. },
  3786. _addItemClass: function(node, type){
  3787. // summary:
  3788. // adds a class with prefix "dojoDndItem"
  3789. // node: Node
  3790. // a node
  3791. // type: String
  3792. // a variable suffix for a class name
  3793. dojo.addClass(node, "dojoDndItem" + type);
  3794. },
  3795. _removeItemClass: function(node, type){
  3796. // summary:
  3797. // removes a class with prefix "dojoDndItem"
  3798. // node: Node
  3799. // a node
  3800. // type: String
  3801. // a variable suffix for a class name
  3802. dojo.removeClass(node, "dojoDndItem" + type);
  3803. },
  3804. _getChildByEvent: function(e){
  3805. // summary:
  3806. // gets a child, which is under the mouse at the moment, or null
  3807. // e: Event
  3808. // a mouse event
  3809. var node = e.target;
  3810. if(node){
  3811. for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
  3812. if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
  3813. }
  3814. }
  3815. return null;
  3816. },
  3817. _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
  3818. // summary:
  3819. // adds all necessary data to the output of the user-supplied creator function
  3820. var t = (this.creator || this.defaultCreator).call(this, item, hint);
  3821. if(!dojo.isArray(t.type)){ t.type = ["text"]; }
  3822. if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
  3823. dojo.addClass(t.node, "dojoDndItem");
  3824. return t;
  3825. }
  3826. });
  3827. dojo.dnd._createNode = function(tag){
  3828. // summary:
  3829. // returns a function, which creates an element of given tag
  3830. // (SPAN by default) and sets its innerHTML to given text
  3831. // tag: String
  3832. // a tag name or empty for SPAN
  3833. if(!tag){ return dojo.dnd._createSpan; }
  3834. return function(text){ // Function
  3835. return dojo.create(tag, {innerHTML: text}); // Node
  3836. };
  3837. };
  3838. dojo.dnd._createTrTd = function(text){
  3839. // summary:
  3840. // creates a TR/TD structure with given text as an innerHTML of TD
  3841. // text: String
  3842. // a text for TD
  3843. var tr = dojo.create("tr");
  3844. dojo.create("td", {innerHTML: text}, tr);
  3845. return tr; // Node
  3846. };
  3847. dojo.dnd._createSpan = function(text){
  3848. // summary:
  3849. // creates a SPAN element with given text as its innerHTML
  3850. // text: String
  3851. // a text for SPAN
  3852. return dojo.create("span", {innerHTML: text}); // Node
  3853. };
  3854. // dojo.dnd._defaultCreatorNodes: Object
  3855. // a dictionary that maps container tag names to child tag names
  3856. dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
  3857. dojo.dnd._defaultCreator = function(node){
  3858. // summary:
  3859. // takes a parent node, and returns an appropriate creator function
  3860. // node: Node
  3861. // a container node
  3862. var tag = node.tagName.toLowerCase();
  3863. var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
  3864. dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
  3865. return function(item, hint){ // Function
  3866. var isObj = item && dojo.isObject(item), data, type, n;
  3867. if(isObj && item.tagName && item.nodeType && item.getAttribute){
  3868. // process a DOM node
  3869. data = item.getAttribute("dndData") || item.innerHTML;
  3870. type = item.getAttribute("dndType");
  3871. type = type ? type.split(/\s*,\s*/) : ["text"];
  3872. n = item; // this node is going to be moved rather than copied
  3873. }else{
  3874. // process a DnD item object or a string
  3875. data = (isObj && item.data) ? item.data : item;
  3876. type = (isObj && item.type) ? item.type : ["text"];
  3877. n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
  3878. }
  3879. if(!n.id){
  3880. n.id = dojo.dnd.getUniqueId();
  3881. }
  3882. return {node: n, data: data, type: type};
  3883. };
  3884. };
  3885. }
  3886. if(!dojo._hasResource["dojo.dnd.Selector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  3887. dojo._hasResource["dojo.dnd.Selector"] = true;
  3888. dojo.provide("dojo.dnd.Selector");
  3889. /*
  3890. Container item states:
  3891. "" - an item is not selected
  3892. "Selected" - an item is selected
  3893. "Anchor" - an item is selected, and is an anchor for a "shift" selection
  3894. */
  3895. /*=====
  3896. dojo.declare("dojo.dnd.__SelectorArgs", [dojo.dnd.__ContainerArgs], {
  3897. // singular: Boolean
  3898. // allows selection of only one element, if true
  3899. singular: false,
  3900. // autoSync: Boolean
  3901. // autosynchronizes the source with its list of DnD nodes,
  3902. autoSync: false
  3903. });
  3904. =====*/
  3905. dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, {
  3906. // summary:
  3907. // a Selector object, which knows how to select its children
  3908. /*=====
  3909. // selection: Set<String>
  3910. // The set of id's that are currently selected, such that this.selection[id] == 1
  3911. // if the node w/that id is selected. Can iterate over selected node's id's like:
  3912. // | for(var id in this.selection)
  3913. selection: {},
  3914. =====*/
  3915. constructor: function(node, params){
  3916. // summary:
  3917. // constructor of the Selector
  3918. // node: Node||String
  3919. // node or node's id to build the selector on
  3920. // params: dojo.dnd.__SelectorArgs?
  3921. // a dictionary of parameters
  3922. if(!params){ params = {}; }
  3923. this.singular = params.singular;
  3924. this.autoSync = params.autoSync;
  3925. // class-specific variables
  3926. this.selection = {};
  3927. this.anchor = null;
  3928. this.simpleSelection = false;
  3929. // set up events
  3930. this.events.push(
  3931. dojo.connect(this.node, "onmousedown", this, "onMouseDown"),
  3932. dojo.connect(this.node, "onmouseup", this, "onMouseUp"));
  3933. },
  3934. // object attributes (for markup)
  3935. singular: false, // is singular property
  3936. // methods
  3937. getSelectedNodes: function(){
  3938. // summary:
  3939. // returns a list (an array) of selected nodes
  3940. var t = new dojo.NodeList();
  3941. var e = dojo.dnd._empty;
  3942. for(var i in this.selection){
  3943. if(i in e){ continue; }
  3944. t.push(dojo.byId(i));
  3945. }
  3946. return t; // NodeList
  3947. },
  3948. selectNone: function(){
  3949. // summary:
  3950. // unselects all items
  3951. return this._removeSelection()._removeAnchor(); // self
  3952. },
  3953. selectAll: function(){
  3954. // summary:
  3955. // selects all items
  3956. this.forInItems(function(data, id){
  3957. this._addItemClass(dojo.byId(id), "Selected");
  3958. this.selection[id] = 1;
  3959. }, this);
  3960. return this._removeAnchor(); // self
  3961. },
  3962. deleteSelectedNodes: function(){
  3963. // summary:
  3964. // deletes all selected items
  3965. var e = dojo.dnd._empty;
  3966. for(var i in this.selection){
  3967. if(i in e){ continue; }
  3968. var n = dojo.byId(i);
  3969. this.delItem(i);
  3970. dojo.destroy(n);
  3971. }
  3972. this.anchor = null;
  3973. this.selection = {};
  3974. return this; // self
  3975. },
  3976. forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
  3977. // summary:
  3978. // iterates over selected items;
  3979. // see `dojo.dnd.Container.forInItems()` for details
  3980. o = o || dojo.global;
  3981. var s = this.selection, e = dojo.dnd._empty;
  3982. for(var i in s){
  3983. if(i in e){ continue; }
  3984. f.call(o, this.getItem(i), i, this);
  3985. }
  3986. },
  3987. sync: function(){
  3988. // summary:
  3989. // sync up the node list with the data map
  3990. dojo.dnd.Selector.superclass.sync.call(this);
  3991. // fix the anchor
  3992. if(this.anchor){
  3993. if(!this.getItem(this.anchor.id)){
  3994. this.anchor = null;
  3995. }
  3996. }
  3997. // fix the selection
  3998. var t = [], e = dojo.dnd._empty;
  3999. for(var i in this.selection){
  4000. if(i in e){ continue; }
  4001. if(!this.getItem(i)){
  4002. t.push(i);
  4003. }
  4004. }
  4005. dojo.forEach(t, function(i){
  4006. delete this.selection[i];
  4007. }, this);
  4008. return this; // self
  4009. },
  4010. insertNodes: function(addSelected, data, before, anchor){
  4011. // summary:
  4012. // inserts new data items (see `dojo.dnd.Container.insertNodes()` method for details)
  4013. // addSelected: Boolean
  4014. // all new nodes will be added to selected items, if true, no selection change otherwise
  4015. // data: Array
  4016. // a list of data items, which should be processed by the creator function
  4017. // before: Boolean
  4018. // insert before the anchor, if true, and after the anchor otherwise
  4019. // anchor: Node
  4020. // the anchor node to be used as a point of insertion
  4021. var oldCreator = this._normalizedCreator;
  4022. this._normalizedCreator = function(item, hint){
  4023. var t = oldCreator.call(this, item, hint);
  4024. if(addSelected){
  4025. if(!this.anchor){
  4026. this.anchor = t.node;
  4027. this._removeItemClass(t.node, "Selected");
  4028. this._addItemClass(this.anchor, "Anchor");
  4029. }else if(this.anchor != t.node){
  4030. this._removeItemClass(t.node, "Anchor");
  4031. this._addItemClass(t.node, "Selected");
  4032. }
  4033. this.selection[t.node.id] = 1;
  4034. }else{
  4035. this._removeItemClass(t.node, "Selected");
  4036. this._removeItemClass(t.node, "Anchor");
  4037. }
  4038. return t;
  4039. };
  4040. dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor);
  4041. this._normalizedCreator = oldCreator;
  4042. return this; // self
  4043. },
  4044. destroy: function(){
  4045. // summary:
  4046. // prepares the object to be garbage-collected
  4047. dojo.dnd.Selector.superclass.destroy.call(this);
  4048. this.selection = this.anchor = null;
  4049. },
  4050. // markup methods
  4051. markupFactory: function(params, node){
  4052. params._skipStartup = true;
  4053. return new dojo.dnd.Selector(node, params);
  4054. },
  4055. // mouse events
  4056. onMouseDown: function(e){
  4057. // summary:
  4058. // event processor for onmousedown
  4059. // e: Event
  4060. // mouse event
  4061. if(this.autoSync){ this.sync(); }
  4062. if(!this.current){ return; }
  4063. if(!this.singular && !dojo.isCopyKey(e) && !e.shiftKey && (this.current.id in this.selection)){
  4064. this.simpleSelection = true;
  4065. if(e.button === dojo.mouseButtons.LEFT){
  4066. // accept the left button and stop the event
  4067. // for IE we don't stop event when multiple buttons are pressed
  4068. dojo.stopEvent(e);
  4069. }
  4070. return;
  4071. }
  4072. if(!this.singular && e.shiftKey){
  4073. if(!dojo.isCopyKey(e)){
  4074. this._removeSelection();
  4075. }
  4076. var c = this.getAllNodes();
  4077. if(c.length){
  4078. if(!this.anchor){
  4079. this.anchor = c[0];
  4080. this._addItemClass(this.anchor, "Anchor");
  4081. }
  4082. this.selection[this.anchor.id] = 1;
  4083. if(this.anchor != this.current){
  4084. var i = 0;
  4085. for(; i < c.length; ++i){
  4086. var node = c[i];
  4087. if(node == this.anchor || node == this.current){ break; }
  4088. }
  4089. for(++i; i < c.length; ++i){
  4090. var node = c[i];
  4091. if(node == this.anchor || node == this.current){ break; }
  4092. this._addItemClass(node, "Selected");
  4093. this.selection[node.id] = 1;
  4094. }
  4095. this._addItemClass(this.current, "Selected");
  4096. this.selection[this.current.id] = 1;
  4097. }
  4098. }
  4099. }else{
  4100. if(this.singular){
  4101. if(this.anchor == this.current){
  4102. if(dojo.isCopyKey(e)){
  4103. this.selectNone();
  4104. }
  4105. }else{
  4106. this.selectNone();
  4107. this.anchor = this.current;
  4108. this._addItemClass(this.anchor, "Anchor");
  4109. this.selection[this.current.id] = 1;
  4110. }
  4111. }else{
  4112. if(dojo.isCopyKey(e)){
  4113. if(this.anchor == this.current){
  4114. delete this.selection[this.anchor.id];
  4115. this._removeAnchor();
  4116. }else{
  4117. if(this.current.id in this.selection){
  4118. this._removeItemClass(this.current, "Selected");
  4119. delete this.selection[this.current.id];
  4120. }else{
  4121. if(this.anchor){
  4122. this._removeItemClass(this.anchor, "Anchor");
  4123. this._addItemClass(this.anchor, "Selected");
  4124. }
  4125. this.anchor = this.current;
  4126. this._addItemClass(this.current, "Anchor");
  4127. this.selection[this.current.id] = 1;
  4128. }
  4129. }
  4130. }else{
  4131. if(!(this.current.id in this.selection)){
  4132. this.selectNone();
  4133. this.anchor = this.current;
  4134. this._addItemClass(this.current, "Anchor");
  4135. this.selection[this.current.id] = 1;
  4136. }
  4137. }
  4138. }
  4139. }
  4140. dojo.stopEvent(e);
  4141. },
  4142. onMouseUp: function(e){
  4143. // summary:
  4144. // event processor for onmouseup
  4145. // e: Event
  4146. // mouse event
  4147. if(!this.simpleSelection){ return; }
  4148. this.simpleSelection = false;
  4149. this.selectNone();
  4150. if(this.current){
  4151. this.anchor = this.current;
  4152. this._addItemClass(this.anchor, "Anchor");
  4153. this.selection[this.current.id] = 1;
  4154. }
  4155. },
  4156. onMouseMove: function(e){
  4157. // summary
  4158. // event processor for onmousemove
  4159. // e: Event
  4160. // mouse event
  4161. this.simpleSelection = false;
  4162. },
  4163. // utilities
  4164. onOverEvent: function(){
  4165. // summary:
  4166. // this function is called once, when mouse is over our container
  4167. this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove");
  4168. },
  4169. onOutEvent: function(){
  4170. // summary:
  4171. // this function is called once, when mouse is out of our container
  4172. dojo.disconnect(this.onmousemoveEvent);
  4173. delete this.onmousemoveEvent;
  4174. },
  4175. _removeSelection: function(){
  4176. // summary:
  4177. // unselects all items
  4178. var e = dojo.dnd._empty;
  4179. for(var i in this.selection){
  4180. if(i in e){ continue; }
  4181. var node = dojo.byId(i);
  4182. if(node){ this._removeItemClass(node, "Selected"); }
  4183. }
  4184. this.selection = {};
  4185. return this; // self
  4186. },
  4187. _removeAnchor: function(){
  4188. if(this.anchor){
  4189. this._removeItemClass(this.anchor, "Anchor");
  4190. this.anchor = null;
  4191. }
  4192. return this; // self
  4193. }
  4194. });
  4195. }
  4196. if(!dojo._hasResource["dojo.dnd.Source"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4197. dojo._hasResource["dojo.dnd.Source"] = true;
  4198. dojo.provide("dojo.dnd.Source");
  4199. /*
  4200. Container property:
  4201. "Horizontal"- if this is the horizontal container
  4202. Source states:
  4203. "" - normal state
  4204. "Moved" - this source is being moved
  4205. "Copied" - this source is being copied
  4206. Target states:
  4207. "" - normal state
  4208. "Disabled" - the target cannot accept an avatar
  4209. Target anchor state:
  4210. "" - item is not selected
  4211. "Before" - insert point is before the anchor
  4212. "After" - insert point is after the anchor
  4213. */
  4214. /*=====
  4215. dojo.dnd.__SourceArgs = function(){
  4216. // summary:
  4217. // a dict of parameters for DnD Source configuration. Note that any
  4218. // property on Source elements may be configured, but this is the
  4219. // short-list
  4220. // isSource: Boolean?
  4221. // can be used as a DnD source. Defaults to true.
  4222. // accept: Array?
  4223. // list of accepted types (text strings) for a target; defaults to
  4224. // ["text"]
  4225. // autoSync: Boolean
  4226. // if true refreshes the node list on every operation; false by default
  4227. // copyOnly: Boolean?
  4228. // copy items, if true, use a state of Ctrl key otherwise,
  4229. // see selfCopy and selfAccept for more details
  4230. // delay: Number
  4231. // the move delay in pixels before detecting a drag; 0 by default
  4232. // horizontal: Boolean?
  4233. // a horizontal container, if true, vertical otherwise or when omitted
  4234. // selfCopy: Boolean?
  4235. // copy items by default when dropping on itself,
  4236. // false by default, works only if copyOnly is true
  4237. // selfAccept: Boolean?
  4238. // accept its own items when copyOnly is true,
  4239. // true by default, works only if copyOnly is true
  4240. // withHandles: Boolean?
  4241. // allows dragging only by handles, false by default
  4242. // generateText: Boolean?
  4243. // generate text node for drag and drop, true by default
  4244. this.isSource = isSource;
  4245. this.accept = accept;
  4246. this.autoSync = autoSync;
  4247. this.copyOnly = copyOnly;
  4248. this.delay = delay;
  4249. this.horizontal = horizontal;
  4250. this.selfCopy = selfCopy;
  4251. this.selfAccept = selfAccept;
  4252. this.withHandles = withHandles;
  4253. this.generateText = true;
  4254. }
  4255. =====*/
  4256. dojo.declare("dojo.dnd.Source", dojo.dnd.Selector, {
  4257. // summary:
  4258. // a Source object, which can be used as a DnD source, or a DnD target
  4259. // object attributes (for markup)
  4260. isSource: true,
  4261. horizontal: false,
  4262. copyOnly: false,
  4263. selfCopy: false,
  4264. selfAccept: true,
  4265. skipForm: false,
  4266. withHandles: false,
  4267. autoSync: false,
  4268. delay: 0, // pixels
  4269. accept: ["text"],
  4270. generateText: true,
  4271. constructor: function(/*DOMNode|String*/node, /*dojo.dnd.__SourceArgs?*/params){
  4272. // summary:
  4273. // a constructor of the Source
  4274. // node:
  4275. // node or node's id to build the source on
  4276. // params:
  4277. // any property of this class may be configured via the params
  4278. // object which is mixed-in to the `dojo.dnd.Source` instance
  4279. dojo.mixin(this, dojo.mixin({}, params));
  4280. var type = this.accept;
  4281. if(type.length){
  4282. this.accept = {};
  4283. for(var i = 0; i < type.length; ++i){
  4284. this.accept[type[i]] = 1;
  4285. }
  4286. }
  4287. // class-specific variables
  4288. this.isDragging = false;
  4289. this.mouseDown = false;
  4290. this.targetAnchor = null;
  4291. this.targetBox = null;
  4292. this.before = true;
  4293. this._lastX = 0;
  4294. this._lastY = 0;
  4295. // states
  4296. this.sourceState = "";
  4297. if(this.isSource){
  4298. dojo.addClass(this.node, "dojoDndSource");
  4299. }
  4300. this.targetState = "";
  4301. if(this.accept){
  4302. dojo.addClass(this.node, "dojoDndTarget");
  4303. }
  4304. if(this.horizontal){
  4305. dojo.addClass(this.node, "dojoDndHorizontal");
  4306. }
  4307. // set up events
  4308. this.topics = [
  4309. dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
  4310. dojo.subscribe("/dnd/start", this, "onDndStart"),
  4311. dojo.subscribe("/dnd/drop", this, "onDndDrop"),
  4312. dojo.subscribe("/dnd/cancel", this, "onDndCancel")
  4313. ];
  4314. },
  4315. // methods
  4316. checkAcceptance: function(source, nodes){
  4317. // summary:
  4318. // checks if the target can accept nodes from this source
  4319. // source: Object
  4320. // the source which provides items
  4321. // nodes: Array
  4322. // the list of transferred items
  4323. if(this == source){
  4324. return !this.copyOnly || this.selfAccept;
  4325. }
  4326. for(var i = 0; i < nodes.length; ++i){
  4327. var type = source.getItem(nodes[i].id).type;
  4328. // type instanceof Array
  4329. var flag = false;
  4330. for(var j = 0; j < type.length; ++j){
  4331. if(type[j] in this.accept){
  4332. flag = true;
  4333. break;
  4334. }
  4335. }
  4336. if(!flag){
  4337. return false; // Boolean
  4338. }
  4339. }
  4340. return true; // Boolean
  4341. },
  4342. copyState: function(keyPressed, self){
  4343. // summary:
  4344. // Returns true if we need to copy items, false to move.
  4345. // It is separated to be overwritten dynamically, if needed.
  4346. // keyPressed: Boolean
  4347. // the "copy" key was pressed
  4348. // self: Boolean?
  4349. // optional flag that means that we are about to drop on itself
  4350. if(keyPressed){ return true; }
  4351. if(arguments.length < 2){
  4352. self = this == dojo.dnd.manager().target;
  4353. }
  4354. if(self){
  4355. if(this.copyOnly){
  4356. return this.selfCopy;
  4357. }
  4358. }else{
  4359. return this.copyOnly;
  4360. }
  4361. return false; // Boolean
  4362. },
  4363. destroy: function(){
  4364. // summary:
  4365. // prepares the object to be garbage-collected
  4366. dojo.dnd.Source.superclass.destroy.call(this);
  4367. dojo.forEach(this.topics, dojo.unsubscribe);
  4368. this.targetAnchor = null;
  4369. },
  4370. // markup methods
  4371. markupFactory: function(params, node){
  4372. params._skipStartup = true;
  4373. return new dojo.dnd.Source(node, params);
  4374. },
  4375. // mouse event processors
  4376. onMouseMove: function(e){
  4377. // summary:
  4378. // event processor for onmousemove
  4379. // e: Event
  4380. // mouse event
  4381. if(this.isDragging && this.targetState == "Disabled"){ return; }
  4382. dojo.dnd.Source.superclass.onMouseMove.call(this, e);
  4383. var m = dojo.dnd.manager();
  4384. if(!this.isDragging){
  4385. if(this.mouseDown && this.isSource &&
  4386. (Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay)){
  4387. var nodes = this.getSelectedNodes();
  4388. if(nodes.length){
  4389. m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e), true));
  4390. }
  4391. }
  4392. }
  4393. if(this.isDragging){
  4394. // calculate before/after
  4395. var before = false;
  4396. if(this.current){
  4397. if(!this.targetBox || this.targetAnchor != this.current){
  4398. this.targetBox = dojo.position(this.current, true);
  4399. }
  4400. if(this.horizontal){
  4401. before = (e.pageX - this.targetBox.x) < (this.targetBox.w / 2);
  4402. }else{
  4403. before = (e.pageY - this.targetBox.y) < (this.targetBox.h / 2);
  4404. }
  4405. }
  4406. if(this.current != this.targetAnchor || before != this.before){
  4407. this._markTargetAnchor(before);
  4408. m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
  4409. }
  4410. }
  4411. },
  4412. onMouseDown: function(e){
  4413. // summary:
  4414. // event processor for onmousedown
  4415. // e: Event
  4416. // mouse event
  4417. if(!this.mouseDown && this._legalMouseDown(e) && (!this.skipForm || !dojo.dnd.isFormElement(e))){
  4418. this.mouseDown = true;
  4419. this._lastX = e.pageX;
  4420. this._lastY = e.pageY;
  4421. dojo.dnd.Source.superclass.onMouseDown.call(this, e);
  4422. }
  4423. },
  4424. onMouseUp: function(e){
  4425. // summary:
  4426. // event processor for onmouseup
  4427. // e: Event
  4428. // mouse event
  4429. if(this.mouseDown){
  4430. this.mouseDown = false;
  4431. dojo.dnd.Source.superclass.onMouseUp.call(this, e);
  4432. }
  4433. },
  4434. // topic event processors
  4435. onDndSourceOver: function(source){
  4436. // summary:
  4437. // topic event processor for /dnd/source/over, called when detected a current source
  4438. // source: Object
  4439. // the source which has the mouse over it
  4440. if(this != source){
  4441. this.mouseDown = false;
  4442. if(this.targetAnchor){
  4443. this._unmarkTargetAnchor();
  4444. }
  4445. }else if(this.isDragging){
  4446. var m = dojo.dnd.manager();
  4447. m.canDrop(this.targetState != "Disabled" && (!this.current || m.source != this || !(this.current.id in this.selection)));
  4448. }
  4449. },
  4450. onDndStart: function(source, nodes, copy){
  4451. // summary:
  4452. // topic event processor for /dnd/start, called to initiate the DnD operation
  4453. // source: Object
  4454. // the source which provides items
  4455. // nodes: Array
  4456. // the list of transferred items
  4457. // copy: Boolean
  4458. // copy items, if true, move items otherwise
  4459. if(this.autoSync){ this.sync(); }
  4460. if(this.isSource){
  4461. this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
  4462. }
  4463. var accepted = this.accept && this.checkAcceptance(source, nodes);
  4464. this._changeState("Target", accepted ? "" : "Disabled");
  4465. if(this == source){
  4466. dojo.dnd.manager().overSource(this);
  4467. }
  4468. this.isDragging = true;
  4469. },
  4470. onDndDrop: function(source, nodes, copy, target){
  4471. // summary:
  4472. // topic event processor for /dnd/drop, called to finish the DnD operation
  4473. // source: Object
  4474. // the source which provides items
  4475. // nodes: Array
  4476. // the list of transferred items
  4477. // copy: Boolean
  4478. // copy items, if true, move items otherwise
  4479. // target: Object
  4480. // the target which accepts items
  4481. if(this == target){
  4482. // this one is for us => move nodes!
  4483. this.onDrop(source, nodes, copy);
  4484. }
  4485. this.onDndCancel();
  4486. },
  4487. onDndCancel: function(){
  4488. // summary:
  4489. // topic event processor for /dnd/cancel, called to cancel the DnD operation
  4490. if(this.targetAnchor){
  4491. this._unmarkTargetAnchor();
  4492. this.targetAnchor = null;
  4493. }
  4494. this.before = true;
  4495. this.isDragging = false;
  4496. this.mouseDown = false;
  4497. this._changeState("Source", "");
  4498. this._changeState("Target", "");
  4499. },
  4500. // local events
  4501. onDrop: function(source, nodes, copy){
  4502. // summary:
  4503. // called only on the current target, when drop is performed
  4504. // source: Object
  4505. // the source which provides items
  4506. // nodes: Array
  4507. // the list of transferred items
  4508. // copy: Boolean
  4509. // copy items, if true, move items otherwise
  4510. if(this != source){
  4511. this.onDropExternal(source, nodes, copy);
  4512. }else{
  4513. this.onDropInternal(nodes, copy);
  4514. }
  4515. },
  4516. onDropExternal: function(source, nodes, copy){
  4517. // summary:
  4518. // called only on the current target, when drop is performed
  4519. // from an external source
  4520. // source: Object
  4521. // the source which provides items
  4522. // nodes: Array
  4523. // the list of transferred items
  4524. // copy: Boolean
  4525. // copy items, if true, move items otherwise
  4526. var oldCreator = this._normalizedCreator;
  4527. // transferring nodes from the source to the target
  4528. if(this.creator){
  4529. // use defined creator
  4530. this._normalizedCreator = function(node, hint){
  4531. return oldCreator.call(this, source.getItem(node.id).data, hint);
  4532. };
  4533. }else{
  4534. // we have no creator defined => move/clone nodes
  4535. if(copy){
  4536. // clone nodes
  4537. this._normalizedCreator = function(node, hint){
  4538. var t = source.getItem(node.id);
  4539. var n = node.cloneNode(true);
  4540. n.id = dojo.dnd.getUniqueId();
  4541. return {node: n, data: t.data, type: t.type};
  4542. };
  4543. }else{
  4544. // move nodes
  4545. this._normalizedCreator = function(node, hint){
  4546. var t = source.getItem(node.id);
  4547. source.delItem(node.id);
  4548. return {node: node, data: t.data, type: t.type};
  4549. };
  4550. }
  4551. }
  4552. this.selectNone();
  4553. if(!copy && !this.creator){
  4554. source.selectNone();
  4555. }
  4556. this.insertNodes(true, nodes, this.before, this.current);
  4557. if(!copy && this.creator){
  4558. source.deleteSelectedNodes();
  4559. }
  4560. this._normalizedCreator = oldCreator;
  4561. },
  4562. onDropInternal: function(nodes, copy){
  4563. // summary:
  4564. // called only on the current target, when drop is performed
  4565. // from the same target/source
  4566. // nodes: Array
  4567. // the list of transferred items
  4568. // copy: Boolean
  4569. // copy items, if true, move items otherwise
  4570. var oldCreator = this._normalizedCreator;
  4571. // transferring nodes within the single source
  4572. if(this.current && this.current.id in this.selection){
  4573. // do nothing
  4574. return;
  4575. }
  4576. if(copy){
  4577. if(this.creator){
  4578. // create new copies of data items
  4579. this._normalizedCreator = function(node, hint){
  4580. return oldCreator.call(this, this.getItem(node.id).data, hint);
  4581. };
  4582. }else{
  4583. // clone nodes
  4584. this._normalizedCreator = function(node, hint){
  4585. var t = this.getItem(node.id);
  4586. var n = node.cloneNode(true);
  4587. n.id = dojo.dnd.getUniqueId();
  4588. return {node: n, data: t.data, type: t.type};
  4589. };
  4590. }
  4591. }else{
  4592. // move nodes
  4593. if(!this.current){
  4594. // do nothing
  4595. return;
  4596. }
  4597. this._normalizedCreator = function(node, hint){
  4598. var t = this.getItem(node.id);
  4599. return {node: node, data: t.data, type: t.type};
  4600. };
  4601. }
  4602. this._removeSelection();
  4603. this.insertNodes(true, nodes, this.before, this.current);
  4604. this._normalizedCreator = oldCreator;
  4605. },
  4606. onDraggingOver: function(){
  4607. // summary:
  4608. // called during the active DnD operation, when items
  4609. // are dragged over this target, and it is not disabled
  4610. },
  4611. onDraggingOut: function(){
  4612. // summary:
  4613. // called during the active DnD operation, when items
  4614. // are dragged away from this target, and it is not disabled
  4615. },
  4616. // utilities
  4617. onOverEvent: function(){
  4618. // summary:
  4619. // this function is called once, when mouse is over our container
  4620. dojo.dnd.Source.superclass.onOverEvent.call(this);
  4621. dojo.dnd.manager().overSource(this);
  4622. if(this.isDragging && this.targetState != "Disabled"){
  4623. this.onDraggingOver();
  4624. }
  4625. },
  4626. onOutEvent: function(){
  4627. // summary:
  4628. // this function is called once, when mouse is out of our container
  4629. dojo.dnd.Source.superclass.onOutEvent.call(this);
  4630. dojo.dnd.manager().outSource(this);
  4631. if(this.isDragging && this.targetState != "Disabled"){
  4632. this.onDraggingOut();
  4633. }
  4634. },
  4635. _markTargetAnchor: function(before){
  4636. // summary:
  4637. // assigns a class to the current target anchor based on "before" status
  4638. // before: Boolean
  4639. // insert before, if true, after otherwise
  4640. if(this.current == this.targetAnchor && this.before == before){ return; }
  4641. if(this.targetAnchor){
  4642. this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
  4643. }
  4644. this.targetAnchor = this.current;
  4645. this.targetBox = null;
  4646. this.before = before;
  4647. if(this.targetAnchor){
  4648. this._addItemClass(this.targetAnchor, this.before ? "Before" : "After");
  4649. }
  4650. },
  4651. _unmarkTargetAnchor: function(){
  4652. // summary:
  4653. // removes a class of the current target anchor based on "before" status
  4654. if(!this.targetAnchor){ return; }
  4655. this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
  4656. this.targetAnchor = null;
  4657. this.targetBox = null;
  4658. this.before = true;
  4659. },
  4660. _markDndStatus: function(copy){
  4661. // summary:
  4662. // changes source's state based on "copy" status
  4663. this._changeState("Source", copy ? "Copied" : "Moved");
  4664. },
  4665. _legalMouseDown: function(e){
  4666. // summary:
  4667. // checks if user clicked on "approved" items
  4668. // e: Event
  4669. // mouse event
  4670. // accept only the left mouse button
  4671. if(!dojo.mouseButtons.isLeft(e)){ return false; }
  4672. if(!this.withHandles){ return true; }
  4673. // check for handles
  4674. for(var node = e.target; node && node !== this.node; node = node.parentNode){
  4675. if(dojo.hasClass(node, "dojoDndHandle")){ return true; }
  4676. if(dojo.hasClass(node, "dojoDndItem") || dojo.hasClass(node, "dojoDndIgnore")){ break; }
  4677. }
  4678. return false; // Boolean
  4679. }
  4680. });
  4681. dojo.declare("dojo.dnd.Target", dojo.dnd.Source, {
  4682. // summary: a Target object, which can be used as a DnD target
  4683. constructor: function(node, params){
  4684. // summary:
  4685. // a constructor of the Target --- see the `dojo.dnd.Source.constructor` for details
  4686. this.isSource = false;
  4687. dojo.removeClass(this.node, "dojoDndSource");
  4688. },
  4689. // markup methods
  4690. markupFactory: function(params, node){
  4691. params._skipStartup = true;
  4692. return new dojo.dnd.Target(node, params);
  4693. }
  4694. });
  4695. dojo.declare("dojo.dnd.AutoSource", dojo.dnd.Source, {
  4696. // summary:
  4697. // a source that syncs its DnD nodes by default
  4698. constructor: function(node, params){
  4699. // summary:
  4700. // constructor of the AutoSource --- see the Source constructor for details
  4701. this.autoSync = true;
  4702. },
  4703. // markup methods
  4704. markupFactory: function(params, node){
  4705. params._skipStartup = true;
  4706. return new dojo.dnd.AutoSource(node, params);
  4707. }
  4708. });
  4709. }
  4710. if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4711. dojo._hasResource["dojo.fx.Toggler"] = true;
  4712. dojo.provide("dojo.fx.Toggler");
  4713. dojo.declare("dojo.fx.Toggler", null, {
  4714. // summary:
  4715. // A simple `dojo.Animation` toggler API.
  4716. //
  4717. // description:
  4718. // class constructor for an animation toggler. It accepts a packed
  4719. // set of arguments about what type of animation to use in each
  4720. // direction, duration, etc. All available members are mixed into
  4721. // these animations from the constructor (for example, `node`,
  4722. // `showDuration`, `hideDuration`).
  4723. //
  4724. // example:
  4725. // | var t = new dojo.fx.Toggler({
  4726. // | node: "nodeId",
  4727. // | showDuration: 500,
  4728. // | // hideDuration will default to "200"
  4729. // | showFunc: dojo.fx.wipeIn,
  4730. // | // hideFunc will default to "fadeOut"
  4731. // | });
  4732. // | t.show(100); // delay showing for 100ms
  4733. // | // ...time passes...
  4734. // | t.hide();
  4735. // node: DomNode
  4736. // the node to target for the showing and hiding animations
  4737. node: null,
  4738. // showFunc: Function
  4739. // The function that returns the `dojo.Animation` to show the node
  4740. showFunc: dojo.fadeIn,
  4741. // hideFunc: Function
  4742. // The function that returns the `dojo.Animation` to hide the node
  4743. hideFunc: dojo.fadeOut,
  4744. // showDuration:
  4745. // Time in milliseconds to run the show Animation
  4746. showDuration: 200,
  4747. // hideDuration:
  4748. // Time in milliseconds to run the hide Animation
  4749. hideDuration: 200,
  4750. // FIXME: need a policy for where the toggler should "be" the next
  4751. // time show/hide are called if we're stopped somewhere in the
  4752. // middle.
  4753. // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
  4754. // each animation individually.
  4755. // FIXME: also would be nice to have events from the animations exposed/bridged
  4756. /*=====
  4757. _showArgs: null,
  4758. _showAnim: null,
  4759. _hideArgs: null,
  4760. _hideAnim: null,
  4761. _isShowing: false,
  4762. _isHiding: false,
  4763. =====*/
  4764. constructor: function(args){
  4765. var _t = this;
  4766. dojo.mixin(_t, args);
  4767. _t.node = args.node;
  4768. _t._showArgs = dojo.mixin({}, args);
  4769. _t._showArgs.node = _t.node;
  4770. _t._showArgs.duration = _t.showDuration;
  4771. _t.showAnim = _t.showFunc(_t._showArgs);
  4772. _t._hideArgs = dojo.mixin({}, args);
  4773. _t._hideArgs.node = _t.node;
  4774. _t._hideArgs.duration = _t.hideDuration;
  4775. _t.hideAnim = _t.hideFunc(_t._hideArgs);
  4776. dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
  4777. dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
  4778. },
  4779. show: function(delay){
  4780. // summary: Toggle the node to showing
  4781. // delay: Integer?
  4782. // Ammount of time to stall playing the show animation
  4783. return this.showAnim.play(delay || 0);
  4784. },
  4785. hide: function(delay){
  4786. // summary: Toggle the node to hidden
  4787. // delay: Integer?
  4788. // Ammount of time to stall playing the hide animation
  4789. return this.hideAnim.play(delay || 0);
  4790. }
  4791. });
  4792. }
  4793. if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  4794. dojo._hasResource["dojo.fx"] = true;
  4795. dojo.provide("dojo.fx");
  4796. /*=====
  4797. dojo.fx = {
  4798. // summary: Effects library on top of Base animations
  4799. };
  4800. =====*/
  4801. (function(){
  4802. var d = dojo,
  4803. _baseObj = {
  4804. _fire: function(evt, args){
  4805. if(this[evt]){
  4806. this[evt].apply(this, args||[]);
  4807. }
  4808. return this;
  4809. }
  4810. };
  4811. var _chain = function(animations){
  4812. this._index = -1;
  4813. this._animations = animations||[];
  4814. this._current = this._onAnimateCtx = this._onEndCtx = null;
  4815. this.duration = 0;
  4816. d.forEach(this._animations, function(a){
  4817. this.duration += a.duration;
  4818. if(a.delay){ this.duration += a.delay; }
  4819. }, this);
  4820. };
  4821. d.extend(_chain, {
  4822. _onAnimate: function(){
  4823. this._fire("onAnimate", arguments);
  4824. },
  4825. _onEnd: function(){
  4826. d.disconnect(this._onAnimateCtx);
  4827. d.disconnect(this._onEndCtx);
  4828. this._onAnimateCtx = this._onEndCtx = null;
  4829. if(this._index + 1 == this._animations.length){
  4830. this._fire("onEnd");
  4831. }else{
  4832. // switch animations
  4833. this._current = this._animations[++this._index];
  4834. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  4835. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  4836. this._current.play(0, true);
  4837. }
  4838. },
  4839. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  4840. if(!this._current){ this._current = this._animations[this._index = 0]; }
  4841. if(!gotoStart && this._current.status() == "playing"){ return this; }
  4842. var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
  4843. this._fire("beforeBegin");
  4844. }),
  4845. onBegin = d.connect(this._current, "onBegin", this, function(arg){
  4846. this._fire("onBegin", arguments);
  4847. }),
  4848. onPlay = d.connect(this._current, "onPlay", this, function(arg){
  4849. this._fire("onPlay", arguments);
  4850. d.disconnect(beforeBegin);
  4851. d.disconnect(onBegin);
  4852. d.disconnect(onPlay);
  4853. });
  4854. if(this._onAnimateCtx){
  4855. d.disconnect(this._onAnimateCtx);
  4856. }
  4857. this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
  4858. if(this._onEndCtx){
  4859. d.disconnect(this._onEndCtx);
  4860. }
  4861. this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
  4862. this._current.play.apply(this._current, arguments);
  4863. return this;
  4864. },
  4865. pause: function(){
  4866. if(this._current){
  4867. var e = d.connect(this._current, "onPause", this, function(arg){
  4868. this._fire("onPause", arguments);
  4869. d.disconnect(e);
  4870. });
  4871. this._current.pause();
  4872. }
  4873. return this;
  4874. },
  4875. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  4876. this.pause();
  4877. var offset = this.duration * percent;
  4878. this._current = null;
  4879. d.some(this._animations, function(a){
  4880. if(a.duration <= offset){
  4881. this._current = a;
  4882. return true;
  4883. }
  4884. offset -= a.duration;
  4885. return false;
  4886. });
  4887. if(this._current){
  4888. this._current.gotoPercent(offset / this._current.duration, andPlay);
  4889. }
  4890. return this;
  4891. },
  4892. stop: function(/*boolean?*/ gotoEnd){
  4893. if(this._current){
  4894. if(gotoEnd){
  4895. for(; this._index + 1 < this._animations.length; ++this._index){
  4896. this._animations[this._index].stop(true);
  4897. }
  4898. this._current = this._animations[this._index];
  4899. }
  4900. var e = d.connect(this._current, "onStop", this, function(arg){
  4901. this._fire("onStop", arguments);
  4902. d.disconnect(e);
  4903. });
  4904. this._current.stop();
  4905. }
  4906. return this;
  4907. },
  4908. status: function(){
  4909. return this._current ? this._current.status() : "stopped";
  4910. },
  4911. destroy: function(){
  4912. if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
  4913. if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
  4914. }
  4915. });
  4916. d.extend(_chain, _baseObj);
  4917. dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
  4918. // summary:
  4919. // Chain a list of `dojo.Animation`s to run in sequence
  4920. //
  4921. // description:
  4922. // Return a `dojo.Animation` which will play all passed
  4923. // `dojo.Animation` instances in sequence, firing its own
  4924. // synthesized events simulating a single animation. (eg:
  4925. // onEnd of this animation means the end of the chain,
  4926. // not the individual animations within)
  4927. //
  4928. // example:
  4929. // Once `node` is faded out, fade in `otherNode`
  4930. // | dojo.fx.chain([
  4931. // | dojo.fadeIn({ node:node }),
  4932. // | dojo.fadeOut({ node:otherNode })
  4933. // | ]).play();
  4934. //
  4935. return new _chain(animations) // dojo.Animation
  4936. };
  4937. var _combine = function(animations){
  4938. this._animations = animations||[];
  4939. this._connects = [];
  4940. this._finished = 0;
  4941. this.duration = 0;
  4942. d.forEach(animations, function(a){
  4943. var duration = a.duration;
  4944. if(a.delay){ duration += a.delay; }
  4945. if(this.duration < duration){ this.duration = duration; }
  4946. this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
  4947. }, this);
  4948. this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
  4949. var self = this;
  4950. d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
  4951. function(evt){
  4952. self._connects.push(d.connect(self._pseudoAnimation, evt,
  4953. function(){ self._fire(evt, arguments); }
  4954. ));
  4955. }
  4956. );
  4957. };
  4958. d.extend(_combine, {
  4959. _doAction: function(action, args){
  4960. d.forEach(this._animations, function(a){
  4961. a[action].apply(a, args);
  4962. });
  4963. return this;
  4964. },
  4965. _onEnd: function(){
  4966. if(++this._finished > this._animations.length){
  4967. this._fire("onEnd");
  4968. }
  4969. },
  4970. _call: function(action, args){
  4971. var t = this._pseudoAnimation;
  4972. t[action].apply(t, args);
  4973. },
  4974. play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
  4975. this._finished = 0;
  4976. this._doAction("play", arguments);
  4977. this._call("play", arguments);
  4978. return this;
  4979. },
  4980. pause: function(){
  4981. this._doAction("pause", arguments);
  4982. this._call("pause", arguments);
  4983. return this;
  4984. },
  4985. gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
  4986. var ms = this.duration * percent;
  4987. d.forEach(this._animations, function(a){
  4988. a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
  4989. });
  4990. this._call("gotoPercent", arguments);
  4991. return this;
  4992. },
  4993. stop: function(/*boolean?*/ gotoEnd){
  4994. this._doAction("stop", arguments);
  4995. this._call("stop", arguments);
  4996. return this;
  4997. },
  4998. status: function(){
  4999. return this._pseudoAnimation.status();
  5000. },
  5001. destroy: function(){
  5002. d.forEach(this._connects, dojo.disconnect);
  5003. }
  5004. });
  5005. d.extend(_combine, _baseObj);
  5006. dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
  5007. // summary:
  5008. // Combine a list of `dojo.Animation`s to run in parallel
  5009. //
  5010. // description:
  5011. // Combine an array of `dojo.Animation`s to run in parallel,
  5012. // providing a new `dojo.Animation` instance encompasing each
  5013. // animation, firing standard animation events.
  5014. //
  5015. // example:
  5016. // Fade out `node` while fading in `otherNode` simultaneously
  5017. // | dojo.fx.combine([
  5018. // | dojo.fadeIn({ node:node }),
  5019. // | dojo.fadeOut({ node:otherNode })
  5020. // | ]).play();
  5021. //
  5022. // example:
  5023. // When the longest animation ends, execute a function:
  5024. // | var anim = dojo.fx.combine([
  5025. // | dojo.fadeIn({ node: n, duration:700 }),
  5026. // | dojo.fadeOut({ node: otherNode, duration: 300 })
  5027. // | ]);
  5028. // | dojo.connect(anim, "onEnd", function(){
  5029. // | // overall animation is done.
  5030. // | });
  5031. // | anim.play(); // play the animation
  5032. //
  5033. return new _combine(animations); // dojo.Animation
  5034. };
  5035. dojo.fx.wipeIn = function(/*Object*/ args){
  5036. // summary:
  5037. // Expand a node to it's natural height.
  5038. //
  5039. // description:
  5040. // Returns an animation that will expand the
  5041. // node defined in 'args' object from it's current height to
  5042. // it's natural height (with no scrollbar).
  5043. // Node must have no margin/border/padding.
  5044. //
  5045. // args: Object
  5046. // A hash-map of standard `dojo.Animation` constructor properties
  5047. // (such as easing: node: duration: and so on)
  5048. //
  5049. // example:
  5050. // | dojo.fx.wipeIn({
  5051. // | node:"someId"
  5052. // | }).play()
  5053. var node = args.node = d.byId(args.node), s = node.style, o;
  5054. var anim = d.animateProperty(d.mixin({
  5055. properties: {
  5056. height: {
  5057. // wrapped in functions so we wait till the last second to query (in case value has changed)
  5058. start: function(){
  5059. // start at current [computed] height, but use 1px rather than 0
  5060. // because 0 causes IE to display the whole panel
  5061. o = s.overflow;
  5062. s.overflow = "hidden";
  5063. if(s.visibility == "hidden" || s.display == "none"){
  5064. s.height = "1px";
  5065. s.display = "";
  5066. s.visibility = "";
  5067. return 1;
  5068. }else{
  5069. var height = d.style(node, "height");
  5070. return Math.max(height, 1);
  5071. }
  5072. },
  5073. end: function(){
  5074. return node.scrollHeight;
  5075. }
  5076. }
  5077. }
  5078. }, args));
  5079. d.connect(anim, "onEnd", function(){
  5080. s.height = "auto";
  5081. s.overflow = o;
  5082. });
  5083. return anim; // dojo.Animation
  5084. };
  5085. dojo.fx.wipeOut = function(/*Object*/ args){
  5086. // summary:
  5087. // Shrink a node to nothing and hide it.
  5088. //
  5089. // description:
  5090. // Returns an animation that will shrink node defined in "args"
  5091. // from it's current height to 1px, and then hide it.
  5092. //
  5093. // args: Object
  5094. // A hash-map of standard `dojo.Animation` constructor properties
  5095. // (such as easing: node: duration: and so on)
  5096. //
  5097. // example:
  5098. // | dojo.fx.wipeOut({ node:"someId" }).play()
  5099. var node = args.node = d.byId(args.node), s = node.style, o;
  5100. var anim = d.animateProperty(d.mixin({
  5101. properties: {
  5102. height: {
  5103. end: 1 // 0 causes IE to display the whole panel
  5104. }
  5105. }
  5106. }, args));
  5107. d.connect(anim, "beforeBegin", function(){
  5108. o = s.overflow;
  5109. s.overflow = "hidden";
  5110. s.display = "";
  5111. });
  5112. d.connect(anim, "onEnd", function(){
  5113. s.overflow = o;
  5114. s.height = "auto";
  5115. s.display = "none";
  5116. });
  5117. return anim; // dojo.Animation
  5118. };
  5119. dojo.fx.slideTo = function(/*Object*/ args){
  5120. // summary:
  5121. // Slide a node to a new top/left position
  5122. //
  5123. // description:
  5124. // Returns an animation that will slide "node"
  5125. // defined in args Object from its current position to
  5126. // the position defined by (args.left, args.top).
  5127. //
  5128. // args: Object
  5129. // A hash-map of standard `dojo.Animation` constructor properties
  5130. // (such as easing: node: duration: and so on). Special args members
  5131. // are `top` and `left`, which indicate the new position to slide to.
  5132. //
  5133. // example:
  5134. // | dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
  5135. var node = args.node = d.byId(args.node),
  5136. top = null, left = null;
  5137. var init = (function(n){
  5138. return function(){
  5139. var cs = d.getComputedStyle(n);
  5140. var pos = cs.position;
  5141. top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
  5142. left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
  5143. if(pos != 'absolute' && pos != 'relative'){
  5144. var ret = d.position(n, true);
  5145. top = ret.y;
  5146. left = ret.x;
  5147. n.style.position="absolute";
  5148. n.style.top=top+"px";
  5149. n.style.left=left+"px";
  5150. }
  5151. };
  5152. })(node);
  5153. init();
  5154. var anim = d.animateProperty(d.mixin({
  5155. properties: {
  5156. top: args.top || 0,
  5157. left: args.left || 0
  5158. }
  5159. }, args));
  5160. d.connect(anim, "beforeBegin", anim, init);
  5161. return anim; // dojo.Animation
  5162. };
  5163. })();
  5164. }
  5165. if(!dojo._hasResource["dojo.fx.easing"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  5166. dojo._hasResource["dojo.fx.easing"] = true;
  5167. dojo.provide("dojo.fx.easing");
  5168. dojo.getObject("fx.easing", true, dojo);
  5169. dojo.fx.easing = {
  5170. // summary:
  5171. // Collection of easing functions to use beyond the default
  5172. // `dojo._defaultEasing` function.
  5173. //
  5174. // description:
  5175. //
  5176. // Easing functions are used to manipulate the iteration through
  5177. // an `dojo.Animation`s _Line. _Line being the properties of an Animation,
  5178. // and the easing function progresses through that Line determing
  5179. // how quickly (or slowly) it should go. Or more accurately: modify
  5180. // the value of the _Line based on the percentage of animation completed.
  5181. //
  5182. // All functions follow a simple naming convention of "ease type" + "when".
  5183. // If the name of the function ends in Out, the easing described appears
  5184. // towards the end of the animation. "In" means during the beginning,
  5185. // and InOut means both ranges of the Animation will applied, both
  5186. // beginning and end.
  5187. //
  5188. // One does not call the easing function directly, it must be passed to
  5189. // the `easing` property of an animation.
  5190. //
  5191. // example:
  5192. // |
  5193. // | var anim = dojo.fadeOut({
  5194. // | node: 'node',
  5195. // | duration: 2000,
  5196. // | // note there is no ()
  5197. // | easing: dojo.fx.easing.quadIn
  5198. // | }).play();
  5199. //
  5200. linear: function(/* Decimal? */n){
  5201. // summary: A linear easing function
  5202. return n;
  5203. },
  5204. quadIn: function(/* Decimal? */n){
  5205. return Math.pow(n, 2);
  5206. },
  5207. quadOut: function(/* Decimal? */n){
  5208. return n * (n - 2) * -1;
  5209. },
  5210. quadInOut: function(/* Decimal? */n){
  5211. n = n * 2;
  5212. if(n < 1){ return Math.pow(n, 2) / 2; }
  5213. return -1 * ((--n) * (n - 2) - 1) / 2;
  5214. },
  5215. cubicIn: function(/* Decimal? */n){
  5216. return Math.pow(n, 3);
  5217. },
  5218. cubicOut: function(/* Decimal? */n){
  5219. return Math.pow(n - 1, 3) + 1;
  5220. },
  5221. cubicInOut: function(/* Decimal? */n){
  5222. n = n * 2;
  5223. if(n < 1){ return Math.pow(n, 3) / 2; }
  5224. n -= 2;
  5225. return (Math.pow(n, 3) + 2) / 2;
  5226. },
  5227. quartIn: function(/* Decimal? */n){
  5228. return Math.pow(n, 4);
  5229. },
  5230. quartOut: function(/* Decimal? */n){
  5231. return -1 * (Math.pow(n - 1, 4) - 1);
  5232. },
  5233. quartInOut: function(/* Decimal? */n){
  5234. n = n * 2;
  5235. if(n < 1){ return Math.pow(n, 4) / 2; }
  5236. n -= 2;
  5237. return -1 / 2 * (Math.pow(n, 4) - 2);
  5238. },
  5239. quintIn: function(/* Decimal? */n){
  5240. return Math.pow(n, 5);
  5241. },
  5242. quintOut: function(/* Decimal? */n){
  5243. return Math.pow(n - 1, 5) + 1;
  5244. },
  5245. quintInOut: function(/* Decimal? */n){
  5246. n = n * 2;
  5247. if(n < 1){ return Math.pow(n, 5) / 2; };
  5248. n -= 2;
  5249. return (Math.pow(n, 5) + 2) / 2;
  5250. },
  5251. sineIn: function(/* Decimal? */n){
  5252. return -1 * Math.cos(n * (Math.PI / 2)) + 1;
  5253. },
  5254. sineOut: function(/* Decimal? */n){
  5255. return Math.sin(n * (Math.PI / 2));
  5256. },
  5257. sineInOut: function(/* Decimal? */n){
  5258. return -1 * (Math.cos(Math.PI * n) - 1) / 2;
  5259. },
  5260. expoIn: function(/* Decimal? */n){
  5261. return (n == 0) ? 0 : Math.pow(2, 10 * (n - 1));
  5262. },
  5263. expoOut: function(/* Decimal? */n){
  5264. return (n == 1) ? 1 : (-1 * Math.pow(2, -10 * n) + 1);
  5265. },
  5266. expoInOut: function(/* Decimal? */n){
  5267. if(n == 0){ return 0; }
  5268. if(n == 1){ return 1; }
  5269. n = n * 2;
  5270. if(n < 1){ return Math.pow(2, 10 * (n - 1)) / 2; }
  5271. --n;
  5272. return (-1 * Math.pow(2, -10 * n) + 2) / 2;
  5273. },
  5274. circIn: function(/* Decimal? */n){
  5275. return -1 * (Math.sqrt(1 - Math.pow(n, 2)) - 1);
  5276. },
  5277. circOut: function(/* Decimal? */n){
  5278. n = n - 1;
  5279. return Math.sqrt(1 - Math.pow(n, 2));
  5280. },
  5281. circInOut: function(/* Decimal? */n){
  5282. n = n * 2;
  5283. if(n < 1){ return -1 / 2 * (Math.sqrt(1 - Math.pow(n, 2)) - 1); }
  5284. n -= 2;
  5285. return 1 / 2 * (Math.sqrt(1 - Math.pow(n, 2)) + 1);
  5286. },
  5287. backIn: function(/* Decimal? */n){
  5288. // summary:
  5289. // An easing function that starts away from the target,
  5290. // and quickly accelerates towards the end value.
  5291. //
  5292. // Use caution when the easing will cause values to become
  5293. // negative as some properties cannot be set to negative values.
  5294. var s = 1.70158;
  5295. return Math.pow(n, 2) * ((s + 1) * n - s);
  5296. },
  5297. backOut: function(/* Decimal? */n){
  5298. // summary:
  5299. // An easing function that pops past the range briefly, and slowly comes back.
  5300. //
  5301. // description:
  5302. // An easing function that pops past the range briefly, and slowly comes back.
  5303. //
  5304. // Use caution when the easing will cause values to become negative as some
  5305. // properties cannot be set to negative values.
  5306. n = n - 1;
  5307. var s = 1.70158;
  5308. return Math.pow(n, 2) * ((s + 1) * n + s) + 1;
  5309. },
  5310. backInOut: function(/* Decimal? */n){
  5311. // summary:
  5312. // An easing function combining the effects of `backIn` and `backOut`
  5313. //
  5314. // description:
  5315. // An easing function combining the effects of `backIn` and `backOut`.
  5316. // Use caution when the easing will cause values to become negative
  5317. // as some properties cannot be set to negative values.
  5318. var s = 1.70158 * 1.525;
  5319. n = n * 2;
  5320. if(n < 1){ return (Math.pow(n, 2) * ((s + 1) * n - s)) / 2; }
  5321. n-=2;
  5322. return (Math.pow(n, 2) * ((s + 1) * n + s) + 2) / 2;
  5323. },
  5324. elasticIn: function(/* Decimal? */n){
  5325. // summary:
  5326. // An easing function the elastically snaps from the start value
  5327. //
  5328. // description:
  5329. // An easing function the elastically snaps from the start value
  5330. //
  5331. // Use caution when the elasticity will cause values to become negative
  5332. // as some properties cannot be set to negative values.
  5333. if(n == 0 || n == 1){ return n; }
  5334. var p = .3;
  5335. var s = p / 4;
  5336. n = n - 1;
  5337. return -1 * Math.pow(2, 10 * n) * Math.sin((n - s) * (2 * Math.PI) / p);
  5338. },
  5339. elasticOut: function(/* Decimal? */n){
  5340. // summary:
  5341. // An easing function that elasticly snaps around the target value,
  5342. // near the end of the Animation
  5343. //
  5344. // description:
  5345. // An easing function that elasticly snaps around the target value,
  5346. // near the end of the Animation
  5347. //
  5348. // Use caution when the elasticity will cause values to become
  5349. // negative as some properties cannot be set to negative values.
  5350. if(n==0 || n == 1){ return n; }
  5351. var p = .3;
  5352. var s = p / 4;
  5353. return Math.pow(2, -10 * n) * Math.sin((n - s) * (2 * Math.PI) / p) + 1;
  5354. },
  5355. elasticInOut: function(/* Decimal? */n){
  5356. // summary:
  5357. // An easing function that elasticly snaps around the value, near
  5358. // the beginning and end of the Animation.
  5359. //
  5360. // description:
  5361. // An easing function that elasticly snaps around the value, near
  5362. // the beginning and end of the Animation.
  5363. //
  5364. // Use caution when the elasticity will cause values to become
  5365. // negative as some properties cannot be set to negative values.
  5366. if(n == 0) return 0;
  5367. n = n * 2;
  5368. if(n == 2) return 1;
  5369. var p = .3 * 1.5;
  5370. var s = p / 4;
  5371. if(n < 1){
  5372. n -= 1;
  5373. return -.5 * (Math.pow(2, 10 * n) * Math.sin((n - s) * (2 * Math.PI) / p));
  5374. }
  5375. n -= 1;
  5376. return .5 * (Math.pow(2, -10 * n) * Math.sin((n - s) * (2 * Math.PI) / p)) + 1;
  5377. },
  5378. bounceIn: function(/* Decimal? */n){
  5379. // summary:
  5380. // An easing function that 'bounces' near the beginning of an Animation
  5381. return (1 - dojo.fx.easing.bounceOut(1 - n)); // Decimal
  5382. },
  5383. bounceOut: function(/* Decimal? */n){
  5384. // summary:
  5385. // An easing function that 'bounces' near the end of an Animation
  5386. var s = 7.5625;
  5387. var p = 2.75;
  5388. var l;
  5389. if(n < (1 / p)){
  5390. l = s * Math.pow(n, 2);
  5391. }else if(n < (2 / p)){
  5392. n -= (1.5 / p);
  5393. l = s * Math.pow(n, 2) + .75;
  5394. }else if(n < (2.5 / p)){
  5395. n -= (2.25 / p);
  5396. l = s * Math.pow(n, 2) + .9375;
  5397. }else{
  5398. n -= (2.625 / p);
  5399. l = s * Math.pow(n, 2) + .984375;
  5400. }
  5401. return l;
  5402. },
  5403. bounceInOut: function(/* Decimal? */n){
  5404. // summary:
  5405. // An easing function that 'bounces' at the beginning and end of the Animation
  5406. if(n < 0.5){ return dojo.fx.easing.bounceIn(n * 2) / 2; }
  5407. return (dojo.fx.easing.bounceOut(n * 2 - 1) / 2) + 0.5; // Decimal
  5408. }
  5409. };
  5410. }
  5411. if(!dojo._hasResource["dojo.io.iframe"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  5412. dojo._hasResource["dojo.io.iframe"] = true;
  5413. dojo.provide("dojo.io.iframe");
  5414. dojo.getObject("io", true, dojo);
  5415. /*=====
  5416. dojo.declare("dojo.io.iframe.__ioArgs", dojo.__IoArgs, {
  5417. constructor: function(){
  5418. // summary:
  5419. // All the properties described in the dojo.__ioArgs type, apply
  5420. // to this type. The following additional properties are allowed
  5421. // for dojo.io.iframe.send():
  5422. // method: String?
  5423. // The HTTP method to use. "GET" or "POST" are the only supported
  5424. // values. It will try to read the value from the form node's
  5425. // method, then try this argument. If neither one exists, then it
  5426. // defaults to POST.
  5427. // handleAs: String?
  5428. // Specifies what format the result data should be given to the
  5429. // load/handle callback. Valid values are: text, html, xml, json,
  5430. // javascript. IMPORTANT: For all values EXCEPT html and xml, The
  5431. // server response should be an HTML file with a textarea element.
  5432. // The response data should be inside the textarea element. Using an
  5433. // HTML document the only reliable, cross-browser way this
  5434. // transport can know when the response has loaded. For the html
  5435. // handleAs value, just return a normal HTML document. NOTE: xml
  5436. // is now supported with this transport (as of 1.1+); a known issue
  5437. // is if the XML document in question is malformed, Internet Explorer
  5438. // will throw an uncatchable error.
  5439. // content: Object?
  5440. // If "form" is one of the other args properties, then the content
  5441. // object properties become hidden form form elements. For
  5442. // instance, a content object of {name1 : "value1"} is converted
  5443. // to a hidden form element with a name of "name1" and a value of
  5444. // "value1". If there is not a "form" property, then the content
  5445. // object is converted into a name=value&name=value string, by
  5446. // using dojo.objectToQuery().
  5447. this.method = method;
  5448. this.handleAs = handleAs;
  5449. this.content = content;
  5450. }
  5451. });
  5452. =====*/
  5453. dojo.io.iframe = {
  5454. // summary:
  5455. // Sends an Ajax I/O call using and Iframe (for instance, to upload files)
  5456. create: function(/*String*/fname, /*String*/onloadstr, /*String?*/uri){
  5457. // summary:
  5458. // Creates a hidden iframe in the page. Used mostly for IO
  5459. // transports. You do not need to call this to start a
  5460. // dojo.io.iframe request. Just call send().
  5461. // fname: String
  5462. // The name of the iframe. Used for the name attribute on the
  5463. // iframe.
  5464. // onloadstr: String
  5465. // A string of JavaScript that will be executed when the content
  5466. // in the iframe loads.
  5467. // uri: String
  5468. // The value of the src attribute on the iframe element. If a
  5469. // value is not given, then dojo/resources/blank.html will be
  5470. // used.
  5471. if(window[fname]){ return window[fname]; }
  5472. if(window.frames[fname]){ return window.frames[fname]; }
  5473. var cframe = null;
  5474. var turi = uri;
  5475. if(!turi){
  5476. if(dojo.config["useXDomain"] && !dojo.config["dojoBlankHtmlUrl"]){
  5477. console.warn("dojo.io.iframe.create: When using cross-domain Dojo builds,"
  5478. + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl"
  5479. + " to the path on your domain to blank.html");
  5480. }
  5481. turi = (dojo.config["dojoBlankHtmlUrl"]||dojo.moduleUrl("dojo", "resources/blank.html"));
  5482. }
  5483. var cframe = dojo.place(
  5484. '<iframe id="'+fname+'" name="'+fname+'" src="'+turi+'" onload="'+onloadstr+
  5485. '" style="position: absolute; left: 1px; top: 1px; height: 1px; width: 1px; visibility: hidden">',
  5486. dojo.body());
  5487. window[fname] = cframe;
  5488. return cframe;
  5489. },
  5490. setSrc: function(/*DOMNode*/iframe, /*String*/src, /*Boolean*/replace){
  5491. //summary:
  5492. // Sets the URL that is loaded in an IFrame. The replace parameter
  5493. // indicates whether location.replace() should be used when
  5494. // changing the location of the iframe.
  5495. try{
  5496. if(!replace){
  5497. if(dojo.isWebKit){
  5498. iframe.location = src;
  5499. }else{
  5500. frames[iframe.name].location = src;
  5501. }
  5502. }else{
  5503. // Fun with DOM 0 incompatibilities!
  5504. var idoc;
  5505. if(dojo.isIE || dojo.isWebKit){
  5506. idoc = iframe.contentWindow.document;
  5507. }else{ // if(d.isMozilla){
  5508. idoc = iframe.contentWindow;
  5509. }
  5510. //For Safari (at least 2.0.3) and Opera, if the iframe
  5511. //has just been created but it doesn't have content
  5512. //yet, then iframe.document may be null. In that case,
  5513. //use iframe.location and return.
  5514. if(!idoc){
  5515. iframe.location = src;
  5516. return;
  5517. }else{
  5518. idoc.location.replace(src);
  5519. }
  5520. }
  5521. }catch(e){
  5522. console.log("dojo.io.iframe.setSrc: ", e);
  5523. }
  5524. },
  5525. doc: function(/*DOMNode*/iframeNode){
  5526. //summary: Returns the document object associated with the iframe DOM Node argument.
  5527. var doc = iframeNode.contentDocument || // W3
  5528. (
  5529. (
  5530. (iframeNode.name) && (iframeNode.document) &&
  5531. (dojo.doc.getElementsByTagName("iframe")[iframeNode.name].contentWindow) &&
  5532. (dojo.doc.getElementsByTagName("iframe")[iframeNode.name].contentWindow.document)
  5533. )
  5534. ) || // IE
  5535. (
  5536. (iframeNode.name)&&(dojo.doc.frames[iframeNode.name])&&
  5537. (dojo.doc.frames[iframeNode.name].document)
  5538. ) || null;
  5539. return doc;
  5540. },
  5541. send: function(/*dojo.io.iframe.__ioArgs*/args){
  5542. //summary:
  5543. // Function that sends the request to the server.
  5544. // This transport can only process one send() request at a time, so if send() is called
  5545. //multiple times, it will queue up the calls and only process one at a time.
  5546. if(!this["_frame"]){
  5547. this._frame = this.create(this._iframeName, dojo._scopeName + ".io.iframe._iframeOnload();");
  5548. }
  5549. //Set up the deferred.
  5550. var dfd = dojo._ioSetArgs(
  5551. args,
  5552. function(/*Deferred*/dfd){
  5553. //summary: canceller function for dojo._ioSetArgs call.
  5554. dfd.canceled = true;
  5555. dfd.ioArgs._callNext();
  5556. },
  5557. function(/*Deferred*/dfd){
  5558. //summary: okHandler function for dojo._ioSetArgs call.
  5559. var value = null;
  5560. try{
  5561. var ioArgs = dfd.ioArgs;
  5562. var dii = dojo.io.iframe;
  5563. var ifd = dii.doc(dii._frame);
  5564. var handleAs = ioArgs.handleAs;
  5565. //Assign correct value based on handleAs value.
  5566. value = ifd; //html
  5567. if(handleAs != "html"){
  5568. if(handleAs == "xml"){
  5569. // FF, Saf 3+ and Opera all seem to be fine with ifd being xml. We have to
  5570. // do it manually for IE6-8. Refs #6334.
  5571. if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
  5572. dojo.query("a", dii._frame.contentWindow.document.documentElement).orphan();
  5573. var xmlText=(dii._frame.contentWindow.document).documentElement.innerText;
  5574. xmlText=xmlText.replace(/>\s+</g, "><");
  5575. xmlText=dojo.trim(xmlText);
  5576. //Reusing some code in base dojo for handling XML content. Simpler and keeps
  5577. //Core from duplicating the effort needed to locate the XML Parser on IE.
  5578. var fauxXhr = { responseText: xmlText };
  5579. value = dojo._contentHandlers["xml"](fauxXhr); // DOMDocument
  5580. }
  5581. }else{
  5582. value = ifd.getElementsByTagName("textarea")[0].value; //text
  5583. if(handleAs == "json"){
  5584. value = dojo.fromJson(value); //json
  5585. }else if(handleAs == "javascript"){
  5586. value = dojo.eval(value); //javascript
  5587. }
  5588. }
  5589. }
  5590. }catch(e){
  5591. value = e;
  5592. }finally{
  5593. ioArgs._callNext();
  5594. }
  5595. return value;
  5596. },
  5597. function(/*Error*/error, /*Deferred*/dfd){
  5598. //summary: errHandler function for dojo._ioSetArgs call.
  5599. dfd.ioArgs._hasError = true;
  5600. dfd.ioArgs._callNext();
  5601. return error;
  5602. }
  5603. );
  5604. //Set up a function that will fire the next iframe request. Make sure it only
  5605. //happens once per deferred.
  5606. dfd.ioArgs._callNext = function(){
  5607. if(!this["_calledNext"]){
  5608. this._calledNext = true;
  5609. dojo.io.iframe._currentDfd = null;
  5610. dojo.io.iframe._fireNextRequest();
  5611. }
  5612. };
  5613. this._dfdQueue.push(dfd);
  5614. this._fireNextRequest();
  5615. //Add it the IO watch queue, to get things like timeout support.
  5616. dojo._ioWatch(
  5617. dfd,
  5618. function(/*Deferred*/dfd){
  5619. //validCheck
  5620. return !dfd.ioArgs["_hasError"];
  5621. },
  5622. function(dfd){
  5623. //ioCheck
  5624. return (!!dfd.ioArgs["_finished"]);
  5625. },
  5626. function(dfd){
  5627. //resHandle
  5628. if(dfd.ioArgs._finished){
  5629. dfd.callback(dfd);
  5630. }else{
  5631. dfd.errback(new Error("Invalid dojo.io.iframe request state"));
  5632. }
  5633. }
  5634. );
  5635. return dfd;
  5636. },
  5637. _currentDfd: null,
  5638. _dfdQueue: [],
  5639. _iframeName: dojo._scopeName + "IoIframe",
  5640. _fireNextRequest: function(){
  5641. //summary: Internal method used to fire the next request in the bind queue.
  5642. try{
  5643. if((this._currentDfd)||(this._dfdQueue.length == 0)){ return; }
  5644. //Find next deferred, skip the canceled ones.
  5645. do{
  5646. var dfd = this._currentDfd = this._dfdQueue.shift();
  5647. } while(dfd && dfd.canceled && this._dfdQueue.length);
  5648. //If no more dfds, cancel.
  5649. if(!dfd || dfd.canceled){
  5650. this._currentDfd = null;
  5651. return;
  5652. }
  5653. var ioArgs = dfd.ioArgs;
  5654. var args = ioArgs.args;
  5655. ioArgs._contentToClean = [];
  5656. var fn = dojo.byId(args["form"]);
  5657. var content = args["content"] || {};
  5658. if(fn){
  5659. if(content){
  5660. // if we have things in content, we need to add them to the form
  5661. // before submission
  5662. var pHandler = function(name, value) {
  5663. dojo.create("input", {type: "hidden", name: name, value: value}, fn);
  5664. ioArgs._contentToClean.push(name);
  5665. };
  5666. for(var x in content){
  5667. var val = content[x];
  5668. if(dojo.isArray(val) && val.length > 1){
  5669. var i;
  5670. for (i = 0; i < val.length; i++) {
  5671. pHandler(x,val[i]);
  5672. }
  5673. }else{
  5674. if(!fn[x]){
  5675. pHandler(x,val);
  5676. }else{
  5677. fn[x].value = val;
  5678. }
  5679. }
  5680. }
  5681. }
  5682. //IE requires going through getAttributeNode instead of just getAttribute in some form cases,
  5683. //so use it for all. See #2844
  5684. var actnNode = fn.getAttributeNode("action");
  5685. var mthdNode = fn.getAttributeNode("method");
  5686. var trgtNode = fn.getAttributeNode("target");
  5687. if(args["url"]){
  5688. ioArgs._originalAction = actnNode ? actnNode.value : null;
  5689. if(actnNode){
  5690. actnNode.value = args.url;
  5691. }else{
  5692. fn.setAttribute("action",args.url);
  5693. }
  5694. }
  5695. if(!mthdNode || !mthdNode.value){
  5696. if(mthdNode){
  5697. mthdNode.value= (args["method"]) ? args["method"] : "post";
  5698. }else{
  5699. fn.setAttribute("method", (args["method"]) ? args["method"] : "post");
  5700. }
  5701. }
  5702. ioArgs._originalTarget = trgtNode ? trgtNode.value: null;
  5703. if(trgtNode){
  5704. trgtNode.value = this._iframeName;
  5705. }else{
  5706. fn.setAttribute("target", this._iframeName);
  5707. }
  5708. fn.target = this._iframeName;
  5709. dojo._ioNotifyStart(dfd);
  5710. fn.submit();
  5711. }else{
  5712. // otherwise we post a GET string by changing URL location for the
  5713. // iframe
  5714. var tmpUrl = args.url + (args.url.indexOf("?") > -1 ? "&" : "?") + ioArgs.query;
  5715. dojo._ioNotifyStart(dfd);
  5716. this.setSrc(this._frame, tmpUrl, true);
  5717. }
  5718. }catch(e){
  5719. dfd.errback(e);
  5720. }
  5721. },
  5722. _iframeOnload: function(){
  5723. var dfd = this._currentDfd;
  5724. if(!dfd){
  5725. this._fireNextRequest();
  5726. return;
  5727. }
  5728. var ioArgs = dfd.ioArgs;
  5729. var args = ioArgs.args;
  5730. var fNode = dojo.byId(args.form);
  5731. if(fNode){
  5732. // remove all the hidden content inputs
  5733. var toClean = ioArgs._contentToClean;
  5734. for(var i = 0; i < toClean.length; i++) {
  5735. var key = toClean[i];
  5736. //Need to cycle over all nodes since we may have added
  5737. //an array value which means that more than one node could
  5738. //have the same .name value.
  5739. for(var j = 0; j < fNode.childNodes.length; j++){
  5740. var chNode = fNode.childNodes[j];
  5741. if(chNode.name == key){
  5742. dojo.destroy(chNode);
  5743. break;
  5744. }
  5745. }
  5746. }
  5747. // restore original action + target
  5748. if(ioArgs["_originalAction"]){
  5749. fNode.setAttribute("action", ioArgs._originalAction);
  5750. }
  5751. if(ioArgs["_originalTarget"]){
  5752. fNode.setAttribute("target", ioArgs._originalTarget);
  5753. fNode.target = ioArgs._originalTarget;
  5754. }
  5755. }
  5756. ioArgs._finished = true;
  5757. }
  5758. };
  5759. }
  5760. if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  5761. dojo._hasResource["dojo.number"] = true;
  5762. dojo.provide("dojo.number");
  5763. dojo.getObject("number", true, dojo);
  5764. /*=====
  5765. dojo.number = {
  5766. // summary: localized formatting and parsing routines for Number
  5767. }
  5768. dojo.number.__FormatOptions = function(){
  5769. // pattern: String?
  5770. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  5771. // with this string. Default value is based on locale. Overriding this property will defeat
  5772. // localization. Literal characters in patterns are not supported.
  5773. // type: String?
  5774. // choose a format type based on the locale from the following:
  5775. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  5776. // places: Number?
  5777. // fixed number of decimal places to show. This overrides any
  5778. // information in the provided pattern.
  5779. // round: Number?
  5780. // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
  5781. // means do not round.
  5782. // locale: String?
  5783. // override the locale used to determine formatting rules
  5784. // fractional: Boolean?
  5785. // If false, show no decimal places, overriding places and pattern settings.
  5786. this.pattern = pattern;
  5787. this.type = type;
  5788. this.places = places;
  5789. this.round = round;
  5790. this.locale = locale;
  5791. this.fractional = fractional;
  5792. }
  5793. =====*/
  5794. dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
  5795. // summary:
  5796. // Format a Number as a String, using locale-specific settings
  5797. // description:
  5798. // Create a string from a Number using a known localized pattern.
  5799. // Formatting patterns appropriate to the locale are chosen from the
  5800. // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
  5801. // delimiters.
  5802. // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
  5803. // value:
  5804. // the number to be formatted
  5805. options = dojo.mixin({}, options || {});
  5806. var locale = dojo.i18n.normalizeLocale(options.locale),
  5807. bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
  5808. options.customs = bundle;
  5809. var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
  5810. if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
  5811. return dojo.number._applyPattern(value, pattern, options); // String
  5812. };
  5813. //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
  5814. dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
  5815. dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
  5816. // summary:
  5817. // Apply pattern to format value as a string using options. Gives no
  5818. // consideration to local customs.
  5819. // value:
  5820. // the number to be formatted.
  5821. // pattern:
  5822. // a pattern string as described by
  5823. // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  5824. // options: dojo.number.__FormatOptions?
  5825. // _applyPattern is usually called via `dojo.number.format()` which
  5826. // populates an extra property in the options parameter, "customs".
  5827. // The customs object specifies group and decimal parameters if set.
  5828. //TODO: support escapes
  5829. options = options || {};
  5830. var group = options.customs.group,
  5831. decimal = options.customs.decimal,
  5832. patternList = pattern.split(';'),
  5833. positivePattern = patternList[0];
  5834. pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
  5835. //TODO: only test against unescaped
  5836. if(pattern.indexOf('%') != -1){
  5837. value *= 100;
  5838. }else if(pattern.indexOf('\u2030') != -1){
  5839. value *= 1000; // per mille
  5840. }else if(pattern.indexOf('\u00a4') != -1){
  5841. group = options.customs.currencyGroup || group;//mixins instead?
  5842. decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
  5843. pattern = pattern.replace(/\u00a4{1,3}/, function(match){
  5844. var prop = ["symbol", "currency", "displayName"][match.length-1];
  5845. return options[prop] || options.currency || "";
  5846. });
  5847. }else if(pattern.indexOf('E') != -1){
  5848. throw new Error("exponential notation not supported");
  5849. }
  5850. //TODO: support @ sig figs?
  5851. var numberPatternRE = dojo.number._numberPatternRE;
  5852. var numberPattern = positivePattern.match(numberPatternRE);
  5853. if(!numberPattern){
  5854. throw new Error("unable to find a number expression in pattern: "+pattern);
  5855. }
  5856. if(options.fractional === false){ options.places = 0; }
  5857. return pattern.replace(numberPatternRE,
  5858. dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
  5859. };
  5860. dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
  5861. // summary:
  5862. // Rounds to the nearest value with the given number of decimal places, away from zero
  5863. // description:
  5864. // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
  5865. // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
  5866. // fractional increments also, such as the nearest quarter.
  5867. // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
  5868. // value:
  5869. // The number to round
  5870. // places:
  5871. // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
  5872. // Must be non-negative.
  5873. // increment:
  5874. // Rounds next place to nearest value of increment/10. 10 by default.
  5875. // example:
  5876. // >>> dojo.number.round(-0.5)
  5877. // -1
  5878. // >>> dojo.number.round(162.295, 2)
  5879. // 162.29 // note floating point error. Should be 162.3
  5880. // >>> dojo.number.round(10.71, 0, 2.5)
  5881. // 10.75
  5882. var factor = 10 / (increment || 10);
  5883. return (factor * +value).toFixed(places) / factor; // Number
  5884. };
  5885. if((0.9).toFixed() == 0){
  5886. // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
  5887. // is just after the rounding place and is >=5
  5888. (function(){
  5889. var round = dojo.number.round;
  5890. dojo.number.round = function(v, p, m){
  5891. var d = Math.pow(10, -p || 0), a = Math.abs(v);
  5892. if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
  5893. d = 0;
  5894. }
  5895. return round(v, p, m) + (v > 0 ? d : -d);
  5896. };
  5897. })();
  5898. }
  5899. /*=====
  5900. dojo.number.__FormatAbsoluteOptions = function(){
  5901. // decimal: String?
  5902. // the decimal separator
  5903. // group: String?
  5904. // the group separator
  5905. // places: Number?|String?
  5906. // number of decimal places. the range "n,m" will format to m places.
  5907. // round: Number?
  5908. // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
  5909. // means don't round.
  5910. this.decimal = decimal;
  5911. this.group = group;
  5912. this.places = places;
  5913. this.round = round;
  5914. }
  5915. =====*/
  5916. dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
  5917. // summary:
  5918. // Apply numeric pattern to absolute value using options. Gives no
  5919. // consideration to local customs.
  5920. // value:
  5921. // the number to be formatted, ignores sign
  5922. // pattern:
  5923. // the number portion of a pattern (e.g. `#,##0.00`)
  5924. options = options || {};
  5925. if(options.places === true){options.places=0;}
  5926. if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
  5927. var patternParts = pattern.split("."),
  5928. comma = typeof options.places == "string" && options.places.indexOf(","),
  5929. maxPlaces = options.places;
  5930. if(comma){
  5931. maxPlaces = options.places.substring(comma + 1);
  5932. }else if(!(maxPlaces >= 0)){
  5933. maxPlaces = (patternParts[1] || []).length;
  5934. }
  5935. if(!(options.round < 0)){
  5936. value = dojo.number.round(value, maxPlaces, options.round);
  5937. }
  5938. var valueParts = String(Math.abs(value)).split("."),
  5939. fractional = valueParts[1] || "";
  5940. if(patternParts[1] || options.places){
  5941. if(comma){
  5942. options.places = options.places.substring(0, comma);
  5943. }
  5944. // Pad fractional with trailing zeros
  5945. var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
  5946. if(pad > fractional.length){
  5947. valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
  5948. }
  5949. // Truncate fractional
  5950. if(maxPlaces < fractional.length){
  5951. valueParts[1] = fractional.substr(0, maxPlaces);
  5952. }
  5953. }else{
  5954. if(valueParts[1]){ valueParts.pop(); }
  5955. }
  5956. // Pad whole with leading zeros
  5957. var patternDigits = patternParts[0].replace(',', '');
  5958. pad = patternDigits.indexOf("0");
  5959. if(pad != -1){
  5960. pad = patternDigits.length - pad;
  5961. if(pad > valueParts[0].length){
  5962. valueParts[0] = dojo.string.pad(valueParts[0], pad);
  5963. }
  5964. // Truncate whole
  5965. if(patternDigits.indexOf("#") == -1){
  5966. valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
  5967. }
  5968. }
  5969. // Add group separators
  5970. var index = patternParts[0].lastIndexOf(','),
  5971. groupSize, groupSize2;
  5972. if(index != -1){
  5973. groupSize = patternParts[0].length - index - 1;
  5974. var remainder = patternParts[0].substr(0, index);
  5975. index = remainder.lastIndexOf(',');
  5976. if(index != -1){
  5977. groupSize2 = remainder.length - index - 1;
  5978. }
  5979. }
  5980. var pieces = [];
  5981. for(var whole = valueParts[0]; whole;){
  5982. var off = whole.length - groupSize;
  5983. pieces.push((off > 0) ? whole.substr(off) : whole);
  5984. whole = (off > 0) ? whole.slice(0, off) : "";
  5985. if(groupSize2){
  5986. groupSize = groupSize2;
  5987. delete groupSize2;
  5988. }
  5989. }
  5990. valueParts[0] = pieces.reverse().join(options.group || ",");
  5991. return valueParts.join(options.decimal || ".");
  5992. };
  5993. /*=====
  5994. dojo.number.__RegexpOptions = function(){
  5995. // pattern: String?
  5996. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  5997. // with this string. Default value is based on locale. Overriding this property will defeat
  5998. // localization.
  5999. // type: String?
  6000. // choose a format type based on the locale from the following:
  6001. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  6002. // locale: String?
  6003. // override the locale used to determine formatting rules
  6004. // strict: Boolean?
  6005. // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
  6006. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
  6007. // places: Number|String?
  6008. // number of decimal places to accept: Infinity, a positive number, or
  6009. // a range "n,m". Defined by pattern or Infinity if pattern not provided.
  6010. this.pattern = pattern;
  6011. this.type = type;
  6012. this.locale = locale;
  6013. this.strict = strict;
  6014. this.places = places;
  6015. }
  6016. =====*/
  6017. dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
  6018. // summary:
  6019. // Builds the regular needed to parse a number
  6020. // description:
  6021. // Returns regular expression with positive and negative match, group
  6022. // and decimal separators
  6023. return dojo.number._parseInfo(options).regexp; // String
  6024. };
  6025. dojo.number._parseInfo = function(/*Object?*/options){
  6026. options = options || {};
  6027. var locale = dojo.i18n.normalizeLocale(options.locale),
  6028. bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
  6029. pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
  6030. //TODO: memoize?
  6031. group = bundle.group,
  6032. decimal = bundle.decimal,
  6033. factor = 1;
  6034. if(pattern.indexOf('%') != -1){
  6035. factor /= 100;
  6036. }else if(pattern.indexOf('\u2030') != -1){
  6037. factor /= 1000; // per mille
  6038. }else{
  6039. var isCurrency = pattern.indexOf('\u00a4') != -1;
  6040. if(isCurrency){
  6041. group = bundle.currencyGroup || group;
  6042. decimal = bundle.currencyDecimal || decimal;
  6043. }
  6044. }
  6045. //TODO: handle quoted escapes
  6046. var patternList = pattern.split(';');
  6047. if(patternList.length == 1){
  6048. patternList.push("-" + patternList[0]);
  6049. }
  6050. var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
  6051. pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
  6052. return pattern.replace(dojo.number._numberPatternRE, function(format){
  6053. var flags = {
  6054. signed: false,
  6055. separator: options.strict ? group : [group,""],
  6056. fractional: options.fractional,
  6057. decimal: decimal,
  6058. exponent: false
  6059. },
  6060. parts = format.split('.'),
  6061. places = options.places;
  6062. // special condition for percent (factor != 1)
  6063. // allow decimal places even if not specified in pattern
  6064. if(parts.length == 1 && factor != 1){
  6065. parts[1] = "###";
  6066. }
  6067. if(parts.length == 1 || places === 0){
  6068. flags.fractional = false;
  6069. }else{
  6070. if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
  6071. if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
  6072. if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
  6073. flags.places = places;
  6074. }
  6075. var groups = parts[0].split(',');
  6076. if(groups.length > 1){
  6077. flags.groupSize = groups.pop().length;
  6078. if(groups.length > 1){
  6079. flags.groupSize2 = groups.pop().length;
  6080. }
  6081. }
  6082. return "("+dojo.number._realNumberRegexp(flags)+")";
  6083. });
  6084. }, true);
  6085. if(isCurrency){
  6086. // substitute the currency symbol for the placeholder in the pattern
  6087. re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
  6088. var prop = ["symbol", "currency", "displayName"][target.length-1],
  6089. symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
  6090. before = before ? "[\\s\\xa0]" : "";
  6091. after = after ? "[\\s\\xa0]" : "";
  6092. if(!options.strict){
  6093. if(before){before += "*";}
  6094. if(after){after += "*";}
  6095. return "(?:"+before+symbol+after+")?";
  6096. }
  6097. return before+symbol+after;
  6098. });
  6099. }
  6100. //TODO: substitute localized sign/percent/permille/etc.?
  6101. // normalize whitespace and return
  6102. return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
  6103. };
  6104. /*=====
  6105. dojo.number.__ParseOptions = function(){
  6106. // pattern: String?
  6107. // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  6108. // with this string. Default value is based on locale. Overriding this property will defeat
  6109. // localization. Literal characters in patterns are not supported.
  6110. // type: String?
  6111. // choose a format type based on the locale from the following:
  6112. // decimal, scientific (not yet supported), percent, currency. decimal by default.
  6113. // locale: String?
  6114. // override the locale used to determine formatting rules
  6115. // strict: Boolean?
  6116. // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
  6117. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
  6118. // fractional: Boolean?|Array?
  6119. // Whether to include the fractional portion, where the number of decimal places are implied by pattern
  6120. // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
  6121. this.pattern = pattern;
  6122. this.type = type;
  6123. this.locale = locale;
  6124. this.strict = strict;
  6125. this.fractional = fractional;
  6126. }
  6127. =====*/
  6128. dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
  6129. // summary:
  6130. // Convert a properly formatted string to a primitive Number, using
  6131. // locale-specific settings.
  6132. // description:
  6133. // Create a Number from a string using a known localized pattern.
  6134. // Formatting patterns are chosen appropriate to the locale
  6135. // and follow the syntax described by
  6136. // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
  6137. // Note that literal characters in patterns are not supported.
  6138. // expression:
  6139. // A string representation of a Number
  6140. var info = dojo.number._parseInfo(options),
  6141. results = (new RegExp("^"+info.regexp+"$")).exec(expression);
  6142. if(!results){
  6143. return NaN; //NaN
  6144. }
  6145. var absoluteMatch = results[1]; // match for the positive expression
  6146. if(!results[1]){
  6147. if(!results[2]){
  6148. return NaN; //NaN
  6149. }
  6150. // matched the negative pattern
  6151. absoluteMatch =results[2];
  6152. info.factor *= -1;
  6153. }
  6154. // Transform it to something Javascript can parse as a number. Normalize
  6155. // decimal point and strip out group separators or alternate forms of whitespace
  6156. absoluteMatch = absoluteMatch.
  6157. replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
  6158. replace(info.decimal, ".");
  6159. // Adjust for negative sign, percent, etc. as necessary
  6160. return absoluteMatch * info.factor; //Number
  6161. };
  6162. /*=====
  6163. dojo.number.__RealNumberRegexpFlags = function(){
  6164. // places: Number?
  6165. // The integer number of decimal places or a range given as "n,m". If
  6166. // not given, the decimal part is optional and the number of places is
  6167. // unlimited.
  6168. // decimal: String?
  6169. // A string for the character used as the decimal point. Default
  6170. // is ".".
  6171. // fractional: Boolean?|Array?
  6172. // Whether decimal places are used. Can be true, false, or [true,
  6173. // false]. Default is [true, false] which means optional.
  6174. // exponent: Boolean?|Array?
  6175. // Express in exponential notation. Can be true, false, or [true,
  6176. // false]. Default is [true, false], (i.e. will match if the
  6177. // exponential part is present are not).
  6178. // eSigned: Boolean?|Array?
  6179. // The leading plus-or-minus sign on the exponent. Can be true,
  6180. // false, or [true, false]. Default is [true, false], (i.e. will
  6181. // match if it is signed or unsigned). flags in regexp.integer can be
  6182. // applied.
  6183. this.places = places;
  6184. this.decimal = decimal;
  6185. this.fractional = fractional;
  6186. this.exponent = exponent;
  6187. this.eSigned = eSigned;
  6188. }
  6189. =====*/
  6190. dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
  6191. // summary:
  6192. // Builds a regular expression to match a real number in exponential
  6193. // notation
  6194. // assign default values to missing parameters
  6195. flags = flags || {};
  6196. //TODO: use mixin instead?
  6197. if(!("places" in flags)){ flags.places = Infinity; }
  6198. if(typeof flags.decimal != "string"){ flags.decimal = "."; }
  6199. if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
  6200. if(!("exponent" in flags)){ flags.exponent = [true, false]; }
  6201. if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
  6202. var integerRE = dojo.number._integerRegexp(flags),
  6203. decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
  6204. function(q){
  6205. var re = "";
  6206. if(q && (flags.places!==0)){
  6207. re = "\\" + flags.decimal;
  6208. if(flags.places == Infinity){
  6209. re = "(?:" + re + "\\d+)?";
  6210. }else{
  6211. re += "\\d{" + flags.places + "}";
  6212. }
  6213. }
  6214. return re;
  6215. },
  6216. true
  6217. );
  6218. var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
  6219. function(q){
  6220. if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
  6221. return "";
  6222. }
  6223. );
  6224. var realRE = integerRE + decimalRE;
  6225. // allow for decimals without integers, e.g. .25
  6226. if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
  6227. return realRE + exponentRE; // String
  6228. };
  6229. /*=====
  6230. dojo.number.__IntegerRegexpFlags = function(){
  6231. // signed: Boolean?
  6232. // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
  6233. // Default is `[true, false]`, (i.e. will match if it is signed
  6234. // or unsigned).
  6235. // separator: String?
  6236. // The character used as the thousands separator. Default is no
  6237. // separator. For more than one symbol use an array, e.g. `[",", ""]`,
  6238. // makes ',' optional.
  6239. // groupSize: Number?
  6240. // group size between separators
  6241. // groupSize2: Number?
  6242. // second grouping, where separators 2..n have a different interval than the first separator (for India)
  6243. this.signed = signed;
  6244. this.separator = separator;
  6245. this.groupSize = groupSize;
  6246. this.groupSize2 = groupSize2;
  6247. }
  6248. =====*/
  6249. dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
  6250. // summary:
  6251. // Builds a regular expression that matches an integer
  6252. // assign default values to missing parameters
  6253. flags = flags || {};
  6254. if(!("signed" in flags)){ flags.signed = [true, false]; }
  6255. if(!("separator" in flags)){
  6256. flags.separator = "";
  6257. }else if(!("groupSize" in flags)){
  6258. flags.groupSize = 3;
  6259. }
  6260. var signRE = dojo.regexp.buildGroupRE(flags.signed,
  6261. function(q){ return q ? "[-+]" : ""; },
  6262. true
  6263. );
  6264. var numberRE = dojo.regexp.buildGroupRE(flags.separator,
  6265. function(sep){
  6266. if(!sep){
  6267. return "(?:\\d+)";
  6268. }
  6269. sep = dojo.regexp.escapeString(sep);
  6270. if(sep == " "){ sep = "\\s"; }
  6271. else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
  6272. var grp = flags.groupSize, grp2 = flags.groupSize2;
  6273. //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
  6274. if(grp2){
  6275. var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
  6276. return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
  6277. }
  6278. return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
  6279. },
  6280. true
  6281. );
  6282. return signRE + numberRE; // String
  6283. };
  6284. }
  6285. if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  6286. dojo._hasResource["dojo.AdapterRegistry"] = true;
  6287. dojo.provide("dojo.AdapterRegistry");
  6288. dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
  6289. // summary:
  6290. // A registry to make contextual calling/searching easier.
  6291. // description:
  6292. // Objects of this class keep list of arrays in the form [name, check,
  6293. // wrap, directReturn] that are used to determine what the contextual
  6294. // result of a set of checked arguments is. All check/wrap functions
  6295. // in this registry should be of the same arity.
  6296. // example:
  6297. // | // create a new registry
  6298. // | var reg = new dojo.AdapterRegistry();
  6299. // | reg.register("handleString",
  6300. // | dojo.isString,
  6301. // | function(str){
  6302. // | // do something with the string here
  6303. // | }
  6304. // | );
  6305. // | reg.register("handleArr",
  6306. // | dojo.isArray,
  6307. // | function(arr){
  6308. // | // do something with the array here
  6309. // | }
  6310. // | );
  6311. // |
  6312. // | // now we can pass reg.match() *either* an array or a string and
  6313. // | // the value we pass will get handled by the right function
  6314. // | reg.match("someValue"); // will call the first function
  6315. // | reg.match(["someValue"]); // will call the second
  6316. this.pairs = [];
  6317. this.returnWrappers = returnWrappers || false; // Boolean
  6318. };
  6319. dojo.extend(dojo.AdapterRegistry, {
  6320. register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
  6321. // summary:
  6322. // register a check function to determine if the wrap function or
  6323. // object gets selected
  6324. // name:
  6325. // a way to identify this matcher.
  6326. // check:
  6327. // a function that arguments are passed to from the adapter's
  6328. // match() function. The check function should return true if the
  6329. // given arguments are appropriate for the wrap function.
  6330. // directReturn:
  6331. // If directReturn is true, the value passed in for wrap will be
  6332. // returned instead of being called. Alternately, the
  6333. // AdapterRegistry can be set globally to "return not call" using
  6334. // the returnWrappers property. Either way, this behavior allows
  6335. // the registry to act as a "search" function instead of a
  6336. // function interception library.
  6337. // override:
  6338. // If override is given and true, the check function will be given
  6339. // highest priority. Otherwise, it will be the lowest priority
  6340. // adapter.
  6341. this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
  6342. },
  6343. match: function(/* ... */){
  6344. // summary:
  6345. // Find an adapter for the given arguments. If no suitable adapter
  6346. // is found, throws an exception. match() accepts any number of
  6347. // arguments, all of which are passed to all matching functions
  6348. // from the registered pairs.
  6349. for(var i = 0; i < this.pairs.length; i++){
  6350. var pair = this.pairs[i];
  6351. if(pair[1].apply(this, arguments)){
  6352. if((pair[3])||(this.returnWrappers)){
  6353. return pair[2];
  6354. }else{
  6355. return pair[2].apply(this, arguments);
  6356. }
  6357. }
  6358. }
  6359. throw new Error("No match found");
  6360. },
  6361. unregister: function(name){
  6362. // summary: Remove a named adapter from the registry
  6363. // FIXME: this is kind of a dumb way to handle this. On a large
  6364. // registry this will be slow-ish and we can use the name as a lookup
  6365. // should we choose to trade memory for speed.
  6366. for(var i = 0; i < this.pairs.length; i++){
  6367. var pair = this.pairs[i];
  6368. if(pair[0] == name){
  6369. this.pairs.splice(i, 1);
  6370. return true;
  6371. }
  6372. }
  6373. return false;
  6374. }
  6375. });
  6376. }
  6377. if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  6378. dojo._hasResource["dijit._base.place"] = true;
  6379. dojo.provide("dijit._base.place");
  6380. dijit.getViewport = function(){
  6381. // summary:
  6382. // Returns the dimensions and scroll position of the viewable area of a browser window
  6383. return dojo.window.getBox();
  6384. };
  6385. /*=====
  6386. dijit.__Position = function(){
  6387. // x: Integer
  6388. // horizontal coordinate in pixels, relative to document body
  6389. // y: Integer
  6390. // vertical coordinate in pixels, relative to document body
  6391. thix.x = x;
  6392. this.y = y;
  6393. }
  6394. =====*/
  6395. dijit.placeOnScreen = function(
  6396. /* DomNode */ node,
  6397. /* dijit.__Position */ pos,
  6398. /* String[] */ corners,
  6399. /* dijit.__Position? */ padding){
  6400. // summary:
  6401. // Positions one of the node's corners at specified position
  6402. // such that node is fully visible in viewport.
  6403. // description:
  6404. // NOTE: node is assumed to be absolutely or relatively positioned.
  6405. // pos:
  6406. // Object like {x: 10, y: 20}
  6407. // corners:
  6408. // Array of Strings representing order to try corners in, like ["TR", "BL"].
  6409. // Possible values are:
  6410. // * "BL" - bottom left
  6411. // * "BR" - bottom right
  6412. // * "TL" - top left
  6413. // * "TR" - top right
  6414. // padding:
  6415. // set padding to put some buffer around the element you want to position.
  6416. // example:
  6417. // Try to place node's top right corner at (10,20).
  6418. // If that makes node go (partially) off screen, then try placing
  6419. // bottom left corner at (10,20).
  6420. // | placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
  6421. var choices = dojo.map(corners, function(corner){
  6422. var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
  6423. if(padding){
  6424. c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
  6425. c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
  6426. }
  6427. return c;
  6428. });
  6429. return dijit._place(node, choices);
  6430. }
  6431. dijit._place = function(/*DomNode*/ node, choices, layoutNode, /*Object*/ aroundNodeCoords){
  6432. // summary:
  6433. // Given a list of spots to put node, put it at the first spot where it fits,
  6434. // of if it doesn't fit anywhere then the place with the least overflow
  6435. // choices: Array
  6436. // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
  6437. // Above example says to put the top-left corner of the node at (10,20)
  6438. // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
  6439. // for things like tooltip, they are displayed differently (and have different dimensions)
  6440. // based on their orientation relative to the parent. This adjusts the popup based on orientation.
  6441. // It also passes in the available size for the popup, which is useful for tooltips to
  6442. // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
  6443. // how much the popup had to be modified to fit into the available space. This is used to determine
  6444. // what the best placement is.
  6445. // aroundNodeCoords: Object
  6446. // Size of aroundNode, ex: {w: 200, h: 50}
  6447. // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
  6448. // viewport over document
  6449. var view = dojo.window.getBox();
  6450. // This won't work if the node is inside a <div style="position: relative">,
  6451. // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong
  6452. // and also it might get cutoff)
  6453. if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
  6454. dojo.body().appendChild(node);
  6455. }
  6456. var best = null;
  6457. dojo.some(choices, function(choice){
  6458. var corner = choice.corner;
  6459. var pos = choice.pos;
  6460. var overflow = 0;
  6461. // calculate amount of space available given specified position of node
  6462. var spaceAvailable = {
  6463. w: corner.charAt(1) == 'L' ? (view.l + view.w) - pos.x : pos.x - view.l,
  6464. h: corner.charAt(1) == 'T' ? (view.t + view.h) - pos.y : pos.y - view.t
  6465. };
  6466. // configure node to be displayed in given position relative to button
  6467. // (need to do this in order to get an accurate size for the node, because
  6468. // a tooltip's size changes based on position, due to triangle)
  6469. if(layoutNode){
  6470. var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
  6471. overflow = typeof res == "undefined" ? 0 : res;
  6472. }
  6473. // get node's size
  6474. var style = node.style;
  6475. var oldDisplay = style.display;
  6476. var oldVis = style.visibility;
  6477. style.visibility = "hidden";
  6478. style.display = "";
  6479. var mb = dojo.marginBox(node);
  6480. style.display = oldDisplay;
  6481. style.visibility = oldVis;
  6482. // coordinates and size of node with specified corner placed at pos,
  6483. // and clipped by viewport
  6484. var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)),
  6485. startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)),
  6486. endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x),
  6487. endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y),
  6488. width = endX - startX,
  6489. height = endY - startY;
  6490. overflow += (mb.w - width) + (mb.h - height);
  6491. if(best == null || overflow < best.overflow){
  6492. best = {
  6493. corner: corner,
  6494. aroundCorner: choice.aroundCorner,
  6495. x: startX,
  6496. y: startY,
  6497. w: width,
  6498. h: height,
  6499. overflow: overflow,
  6500. spaceAvailable: spaceAvailable
  6501. };
  6502. }
  6503. return !overflow;
  6504. });
  6505. // In case the best position is not the last one we checked, need to call
  6506. // layoutNode() again.
  6507. if(best.overflow && layoutNode){
  6508. layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
  6509. }
  6510. // And then position the node. Do this last, after the layoutNode() above
  6511. // has sized the node, due to browser quirks when the viewport is scrolled
  6512. // (specifically that a Tooltip will shrink to fit as though the window was
  6513. // scrolled to the left).
  6514. //
  6515. // In RTL mode, set style.right rather than style.left so in the common case,
  6516. // window resizes move the popup along with the aroundNode.
  6517. var l = dojo._isBodyLtr(),
  6518. s = node.style;
  6519. s.top = best.y + "px";
  6520. s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
  6521. s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
  6522. return best;
  6523. }
  6524. dijit.placeOnScreenAroundNode = function(
  6525. /* DomNode */ node,
  6526. /* DomNode */ aroundNode,
  6527. /* Object */ aroundCorners,
  6528. /* Function? */ layoutNode){
  6529. // summary:
  6530. // Position node adjacent or kitty-corner to aroundNode
  6531. // such that it's fully visible in viewport.
  6532. //
  6533. // description:
  6534. // Place node such that corner of node touches a corner of
  6535. // aroundNode, and that node is fully visible.
  6536. //
  6537. // aroundCorners:
  6538. // Ordered list of pairs of corners to try matching up.
  6539. // Each pair of corners is represented as a key/value in the hash,
  6540. // where the key corresponds to the aroundNode's corner, and
  6541. // the value corresponds to the node's corner:
  6542. //
  6543. // | { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...}
  6544. //
  6545. // The following strings are used to represent the four corners:
  6546. // * "BL" - bottom left
  6547. // * "BR" - bottom right
  6548. // * "TL" - top left
  6549. // * "TR" - top right
  6550. //
  6551. // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
  6552. // For things like tooltip, they are displayed differently (and have different dimensions)
  6553. // based on their orientation relative to the parent. This adjusts the popup based on orientation.
  6554. //
  6555. // example:
  6556. // | dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
  6557. // This will try to position node such that node's top-left corner is at the same position
  6558. // as the bottom left corner of the aroundNode (ie, put node below
  6559. // aroundNode, with left edges aligned). If that fails it will try to put
  6560. // the bottom-right corner of node where the top right corner of aroundNode is
  6561. // (ie, put node above aroundNode, with right edges aligned)
  6562. //
  6563. // get coordinates of aroundNode
  6564. aroundNode = dojo.byId(aroundNode);
  6565. var aroundNodePos = dojo.position(aroundNode, true);
  6566. // place the node around the calculated rectangle
  6567. return dijit._placeOnScreenAroundRect(node,
  6568. aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h, // rectangle
  6569. aroundCorners, layoutNode);
  6570. };
  6571. /*=====
  6572. dijit.__Rectangle = function(){
  6573. // x: Integer
  6574. // horizontal offset in pixels, relative to document body
  6575. // y: Integer
  6576. // vertical offset in pixels, relative to document body
  6577. // width: Integer
  6578. // width in pixels
  6579. // height: Integer
  6580. // height in pixels
  6581. this.x = x;
  6582. this.y = y;
  6583. this.width = width;
  6584. this.height = height;
  6585. }
  6586. =====*/
  6587. dijit.placeOnScreenAroundRectangle = function(
  6588. /* DomNode */ node,
  6589. /* dijit.__Rectangle */ aroundRect,
  6590. /* Object */ aroundCorners,
  6591. /* Function */ layoutNode){
  6592. // summary:
  6593. // Like dijit.placeOnScreenAroundNode(), except that the "around"
  6594. // parameter is an arbitrary rectangle on the screen (x, y, width, height)
  6595. // instead of a dom node.
  6596. return dijit._placeOnScreenAroundRect(node,
  6597. aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height, // rectangle
  6598. aroundCorners, layoutNode);
  6599. };
  6600. dijit._placeOnScreenAroundRect = function(
  6601. /* DomNode */ node,
  6602. /* Number */ x,
  6603. /* Number */ y,
  6604. /* Number */ width,
  6605. /* Number */ height,
  6606. /* Object */ aroundCorners,
  6607. /* Function */ layoutNode){
  6608. // summary:
  6609. // Like dijit.placeOnScreenAroundNode(), except it accepts coordinates
  6610. // of a rectangle to place node adjacent to.
  6611. // TODO: combine with placeOnScreenAroundRectangle()
  6612. // Generate list of possible positions for node
  6613. var choices = [];
  6614. for(var nodeCorner in aroundCorners){
  6615. choices.push( {
  6616. aroundCorner: nodeCorner,
  6617. corner: aroundCorners[nodeCorner],
  6618. pos: {
  6619. x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width),
  6620. y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height)
  6621. }
  6622. });
  6623. }
  6624. return dijit._place(node, choices, layoutNode, {w: width, h: height});
  6625. };
  6626. dijit.placementRegistry= new dojo.AdapterRegistry();
  6627. dijit.placementRegistry.register("node",
  6628. function(n, x){
  6629. return typeof x == "object" &&
  6630. typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined";
  6631. },
  6632. dijit.placeOnScreenAroundNode);
  6633. dijit.placementRegistry.register("rect",
  6634. function(n, x){
  6635. return typeof x == "object" &&
  6636. "x" in x && "y" in x && "width" in x && "height" in x;
  6637. },
  6638. dijit.placeOnScreenAroundRectangle);
  6639. dijit.placeOnScreenAroundElement = function(
  6640. /* DomNode */ node,
  6641. /* Object */ aroundElement,
  6642. /* Object */ aroundCorners,
  6643. /* Function */ layoutNode){
  6644. // summary:
  6645. // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
  6646. // for the "around" argument and finds a proper processor to place a node.
  6647. return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments);
  6648. };
  6649. dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
  6650. // summary:
  6651. // Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
  6652. //
  6653. // position: String[]
  6654. // This variable controls the position of the drop down.
  6655. // It's an array of strings with the following values:
  6656. //
  6657. // * before: places drop down to the left of the target node/widget, or to the right in
  6658. // the case of RTL scripts like Hebrew and Arabic
  6659. // * after: places drop down to the right of the target node/widget, or to the left in
  6660. // the case of RTL scripts like Hebrew and Arabic
  6661. // * above: drop down goes above target node
  6662. // * below: drop down goes below target node
  6663. //
  6664. // The list is positions is tried, in order, until a position is found where the drop down fits
  6665. // within the viewport.
  6666. //
  6667. // leftToRight: Boolean
  6668. // Whether the popup will be displaying in leftToRight mode.
  6669. //
  6670. var align = {};
  6671. dojo.forEach(position, function(pos){
  6672. switch(pos){
  6673. case "after":
  6674. align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
  6675. break;
  6676. case "before":
  6677. align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
  6678. break;
  6679. case "below-alt":
  6680. leftToRight = !leftToRight;
  6681. // fall through
  6682. case "below":
  6683. // first try to align left borders, next try to align right borders (or reverse for RTL mode)
  6684. align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR";
  6685. align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL";
  6686. break;
  6687. case "above-alt":
  6688. leftToRight = !leftToRight;
  6689. // fall through
  6690. case "above":
  6691. default:
  6692. // first try to align left borders, next try to align right borders (or reverse for RTL mode)
  6693. align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR";
  6694. align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL";
  6695. break;
  6696. }
  6697. });
  6698. return align;
  6699. };
  6700. }
  6701. if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  6702. dojo._hasResource["dijit._Container"] = true;
  6703. dojo.provide("dijit._Container");
  6704. dojo.declare("dijit._Container",
  6705. null,
  6706. {
  6707. // summary:
  6708. // Mixin for widgets that contain a set of widget children.
  6709. // description:
  6710. // Use this mixin for widgets that needs to know about and
  6711. // keep track of their widget children. Suitable for widgets like BorderContainer
  6712. // and TabContainer which contain (only) a set of child widgets.
  6713. //
  6714. // It's not suitable for widgets like ContentPane
  6715. // which contains mixed HTML (plain DOM nodes in addition to widgets),
  6716. // and where contained widgets are not necessarily directly below
  6717. // this.containerNode. In that case calls like addChild(node, position)
  6718. // wouldn't make sense.
  6719. // isContainer: [protected] Boolean
  6720. // Indicates that this widget acts as a "parent" to the descendant widgets.
  6721. // When the parent is started it will call startup() on the child widgets.
  6722. // See also `isLayoutContainer`.
  6723. isContainer: true,
  6724. buildRendering: function(){
  6725. this.inherited(arguments);
  6726. if(!this.containerNode){
  6727. // all widgets with descendants must set containerNode
  6728. this.containerNode = this.domNode;
  6729. }
  6730. },
  6731. addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
  6732. // summary:
  6733. // Makes the given widget a child of this widget.
  6734. // description:
  6735. // Inserts specified child widget's dom node as a child of this widget's
  6736. // container node, and possibly does other processing (such as layout).
  6737. var refNode = this.containerNode;
  6738. if(insertIndex && typeof insertIndex == "number"){
  6739. var children = this.getChildren();
  6740. if(children && children.length >= insertIndex){
  6741. refNode = children[insertIndex-1].domNode;
  6742. insertIndex = "after";
  6743. }
  6744. }
  6745. dojo.place(widget.domNode, refNode, insertIndex);
  6746. // If I've been started but the child widget hasn't been started,
  6747. // start it now. Make sure to do this after widget has been
  6748. // inserted into the DOM tree, so it can see that it's being controlled by me,
  6749. // so it doesn't try to size itself.
  6750. if(this._started && !widget._started){
  6751. widget.startup();
  6752. }
  6753. },
  6754. removeChild: function(/*Widget or int*/ widget){
  6755. // summary:
  6756. // Removes the passed widget instance from this widget but does
  6757. // not destroy it. You can also pass in an integer indicating
  6758. // the index within the container to remove
  6759. if(typeof widget == "number"){
  6760. widget = this.getChildren()[widget];
  6761. }
  6762. if(widget){
  6763. var node = widget.domNode;
  6764. if(node && node.parentNode){
  6765. node.parentNode.removeChild(node); // detach but don't destroy
  6766. }
  6767. }
  6768. },
  6769. hasChildren: function(){
  6770. // summary:
  6771. // Returns true if widget has children, i.e. if this.containerNode contains something.
  6772. return this.getChildren().length > 0; // Boolean
  6773. },
  6774. destroyDescendants: function(/*Boolean*/ preserveDom){
  6775. // summary:
  6776. // Destroys all the widgets inside this.containerNode,
  6777. // but not this widget itself
  6778. dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); });
  6779. },
  6780. _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
  6781. // summary:
  6782. // Get the next or previous widget sibling of child
  6783. // dir:
  6784. // if 1, get the next sibling
  6785. // if -1, get the previous sibling
  6786. // tags:
  6787. // private
  6788. var node = child.domNode,
  6789. which = (dir>0 ? "nextSibling" : "previousSibling");
  6790. do{
  6791. node = node[which];
  6792. }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
  6793. return node && dijit.byNode(node); // dijit._Widget
  6794. },
  6795. getIndexOfChild: function(/*dijit._Widget*/ child){
  6796. // summary:
  6797. // Gets the index of the child in this container or -1 if not found
  6798. return dojo.indexOf(this.getChildren(), child); // int
  6799. },
  6800. startup: function(){
  6801. // summary:
  6802. // Called after all the widgets have been instantiated and their
  6803. // dom nodes have been inserted somewhere under dojo.doc.body.
  6804. //
  6805. // Widgets should override this method to do any initialization
  6806. // dependent on other widgets existing, and then call
  6807. // this superclass method to finish things off.
  6808. //
  6809. // startup() in subclasses shouldn't do anything
  6810. // size related because the size of the widget hasn't been set yet.
  6811. if(this._started){ return; }
  6812. // Startup all children of this widget
  6813. dojo.forEach(this.getChildren(), function(child){ child.startup(); });
  6814. this.inherited(arguments);
  6815. }
  6816. }
  6817. );
  6818. }
  6819. if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  6820. dojo._hasResource["dijit._base.manager"] = true;
  6821. dojo.provide("dijit._base.manager");
  6822. dojo.declare("dijit.WidgetSet", null, {
  6823. // summary:
  6824. // A set of widgets indexed by id. A default instance of this class is
  6825. // available as `dijit.registry`
  6826. //
  6827. // example:
  6828. // Create a small list of widgets:
  6829. // | var ws = new dijit.WidgetSet();
  6830. // | ws.add(dijit.byId("one"));
  6831. // | ws.add(dijit.byId("two"));
  6832. // | // destroy both:
  6833. // | ws.forEach(function(w){ w.destroy(); });
  6834. //
  6835. // example:
  6836. // Using dijit.registry:
  6837. // | dijit.registry.forEach(function(w){ /* do something */ });
  6838. constructor: function(){
  6839. this._hash = {};
  6840. this.length = 0;
  6841. },
  6842. add: function(/*dijit._Widget*/ widget){
  6843. // summary:
  6844. // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
  6845. //
  6846. // widget: dijit._Widget
  6847. // Any dijit._Widget subclass.
  6848. if(this._hash[widget.id]){
  6849. throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
  6850. }
  6851. this._hash[widget.id] = widget;
  6852. this.length++;
  6853. },
  6854. remove: function(/*String*/ id){
  6855. // summary:
  6856. // Remove a widget from this WidgetSet. Does not destroy the widget; simply
  6857. // removes the reference.
  6858. if(this._hash[id]){
  6859. delete this._hash[id];
  6860. this.length--;
  6861. }
  6862. },
  6863. forEach: function(/*Function*/ func, /* Object? */thisObj){
  6864. // summary:
  6865. // Call specified function for each widget in this set.
  6866. //
  6867. // func:
  6868. // A callback function to run for each item. Is passed the widget, the index
  6869. // in the iteration, and the full hash, similar to `dojo.forEach`.
  6870. //
  6871. // thisObj:
  6872. // An optional scope parameter
  6873. //
  6874. // example:
  6875. // Using the default `dijit.registry` instance:
  6876. // | dijit.registry.forEach(function(widget){
  6877. // | console.log(widget.declaredClass);
  6878. // | });
  6879. //
  6880. // returns:
  6881. // Returns self, in order to allow for further chaining.
  6882. thisObj = thisObj || dojo.global;
  6883. var i = 0, id;
  6884. for(id in this._hash){
  6885. func.call(thisObj, this._hash[id], i++, this._hash);
  6886. }
  6887. return this; // dijit.WidgetSet
  6888. },
  6889. filter: function(/*Function*/ filter, /* Object? */thisObj){
  6890. // summary:
  6891. // Filter down this WidgetSet to a smaller new WidgetSet
  6892. // Works the same as `dojo.filter` and `dojo.NodeList.filter`
  6893. //
  6894. // filter:
  6895. // Callback function to test truthiness. Is passed the widget
  6896. // reference and the pseudo-index in the object.
  6897. //
  6898. // thisObj: Object?
  6899. // Option scope to use for the filter function.
  6900. //
  6901. // example:
  6902. // Arbitrary: select the odd widgets in this list
  6903. // | dijit.registry.filter(function(w, i){
  6904. // | return i % 2 == 0;
  6905. // | }).forEach(function(w){ /* odd ones */ });
  6906. thisObj = thisObj || dojo.global;
  6907. var res = new dijit.WidgetSet(), i = 0, id;
  6908. for(id in this._hash){
  6909. var w = this._hash[id];
  6910. if(filter.call(thisObj, w, i++, this._hash)){
  6911. res.add(w);
  6912. }
  6913. }
  6914. return res; // dijit.WidgetSet
  6915. },
  6916. byId: function(/*String*/ id){
  6917. // summary:
  6918. // Find a widget in this list by it's id.
  6919. // example:
  6920. // Test if an id is in a particular WidgetSet
  6921. // | var ws = new dijit.WidgetSet();
  6922. // | ws.add(dijit.byId("bar"));
  6923. // | var t = ws.byId("bar") // returns a widget
  6924. // | var x = ws.byId("foo"); // returns undefined
  6925. return this._hash[id]; // dijit._Widget
  6926. },
  6927. byClass: function(/*String*/ cls){
  6928. // summary:
  6929. // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
  6930. //
  6931. // cls: String
  6932. // The Class to scan for. Full dot-notated string.
  6933. //
  6934. // example:
  6935. // Find all `dijit.TitlePane`s in a page:
  6936. // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
  6937. var res = new dijit.WidgetSet(), id, widget;
  6938. for(id in this._hash){
  6939. widget = this._hash[id];
  6940. if(widget.declaredClass == cls){
  6941. res.add(widget);
  6942. }
  6943. }
  6944. return res; // dijit.WidgetSet
  6945. },
  6946. toArray: function(){
  6947. // summary:
  6948. // Convert this WidgetSet into a true Array
  6949. //
  6950. // example:
  6951. // Work with the widget .domNodes in a real Array
  6952. // | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
  6953. var ar = [];
  6954. for(var id in this._hash){
  6955. ar.push(this._hash[id]);
  6956. }
  6957. return ar; // dijit._Widget[]
  6958. },
  6959. map: function(/* Function */func, /* Object? */thisObj){
  6960. // summary:
  6961. // Create a new Array from this WidgetSet, following the same rules as `dojo.map`
  6962. // example:
  6963. // | var nodes = dijit.registry.map(function(w){ return w.domNode; });
  6964. //
  6965. // returns:
  6966. // A new array of the returned values.
  6967. return dojo.map(this.toArray(), func, thisObj); // Array
  6968. },
  6969. every: function(func, thisObj){
  6970. // summary:
  6971. // A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
  6972. //
  6973. // func: Function
  6974. // A callback function run for every widget in this list. Exits loop
  6975. // when the first false return is encountered.
  6976. //
  6977. // thisObj: Object?
  6978. // Optional scope parameter to use for the callback
  6979. thisObj = thisObj || dojo.global;
  6980. var x = 0, i;
  6981. for(i in this._hash){
  6982. if(!func.call(thisObj, this._hash[i], x++, this._hash)){
  6983. return false; // Boolean
  6984. }
  6985. }
  6986. return true; // Boolean
  6987. },
  6988. some: function(func, thisObj){
  6989. // summary:
  6990. // A synthetic clone of `dojo.some` acting explictly on this WidgetSet
  6991. //
  6992. // func: Function
  6993. // A callback function run for every widget in this list. Exits loop
  6994. // when the first true return is encountered.
  6995. //
  6996. // thisObj: Object?
  6997. // Optional scope parameter to use for the callback
  6998. thisObj = thisObj || dojo.global;
  6999. var x = 0, i;
  7000. for(i in this._hash){
  7001. if(func.call(thisObj, this._hash[i], x++, this._hash)){
  7002. return true; // Boolean
  7003. }
  7004. }
  7005. return false; // Boolean
  7006. }
  7007. });
  7008. (function(){
  7009. /*=====
  7010. dijit.registry = {
  7011. // summary:
  7012. // A list of widgets on a page.
  7013. // description:
  7014. // Is an instance of `dijit.WidgetSet`
  7015. };
  7016. =====*/
  7017. dijit.registry = new dijit.WidgetSet();
  7018. var hash = dijit.registry._hash,
  7019. attr = dojo.attr,
  7020. hasAttr = dojo.hasAttr,
  7021. style = dojo.style;
  7022. dijit.byId = function(/*String|dijit._Widget*/ id){
  7023. // summary:
  7024. // Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
  7025. return typeof id == "string" ? hash[id] : id; // dijit._Widget
  7026. };
  7027. var _widgetTypeCtr = {};
  7028. dijit.getUniqueId = function(/*String*/widgetType){
  7029. // summary:
  7030. // Generates a unique id for a given widgetType
  7031. var id;
  7032. do{
  7033. id = widgetType + "_" +
  7034. (widgetType in _widgetTypeCtr ?
  7035. ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
  7036. }while(hash[id]);
  7037. return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
  7038. };
  7039. dijit.findWidgets = function(/*DomNode*/ root){
  7040. // summary:
  7041. // Search subtree under root returning widgets found.
  7042. // Doesn't search for nested widgets (ie, widgets inside other widgets).
  7043. var outAry = [];
  7044. function getChildrenHelper(root){
  7045. for(var node = root.firstChild; node; node = node.nextSibling){
  7046. if(node.nodeType == 1){
  7047. var widgetId = node.getAttribute("widgetId");
  7048. if(widgetId){
  7049. var widget = hash[widgetId];
  7050. if(widget){ // may be null on page w/multiple dojo's loaded
  7051. outAry.push(widget);
  7052. }
  7053. }else{
  7054. getChildrenHelper(node);
  7055. }
  7056. }
  7057. }
  7058. }
  7059. getChildrenHelper(root);
  7060. return outAry;
  7061. };
  7062. dijit._destroyAll = function(){
  7063. // summary:
  7064. // Code to destroy all widgets and do other cleanup on page unload
  7065. // Clean up focus manager lingering references to widgets and nodes
  7066. dijit._curFocus = null;
  7067. dijit._prevFocus = null;
  7068. dijit._activeStack = [];
  7069. // Destroy all the widgets, top down
  7070. dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
  7071. // Avoid double destroy of widgets like Menu that are attached to <body>
  7072. // even though they are logically children of other widgets.
  7073. if(!widget._destroyed){
  7074. if(widget.destroyRecursive){
  7075. widget.destroyRecursive();
  7076. }else if(widget.destroy){
  7077. widget.destroy();
  7078. }
  7079. }
  7080. });
  7081. };
  7082. if(dojo.isIE){
  7083. // Only run _destroyAll() for IE because we think it's only necessary in that case,
  7084. // and because it causes problems on FF. See bug #3531 for details.
  7085. dojo.addOnWindowUnload(function(){
  7086. dijit._destroyAll();
  7087. });
  7088. }
  7089. dijit.byNode = function(/*DOMNode*/ node){
  7090. // summary:
  7091. // Returns the widget corresponding to the given DOMNode
  7092. return hash[node.getAttribute("widgetId")]; // dijit._Widget
  7093. };
  7094. dijit.getEnclosingWidget = function(/*DOMNode*/ node){
  7095. // summary:
  7096. // Returns the widget whose DOM tree contains the specified DOMNode, or null if
  7097. // the node is not contained within the DOM tree of any widget
  7098. while(node){
  7099. var id = node.getAttribute && node.getAttribute("widgetId");
  7100. if(id){
  7101. return hash[id];
  7102. }
  7103. node = node.parentNode;
  7104. }
  7105. return null;
  7106. };
  7107. var shown = (dijit._isElementShown = function(/*Element*/ elem){
  7108. var s = style(elem);
  7109. return (s.visibility != "hidden")
  7110. && (s.visibility != "collapsed")
  7111. && (s.display != "none")
  7112. && (attr(elem, "type") != "hidden");
  7113. });
  7114. dijit.hasDefaultTabStop = function(/*Element*/ elem){
  7115. // summary:
  7116. // Tests if element is tab-navigable even without an explicit tabIndex setting
  7117. // No explicit tabIndex setting, need to investigate node type
  7118. switch(elem.nodeName.toLowerCase()){
  7119. case "a":
  7120. // An <a> w/out a tabindex is only navigable if it has an href
  7121. return hasAttr(elem, "href");
  7122. case "area":
  7123. case "button":
  7124. case "input":
  7125. case "object":
  7126. case "select":
  7127. case "textarea":
  7128. // These are navigable by default
  7129. return true;
  7130. case "iframe":
  7131. // If it's an editor <iframe> then it's tab navigable.
  7132. var body;
  7133. try{
  7134. // non-IE
  7135. var contentDocument = elem.contentDocument;
  7136. if("designMode" in contentDocument && contentDocument.designMode == "on"){
  7137. return true;
  7138. }
  7139. body = contentDocument.body;
  7140. }catch(e1){
  7141. // contentWindow.document isn't accessible within IE7/8
  7142. // if the iframe.src points to a foreign url and this
  7143. // page contains an element, that could get focus
  7144. try{
  7145. body = elem.contentWindow.document.body;
  7146. }catch(e2){
  7147. return false;
  7148. }
  7149. }
  7150. return body.contentEditable == 'true' || (body.firstChild && body.firstChild.contentEditable == 'true');
  7151. default:
  7152. return elem.contentEditable == 'true';
  7153. }
  7154. };
  7155. var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
  7156. // summary:
  7157. // Tests if an element is tab-navigable
  7158. // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
  7159. if(attr(elem, "disabled")){
  7160. return false;
  7161. }else if(hasAttr(elem, "tabIndex")){
  7162. // Explicit tab index setting
  7163. return attr(elem, "tabIndex") >= 0; // boolean
  7164. }else{
  7165. // No explicit tabIndex setting, so depends on node type
  7166. return dijit.hasDefaultTabStop(elem);
  7167. }
  7168. });
  7169. dijit._getTabNavigable = function(/*DOMNode*/ root){
  7170. // summary:
  7171. // Finds descendants of the specified root node.
  7172. //
  7173. // description:
  7174. // Finds the following descendants of the specified root node:
  7175. // * the first tab-navigable element in document order
  7176. // without a tabIndex or with tabIndex="0"
  7177. // * the last tab-navigable element in document order
  7178. // without a tabIndex or with tabIndex="0"
  7179. // * the first element in document order with the lowest
  7180. // positive tabIndex value
  7181. // * the last element in document order with the highest
  7182. // positive tabIndex value
  7183. var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
  7184. function radioName(node) {
  7185. // If this element is part of a radio button group, return the name for that group.
  7186. return node && node.tagName.toLowerCase() == "input" &&
  7187. node.type && node.type.toLowerCase() == "radio" &&
  7188. node.name && node.name.toLowerCase();
  7189. }
  7190. var walkTree = function(/*DOMNode*/parent){
  7191. dojo.query("> *", parent).forEach(function(child){
  7192. // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
  7193. // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
  7194. if((dojo.isIE <= 9 && child.scopeName !== "HTML") || !shown(child)){
  7195. return;
  7196. }
  7197. if(isTabNavigable(child)){
  7198. var tabindex = attr(child, "tabIndex");
  7199. if(!hasAttr(child, "tabIndex") || tabindex == 0){
  7200. if(!first){ first = child; }
  7201. last = child;
  7202. }else if(tabindex > 0){
  7203. if(!lowest || tabindex < lowestTabindex){
  7204. lowestTabindex = tabindex;
  7205. lowest = child;
  7206. }
  7207. if(!highest || tabindex >= highestTabindex){
  7208. highestTabindex = tabindex;
  7209. highest = child;
  7210. }
  7211. }
  7212. var rn = radioName(child);
  7213. if(dojo.attr(child, "checked") && rn) {
  7214. radioSelected[rn] = child;
  7215. }
  7216. }
  7217. if(child.nodeName.toUpperCase() != 'SELECT'){
  7218. walkTree(child);
  7219. }
  7220. });
  7221. };
  7222. if(shown(root)){ walkTree(root) }
  7223. function rs(node) {
  7224. // substitute checked radio button for unchecked one, if there is a checked one with the same name.
  7225. return radioSelected[radioName(node)] || node;
  7226. }
  7227. return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
  7228. }
  7229. dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
  7230. // summary:
  7231. // Finds the descendant of the specified root node
  7232. // that is first in the tabbing order
  7233. var elems = dijit._getTabNavigable(dojo.byId(root));
  7234. return elems.lowest ? elems.lowest : elems.first; // DomNode
  7235. };
  7236. dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
  7237. // summary:
  7238. // Finds the descendant of the specified root node
  7239. // that is last in the tabbing order
  7240. var elems = dijit._getTabNavigable(dojo.byId(root));
  7241. return elems.last ? elems.last : elems.highest; // DomNode
  7242. };
  7243. /*=====
  7244. dojo.mixin(dijit, {
  7245. // defaultDuration: Integer
  7246. // The default animation speed (in ms) to use for all Dijit
  7247. // transitional animations, unless otherwise specified
  7248. // on a per-instance basis. Defaults to 200, overrided by
  7249. // `djConfig.defaultDuration`
  7250. defaultDuration: 200
  7251. });
  7252. =====*/
  7253. dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
  7254. })();
  7255. }
  7256. if(!dojo._hasResource["dojo.Stateful"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7257. dojo._hasResource["dojo.Stateful"] = true;
  7258. dojo.provide("dojo.Stateful");
  7259. dojo.declare("dojo.Stateful", null, {
  7260. // summary:
  7261. // Base class for objects that provide named properties with optional getter/setter
  7262. // control and the ability to watch for property changes
  7263. // example:
  7264. // | var obj = new dojo.Stateful();
  7265. // | obj.watch("foo", function(){
  7266. // | console.log("foo changed to " + this.get("foo"));
  7267. // | });
  7268. // | obj.set("foo","bar");
  7269. postscript: function(mixin){
  7270. if(mixin){
  7271. dojo.mixin(this, mixin);
  7272. }
  7273. },
  7274. get: function(/*String*/name){
  7275. // summary:
  7276. // Get a property on a Stateful instance.
  7277. // name:
  7278. // The property to get.
  7279. // description:
  7280. // Get a named property on a Stateful object. The property may
  7281. // potentially be retrieved via a getter method in subclasses. In the base class
  7282. // this just retrieves the object's property.
  7283. // For example:
  7284. // | stateful = new dojo.Stateful({foo: 3});
  7285. // | stateful.get("foo") // returns 3
  7286. // | stateful.foo // returns 3
  7287. return this[name];
  7288. },
  7289. set: function(/*String*/name, /*Object*/value){
  7290. // summary:
  7291. // Set a property on a Stateful instance
  7292. // name:
  7293. // The property to set.
  7294. // value:
  7295. // The value to set in the property.
  7296. // description:
  7297. // Sets named properties on a stateful object and notifies any watchers of
  7298. // the property. A programmatic setter may be defined in subclasses.
  7299. // For example:
  7300. // | stateful = new dojo.Stateful();
  7301. // | stateful.watch(function(name, oldValue, value){
  7302. // | // this will be called on the set below
  7303. // | }
  7304. // | stateful.set(foo, 5);
  7305. //
  7306. // set() may also be called with a hash of name/value pairs, ex:
  7307. // | myObj.set({
  7308. // | foo: "Howdy",
  7309. // | bar: 3
  7310. // | })
  7311. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  7312. if(typeof name === "object"){
  7313. for(var x in name){
  7314. this.set(x, name[x]);
  7315. }
  7316. return this;
  7317. }
  7318. var oldValue = this[name];
  7319. this[name] = value;
  7320. if(this._watchCallbacks){
  7321. this._watchCallbacks(name, oldValue, value);
  7322. }
  7323. return this;
  7324. },
  7325. watch: function(/*String?*/name, /*Function*/callback){
  7326. // summary:
  7327. // Watches a property for changes
  7328. // name:
  7329. // Indicates the property to watch. This is optional (the callback may be the
  7330. // only parameter), and if omitted, all the properties will be watched
  7331. // returns:
  7332. // An object handle for the watch. The unwatch method of this object
  7333. // can be used to discontinue watching this property:
  7334. // | var watchHandle = obj.watch("foo", callback);
  7335. // | watchHandle.unwatch(); // callback won't be called now
  7336. // callback:
  7337. // The function to execute when the property changes. This will be called after
  7338. // the property has been changed. The callback will be called with the |this|
  7339. // set to the instance, the first argument as the name of the property, the
  7340. // second argument as the old value and the third argument as the new value.
  7341. var callbacks = this._watchCallbacks;
  7342. if(!callbacks){
  7343. var self = this;
  7344. callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
  7345. var notify = function(propertyCallbacks){
  7346. if(propertyCallbacks){
  7347. propertyCallbacks = propertyCallbacks.slice();
  7348. for(var i = 0, l = propertyCallbacks.length; i < l; i++){
  7349. try{
  7350. propertyCallbacks[i].call(self, name, oldValue, value);
  7351. }catch(e){
  7352. console.error(e);
  7353. }
  7354. }
  7355. }
  7356. };
  7357. notify(callbacks['_' + name]);
  7358. if(!ignoreCatchall){
  7359. notify(callbacks["*"]); // the catch-all
  7360. }
  7361. }; // we use a function instead of an object so it will be ignored by JSON conversion
  7362. }
  7363. if(!callback && typeof name === "function"){
  7364. callback = name;
  7365. name = "*";
  7366. }else{
  7367. // prepend with dash to prevent name conflicts with function (like "name" property)
  7368. name = '_' + name;
  7369. }
  7370. var propertyCallbacks = callbacks[name];
  7371. if(typeof propertyCallbacks !== "object"){
  7372. propertyCallbacks = callbacks[name] = [];
  7373. }
  7374. propertyCallbacks.push(callback);
  7375. return {
  7376. unwatch: function(){
  7377. propertyCallbacks.splice(dojo.indexOf(propertyCallbacks, callback), 1);
  7378. }
  7379. };
  7380. }
  7381. });
  7382. }
  7383. if(!dojo._hasResource["dijit._WidgetBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7384. dojo._hasResource["dijit._WidgetBase"] = true;
  7385. dojo.provide("dijit._WidgetBase");
  7386. (function(){
  7387. function isEqual(a, b){
  7388. // summary:
  7389. // Function that determines whether two values are identical,
  7390. // taking into account that NaN is not normally equal to itself
  7391. // in JS.
  7392. return a === b || (/* a is NaN */ a !== a && /* b is NaN */ b !== b);
  7393. }
  7394. dojo.declare("dijit._WidgetBase", dojo.Stateful, {
  7395. // summary:
  7396. // Future base class for all Dijit widgets.
  7397. // _Widget extends this class adding support for various features needed by desktop.
  7398. // id: [const] String
  7399. // A unique, opaque ID string that can be assigned by users or by the
  7400. // system. If the developer passes an ID which is known not to be
  7401. // unique, the specified ID is ignored and the system-generated ID is
  7402. // used instead.
  7403. id: "",
  7404. // lang: [const] String
  7405. // Rarely used. Overrides the default Dojo locale used to render this widget,
  7406. // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
  7407. // Value must be among the list of locales specified during by the Dojo bootstrap,
  7408. // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
  7409. lang: "",
  7410. // dir: [const] String
  7411. // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
  7412. // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
  7413. // default direction.
  7414. dir: "",
  7415. // class: String
  7416. // HTML class attribute
  7417. "class": "",
  7418. // style: String||Object
  7419. // HTML style attributes as cssText string or name/value hash
  7420. style: "",
  7421. // title: String
  7422. // HTML title attribute.
  7423. //
  7424. // For form widgets this specifies a tooltip to display when hovering over
  7425. // the widget (just like the native HTML title attribute).
  7426. //
  7427. // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
  7428. // etc., it's used to specify the tab label, accordion pane title, etc.
  7429. title: "",
  7430. // tooltip: String
  7431. // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
  7432. // this specifies the tooltip to appear when the mouse is hovered over that text.
  7433. tooltip: "",
  7434. // baseClass: [protected] String
  7435. // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
  7436. // widget state.
  7437. baseClass: "",
  7438. // srcNodeRef: [readonly] DomNode
  7439. // pointer to original DOM node
  7440. srcNodeRef: null,
  7441. // domNode: [readonly] DomNode
  7442. // This is our visible representation of the widget! Other DOM
  7443. // Nodes may by assigned to other properties, usually through the
  7444. // template system's dojoAttachPoint syntax, but the domNode
  7445. // property is the canonical "top level" node in widget UI.
  7446. domNode: null,
  7447. // containerNode: [readonly] DomNode
  7448. // Designates where children of the source DOM node will be placed.
  7449. // "Children" in this case refers to both DOM nodes and widgets.
  7450. // For example, for myWidget:
  7451. //
  7452. // | <div dojoType=myWidget>
  7453. // | <b> here's a plain DOM node
  7454. // | <span dojoType=subWidget>and a widget</span>
  7455. // | <i> and another plain DOM node </i>
  7456. // | </div>
  7457. //
  7458. // containerNode would point to:
  7459. //
  7460. // | <b> here's a plain DOM node
  7461. // | <span dojoType=subWidget>and a widget</span>
  7462. // | <i> and another plain DOM node </i>
  7463. //
  7464. // In templated widgets, "containerNode" is set via a
  7465. // dojoAttachPoint assignment.
  7466. //
  7467. // containerNode must be defined for any widget that accepts innerHTML
  7468. // (like ContentPane or BorderContainer or even Button), and conversely
  7469. // is null for widgets that don't, like TextBox.
  7470. containerNode: null,
  7471. /*=====
  7472. // _started: Boolean
  7473. // startup() has completed.
  7474. _started: false,
  7475. =====*/
  7476. // attributeMap: [protected] Object
  7477. // attributeMap sets up a "binding" between attributes (aka properties)
  7478. // of the widget and the widget's DOM.
  7479. // Changes to widget attributes listed in attributeMap will be
  7480. // reflected into the DOM.
  7481. //
  7482. // For example, calling set('title', 'hello')
  7483. // on a TitlePane will automatically cause the TitlePane's DOM to update
  7484. // with the new title.
  7485. //
  7486. // attributeMap is a hash where the key is an attribute of the widget,
  7487. // and the value reflects a binding to a:
  7488. //
  7489. // - DOM node attribute
  7490. // | focus: {node: "focusNode", type: "attribute"}
  7491. // Maps this.focus to this.focusNode.focus
  7492. //
  7493. // - DOM node innerHTML
  7494. // | title: { node: "titleNode", type: "innerHTML" }
  7495. // Maps this.title to this.titleNode.innerHTML
  7496. //
  7497. // - DOM node innerText
  7498. // | title: { node: "titleNode", type: "innerText" }
  7499. // Maps this.title to this.titleNode.innerText
  7500. //
  7501. // - DOM node CSS class
  7502. // | myClass: { node: "domNode", type: "class" }
  7503. // Maps this.myClass to this.domNode.className
  7504. //
  7505. // If the value is an array, then each element in the array matches one of the
  7506. // formats of the above list.
  7507. //
  7508. // There are also some shorthands for backwards compatibility:
  7509. // - string --> { node: string, type: "attribute" }, for example:
  7510. // | "focusNode" ---> { node: "focusNode", type: "attribute" }
  7511. // - "" --> { node: "domNode", type: "attribute" }
  7512. attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},
  7513. // _blankGif: [protected] String
  7514. // Path to a blank 1x1 image.
  7515. // Used by <img> nodes in templates that really get their image via CSS background-image.
  7516. _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(),
  7517. //////////// INITIALIZATION METHODS ///////////////////////////////////////
  7518. postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
  7519. // summary:
  7520. // Kicks off widget instantiation. See create() for details.
  7521. // tags:
  7522. // private
  7523. this.create(params, srcNodeRef);
  7524. },
  7525. create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
  7526. // summary:
  7527. // Kick off the life-cycle of a widget
  7528. // params:
  7529. // Hash of initialization parameters for widget, including
  7530. // scalar values (like title, duration etc.) and functions,
  7531. // typically callbacks like onClick.
  7532. // srcNodeRef:
  7533. // If a srcNodeRef (DOM node) is specified:
  7534. // - use srcNodeRef.innerHTML as my contents
  7535. // - if this is a behavioral widget then apply behavior
  7536. // to that srcNodeRef
  7537. // - otherwise, replace srcNodeRef with my generated DOM
  7538. // tree
  7539. // description:
  7540. // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
  7541. // etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget
  7542. // for a discussion of the widget creation lifecycle.
  7543. //
  7544. // Of course, adventurous developers could override create entirely, but this should
  7545. // only be done as a last resort.
  7546. // tags:
  7547. // private
  7548. // store pointer to original DOM tree
  7549. this.srcNodeRef = dojo.byId(srcNodeRef);
  7550. // For garbage collection. An array of handles returned by Widget.connect()
  7551. // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
  7552. this._connects = [];
  7553. // For garbage collection. An array of handles returned by Widget.subscribe()
  7554. // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe()
  7555. this._subscribes = [];
  7556. // mix in our passed parameters
  7557. if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
  7558. if(params){
  7559. this.params = params;
  7560. dojo._mixin(this, params);
  7561. }
  7562. this.postMixInProperties();
  7563. // generate an id for the widget if one wasn't specified
  7564. // (be sure to do this before buildRendering() because that function might
  7565. // expect the id to be there.)
  7566. if(!this.id){
  7567. this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
  7568. }
  7569. dijit.registry.add(this);
  7570. this.buildRendering();
  7571. if(this.domNode){
  7572. // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
  7573. // Also calls custom setters for all attributes with custom setters.
  7574. this._applyAttributes();
  7575. // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
  7576. // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
  7577. // widget being attached to the DOM since it isn't when a widget is created programmatically like
  7578. // new MyWidget({}). See #11635.
  7579. var source = this.srcNodeRef;
  7580. if(source && source.parentNode && this.domNode !== source){
  7581. source.parentNode.replaceChild(this.domNode, source);
  7582. }
  7583. }
  7584. if(this.domNode){
  7585. // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
  7586. // assuming that dojo._scopeName even exists in 2.0
  7587. this.domNode.setAttribute("widgetId", this.id);
  7588. }
  7589. this.postCreate();
  7590. // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
  7591. if(this.srcNodeRef && !this.srcNodeRef.parentNode){
  7592. delete this.srcNodeRef;
  7593. }
  7594. this._created = true;
  7595. },
  7596. _applyAttributes: function(){
  7597. // summary:
  7598. // Step during widget creation to copy all widget attributes to the
  7599. // DOM as per attributeMap and _setXXXAttr functions.
  7600. // description:
  7601. // Skips over blank/false attribute values, unless they were explicitly specified
  7602. // as parameters to the widget, since those are the default anyway,
  7603. // and setting tabIndex="" is different than not setting tabIndex at all.
  7604. //
  7605. // It processes the attributes in the attribute map first, and then
  7606. // it goes through and processes the attributes for the _setXXXAttr
  7607. // functions that have been specified
  7608. // tags:
  7609. // private
  7610. var condAttrApply = function(attr, scope){
  7611. if((scope.params && attr in scope.params) || scope[attr]){
  7612. scope.set(attr, scope[attr]);
  7613. }
  7614. };
  7615. // Do the attributes in attributeMap
  7616. for(var attr in this.attributeMap){
  7617. condAttrApply(attr, this);
  7618. }
  7619. // And also any attributes with custom setters
  7620. dojo.forEach(this._getSetterAttributes(), function(a){
  7621. if(!(a in this.attributeMap)){
  7622. condAttrApply(a, this);
  7623. }
  7624. }, this);
  7625. },
  7626. _getSetterAttributes: function(){
  7627. // summary:
  7628. // Returns list of attributes with custom setters for this widget
  7629. var ctor = this.constructor;
  7630. if(!ctor._setterAttrs){
  7631. var r = (ctor._setterAttrs = []),
  7632. attrs,
  7633. proto = ctor.prototype;
  7634. for(var fxName in proto){
  7635. if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){
  7636. r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1));
  7637. }
  7638. }
  7639. }
  7640. return ctor._setterAttrs; // String[]
  7641. },
  7642. postMixInProperties: function(){
  7643. // summary:
  7644. // Called after the parameters to the widget have been read-in,
  7645. // but before the widget template is instantiated. Especially
  7646. // useful to set properties that are referenced in the widget
  7647. // template.
  7648. // tags:
  7649. // protected
  7650. },
  7651. buildRendering: function(){
  7652. // summary:
  7653. // Construct the UI for this widget, setting this.domNode
  7654. // description:
  7655. // Most widgets will mixin `dijit._Templated`, which implements this
  7656. // method.
  7657. // tags:
  7658. // protected
  7659. if(!this.domNode){
  7660. // Create root node if it wasn't created by _Templated
  7661. this.domNode = this.srcNodeRef || dojo.create('div');
  7662. }
  7663. // baseClass is a single class name or occasionally a space-separated list of names.
  7664. // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
  7665. // TODO: make baseClass custom setter
  7666. if(this.baseClass){
  7667. var classes = this.baseClass.split(" ");
  7668. if(!this.isLeftToRight()){
  7669. classes = classes.concat( dojo.map(classes, function(name){ return name+"Rtl"; }));
  7670. }
  7671. dojo.addClass(this.domNode, classes);
  7672. }
  7673. },
  7674. postCreate: function(){
  7675. // summary:
  7676. // Processing after the DOM fragment is created
  7677. // description:
  7678. // Called after the DOM fragment has been created, but not necessarily
  7679. // added to the document. Do not include any operations which rely on
  7680. // node dimensions or placement.
  7681. // tags:
  7682. // protected
  7683. },
  7684. startup: function(){
  7685. // summary:
  7686. // Processing after the DOM fragment is added to the document
  7687. // description:
  7688. // Called after a widget and its children have been created and added to the page,
  7689. // and all related widgets have finished their create() cycle, up through postCreate().
  7690. // This is useful for composite widgets that need to control or layout sub-widgets.
  7691. // Many layout widgets can use this as a wiring phase.
  7692. this._started = true;
  7693. },
  7694. //////////// DESTROY FUNCTIONS ////////////////////////////////
  7695. destroyRecursive: function(/*Boolean?*/ preserveDom){
  7696. // summary:
  7697. // Destroy this widget and its descendants
  7698. // description:
  7699. // This is the generic "destructor" function that all widget users
  7700. // should call to cleanly discard with a widget. Once a widget is
  7701. // destroyed, it is removed from the manager object.
  7702. // preserveDom:
  7703. // If true, this method will leave the original DOM structure
  7704. // alone of descendant Widgets. Note: This will NOT work with
  7705. // dijit._Templated widgets.
  7706. this._beingDestroyed = true;
  7707. this.destroyDescendants(preserveDom);
  7708. this.destroy(preserveDom);
  7709. },
  7710. destroy: function(/*Boolean*/ preserveDom){
  7711. // summary:
  7712. // Destroy this widget, but not its descendants.
  7713. // This method will, however, destroy internal widgets such as those used within a template.
  7714. // preserveDom: Boolean
  7715. // If true, this method will leave the original DOM structure alone.
  7716. // Note: This will not yet work with _Templated widgets
  7717. this._beingDestroyed = true;
  7718. this.uninitialize();
  7719. var d = dojo,
  7720. dfe = d.forEach,
  7721. dun = d.unsubscribe;
  7722. dfe(this._connects, function(array){
  7723. dfe(array, d.disconnect);
  7724. });
  7725. dfe(this._subscribes, function(handle){
  7726. dun(handle);
  7727. });
  7728. // destroy widgets created as part of template, etc.
  7729. dfe(this._supportingWidgets || [], function(w){
  7730. if(w.destroyRecursive){
  7731. w.destroyRecursive();
  7732. }else if(w.destroy){
  7733. w.destroy();
  7734. }
  7735. });
  7736. this.destroyRendering(preserveDom);
  7737. dijit.registry.remove(this.id);
  7738. this._destroyed = true;
  7739. },
  7740. destroyRendering: function(/*Boolean?*/ preserveDom){
  7741. // summary:
  7742. // Destroys the DOM nodes associated with this widget
  7743. // preserveDom:
  7744. // If true, this method will leave the original DOM structure alone
  7745. // during tear-down. Note: this will not work with _Templated
  7746. // widgets yet.
  7747. // tags:
  7748. // protected
  7749. if(this.bgIframe){
  7750. this.bgIframe.destroy(preserveDom);
  7751. delete this.bgIframe;
  7752. }
  7753. if(this.domNode){
  7754. if(preserveDom){
  7755. dojo.removeAttr(this.domNode, "widgetId");
  7756. }else{
  7757. dojo.destroy(this.domNode);
  7758. }
  7759. delete this.domNode;
  7760. }
  7761. if(this.srcNodeRef){
  7762. if(!preserveDom){
  7763. dojo.destroy(this.srcNodeRef);
  7764. }
  7765. delete this.srcNodeRef;
  7766. }
  7767. },
  7768. destroyDescendants: function(/*Boolean?*/ preserveDom){
  7769. // summary:
  7770. // Recursively destroy the children of this widget and their
  7771. // descendants.
  7772. // preserveDom:
  7773. // If true, the preserveDom attribute is passed to all descendant
  7774. // widget's .destroy() method. Not for use with _Templated
  7775. // widgets.
  7776. // get all direct descendants and destroy them recursively
  7777. dojo.forEach(this.getChildren(), function(widget){
  7778. if(widget.destroyRecursive){
  7779. widget.destroyRecursive(preserveDom);
  7780. }
  7781. });
  7782. },
  7783. uninitialize: function(){
  7784. // summary:
  7785. // Stub function. Override to implement custom widget tear-down
  7786. // behavior.
  7787. // tags:
  7788. // protected
  7789. return false;
  7790. },
  7791. ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
  7792. _setClassAttr: function(/*String*/ value){
  7793. // summary:
  7794. // Custom setter for the CSS "class" attribute
  7795. // tags:
  7796. // protected
  7797. var mapNode = this[this.attributeMap["class"] || 'domNode'];
  7798. dojo.replaceClass(mapNode, value, this["class"]);
  7799. this._set("class", value);
  7800. },
  7801. _setStyleAttr: function(/*String||Object*/ value){
  7802. // summary:
  7803. // Sets the style attribute of the widget according to value,
  7804. // which is either a hash like {height: "5px", width: "3px"}
  7805. // or a plain string
  7806. // description:
  7807. // Determines which node to set the style on based on style setting
  7808. // in attributeMap.
  7809. // tags:
  7810. // protected
  7811. var mapNode = this[this.attributeMap.style || 'domNode'];
  7812. // Note: technically we should revert any style setting made in a previous call
  7813. // to his method, but that's difficult to keep track of.
  7814. if(dojo.isObject(value)){
  7815. dojo.style(mapNode, value);
  7816. }else{
  7817. if(mapNode.style.cssText){
  7818. mapNode.style.cssText += "; " + value;
  7819. }else{
  7820. mapNode.style.cssText = value;
  7821. }
  7822. }
  7823. this._set("style", value);
  7824. },
  7825. _attrToDom: function(/*String*/ attr, /*String*/ value){
  7826. // summary:
  7827. // Reflect a widget attribute (title, tabIndex, duration etc.) to
  7828. // the widget DOM, as specified in attributeMap.
  7829. // Note some attributes like "type"
  7830. // cannot be processed this way as they are not mutable.
  7831. //
  7832. // tags:
  7833. // private
  7834. var commands = this.attributeMap[attr];
  7835. dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){
  7836. // Get target node and what we are doing to that node
  7837. var mapNode = this[command.node || command || "domNode"]; // DOM node
  7838. var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
  7839. switch(type){
  7840. case "attribute":
  7841. if(dojo.isFunction(value)){ // functions execute in the context of the widget
  7842. value = dojo.hitch(this, value);
  7843. }
  7844. // Get the name of the DOM node attribute; usually it's the same
  7845. // as the name of the attribute in the widget (attr), but can be overridden.
  7846. // Also maps handler names to lowercase, like onSubmit --> onsubmit
  7847. var attrName = command.attribute ? command.attribute :
  7848. (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
  7849. dojo.attr(mapNode, attrName, value);
  7850. break;
  7851. case "innerText":
  7852. mapNode.innerHTML = "";
  7853. mapNode.appendChild(dojo.doc.createTextNode(value));
  7854. break;
  7855. case "innerHTML":
  7856. mapNode.innerHTML = value;
  7857. break;
  7858. case "class":
  7859. dojo.replaceClass(mapNode, value, this[attr]);
  7860. break;
  7861. }
  7862. }, this);
  7863. },
  7864. get: function(name){
  7865. // summary:
  7866. // Get a property from a widget.
  7867. // name:
  7868. // The property to get.
  7869. // description:
  7870. // Get a named property from a widget. The property may
  7871. // potentially be retrieved via a getter method. If no getter is defined, this
  7872. // just retrieves the object's property.
  7873. // For example, if the widget has a properties "foo"
  7874. // and "bar" and a method named "_getFooAttr", calling:
  7875. // | myWidget.get("foo");
  7876. // would be equivalent to writing:
  7877. // | widget._getFooAttr();
  7878. // and:
  7879. // | myWidget.get("bar");
  7880. // would be equivalent to writing:
  7881. // | widget.bar;
  7882. var names = this._getAttrNames(name);
  7883. return this[names.g] ? this[names.g]() : this[name];
  7884. },
  7885. set: function(name, value){
  7886. // summary:
  7887. // Set a property on a widget
  7888. // name:
  7889. // The property to set.
  7890. // value:
  7891. // The value to set in the property.
  7892. // description:
  7893. // Sets named properties on a widget which may potentially be handled by a
  7894. // setter in the widget.
  7895. // For example, if the widget has a properties "foo"
  7896. // and "bar" and a method named "_setFooAttr", calling:
  7897. // | myWidget.set("foo", "Howdy!");
  7898. // would be equivalent to writing:
  7899. // | widget._setFooAttr("Howdy!");
  7900. // and:
  7901. // | myWidget.set("bar", 3);
  7902. // would be equivalent to writing:
  7903. // | widget.bar = 3;
  7904. //
  7905. // set() may also be called with a hash of name/value pairs, ex:
  7906. // | myWidget.set({
  7907. // | foo: "Howdy",
  7908. // | bar: 3
  7909. // | })
  7910. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  7911. if(typeof name === "object"){
  7912. for(var x in name){
  7913. this.set(x, name[x]);
  7914. }
  7915. return this;
  7916. }
  7917. var names = this._getAttrNames(name);
  7918. if(this[names.s]){
  7919. // use the explicit setter
  7920. var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
  7921. }else{
  7922. // if param is specified as DOM node attribute, copy it
  7923. if(name in this.attributeMap){
  7924. this._attrToDom(name, value);
  7925. }
  7926. this._set(name, value);
  7927. }
  7928. return result || this;
  7929. },
  7930. _attrPairNames: {}, // shared between all widgets
  7931. _getAttrNames: function(name){
  7932. // summary:
  7933. // Helper function for get() and set().
  7934. // Caches attribute name values so we don't do the string ops every time.
  7935. // tags:
  7936. // private
  7937. var apn = this._attrPairNames;
  7938. if(apn[name]){ return apn[name]; }
  7939. var uc = name.charAt(0).toUpperCase() + name.substr(1);
  7940. return (apn[name] = {
  7941. n: name+"Node",
  7942. s: "_set"+uc+"Attr",
  7943. g: "_get"+uc+"Attr"
  7944. });
  7945. },
  7946. _set: function(/*String*/ name, /*anything*/ value){
  7947. // summary:
  7948. // Helper function to set new value for specified attribute, and call handlers
  7949. // registered with watch() if the value has changed.
  7950. var oldValue = this[name];
  7951. this[name] = value;
  7952. if(this._watchCallbacks && this._created && !isEqual(value, oldValue)){
  7953. this._watchCallbacks(name, oldValue, value);
  7954. }
  7955. },
  7956. toString: function(){
  7957. // summary:
  7958. // Returns a string that represents the widget
  7959. // description:
  7960. // When a widget is cast to a string, this method will be used to generate the
  7961. // output. Currently, it does not implement any sort of reversible
  7962. // serialization.
  7963. return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
  7964. },
  7965. getDescendants: function(){
  7966. // summary:
  7967. // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
  7968. // This method should generally be avoided as it returns widgets declared in templates, which are
  7969. // supposed to be internal/hidden, but it's left here for back-compat reasons.
  7970. return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[]
  7971. },
  7972. getChildren: function(){
  7973. // summary:
  7974. // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
  7975. // Does not return nested widgets, nor widgets that are part of this widget's template.
  7976. return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[]
  7977. },
  7978. connect: function(
  7979. /*Object|null*/ obj,
  7980. /*String|Function*/ event,
  7981. /*String|Function*/ method){
  7982. // summary:
  7983. // Connects specified obj/event to specified method of this object
  7984. // and registers for disconnect() on widget destroy.
  7985. // description:
  7986. // Provide widget-specific analog to dojo.connect, except with the
  7987. // implicit use of this widget as the target object.
  7988. // Events connected with `this.connect` are disconnected upon
  7989. // destruction.
  7990. // returns:
  7991. // A handle that can be passed to `disconnect` in order to disconnect before
  7992. // the widget is destroyed.
  7993. // example:
  7994. // | var btn = new dijit.form.Button();
  7995. // | // when foo.bar() is called, call the listener we're going to
  7996. // | // provide in the scope of btn
  7997. // | btn.connect(foo, "bar", function(){
  7998. // | console.debug(this.toString());
  7999. // | });
  8000. // tags:
  8001. // protected
  8002. var handles = [dojo._connect(obj, event, this, method)];
  8003. this._connects.push(handles);
  8004. return handles; // _Widget.Handle
  8005. },
  8006. disconnect: function(/* _Widget.Handle */ handles){
  8007. // summary:
  8008. // Disconnects handle created by `connect`.
  8009. // Also removes handle from this widget's list of connects.
  8010. // tags:
  8011. // protected
  8012. for(var i=0; i<this._connects.length; i++){
  8013. if(this._connects[i] == handles){
  8014. dojo.forEach(handles, dojo.disconnect);
  8015. this._connects.splice(i, 1);
  8016. return;
  8017. }
  8018. }
  8019. },
  8020. subscribe: function(
  8021. /*String*/ topic,
  8022. /*String|Function*/ method){
  8023. // summary:
  8024. // Subscribes to the specified topic and calls the specified method
  8025. // of this object and registers for unsubscribe() on widget destroy.
  8026. // description:
  8027. // Provide widget-specific analog to dojo.subscribe, except with the
  8028. // implicit use of this widget as the target object.
  8029. // example:
  8030. // | var btn = new dijit.form.Button();
  8031. // | // when /my/topic is published, this button changes its label to
  8032. // | // be the parameter of the topic.
  8033. // | btn.subscribe("/my/topic", function(v){
  8034. // | this.set("label", v);
  8035. // | });
  8036. var handle = dojo.subscribe(topic, this, method);
  8037. // return handles for Any widget that may need them
  8038. this._subscribes.push(handle);
  8039. return handle;
  8040. },
  8041. unsubscribe: function(/*Object*/ handle){
  8042. // summary:
  8043. // Unsubscribes handle created by this.subscribe.
  8044. // Also removes handle from this widget's list of subscriptions
  8045. for(var i=0; i<this._subscribes.length; i++){
  8046. if(this._subscribes[i] == handle){
  8047. dojo.unsubscribe(handle);
  8048. this._subscribes.splice(i, 1);
  8049. return;
  8050. }
  8051. }
  8052. },
  8053. isLeftToRight: function(){
  8054. // summary:
  8055. // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
  8056. // tags:
  8057. // protected
  8058. return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean
  8059. },
  8060. placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
  8061. // summary:
  8062. // Place this widget's domNode reference somewhere in the DOM based
  8063. // on standard dojo.place conventions, or passing a Widget reference that
  8064. // contains and addChild member.
  8065. //
  8066. // description:
  8067. // A convenience function provided in all _Widgets, providing a simple
  8068. // shorthand mechanism to put an existing (or newly created) Widget
  8069. // somewhere in the dom, and allow chaining.
  8070. //
  8071. // reference:
  8072. // The String id of a domNode, a domNode reference, or a reference to a Widget posessing
  8073. // an addChild method.
  8074. //
  8075. // position:
  8076. // If passed a string or domNode reference, the position argument
  8077. // accepts a string just as dojo.place does, one of: "first", "last",
  8078. // "before", or "after".
  8079. //
  8080. // If passed a _Widget reference, and that widget reference has an ".addChild" method,
  8081. // it will be called passing this widget instance into that method, supplying the optional
  8082. // position index passed.
  8083. //
  8084. // returns:
  8085. // dijit._Widget
  8086. // Provides a useful return of the newly created dijit._Widget instance so you
  8087. // can "chain" this function by instantiating, placing, then saving the return value
  8088. // to a variable.
  8089. //
  8090. // example:
  8091. // | // create a Button with no srcNodeRef, and place it in the body:
  8092. // | var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body());
  8093. // | // now, 'button' is still the widget reference to the newly created button
  8094. // | dojo.connect(button, "onClick", function(e){ console.log('click'); });
  8095. //
  8096. // example:
  8097. // | // create a button out of a node with id="src" and append it to id="wrapper":
  8098. // | var button = new dijit.form.Button({},"src").placeAt("wrapper");
  8099. //
  8100. // example:
  8101. // | // place a new button as the first element of some div
  8102. // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
  8103. //
  8104. // example:
  8105. // | // create a contentpane and add it to a TabContainer
  8106. // | var tc = dijit.byId("myTabs");
  8107. // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
  8108. if(reference.declaredClass && reference.addChild){
  8109. reference.addChild(this, position);
  8110. }else{
  8111. dojo.place(this.domNode, reference, position);
  8112. }
  8113. return this;
  8114. },
  8115. defer: function(fcn, delay){
  8116. // summary:
  8117. // Wrapper to setTimeout to avoid deferred functions executing
  8118. // after the originating widget has been destroyed.
  8119. // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
  8120. // fcn: function reference
  8121. // delay: Optional number (defaults to 0)
  8122. // tags:
  8123. // protected.
  8124. var timer = setTimeout(dojo.hitch(this,
  8125. function(){
  8126. timer = null;
  8127. if(!this._destroyed){
  8128. dojo.hitch(this, fcn)();
  8129. }
  8130. }),
  8131. delay || 0
  8132. );
  8133. return {
  8134. remove: function(){
  8135. if(timer){
  8136. clearTimeout(timer);
  8137. timer = null;
  8138. }
  8139. return null; // so this works well: handle = handle.remove();
  8140. }
  8141. };
  8142. }
  8143. });
  8144. })();
  8145. }
  8146. if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8147. dojo._hasResource["dijit._base.focus"] = true;
  8148. dojo.provide("dijit._base.focus");
  8149. // summary:
  8150. // These functions are used to query or set the focus and selection.
  8151. //
  8152. // Also, they trace when widgets become activated/deactivated,
  8153. // so that the widget can fire _onFocus/_onBlur events.
  8154. // "Active" here means something similar to "focused", but
  8155. // "focus" isn't quite the right word because we keep track of
  8156. // a whole stack of "active" widgets. Example: ComboButton --> Menu -->
  8157. // MenuItem. The onBlur event for ComboButton doesn't fire due to focusing
  8158. // on the Menu or a MenuItem, since they are considered part of the
  8159. // ComboButton widget. It only happens when focus is shifted
  8160. // somewhere completely different.
  8161. dojo.mixin(dijit, {
  8162. // _curFocus: DomNode
  8163. // Currently focused item on screen
  8164. _curFocus: null,
  8165. // _prevFocus: DomNode
  8166. // Previously focused item on screen
  8167. _prevFocus: null,
  8168. isCollapsed: function(){
  8169. // summary:
  8170. // Returns true if there is no text selected
  8171. return dijit.getBookmark().isCollapsed;
  8172. },
  8173. getBookmark: function(){
  8174. // summary:
  8175. // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
  8176. var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus;
  8177. if(dojo.global.getSelection){
  8178. //W3C Range API for selections.
  8179. sel = dojo.global.getSelection();
  8180. if(sel){
  8181. if(sel.isCollapsed){
  8182. tg = cf? cf.tagName : "";
  8183. if(tg){
  8184. //Create a fake rangelike item to restore selections.
  8185. tg = tg.toLowerCase();
  8186. if(tg == "textarea" ||
  8187. (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
  8188. sel = {
  8189. start: cf.selectionStart,
  8190. end: cf.selectionEnd,
  8191. node: cf,
  8192. pRange: true
  8193. };
  8194. return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
  8195. }
  8196. }
  8197. bm = {isCollapsed:true};
  8198. if(sel.rangeCount){
  8199. bm.mark = sel.getRangeAt(0).cloneRange();
  8200. }
  8201. }else{
  8202. rg = sel.getRangeAt(0);
  8203. bm = {isCollapsed: false, mark: rg.cloneRange()};
  8204. }
  8205. }
  8206. }else if(sel){
  8207. // If the current focus was a input of some sort and no selection, don't bother saving
  8208. // a native bookmark. This is because it causes issues with dialog/page selection restore.
  8209. // So, we need to create psuedo bookmarks to work with.
  8210. tg = cf ? cf.tagName : "";
  8211. tg = tg.toLowerCase();
  8212. if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
  8213. if(sel.type && sel.type.toLowerCase() == "none"){
  8214. return {
  8215. isCollapsed: true,
  8216. mark: null
  8217. }
  8218. }else{
  8219. rg = sel.createRange();
  8220. return {
  8221. isCollapsed: rg.text && rg.text.length?false:true,
  8222. mark: {
  8223. range: rg,
  8224. pRange: true
  8225. }
  8226. };
  8227. }
  8228. }
  8229. bm = {};
  8230. //'IE' way for selections.
  8231. try{
  8232. // createRange() throws exception when dojo in iframe
  8233. //and nothing selected, see #9632
  8234. rg = sel.createRange();
  8235. bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
  8236. }catch(e){
  8237. bm.isCollapsed = true;
  8238. return bm;
  8239. }
  8240. if(sel.type.toUpperCase() == 'CONTROL'){
  8241. if(rg.length){
  8242. bm.mark=[];
  8243. var i=0,len=rg.length;
  8244. while(i<len){
  8245. bm.mark.push(rg.item(i++));
  8246. }
  8247. }else{
  8248. bm.isCollapsed = true;
  8249. bm.mark = null;
  8250. }
  8251. }else{
  8252. bm.mark = rg.getBookmark();
  8253. }
  8254. }else{
  8255. console.warn("No idea how to store the current selection for this browser!");
  8256. }
  8257. return bm; // Object
  8258. },
  8259. moveToBookmark: function(/*Object*/bookmark){
  8260. // summary:
  8261. // Moves current selection to a bookmark
  8262. // bookmark:
  8263. // This should be a returned object from dijit.getBookmark()
  8264. var _doc = dojo.doc,
  8265. mark = bookmark.mark;
  8266. if(mark){
  8267. if(dojo.global.getSelection){
  8268. //W3C Rangi API (FF, WebKit, Opera, etc)
  8269. var sel = dojo.global.getSelection();
  8270. if(sel && sel.removeAllRanges){
  8271. if(mark.pRange){
  8272. var r = mark;
  8273. var n = r.node;
  8274. n.selectionStart = r.start;
  8275. n.selectionEnd = r.end;
  8276. }else{
  8277. sel.removeAllRanges();
  8278. sel.addRange(mark);
  8279. }
  8280. }else{
  8281. console.warn("No idea how to restore selection for this browser!");
  8282. }
  8283. }else if(_doc.selection && mark){
  8284. //'IE' way.
  8285. var rg;
  8286. if(mark.pRange){
  8287. rg = mark.range;
  8288. }else if(dojo.isArray(mark)){
  8289. rg = _doc.body.createControlRange();
  8290. //rg.addElement does not have call/apply method, so can not call it directly
  8291. //rg is not available in "range.addElement(item)", so can't use that either
  8292. dojo.forEach(mark, function(n){
  8293. rg.addElement(n);
  8294. });
  8295. }else{
  8296. rg = _doc.body.createTextRange();
  8297. rg.moveToBookmark(mark);
  8298. }
  8299. rg.select();
  8300. }
  8301. }
  8302. },
  8303. getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
  8304. // summary:
  8305. // Called as getFocus(), this returns an Object showing the current focus
  8306. // and selected text.
  8307. //
  8308. // Called as getFocus(widget), where widget is a (widget representing) a button
  8309. // that was just pressed, it returns where focus was before that button
  8310. // was pressed. (Pressing the button may have either shifted focus to the button,
  8311. // or removed focus altogether.) In this case the selected text is not returned,
  8312. // since it can't be accurately determined.
  8313. //
  8314. // menu: dijit._Widget or {domNode: DomNode} structure
  8315. // The button that was just pressed. If focus has disappeared or moved
  8316. // to this button, returns the previous focus. In this case the bookmark
  8317. // information is already lost, and null is returned.
  8318. //
  8319. // openedForWindow:
  8320. // iframe in which menu was opened
  8321. //
  8322. // returns:
  8323. // A handle to restore focus/selection, to be passed to `dijit.focus`
  8324. var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus;
  8325. return {
  8326. node: node,
  8327. bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark),
  8328. openedForWindow: openedForWindow
  8329. }; // Object
  8330. },
  8331. focus: function(/*Object || DomNode */ handle){
  8332. // summary:
  8333. // Sets the focused node and the selection according to argument.
  8334. // To set focus to an iframe's content, pass in the iframe itself.
  8335. // handle:
  8336. // object returned by get(), or a DomNode
  8337. if(!handle){ return; }
  8338. var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
  8339. bookmark = handle.bookmark,
  8340. openedForWindow = handle.openedForWindow,
  8341. collapsed = bookmark ? bookmark.isCollapsed : false;
  8342. // Set the focus
  8343. // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
  8344. // but we need to set focus to iframe.contentWindow
  8345. if(node){
  8346. var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
  8347. if(focusNode && focusNode.focus){
  8348. try{
  8349. // Gecko throws sometimes if setting focus is impossible,
  8350. // node not displayed or something like that
  8351. focusNode.focus();
  8352. }catch(e){/*quiet*/}
  8353. }
  8354. dijit._onFocusNode(node);
  8355. }
  8356. // set the selection
  8357. // do not need to restore if current selection is not empty
  8358. // (use keyboard to select a menu item) or if previous selection was collapsed
  8359. // as it may cause focus shift (Esp in IE).
  8360. if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){
  8361. if(openedForWindow){
  8362. openedForWindow.focus();
  8363. }
  8364. try{
  8365. dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]);
  8366. }catch(e2){
  8367. /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
  8368. }
  8369. }
  8370. },
  8371. // _activeStack: dijit._Widget[]
  8372. // List of currently active widgets (focused widget and it's ancestors)
  8373. _activeStack: [],
  8374. registerIframe: function(/*DomNode*/ iframe){
  8375. // summary:
  8376. // Registers listeners on the specified iframe so that any click
  8377. // or focus event on that iframe (or anything in it) is reported
  8378. // as a focus/click event on the <iframe> itself.
  8379. // description:
  8380. // Currently only used by editor.
  8381. // returns:
  8382. // Handle to pass to unregisterIframe()
  8383. return dijit.registerWin(iframe.contentWindow, iframe);
  8384. },
  8385. unregisterIframe: function(/*Object*/ handle){
  8386. // summary:
  8387. // Unregisters listeners on the specified iframe created by registerIframe.
  8388. // After calling be sure to delete or null out the handle itself.
  8389. // handle:
  8390. // Handle returned by registerIframe()
  8391. dijit.unregisterWin(handle);
  8392. },
  8393. registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
  8394. // summary:
  8395. // Registers listeners on the specified window (either the main
  8396. // window or an iframe's window) to detect when the user has clicked somewhere
  8397. // or focused somewhere.
  8398. // description:
  8399. // Users should call registerIframe() instead of this method.
  8400. // targetWindow:
  8401. // If specified this is the window associated with the iframe,
  8402. // i.e. iframe.contentWindow.
  8403. // effectiveNode:
  8404. // If specified, report any focus events inside targetWindow as
  8405. // an event on effectiveNode, rather than on evt.target.
  8406. // returns:
  8407. // Handle to pass to unregisterWin()
  8408. // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
  8409. var mousedownListener = function(evt){
  8410. dijit._justMouseDowned = true;
  8411. setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
  8412. // workaround weird IE bug where the click is on an orphaned node
  8413. // (first time clicking a Select/DropDownButton inside a TooltipDialog)
  8414. if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){
  8415. return;
  8416. }
  8417. dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
  8418. };
  8419. //dojo.connect(targetWindow, "onscroll", ???);
  8420. // Listen for blur and focus events on targetWindow's document.
  8421. // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
  8422. // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
  8423. // fire.
  8424. // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
  8425. // (at least for FF) the focus event doesn't fire on <html> or <body>.
  8426. var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document;
  8427. if(doc){
  8428. if(dojo.isIE){
  8429. targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
  8430. var activateListener = function(evt){
  8431. // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
  8432. // Should consider those more like a mouse-click than a focus....
  8433. if(evt.srcElement.tagName.toLowerCase() != "#document" &&
  8434. dijit.isTabNavigable(evt.srcElement)){
  8435. dijit._onFocusNode(effectiveNode || evt.srcElement);
  8436. }else{
  8437. dijit._onTouchNode(effectiveNode || evt.srcElement);
  8438. }
  8439. };
  8440. doc.attachEvent('onactivate', activateListener);
  8441. var deactivateListener = function(evt){
  8442. dijit._onBlurNode(effectiveNode || evt.srcElement);
  8443. };
  8444. doc.attachEvent('ondeactivate', deactivateListener);
  8445. return function(){
  8446. targetWindow.document.detachEvent('onmousedown', mousedownListener);
  8447. doc.detachEvent('onactivate', activateListener);
  8448. doc.detachEvent('ondeactivate', deactivateListener);
  8449. doc = null; // prevent memory leak (apparent circular reference via closure)
  8450. };
  8451. }else if(doc.body){
  8452. doc.body.addEventListener('mousedown', mousedownListener, true);
  8453. var focusListener = function(evt){
  8454. dijit._onFocusNode(effectiveNode || evt.target);
  8455. };
  8456. doc.addEventListener('focus', focusListener, true);
  8457. var blurListener = function(evt){
  8458. dijit._onBlurNode(effectiveNode || evt.target);
  8459. };
  8460. doc.addEventListener('blur', blurListener, true);
  8461. return function(){
  8462. doc.body.removeEventListener('mousedown', mousedownListener, true);
  8463. doc.removeEventListener('focus', focusListener, true);
  8464. doc.removeEventListener('blur', blurListener, true);
  8465. doc = null; // prevent memory leak (apparent circular reference via closure)
  8466. };
  8467. }
  8468. }
  8469. },
  8470. unregisterWin: function(/*Handle*/ handle){
  8471. // summary:
  8472. // Unregisters listeners on the specified window (either the main
  8473. // window or an iframe's window) according to handle returned from registerWin().
  8474. // After calling be sure to delete or null out the handle itself.
  8475. // Currently our handle is actually a function
  8476. handle && handle();
  8477. },
  8478. _onBlurNode: function(/*DomNode*/ node){
  8479. // summary:
  8480. // Called when focus leaves a node.
  8481. // Usually ignored, _unless_ it *isn't* follwed by touching another node,
  8482. // which indicates that we tabbed off the last field on the page,
  8483. // in which case every widget is marked inactive
  8484. dijit._prevFocus = dijit._curFocus;
  8485. dijit._curFocus = null;
  8486. if(dijit._justMouseDowned){
  8487. // the mouse down caused a new widget to be marked as active; this blur event
  8488. // is coming late, so ignore it.
  8489. return;
  8490. }
  8491. // if the blur event isn't followed by a focus event then mark all widgets as inactive.
  8492. if(dijit._clearActiveWidgetsTimer){
  8493. clearTimeout(dijit._clearActiveWidgetsTimer);
  8494. }
  8495. dijit._clearActiveWidgetsTimer = setTimeout(function(){
  8496. delete dijit._clearActiveWidgetsTimer;
  8497. dijit._setStack([]);
  8498. dijit._prevFocus = null;
  8499. }, 100);
  8500. },
  8501. _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
  8502. // summary:
  8503. // Callback when node is focused or mouse-downed
  8504. // node:
  8505. // The node that was touched.
  8506. // by:
  8507. // "mouse" if the focus/touch was caused by a mouse down event
  8508. // ignore the recent blurNode event
  8509. if(dijit._clearActiveWidgetsTimer){
  8510. clearTimeout(dijit._clearActiveWidgetsTimer);
  8511. delete dijit._clearActiveWidgetsTimer;
  8512. }
  8513. // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
  8514. var newStack=[];
  8515. try{
  8516. while(node){
  8517. var popupParent = dojo.attr(node, "dijitPopupParent");
  8518. if(popupParent){
  8519. node=dijit.byId(popupParent).domNode;
  8520. }else if(node.tagName && node.tagName.toLowerCase() == "body"){
  8521. // is this the root of the document or just the root of an iframe?
  8522. if(node === dojo.body()){
  8523. // node is the root of the main document
  8524. break;
  8525. }
  8526. // otherwise, find the iframe this node refers to (can't access it via parentNode,
  8527. // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
  8528. node=dojo.window.get(node.ownerDocument).frameElement;
  8529. }else{
  8530. // if this node is the root node of a widget, then add widget id to stack,
  8531. // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
  8532. // to support MenuItem)
  8533. var id = node.getAttribute && node.getAttribute("widgetId"),
  8534. widget = id && dijit.byId(id);
  8535. if(widget && !(by == "mouse" && widget.get("disabled"))){
  8536. newStack.unshift(id);
  8537. }
  8538. node=node.parentNode;
  8539. }
  8540. }
  8541. }catch(e){ /* squelch */ }
  8542. dijit._setStack(newStack, by);
  8543. },
  8544. _onFocusNode: function(/*DomNode*/ node){
  8545. // summary:
  8546. // Callback when node is focused
  8547. if(!node){
  8548. return;
  8549. }
  8550. if(node.nodeType == 9){
  8551. // Ignore focus events on the document itself. This is here so that
  8552. // (for example) clicking the up/down arrows of a spinner
  8553. // (which don't get focus) won't cause that widget to blur. (FF issue)
  8554. return;
  8555. }
  8556. dijit._onTouchNode(node);
  8557. if(node == dijit._curFocus){ return; }
  8558. if(dijit._curFocus){
  8559. dijit._prevFocus = dijit._curFocus;
  8560. }
  8561. dijit._curFocus = node;
  8562. dojo.publish("focusNode", [node]);
  8563. },
  8564. _setStack: function(/*String[]*/ newStack, /*String*/ by){
  8565. // summary:
  8566. // The stack of active widgets has changed. Send out appropriate events and records new stack.
  8567. // newStack:
  8568. // array of widget id's, starting from the top (outermost) widget
  8569. // by:
  8570. // "mouse" if the focus/touch was caused by a mouse down event
  8571. var oldStack = dijit._activeStack;
  8572. dijit._activeStack = newStack;
  8573. // compare old stack to new stack to see how many elements they have in common
  8574. for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
  8575. if(oldStack[nCommon] != newStack[nCommon]){
  8576. break;
  8577. }
  8578. }
  8579. var widget;
  8580. // for all elements that have gone out of focus, send blur event
  8581. for(var i=oldStack.length-1; i>=nCommon; i--){
  8582. widget = dijit.byId(oldStack[i]);
  8583. if(widget){
  8584. widget._focused = false;
  8585. widget.set("focused", false);
  8586. widget._hasBeenBlurred = true;
  8587. if(widget._onBlur){
  8588. widget._onBlur(by);
  8589. }
  8590. dojo.publish("widgetBlur", [widget, by]);
  8591. }
  8592. }
  8593. // for all element that have come into focus, send focus event
  8594. for(i=nCommon; i<newStack.length; i++){
  8595. widget = dijit.byId(newStack[i]);
  8596. if(widget){
  8597. widget._focused = true;
  8598. widget.set("focused", true);
  8599. if(widget._onFocus){
  8600. widget._onFocus(by);
  8601. }
  8602. dojo.publish("widgetFocus", [widget, by]);
  8603. }
  8604. }
  8605. }
  8606. });
  8607. // register top window and all the iframes it contains
  8608. dojo.addOnLoad(function(){
  8609. var handle = dijit.registerWin(window);
  8610. if(dojo.isIE){
  8611. dojo.addOnWindowUnload(function(){
  8612. dijit.unregisterWin(handle);
  8613. handle = null;
  8614. })
  8615. }
  8616. });
  8617. }
  8618. if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8619. dojo._hasResource["dijit._base.window"] = true;
  8620. dojo.provide("dijit._base.window");
  8621. dijit.getDocumentWindow = function(doc){
  8622. return dojo.window.get(doc);
  8623. };
  8624. }
  8625. if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8626. dojo._hasResource["dijit._base.popup"] = true;
  8627. dojo.provide("dijit._base.popup");
  8628. /*=====
  8629. dijit.popup.__OpenArgs = function(){
  8630. // popup: Widget
  8631. // widget to display
  8632. // parent: Widget
  8633. // the button etc. that is displaying this popup
  8634. // around: DomNode
  8635. // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
  8636. // x: Integer
  8637. // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
  8638. // y: Integer
  8639. // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
  8640. // orient: Object|String
  8641. // When the around parameter is specified, orient should be an
  8642. // ordered list of tuples of the form (around-node-corner, popup-node-corner).
  8643. // dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
  8644. // until the popup appears fully within the viewport.
  8645. //
  8646. // The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
  8647. // 1. (BL, TL)
  8648. // 2. (TL, BL)
  8649. // where BL means "bottom left" and "TL" means "top left".
  8650. // So by default, it first tries putting the popup below the around node, left-aligning them,
  8651. // and then tries to put it above the around node, still left-aligning them. Note that the
  8652. // default is horizontally reversed when in RTL mode.
  8653. //
  8654. // When an (x,y) position is specified rather than an around node, orient is either
  8655. // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
  8656. // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
  8657. // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
  8658. // and the top-right corner.
  8659. // onCancel: Function
  8660. // callback when user has canceled the popup by
  8661. // 1. hitting ESC or
  8662. // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
  8663. // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
  8664. // onClose: Function
  8665. // callback whenever this popup is closed
  8666. // onExecute: Function
  8667. // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
  8668. // padding: dijit.__Position
  8669. // adding a buffer around the opening position. This is only useful when around is not set.
  8670. this.popup = popup;
  8671. this.parent = parent;
  8672. this.around = around;
  8673. this.x = x;
  8674. this.y = y;
  8675. this.orient = orient;
  8676. this.onCancel = onCancel;
  8677. this.onClose = onClose;
  8678. this.onExecute = onExecute;
  8679. this.padding = padding;
  8680. }
  8681. =====*/
  8682. dijit.popup = {
  8683. // summary:
  8684. // This singleton is used to show/hide widgets as popups.
  8685. // _stack: dijit._Widget[]
  8686. // Stack of currently popped up widgets.
  8687. // (someone opened _stack[0], and then it opened _stack[1], etc.)
  8688. _stack: [],
  8689. // _beginZIndex: Number
  8690. // Z-index of the first popup. (If first popup opens other
  8691. // popups they get a higher z-index.)
  8692. _beginZIndex: 1000,
  8693. _idGen: 1,
  8694. _createWrapper: function(/*Widget || DomNode*/ widget){
  8695. // summary:
  8696. // Initialization for widgets that will be used as popups.
  8697. // Puts widget inside a wrapper DIV (if not already in one),
  8698. // and returns pointer to that wrapper DIV.
  8699. var node = widget.domNode || widget,
  8700. wrapper = widget.declaredClass ? widget._popupWrapper :
  8701. node.parentNode && dojo.hasClass(node.parentNode, "dijitPopup") ? node.parentNode : null;
  8702. if(!wrapper){
  8703. // Create wrapper <div> for when this widget [in the future] will be used as a popup.
  8704. // This is done early because of IE bugs where creating/moving DOM nodes causes focus
  8705. // to go wonky, see tests/robot/Toolbar.html to reproduce
  8706. wrapper = dojo.create("div",{
  8707. "class":"dijitPopup",
  8708. style:{ display: "none"},
  8709. role: "presentation"
  8710. }, dojo.body());
  8711. wrapper.appendChild(node);
  8712. var s = node.style;
  8713. s.display = "";
  8714. s.visibility = "";
  8715. s.position = "";
  8716. s.top = "0px";
  8717. if(widget.declaredClass){ // TODO: in 2.0 change signature to always take widget, then remove if()
  8718. widget._popupWrapper = wrapper;
  8719. dojo.connect(widget, "destroy", function(){
  8720. dojo.destroy(wrapper);
  8721. delete widget._popupWrapper;
  8722. });
  8723. }
  8724. }
  8725. return wrapper; // DOMNode
  8726. },
  8727. moveOffScreen: function(/*Widget || DomNode*/ widget){
  8728. // summary:
  8729. // Moves the popup widget off-screen.
  8730. // Do not use this method to hide popups when not in use, because
  8731. // that will create an accessibility issue: the offscreen popup is
  8732. // still in the tabbing order.
  8733. // Create wrapper if not already there
  8734. var wrapper = this._createWrapper(widget);
  8735. dojo.style(wrapper, {
  8736. visibility: "hidden",
  8737. top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
  8738. display: ""
  8739. });
  8740. },
  8741. hide: function(/*dijit._Widget*/ widget){
  8742. // summary:
  8743. // Hide this popup widget (until it is ready to be shown).
  8744. // Initialization for widgets that will be used as popups
  8745. //
  8746. // Also puts widget inside a wrapper DIV (if not already in one)
  8747. //
  8748. // If popup widget needs to layout it should
  8749. // do so when it is made visible, and popup._onShow() is called.
  8750. // Create wrapper if not already there
  8751. var wrapper = this._createWrapper(widget);
  8752. dojo.style(wrapper, "display", "none");
  8753. },
  8754. getTopPopup: function(){
  8755. // summary:
  8756. // Compute the closest ancestor popup that's *not* a child of another popup.
  8757. // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
  8758. var stack = this._stack;
  8759. for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
  8760. /* do nothing, just trying to get right value for pi */
  8761. }
  8762. return stack[pi];
  8763. },
  8764. open: function(/*dijit.popup.__OpenArgs*/ args){
  8765. // summary:
  8766. // Popup the widget at the specified position
  8767. //
  8768. // example:
  8769. // opening at the mouse position
  8770. // | dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
  8771. //
  8772. // example:
  8773. // opening the widget as a dropdown
  8774. // | dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
  8775. //
  8776. // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
  8777. // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
  8778. var stack = this._stack,
  8779. widget = args.popup,
  8780. orient = args.orient || (
  8781. (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ?
  8782. {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
  8783. {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
  8784. ),
  8785. around = args.around,
  8786. id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
  8787. // If we are opening a new popup that isn't a child of a currently opened popup, then
  8788. // close currently opened popup(s). This should happen automatically when the old popups
  8789. // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
  8790. while(stack.length && (!args.parent || !dojo.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
  8791. dijit.popup.close(stack[stack.length-1].widget);
  8792. }
  8793. // Get pointer to popup wrapper, and create wrapper if it doesn't exist
  8794. var wrapper = this._createWrapper(widget);
  8795. dojo.attr(wrapper, {
  8796. id: id,
  8797. style: {
  8798. zIndex: this._beginZIndex + stack.length
  8799. },
  8800. "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
  8801. dijitPopupParent: args.parent ? args.parent.id : ""
  8802. });
  8803. if(dojo.isIE || dojo.isMoz){
  8804. if(!widget.bgIframe){
  8805. // setting widget.bgIframe triggers cleanup in _Widget.destroy()
  8806. widget.bgIframe = new dijit.BackgroundIframe(wrapper);
  8807. }
  8808. }
  8809. // position the wrapper node and make it visible
  8810. var best = around ?
  8811. dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
  8812. dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
  8813. wrapper.style.display = "";
  8814. wrapper.style.visibility = "visible";
  8815. widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown
  8816. var handlers = [];
  8817. // provide default escape and tab key handling
  8818. // (this will work for any widget, not just menu)
  8819. handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
  8820. if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
  8821. dojo.stopEvent(evt);
  8822. args.onCancel();
  8823. }else if(evt.charOrCode === dojo.keys.TAB){
  8824. dojo.stopEvent(evt);
  8825. var topPopup = this.getTopPopup();
  8826. if(topPopup && topPopup.onCancel){
  8827. topPopup.onCancel();
  8828. }
  8829. }
  8830. }));
  8831. // watch for cancel/execute events on the popup and notify the caller
  8832. // (for a menu, "execute" means clicking an item)
  8833. if(widget.onCancel){
  8834. handlers.push(dojo.connect(widget, "onCancel", args.onCancel));
  8835. }
  8836. handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){
  8837. var topPopup = this.getTopPopup();
  8838. if(topPopup && topPopup.onExecute){
  8839. topPopup.onExecute();
  8840. }
  8841. }));
  8842. stack.push({
  8843. widget: widget,
  8844. parent: args.parent,
  8845. onExecute: args.onExecute,
  8846. onCancel: args.onCancel,
  8847. onClose: args.onClose,
  8848. handlers: handlers
  8849. });
  8850. if(widget.onOpen){
  8851. // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
  8852. widget.onOpen(best);
  8853. }
  8854. return best;
  8855. },
  8856. close: function(/*dijit._Widget?*/ popup){
  8857. // summary:
  8858. // Close specified popup and any popups that it parented.
  8859. // If no popup is specified, closes all popups.
  8860. var stack = this._stack;
  8861. // Basically work backwards from the top of the stack closing popups
  8862. // until we hit the specified popup, but IIRC there was some issue where closing
  8863. // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
  8864. // closing C might close B indirectly and then the while() condition will run where stack==[A]...
  8865. // so the while condition is constructed defensively.
  8866. while((popup && dojo.some(stack, function(elem){return elem.widget == popup;})) ||
  8867. (!popup && stack.length)){
  8868. var top = stack.pop(),
  8869. widget = top.widget,
  8870. onClose = top.onClose;
  8871. if(widget.onClose){
  8872. // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
  8873. widget.onClose();
  8874. }
  8875. dojo.forEach(top.handlers, dojo.disconnect);
  8876. // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
  8877. if(widget && widget.domNode){
  8878. this.hide(widget);
  8879. }
  8880. if(onClose){
  8881. onClose();
  8882. }
  8883. }
  8884. }
  8885. };
  8886. // TODO: remove dijit._frames, it isn't being used much, since popups never release their
  8887. // iframes (see [22236])
  8888. dijit._frames = new function(){
  8889. // summary:
  8890. // cache of iframes
  8891. var queue = [];
  8892. this.pop = function(){
  8893. var iframe;
  8894. if(queue.length){
  8895. iframe = queue.pop();
  8896. iframe.style.display="";
  8897. }else{
  8898. if(dojo.isIE < 9){
  8899. var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
  8900. var html="<iframe src='" + burl + "'"
  8901. + " style='position: absolute; left: 0px; top: 0px;"
  8902. + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
  8903. iframe = dojo.doc.createElement(html);
  8904. }else{
  8905. iframe = dojo.create("iframe");
  8906. iframe.src = 'javascript:""';
  8907. iframe.className = "dijitBackgroundIframe";
  8908. dojo.style(iframe, "opacity", 0.1);
  8909. }
  8910. iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
  8911. dijit.setWaiRole(iframe,"presentation");
  8912. }
  8913. return iframe;
  8914. };
  8915. this.push = function(iframe){
  8916. iframe.style.display="none";
  8917. queue.push(iframe);
  8918. }
  8919. }();
  8920. dijit.BackgroundIframe = function(/*DomNode*/ node){
  8921. // summary:
  8922. // For IE/FF z-index schenanigans. id attribute is required.
  8923. //
  8924. // description:
  8925. // new dijit.BackgroundIframe(node)
  8926. // Makes a background iframe as a child of node, that fills
  8927. // area (and position) of node
  8928. if(!node.id){ throw new Error("no id"); }
  8929. if(dojo.isIE || dojo.isMoz){
  8930. var iframe = (this.iframe = dijit._frames.pop());
  8931. node.appendChild(iframe);
  8932. if(dojo.isIE<7 || dojo.isQuirks){
  8933. this.resize(node);
  8934. this._conn = dojo.connect(node, 'onresize', this, function(){
  8935. this.resize(node);
  8936. });
  8937. }else{
  8938. dojo.style(iframe, {
  8939. width: '100%',
  8940. height: '100%'
  8941. });
  8942. }
  8943. }
  8944. };
  8945. dojo.extend(dijit.BackgroundIframe, {
  8946. resize: function(node){
  8947. // summary:
  8948. // Resize the iframe so it's the same size as node.
  8949. // Needed on IE6 and IE/quirks because height:100% doesn't work right.
  8950. if(this.iframe){
  8951. dojo.style(this.iframe, {
  8952. width: node.offsetWidth + 'px',
  8953. height: node.offsetHeight + 'px'
  8954. });
  8955. }
  8956. },
  8957. destroy: function(){
  8958. // summary:
  8959. // destroy the iframe
  8960. if(this._conn){
  8961. dojo.disconnect(this._conn);
  8962. this._conn = null;
  8963. }
  8964. if(this.iframe){
  8965. dijit._frames.push(this.iframe);
  8966. delete this.iframe;
  8967. }
  8968. }
  8969. });
  8970. }
  8971. if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8972. dojo._hasResource["dijit._base.scroll"] = true;
  8973. dojo.provide("dijit._base.scroll");
  8974. dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
  8975. // summary:
  8976. // Scroll the passed node into view, if it is not already.
  8977. // Deprecated, use `dojo.window.scrollIntoView` instead.
  8978. dojo.window.scrollIntoView(node, pos);
  8979. };
  8980. }
  8981. if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  8982. dojo._hasResource["dojo.uacss"] = true;
  8983. dojo.provide("dojo.uacss");
  8984. (function(){
  8985. // summary:
  8986. // Applies pre-set CSS classes to the top-level HTML node, based on:
  8987. // - browser (ex: dj_ie)
  8988. // - browser version (ex: dj_ie6)
  8989. // - box model (ex: dj_contentBox)
  8990. // - text direction (ex: dijitRtl)
  8991. //
  8992. // In addition, browser, browser version, and box model are
  8993. // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
  8994. var d = dojo,
  8995. html = d.doc.documentElement,
  8996. ie = d.isIE,
  8997. opera = d.isOpera,
  8998. maj = Math.floor,
  8999. ff = d.isFF,
  9000. boxModel = d.boxModel.replace(/-/,''),
  9001. classes = {
  9002. dj_quirks: d.isQuirks,
  9003. // NOTE: Opera not supported by dijit
  9004. dj_opera: opera,
  9005. dj_khtml: d.isKhtml,
  9006. dj_webkit: d.isWebKit,
  9007. dj_safari: d.isSafari,
  9008. dj_chrome: d.isChrome,
  9009. dj_gecko: d.isMozilla
  9010. }; // no dojo unsupported browsers
  9011. if(ie){
  9012. classes["dj_ie"] = true;
  9013. classes["dj_ie" + maj(ie)] = true;
  9014. classes["dj_iequirks"] = d.isQuirks;
  9015. }
  9016. if(ff){
  9017. classes["dj_ff" + maj(ff)] = true;
  9018. }
  9019. classes["dj_" + boxModel] = true;
  9020. // apply browser, browser version, and box model class names
  9021. var classStr = "";
  9022. for(var clz in classes){
  9023. if(classes[clz]){
  9024. classStr += clz + " ";
  9025. }
  9026. }
  9027. html.className = d.trim(html.className + " " + classStr);
  9028. // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
  9029. // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
  9030. // Unshift() is to run sniff code before the parser.
  9031. dojo._loaders.unshift(function(){
  9032. if(!dojo._isBodyLtr()){
  9033. var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")
  9034. html.className = d.trim(html.className + " " + rtlClassStr);
  9035. }
  9036. });
  9037. })();
  9038. }
  9039. if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9040. dojo._hasResource["dijit._base.sniff"] = true;
  9041. dojo.provide("dijit._base.sniff");
  9042. // summary:
  9043. // Applies pre-set CSS classes to the top-level HTML node, see
  9044. // `dojo.uacss` for details.
  9045. //
  9046. // Simply doing a require on this module will
  9047. // establish this CSS. Modified version of Morris' CSS hack.
  9048. }
  9049. if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9050. dojo._hasResource["dijit._base.typematic"] = true;
  9051. dojo.provide("dijit._base.typematic");
  9052. dijit.typematic = {
  9053. // summary:
  9054. // These functions are used to repetitively call a user specified callback
  9055. // method when a specific key or mouse click over a specific DOM node is
  9056. // held down for a specific amount of time.
  9057. // Only 1 such event is allowed to occur on the browser page at 1 time.
  9058. _fireEventAndReload: function(){
  9059. this._timer = null;
  9060. this._callback(++this._count, this._node, this._evt);
  9061. // Schedule next event, timer is at most minDelay (default 10ms) to avoid
  9062. // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
  9063. this._currentTimeout = Math.max(
  9064. this._currentTimeout < 0 ? this._initialDelay :
  9065. (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
  9066. this._minDelay);
  9067. this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout);
  9068. },
  9069. trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
  9070. // summary:
  9071. // Start a timed, repeating callback sequence.
  9072. // If already started, the function call is ignored.
  9073. // This method is not normally called by the user but can be
  9074. // when the normal listener code is insufficient.
  9075. // evt:
  9076. // key or mouse event object to pass to the user callback
  9077. // _this:
  9078. // pointer to the user's widget space.
  9079. // node:
  9080. // the DOM node object to pass the the callback function
  9081. // callback:
  9082. // function to call until the sequence is stopped called with 3 parameters:
  9083. // count:
  9084. // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
  9085. // node:
  9086. // the DOM node object passed in
  9087. // evt:
  9088. // key or mouse event object
  9089. // obj:
  9090. // user space object used to uniquely identify each typematic sequence
  9091. // subsequentDelay (optional):
  9092. // if > 1, the number of milliseconds until the 3->n events occur
  9093. // or else the fractional time multiplier for the next event's delay, default=0.9
  9094. // initialDelay (optional):
  9095. // the number of milliseconds until the 2nd event occurs, default=500ms
  9096. // minDelay (optional):
  9097. // the maximum delay in milliseconds for event to fire, default=10ms
  9098. if(obj != this._obj){
  9099. this.stop();
  9100. this._initialDelay = initialDelay || 500;
  9101. this._subsequentDelay = subsequentDelay || 0.90;
  9102. this._minDelay = minDelay || 10;
  9103. this._obj = obj;
  9104. this._evt = evt;
  9105. this._node = node;
  9106. this._currentTimeout = -1;
  9107. this._count = -1;
  9108. this._callback = dojo.hitch(_this, callback);
  9109. this._fireEventAndReload();
  9110. this._evt = dojo.mixin({faux: true}, evt);
  9111. }
  9112. },
  9113. stop: function(){
  9114. // summary:
  9115. // Stop an ongoing timed, repeating callback sequence.
  9116. if(this._timer){
  9117. clearTimeout(this._timer);
  9118. this._timer = null;
  9119. }
  9120. if(this._obj){
  9121. this._callback(-1, this._node, this._evt);
  9122. this._obj = null;
  9123. }
  9124. },
  9125. addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
  9126. // summary:
  9127. // Start listening for a specific typematic key.
  9128. // See also the trigger method for other parameters.
  9129. // keyObject:
  9130. // an object defining the key to listen for:
  9131. // charOrCode:
  9132. // the printable character (string) or keyCode (number) to listen for.
  9133. // keyCode:
  9134. // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
  9135. // charCode:
  9136. // (deprecated - use charOrCode) the charCode (number) to listen for.
  9137. // ctrlKey:
  9138. // desired ctrl key state to initiate the callback sequence:
  9139. // - pressed (true)
  9140. // - released (false)
  9141. // - either (unspecified)
  9142. // altKey:
  9143. // same as ctrlKey but for the alt key
  9144. // shiftKey:
  9145. // same as ctrlKey but for the shift key
  9146. // returns:
  9147. // an array of dojo.connect handles
  9148. if(keyObject.keyCode){
  9149. keyObject.charOrCode = keyObject.keyCode;
  9150. dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
  9151. }else if(keyObject.charCode){
  9152. keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
  9153. dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
  9154. }
  9155. return [
  9156. dojo.connect(node, "onkeypress", this, function(evt){
  9157. if(evt.charOrCode == keyObject.charOrCode &&
  9158. (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
  9159. (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
  9160. (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
  9161. (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
  9162. dojo.stopEvent(evt);
  9163. dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
  9164. }else if(dijit.typematic._obj == keyObject){
  9165. dijit.typematic.stop();
  9166. }
  9167. }),
  9168. dojo.connect(node, "onkeyup", this, function(evt){
  9169. if(dijit.typematic._obj == keyObject){
  9170. dijit.typematic.stop();
  9171. }
  9172. })
  9173. ];
  9174. },
  9175. addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
  9176. // summary:
  9177. // Start listening for a typematic mouse click.
  9178. // See the trigger method for other parameters.
  9179. // returns:
  9180. // an array of dojo.connect handles
  9181. var dc = dojo.connect;
  9182. return [
  9183. dc(node, "mousedown", this, function(evt){
  9184. dojo.stopEvent(evt);
  9185. dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
  9186. }),
  9187. dc(node, "mouseup", this, function(evt){
  9188. dojo.stopEvent(evt);
  9189. dijit.typematic.stop();
  9190. }),
  9191. dc(node, "mouseout", this, function(evt){
  9192. dojo.stopEvent(evt);
  9193. dijit.typematic.stop();
  9194. }),
  9195. dc(node, "mousemove", this, function(evt){
  9196. evt.preventDefault();
  9197. }),
  9198. dc(node, "dblclick", this, function(evt){
  9199. dojo.stopEvent(evt);
  9200. if(dojo.isIE < 9){
  9201. dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
  9202. setTimeout(dojo.hitch(this, dijit.typematic.stop), 50);
  9203. }
  9204. })
  9205. ];
  9206. },
  9207. addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
  9208. // summary:
  9209. // Start listening for a specific typematic key and mouseclick.
  9210. // This is a thin wrapper to addKeyListener and addMouseListener.
  9211. // See the addMouseListener and addKeyListener methods for other parameters.
  9212. // mouseNode:
  9213. // the DOM node object to listen on for mouse events.
  9214. // keyNode:
  9215. // the DOM node object to listen on for key events.
  9216. // returns:
  9217. // an array of dojo.connect handles
  9218. return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat(
  9219. this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay));
  9220. }
  9221. };
  9222. }
  9223. if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9224. dojo._hasResource["dijit._base.wai"] = true;
  9225. dojo.provide("dijit._base.wai");
  9226. dijit.wai = {
  9227. onload: function(){
  9228. // summary:
  9229. // Detects if we are in high-contrast mode or not
  9230. // This must be a named function and not an anonymous
  9231. // function, so that the widget parsing code can make sure it
  9232. // registers its onload function after this function.
  9233. // DO NOT USE "this" within this function.
  9234. // create div for testing if high contrast mode is on or images are turned off
  9235. var div = dojo.create("div",{
  9236. id: "a11yTestNode",
  9237. style:{
  9238. cssText:'border: 1px solid;'
  9239. + 'border-color:red green;'
  9240. + 'position: absolute;'
  9241. + 'height: 5px;'
  9242. + 'top: -999px;'
  9243. + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");'
  9244. }
  9245. }, dojo.body());
  9246. // test it
  9247. var cs = dojo.getComputedStyle(div);
  9248. if(cs){
  9249. var bkImg = cs.backgroundImage;
  9250. var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
  9251. dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
  9252. if(dojo.isIE){
  9253. div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
  9254. }else{
  9255. dojo.body().removeChild(div);
  9256. }
  9257. }
  9258. }
  9259. };
  9260. // Test if computer is in high contrast mode.
  9261. // Make sure the a11y test runs first, before widgets are instantiated.
  9262. if(dojo.isIE || dojo.isMoz){ // NOTE: checking in Safari messes things up
  9263. dojo._loaders.unshift(dijit.wai.onload);
  9264. }
  9265. dojo.mixin(dijit, {
  9266. hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
  9267. // summary:
  9268. // Determines if an element has a particular role.
  9269. // returns:
  9270. // True if elem has the specific role attribute and false if not.
  9271. // For backwards compatibility if role parameter not provided,
  9272. // returns true if has a role
  9273. var waiRole = this.getWaiRole(elem);
  9274. return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
  9275. },
  9276. getWaiRole: function(/*Element*/ elem){
  9277. // summary:
  9278. // Gets the role for an element (which should be a wai role).
  9279. // returns:
  9280. // The role of elem or an empty string if elem
  9281. // does not have a role.
  9282. return dojo.trim((dojo.attr(elem, "role") || "").replace("wairole:",""));
  9283. },
  9284. setWaiRole: function(/*Element*/ elem, /*String*/ role){
  9285. // summary:
  9286. // Sets the role on an element.
  9287. // description:
  9288. // Replace existing role attribute with new role.
  9289. dojo.attr(elem, "role", role);
  9290. },
  9291. removeWaiRole: function(/*Element*/ elem, /*String*/ role){
  9292. // summary:
  9293. // Removes the specified role from an element.
  9294. // Removes role attribute if no specific role provided (for backwards compat.)
  9295. var roleValue = dojo.attr(elem, "role");
  9296. if(!roleValue){ return; }
  9297. if(role){
  9298. var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
  9299. dojo.attr(elem, "role", t);
  9300. }else{
  9301. elem.removeAttribute("role");
  9302. }
  9303. },
  9304. hasWaiState: function(/*Element*/ elem, /*String*/ state){
  9305. // summary:
  9306. // Determines if an element has a given state.
  9307. // description:
  9308. // Checks for an attribute called "aria-"+state.
  9309. // returns:
  9310. // true if elem has a value for the given state and
  9311. // false if it does not.
  9312. return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
  9313. },
  9314. getWaiState: function(/*Element*/ elem, /*String*/ state){
  9315. // summary:
  9316. // Gets the value of a state on an element.
  9317. // description:
  9318. // Checks for an attribute called "aria-"+state.
  9319. // returns:
  9320. // The value of the requested state on elem
  9321. // or an empty string if elem has no value for state.
  9322. return elem.getAttribute("aria-"+state) || "";
  9323. },
  9324. setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
  9325. // summary:
  9326. // Sets a state on an element.
  9327. // description:
  9328. // Sets an attribute called "aria-"+state.
  9329. elem.setAttribute("aria-"+state, value);
  9330. },
  9331. removeWaiState: function(/*Element*/ elem, /*String*/ state){
  9332. // summary:
  9333. // Removes a state from an element.
  9334. // description:
  9335. // Sets an attribute called "aria-"+state.
  9336. elem.removeAttribute("aria-"+state);
  9337. }
  9338. });
  9339. }
  9340. if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9341. dojo._hasResource["dijit._base"] = true;
  9342. dojo.provide("dijit._base");
  9343. }
  9344. if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9345. dojo._hasResource["dijit._Widget"] = true;
  9346. dojo.provide("dijit._Widget");
  9347. ////////////////// DEFERRED CONNECTS ///////////////////
  9348. // This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets'
  9349. // DOM nodes) until someone actually needs to monitor that event.
  9350. dojo.connect(dojo, "_connect",
  9351. function(/*dijit._Widget*/ widget, /*String*/ event){
  9352. if(widget && dojo.isFunction(widget._onConnect)){
  9353. widget._onConnect(event);
  9354. }
  9355. });
  9356. dijit._connectOnUseEventHandler = function(/*Event*/ event){};
  9357. ////////////////// ONDIJITCLICK SUPPORT ///////////////////
  9358. // Keep track of where the last keydown event was, to help avoid generating
  9359. // spurious ondijitclick events when:
  9360. // 1. focus is on a <button> or <a>
  9361. // 2. user presses then releases the ENTER key
  9362. // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
  9363. // 4. onkeyup event fires, causing the ondijitclick handler to fire
  9364. dijit._lastKeyDownNode = null;
  9365. if(dojo.isIE){
  9366. (function(){
  9367. var keydownCallback = function(evt){
  9368. dijit._lastKeyDownNode = evt.srcElement;
  9369. };
  9370. dojo.doc.attachEvent('onkeydown', keydownCallback);
  9371. dojo.addOnWindowUnload(function(){
  9372. dojo.doc.detachEvent('onkeydown', keydownCallback);
  9373. });
  9374. })();
  9375. }else{
  9376. dojo.doc.addEventListener('keydown', function(evt){
  9377. dijit._lastKeyDownNode = evt.target;
  9378. }, true);
  9379. }
  9380. (function(){
  9381. dojo.declare("dijit._Widget", dijit._WidgetBase, {
  9382. // summary:
  9383. // Base class for all Dijit widgets.
  9384. //
  9385. // Extends _WidgetBase, adding support for:
  9386. // - deferred connections
  9387. // A call like dojo.connect(myWidget, "onMouseMove", func)
  9388. // will essentially do a dojo.connect(myWidget.domNode, "onMouseMove", func)
  9389. // - ondijitclick
  9390. // Support new dojoAttachEvent="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
  9391. // - focus related functions
  9392. // In particular, the onFocus()/onBlur() callbacks. Driven internally by
  9393. // dijit/_base/focus.js.
  9394. // - deprecated methods
  9395. // - onShow(), onHide(), onClose()
  9396. //
  9397. // Also, by loading code in dijit/_base, turns on:
  9398. // - browser sniffing (putting browser id like .dj_ie on <html> node)
  9399. // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
  9400. ////////////////// DEFERRED CONNECTS ///////////////////
  9401. // _deferredConnects: [protected] Object
  9402. // attributeMap addendum for event handlers that should be connected only on first use
  9403. _deferredConnects: {
  9404. onClick: "",
  9405. onDblClick: "",
  9406. onKeyDown: "",
  9407. onKeyPress: "",
  9408. onKeyUp: "",
  9409. onMouseMove: "",
  9410. onMouseDown: "",
  9411. onMouseOut: "",
  9412. onMouseOver: "",
  9413. onMouseLeave: "",
  9414. onMouseEnter: "",
  9415. onMouseUp: ""
  9416. },
  9417. onClick: dijit._connectOnUseEventHandler,
  9418. /*=====
  9419. onClick: function(event){
  9420. // summary:
  9421. // Connect to this function to receive notifications of mouse click events.
  9422. // event:
  9423. // mouse Event
  9424. // tags:
  9425. // callback
  9426. },
  9427. =====*/
  9428. onDblClick: dijit._connectOnUseEventHandler,
  9429. /*=====
  9430. onDblClick: function(event){
  9431. // summary:
  9432. // Connect to this function to receive notifications of mouse double click events.
  9433. // event:
  9434. // mouse Event
  9435. // tags:
  9436. // callback
  9437. },
  9438. =====*/
  9439. onKeyDown: dijit._connectOnUseEventHandler,
  9440. /*=====
  9441. onKeyDown: function(event){
  9442. // summary:
  9443. // Connect to this function to receive notifications of keys being pressed down.
  9444. // event:
  9445. // key Event
  9446. // tags:
  9447. // callback
  9448. },
  9449. =====*/
  9450. onKeyPress: dijit._connectOnUseEventHandler,
  9451. /*=====
  9452. onKeyPress: function(event){
  9453. // summary:
  9454. // Connect to this function to receive notifications of printable keys being typed.
  9455. // event:
  9456. // key Event
  9457. // tags:
  9458. // callback
  9459. },
  9460. =====*/
  9461. onKeyUp: dijit._connectOnUseEventHandler,
  9462. /*=====
  9463. onKeyUp: function(event){
  9464. // summary:
  9465. // Connect to this function to receive notifications of keys being released.
  9466. // event:
  9467. // key Event
  9468. // tags:
  9469. // callback
  9470. },
  9471. =====*/
  9472. onMouseDown: dijit._connectOnUseEventHandler,
  9473. /*=====
  9474. onMouseDown: function(event){
  9475. // summary:
  9476. // Connect to this function to receive notifications of when the mouse button is pressed down.
  9477. // event:
  9478. // mouse Event
  9479. // tags:
  9480. // callback
  9481. },
  9482. =====*/
  9483. onMouseMove: dijit._connectOnUseEventHandler,
  9484. /*=====
  9485. onMouseMove: function(event){
  9486. // summary:
  9487. // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
  9488. // event:
  9489. // mouse Event
  9490. // tags:
  9491. // callback
  9492. },
  9493. =====*/
  9494. onMouseOut: dijit._connectOnUseEventHandler,
  9495. /*=====
  9496. onMouseOut: function(event){
  9497. // summary:
  9498. // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
  9499. // event:
  9500. // mouse Event
  9501. // tags:
  9502. // callback
  9503. },
  9504. =====*/
  9505. onMouseOver: dijit._connectOnUseEventHandler,
  9506. /*=====
  9507. onMouseOver: function(event){
  9508. // summary:
  9509. // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
  9510. // event:
  9511. // mouse Event
  9512. // tags:
  9513. // callback
  9514. },
  9515. =====*/
  9516. onMouseLeave: dijit._connectOnUseEventHandler,
  9517. /*=====
  9518. onMouseLeave: function(event){
  9519. // summary:
  9520. // Connect to this function to receive notifications of when the mouse moves off of this widget.
  9521. // event:
  9522. // mouse Event
  9523. // tags:
  9524. // callback
  9525. },
  9526. =====*/
  9527. onMouseEnter: dijit._connectOnUseEventHandler,
  9528. /*=====
  9529. onMouseEnter: function(event){
  9530. // summary:
  9531. // Connect to this function to receive notifications of when the mouse moves onto this widget.
  9532. // event:
  9533. // mouse Event
  9534. // tags:
  9535. // callback
  9536. },
  9537. =====*/
  9538. onMouseUp: dijit._connectOnUseEventHandler,
  9539. /*=====
  9540. onMouseUp: function(event){
  9541. // summary:
  9542. // Connect to this function to receive notifications of when the mouse button is released.
  9543. // event:
  9544. // mouse Event
  9545. // tags:
  9546. // callback
  9547. },
  9548. =====*/
  9549. create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
  9550. // To avoid double-connects, remove entries from _deferredConnects
  9551. // that have been setup manually by a subclass (ex, by dojoAttachEvent).
  9552. // If a subclass has redefined a callback (ex: onClick) then assume it's being
  9553. // connected to manually.
  9554. this._deferredConnects = dojo.clone(this._deferredConnects);
  9555. for(var attr in this.attributeMap){
  9556. delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
  9557. }
  9558. for(attr in this._deferredConnects){
  9559. if(this[attr] !== dijit._connectOnUseEventHandler){
  9560. delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists
  9561. }
  9562. }
  9563. this.inherited(arguments);
  9564. if(this.domNode){
  9565. // If the developer has specified a handler as a widget parameter
  9566. // (ex: new Button({onClick: ...})
  9567. // then naturally need to connect from DOM node to that handler immediately,
  9568. for(attr in this.params){
  9569. this._onConnect(attr);
  9570. }
  9571. }
  9572. },
  9573. _onConnect: function(/*String*/ event){
  9574. // summary:
  9575. // Called when someone connects to one of my handlers.
  9576. // "Turn on" that handler if it isn't active yet.
  9577. //
  9578. // This is also called for every single initialization parameter
  9579. // so need to do nothing for parameters like "id".
  9580. // tags:
  9581. // private
  9582. if(event in this._deferredConnects){
  9583. var mapNode = this[this._deferredConnects[event] || 'domNode'];
  9584. this.connect(mapNode, event.toLowerCase(), event);
  9585. delete this._deferredConnects[event];
  9586. }
  9587. },
  9588. ////////////////// FOCUS RELATED ///////////////////
  9589. // _onFocus() and _onBlur() are called by the focus manager
  9590. // focused: [readonly] Boolean
  9591. // This widget or a widget it contains has focus, or is "active" because
  9592. // it was recently clicked.
  9593. focused: false,
  9594. isFocusable: function(){
  9595. // summary:
  9596. // Return true if this widget can currently be focused
  9597. // and false if not
  9598. return this.focus && (dojo.style(this.domNode, "display") != "none");
  9599. },
  9600. onFocus: function(){
  9601. // summary:
  9602. // Called when the widget becomes "active" because
  9603. // it or a widget inside of it either has focus, or has recently
  9604. // been clicked.
  9605. // tags:
  9606. // callback
  9607. },
  9608. onBlur: function(){
  9609. // summary:
  9610. // Called when the widget stops being "active" because
  9611. // focus moved to something outside of it, or the user
  9612. // clicked somewhere outside of it, or the widget was
  9613. // hidden.
  9614. // tags:
  9615. // callback
  9616. },
  9617. _onFocus: function(e){
  9618. // summary:
  9619. // This is where widgets do processing for when they are active,
  9620. // such as changing CSS classes. See onFocus() for more details.
  9621. // tags:
  9622. // protected
  9623. this.onFocus();
  9624. },
  9625. _onBlur: function(){
  9626. // summary:
  9627. // This is where widgets do processing for when they stop being active,
  9628. // such as changing CSS classes. See onBlur() for more details.
  9629. // tags:
  9630. // protected
  9631. this.onBlur();
  9632. },
  9633. ////////////////// DEPRECATED METHODS ///////////////////
  9634. setAttribute: function(/*String*/ attr, /*anything*/ value){
  9635. // summary:
  9636. // Deprecated. Use set() instead.
  9637. // tags:
  9638. // deprecated
  9639. dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
  9640. this.set(attr, value);
  9641. },
  9642. attr: function(/*String|Object*/name, /*Object?*/value){
  9643. // summary:
  9644. // Set or get properties on a widget instance.
  9645. // name:
  9646. // The property to get or set. If an object is passed here and not
  9647. // a string, its keys are used as names of attributes to be set
  9648. // and the value of the object as values to set in the widget.
  9649. // value:
  9650. // Optional. If provided, attr() operates as a setter. If omitted,
  9651. // the current value of the named property is returned.
  9652. // description:
  9653. // This method is deprecated, use get() or set() directly.
  9654. // Print deprecation warning but only once per calling function
  9655. if(dojo.config.isDebug){
  9656. var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
  9657. caller = (arguments.callee.caller || "unknown caller").toString();
  9658. if(!alreadyCalledHash[caller]){
  9659. dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
  9660. caller, "", "2.0");
  9661. alreadyCalledHash[caller] = true;
  9662. }
  9663. }
  9664. var args = arguments.length;
  9665. if(args >= 2 || typeof name === "object"){ // setter
  9666. return this.set.apply(this, arguments);
  9667. }else{ // getter
  9668. return this.get(name);
  9669. }
  9670. },
  9671. ////////////////// ONDIJITCLICK SUPPORT ///////////////////
  9672. // nodesWithKeyClick: [private] String[]
  9673. // List of nodes that correctly handle click events via native browser support,
  9674. // and don't need dijit's help
  9675. nodesWithKeyClick: ["input", "button"],
  9676. connect: function(
  9677. /*Object|null*/ obj,
  9678. /*String|Function*/ event,
  9679. /*String|Function*/ method){
  9680. // summary:
  9681. // Connects specified obj/event to specified method of this object
  9682. // and registers for disconnect() on widget destroy.
  9683. // description:
  9684. // Provide widget-specific analog to dojo.connect, except with the
  9685. // implicit use of this widget as the target object.
  9686. // This version of connect also provides a special "ondijitclick"
  9687. // event which triggers on a click or space or enter keyup.
  9688. // Events connected with `this.connect` are disconnected upon
  9689. // destruction.
  9690. // returns:
  9691. // A handle that can be passed to `disconnect` in order to disconnect before
  9692. // the widget is destroyed.
  9693. // example:
  9694. // | var btn = new dijit.form.Button();
  9695. // | // when foo.bar() is called, call the listener we're going to
  9696. // | // provide in the scope of btn
  9697. // | btn.connect(foo, "bar", function(){
  9698. // | console.debug(this.toString());
  9699. // | });
  9700. // tags:
  9701. // protected
  9702. var d = dojo,
  9703. dc = d._connect,
  9704. handles = this.inherited(arguments, [obj, event == "ondijitclick" ? "onclick" : event, method]);
  9705. if(event == "ondijitclick"){
  9706. // add key based click activation for unsupported nodes.
  9707. // do all processing onkey up to prevent spurious clicks
  9708. // for details see comments at top of this file where _lastKeyDownNode is defined
  9709. if(d.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button
  9710. var m = d.hitch(this, method);
  9711. handles.push(
  9712. dc(obj, "onkeydown", this, function(e){
  9713. //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
  9714. if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
  9715. !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
  9716. // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
  9717. dijit._lastKeyDownNode = e.target;
  9718. // Stop event to prevent scrolling on space key in IE.
  9719. // But don't do this for _HasDropDown because it surpresses the onkeypress
  9720. // event needed to open the drop down when the user presses the SPACE key.
  9721. if(!("openDropDown" in this && obj == this._buttonNode)){
  9722. e.preventDefault();
  9723. }
  9724. }
  9725. }),
  9726. dc(obj, "onkeyup", this, function(e){
  9727. //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
  9728. if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
  9729. e.target == dijit._lastKeyDownNode && // === breaks greasemonkey
  9730. !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
  9731. //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
  9732. dijit._lastKeyDownNode = null;
  9733. return m(e);
  9734. }
  9735. })
  9736. );
  9737. }
  9738. }
  9739. return handles; // _Widget.Handle
  9740. },
  9741. ////////////////// MISCELLANEOUS METHODS ///////////////////
  9742. _onShow: function(){
  9743. // summary:
  9744. // Internal method called when this widget is made visible.
  9745. // See `onShow` for details.
  9746. this.onShow();
  9747. },
  9748. onShow: function(){
  9749. // summary:
  9750. // Called when this widget becomes the selected pane in a
  9751. // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
  9752. // `dijit.layout.AccordionContainer`, etc.
  9753. //
  9754. // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
  9755. // tags:
  9756. // callback
  9757. },
  9758. onHide: function(){
  9759. // summary:
  9760. // Called when another widget becomes the selected pane in a
  9761. // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
  9762. // `dijit.layout.AccordionContainer`, etc.
  9763. //
  9764. // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
  9765. // tags:
  9766. // callback
  9767. },
  9768. onClose: function(){
  9769. // summary:
  9770. // Called when this widget is being displayed as a popup (ex: a Calendar popped
  9771. // up from a DateTextBox), and it is hidden.
  9772. // This is called from the dijit.popup code, and should not be called directly.
  9773. //
  9774. // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
  9775. // Callback if a user tries to close the child. Child will be closed if this function returns true.
  9776. // tags:
  9777. // extension
  9778. return true; // Boolean
  9779. }
  9780. });
  9781. })();
  9782. }
  9783. if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9784. dojo._hasResource["dojo.cache"] = true;
  9785. dojo.provide("dojo.cache");
  9786. /*=====
  9787. dojo.cache = {
  9788. // summary:
  9789. // A way to cache string content that is fetchable via `dojo.moduleUrl`.
  9790. };
  9791. =====*/
  9792. var cache = {};
  9793. dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
  9794. // summary:
  9795. // A getter and setter for storing the string content associated with the
  9796. // module and url arguments.
  9797. // description:
  9798. // module and url are used to call `dojo.moduleUrl()` to generate a module URL.
  9799. // If value is specified, the cache value for the moduleUrl will be set to
  9800. // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
  9801. // in its internal cache and return that cached value for the URL. To clear
  9802. // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
  9803. // the URL contents, only modules on the same domain of the page can use this capability.
  9804. // The build system can inline the cache values though, to allow for xdomain hosting.
  9805. // module: String||Object
  9806. // If a String, the module name to use for the base part of the URL, similar to module argument
  9807. // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
  9808. // generates a valid path for the cache item. For example, a dojo._Url object.
  9809. // url: String
  9810. // The rest of the path to append to the path derived from the module argument. If
  9811. // module is an object, then this second argument should be the "value" argument instead.
  9812. // value: String||Object?
  9813. // If a String, the value to use in the cache for the module/url combination.
  9814. // If an Object, it can have two properties: value and sanitize. The value property
  9815. // should be the value to use in the cache, and sanitize can be set to true or false,
  9816. // to indicate if XML declarations should be removed from the value and if the HTML
  9817. // inside a body tag in the value should be extracted as the real value. The value argument
  9818. // or the value property on the value argument are usually only used by the build system
  9819. // as it inlines cache content.
  9820. // example:
  9821. // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
  9822. // of call is used to avoid an issue with the build system erroneously trying to intern
  9823. // this example. To get the build system to intern your dojo.cache calls, use the
  9824. // "dojo.cache" style of call):
  9825. // | //If template.html contains "<h1>Hello</h1>" that will be
  9826. // | //the value for the text variable.
  9827. // | var text = dojo["cache"]("my.module", "template.html");
  9828. // example:
  9829. // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
  9830. // (the dojo["cache"] style of call is used to avoid an issue with the build system
  9831. // erroneously trying to intern this example. To get the build system to intern your
  9832. // dojo.cache calls, use the "dojo.cache" style of call):
  9833. // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
  9834. // | //text variable will contain just "<h1>Hello</h1>".
  9835. // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
  9836. // example:
  9837. // Same example as previous, but demostrates how an object can be passed in as
  9838. // the first argument, then the value argument can then be the second argument.
  9839. // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
  9840. // | //text variable will contain just "<h1>Hello</h1>".
  9841. // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
  9842. //Module could be a string, or an object that has a toString() method
  9843. //that will return a useful path. If it is an object, then the "url" argument
  9844. //will actually be the value argument.
  9845. if(typeof module == "string"){
  9846. var pathObj = dojo.moduleUrl(module, url);
  9847. }else{
  9848. pathObj = module;
  9849. value = url;
  9850. }
  9851. var key = pathObj.toString();
  9852. var val = value;
  9853. if(value != undefined && !dojo.isString(value)){
  9854. val = ("value" in value ? value.value : undefined);
  9855. }
  9856. var sanitize = value && value.sanitize ? true : false;
  9857. if(typeof val == "string"){
  9858. //We have a string, set cache value
  9859. val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
  9860. }else if(val === null){
  9861. //Remove cached value
  9862. delete cache[key];
  9863. }else{
  9864. //Allow cache values to be empty strings. If key property does
  9865. //not exist, fetch it.
  9866. if(!(key in cache)){
  9867. val = dojo._getText(key);
  9868. cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
  9869. }
  9870. val = cache[key];
  9871. }
  9872. return val; //String
  9873. };
  9874. dojo.cache._sanitize = function(/*String*/val){
  9875. // summary:
  9876. // Strips <?xml ...?> declarations so that external SVG and XML
  9877. // documents can be added to a document without worry. Also, if the string
  9878. // is an HTML document, only the part inside the body tag is returned.
  9879. // description:
  9880. // Copied from dijit._Templated._sanitizeTemplateString.
  9881. if(val){
  9882. val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
  9883. var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
  9884. if(matches){
  9885. val = matches[1];
  9886. }
  9887. }else{
  9888. val = "";
  9889. }
  9890. return val; //String
  9891. };
  9892. }
  9893. if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  9894. dojo._hasResource["dijit._Templated"] = true;
  9895. dojo.provide("dijit._Templated");
  9896. dojo.declare("dijit._Templated",
  9897. null,
  9898. {
  9899. // summary:
  9900. // Mixin for widgets that are instantiated from a template
  9901. // templateString: [protected] String
  9902. // A string that represents the widget template. Pre-empts the
  9903. // templatePath. In builds that have their strings "interned", the
  9904. // templatePath is converted to an inline templateString, thereby
  9905. // preventing a synchronous network call.
  9906. //
  9907. // Use in conjunction with dojo.cache() to load from a file.
  9908. templateString: null,
  9909. // templatePath: [protected deprecated] String
  9910. // Path to template (HTML file) for this widget relative to dojo.baseUrl.
  9911. // Deprecated: use templateString with dojo.cache() instead.
  9912. templatePath: null,
  9913. // widgetsInTemplate: [protected] Boolean
  9914. // Should we parse the template to find widgets that might be
  9915. // declared in markup inside it? False by default.
  9916. widgetsInTemplate: false,
  9917. // skipNodeCache: [protected] Boolean
  9918. // If using a cached widget template node poses issues for a
  9919. // particular widget class, it can set this property to ensure
  9920. // that its template is always re-built from a string
  9921. _skipNodeCache: false,
  9922. // _earlyTemplatedStartup: Boolean
  9923. // A fallback to preserve the 1.0 - 1.3 behavior of children in
  9924. // templates having their startup called before the parent widget
  9925. // fires postCreate. Defaults to 'false', causing child widgets to
  9926. // have their .startup() called immediately before a parent widget
  9927. // .startup(), but always after the parent .postCreate(). Set to
  9928. // 'true' to re-enable to previous, arguably broken, behavior.
  9929. _earlyTemplatedStartup: false,
  9930. /*=====
  9931. // _attachPoints: [private] String[]
  9932. // List of widget attribute names associated with dojoAttachPoint=... in the
  9933. // template, ex: ["containerNode", "labelNode"]
  9934. _attachPoints: [],
  9935. =====*/
  9936. /*=====
  9937. // _attachEvents: [private] Handle[]
  9938. // List of connections associated with dojoAttachEvent=... in the
  9939. // template
  9940. _attachEvents: [],
  9941. =====*/
  9942. constructor: function(){
  9943. this._attachPoints = [];
  9944. this._attachEvents = [];
  9945. },
  9946. _stringRepl: function(tmpl){
  9947. // summary:
  9948. // Does substitution of ${foo} type properties in template string
  9949. // tags:
  9950. // private
  9951. var className = this.declaredClass, _this = this;
  9952. // Cache contains a string because we need to do property replacement
  9953. // do the property replacement
  9954. return dojo.string.substitute(tmpl, this, function(value, key){
  9955. if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); }
  9956. if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
  9957. if(value == null){ return ""; }
  9958. // Substitution keys beginning with ! will skip the transform step,
  9959. // in case a user wishes to insert unescaped markup, e.g. ${!foo}
  9960. return key.charAt(0) == "!" ? value :
  9961. // Safer substitution, see heading "Attribute values" in
  9962. // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
  9963. value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
  9964. }, this);
  9965. },
  9966. buildRendering: function(){
  9967. // summary:
  9968. // Construct the UI for this widget from a template, setting this.domNode.
  9969. // tags:
  9970. // protected
  9971. // Lookup cached version of template, and download to cache if it
  9972. // isn't there already. Returns either a DomNode or a string, depending on
  9973. // whether or not the template contains ${foo} replacement parameters.
  9974. var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
  9975. var node;
  9976. if(dojo.isString(cached)){
  9977. node = dojo._toDom(this._stringRepl(cached));
  9978. if(node.nodeType != 1){
  9979. // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
  9980. throw new Error("Invalid template: " + cached);
  9981. }
  9982. }else{
  9983. // if it's a node, all we have to do is clone it
  9984. node = cached.cloneNode(true);
  9985. }
  9986. this.domNode = node;
  9987. // Call down to _Widget.buildRendering() to get base classes assigned
  9988. // TODO: change the baseClass assignment to attributeMap
  9989. this.inherited(arguments);
  9990. // recurse through the node, looking for, and attaching to, our
  9991. // attachment points and events, which should be defined on the template node.
  9992. this._attachTemplateNodes(node);
  9993. if(this.widgetsInTemplate){
  9994. // Store widgets that we need to start at a later point in time
  9995. var cw = (this._startupWidgets = dojo.parser.parse(node, {
  9996. noStart: !this._earlyTemplatedStartup,
  9997. template: true,
  9998. inherited: {dir: this.dir, lang: this.lang},
  9999. propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
  10000. scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
  10001. }));
  10002. this._supportingWidgets = dijit.findWidgets(node);
  10003. this._attachTemplateNodes(cw, function(n,p){
  10004. return n[p];
  10005. });
  10006. }
  10007. this._fillContent(this.srcNodeRef);
  10008. },
  10009. _fillContent: function(/*DomNode*/ source){
  10010. // summary:
  10011. // Relocate source contents to templated container node.
  10012. // this.containerNode must be able to receive children, or exceptions will be thrown.
  10013. // tags:
  10014. // protected
  10015. var dest = this.containerNode;
  10016. if(source && dest){
  10017. while(source.hasChildNodes()){
  10018. dest.appendChild(source.firstChild);
  10019. }
  10020. }
  10021. },
  10022. _attachTemplateNodes: function(rootNode, getAttrFunc){
  10023. // summary:
  10024. // Iterate through the template and attach functions and nodes accordingly.
  10025. // Alternately, if rootNode is an array of widgets, then will process dojoAttachPoint
  10026. // etc. for those widgets.
  10027. // description:
  10028. // Map widget properties and functions to the handlers specified in
  10029. // the dom node and it's descendants. This function iterates over all
  10030. // nodes and looks for these properties:
  10031. // * dojoAttachPoint
  10032. // * dojoAttachEvent
  10033. // * waiRole
  10034. // * waiState
  10035. // rootNode: DomNode|Array[Widgets]
  10036. // the node to search for properties. All children will be searched.
  10037. // getAttrFunc: Function?
  10038. // a function which will be used to obtain property for a given
  10039. // DomNode/Widget
  10040. // tags:
  10041. // private
  10042. getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
  10043. var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
  10044. var x = dojo.isArray(rootNode) ? 0 : -1;
  10045. for(; x<nodes.length; x++){
  10046. var baseNode = (x == -1) ? rootNode : nodes[x];
  10047. if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
  10048. continue;
  10049. }
  10050. // Process dojoAttachPoint
  10051. var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
  10052. if(attachPoint){
  10053. var point, points = attachPoint.split(/\s*,\s*/);
  10054. while((point = points.shift())){
  10055. if(dojo.isArray(this[point])){
  10056. this[point].push(baseNode);
  10057. }else{
  10058. this[point]=baseNode;
  10059. }
  10060. this._attachPoints.push(point);
  10061. }
  10062. }
  10063. // Process dojoAttachEvent
  10064. var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");;
  10065. if(attachEvent){
  10066. // NOTE: we want to support attributes that have the form
  10067. // "domEvent: nativeEvent; ..."
  10068. var event, events = attachEvent.split(/\s*,\s*/);
  10069. var trim = dojo.trim;
  10070. while((event = events.shift())){
  10071. if(event){
  10072. var thisFunc = null;
  10073. if(event.indexOf(":") != -1){
  10074. // oh, if only JS had tuple assignment
  10075. var funcNameArr = event.split(":");
  10076. event = trim(funcNameArr[0]);
  10077. thisFunc = trim(funcNameArr[1]);
  10078. }else{
  10079. event = trim(event);
  10080. }
  10081. if(!thisFunc){
  10082. thisFunc = event;
  10083. }
  10084. this._attachEvents.push(this.connect(baseNode, event, thisFunc));
  10085. }
  10086. }
  10087. }
  10088. // waiRole, waiState
  10089. // TODO: remove this in 2.0, templates are now using role=... and aria-XXX=... attributes directicly
  10090. var role = getAttrFunc(baseNode, "waiRole");
  10091. if(role){
  10092. dijit.setWaiRole(baseNode, role);
  10093. }
  10094. var values = getAttrFunc(baseNode, "waiState");
  10095. if(values){
  10096. dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
  10097. if(stateValue.indexOf('-') != -1){
  10098. var pair = stateValue.split('-');
  10099. dijit.setWaiState(baseNode, pair[0], pair[1]);
  10100. }
  10101. });
  10102. }
  10103. }
  10104. },
  10105. startup: function(){
  10106. dojo.forEach(this._startupWidgets, function(w){
  10107. if(w && !w._started && w.startup){
  10108. w.startup();
  10109. }
  10110. });
  10111. this.inherited(arguments);
  10112. },
  10113. destroyRendering: function(){
  10114. // Delete all attach points to prevent IE6 memory leaks.
  10115. dojo.forEach(this._attachPoints, function(point){
  10116. delete this[point];
  10117. }, this);
  10118. this._attachPoints = [];
  10119. // And same for event handlers
  10120. dojo.forEach(this._attachEvents, this.disconnect, this);
  10121. this._attachEvents = [];
  10122. this.inherited(arguments);
  10123. }
  10124. }
  10125. );
  10126. // key is either templatePath or templateString; object is either string or DOM tree
  10127. dijit._Templated._templateCache = {};
  10128. dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
  10129. // summary:
  10130. // Static method to get a template based on the templatePath or
  10131. // templateString key
  10132. // templatePath: String||dojo.uri.Uri
  10133. // The URL to get the template from.
  10134. // templateString: String?
  10135. // a string to use in lieu of fetching the template from a URL. Takes precedence
  10136. // over templatePath
  10137. // returns: Mixed
  10138. // Either string (if there are ${} variables that need to be replaced) or just
  10139. // a DOM tree (if the node can be cloned directly)
  10140. // is it already cached?
  10141. var tmplts = dijit._Templated._templateCache;
  10142. var key = templateString || templatePath;
  10143. var cached = tmplts[key];
  10144. if(cached){
  10145. try{
  10146. // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value
  10147. if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
  10148. // string or node of the same document
  10149. return cached;
  10150. }
  10151. }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
  10152. dojo.destroy(cached);
  10153. }
  10154. // If necessary, load template string from template path
  10155. if(!templateString){
  10156. templateString = dojo.cache(templatePath, {sanitize: true});
  10157. }
  10158. templateString = dojo.string.trim(templateString);
  10159. if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
  10160. // there are variables in the template so all we can do is cache the string
  10161. return (tmplts[key] = templateString); //String
  10162. }else{
  10163. // there are no variables in the template so we can cache the DOM tree
  10164. var node = dojo._toDom(templateString);
  10165. if(node.nodeType != 1){
  10166. throw new Error("Invalid template: " + templateString);
  10167. }
  10168. return (tmplts[key] = node); //Node
  10169. }
  10170. };
  10171. if(dojo.isIE){
  10172. dojo.addOnWindowUnload(function(){
  10173. var cache = dijit._Templated._templateCache;
  10174. for(var key in cache){
  10175. var value = cache[key];
  10176. if(typeof value == "object"){ // value is either a string or a DOM node template
  10177. dojo.destroy(value);
  10178. }
  10179. delete cache[key];
  10180. }
  10181. });
  10182. }
  10183. // These arguments can be specified for widgets which are used in templates.
  10184. // Since any widget can be specified as sub widgets in template, mix it
  10185. // into the base widget class. (This is a hack, but it's effective.)
  10186. dojo.extend(dijit._Widget,{
  10187. dojoAttachEvent: "",
  10188. dojoAttachPoint: "",
  10189. waiRole: "",
  10190. waiState:""
  10191. });
  10192. }
  10193. if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10194. dojo._hasResource["dijit._CssStateMixin"] = true;
  10195. dojo.provide("dijit._CssStateMixin");
  10196. dojo.declare("dijit._CssStateMixin", [], {
  10197. // summary:
  10198. // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
  10199. // state changes, and also higher-level state changes such becoming disabled or selected.
  10200. //
  10201. // description:
  10202. // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
  10203. // maintain CSS classes on the widget root node (this.domNode) depending on hover,
  10204. // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
  10205. // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
  10206. //
  10207. // It also sets CSS like dijitButtonDisabled based on widget semantic state.
  10208. //
  10209. // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
  10210. // within the widget).
  10211. // cssStateNodes: [protected] Object
  10212. // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
  10213. //.
  10214. // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
  10215. // (like "dijitUpArrowButton"). Example:
  10216. // | {
  10217. // | "upArrowButton": "dijitUpArrowButton",
  10218. // | "downArrowButton": "dijitDownArrowButton"
  10219. // | }
  10220. // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
  10221. // is hovered, etc.
  10222. cssStateNodes: {},
  10223. // hovering: [readonly] Boolean
  10224. // True if cursor is over this widget
  10225. hovering: false,
  10226. // active: [readonly] Boolean
  10227. // True if mouse was pressed while over this widget, and hasn't been released yet
  10228. active: false,
  10229. _applyAttributes: function(){
  10230. // This code would typically be in postCreate(), but putting in _applyAttributes() for
  10231. // performance: so the class changes happen before DOM is inserted into the document.
  10232. // Change back to postCreate() in 2.0. See #11635.
  10233. this.inherited(arguments);
  10234. // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
  10235. dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
  10236. this.connect(this.domNode, e, "_cssMouseEvent");
  10237. }, this);
  10238. // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
  10239. dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
  10240. this.watch(attr, dojo.hitch(this, "_setStateClass"));
  10241. }, this);
  10242. // Events on sub nodes within the widget
  10243. for(var ap in this.cssStateNodes){
  10244. this._trackMouseState(this[ap], this.cssStateNodes[ap]);
  10245. }
  10246. // Set state initially; there's probably no hover/active/focus state but widget might be
  10247. // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
  10248. this._setStateClass();
  10249. },
  10250. _cssMouseEvent: function(/*Event*/ event){
  10251. // summary:
  10252. // Sets hovering and active properties depending on mouse state,
  10253. // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
  10254. if(!this.disabled){
  10255. switch(event.type){
  10256. case "mouseenter":
  10257. case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
  10258. this._set("hovering", true);
  10259. this._set("active", this._mouseDown);
  10260. break;
  10261. case "mouseleave":
  10262. case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
  10263. this._set("hovering", false);
  10264. this._set("active", false);
  10265. break;
  10266. case "mousedown" :
  10267. this._set("active", true);
  10268. this._mouseDown = true;
  10269. // Set a global event to handle mouseup, so it fires properly
  10270. // even if the cursor leaves this.domNode before the mouse up event.
  10271. // Alternately could set active=false on mouseout.
  10272. var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
  10273. this._mouseDown = false;
  10274. this._set("active", false);
  10275. this.disconnect(mouseUpConnector);
  10276. });
  10277. break;
  10278. }
  10279. }
  10280. },
  10281. _setStateClass: function(){
  10282. // summary:
  10283. // Update the visual state of the widget by setting the css classes on this.domNode
  10284. // (or this.stateNode if defined) by combining this.baseClass with
  10285. // various suffixes that represent the current widget state(s).
  10286. //
  10287. // description:
  10288. // In the case where a widget has multiple
  10289. // states, it sets the class based on all possible
  10290. // combinations. For example, an invalid form widget that is being hovered
  10291. // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
  10292. //
  10293. // The widget may have one or more of the following states, determined
  10294. // by this.state, this.checked, this.valid, and this.selected:
  10295. // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
  10296. // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
  10297. // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
  10298. // - Selected - ex: currently selected tab will have this.selected==true
  10299. //
  10300. // In addition, it may have one or more of the following states,
  10301. // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
  10302. // - Disabled - if the widget is disabled
  10303. // - Active - if the mouse (or space/enter key?) is being pressed down
  10304. // - Focused - if the widget has focus
  10305. // - Hover - if the mouse is over the widget
  10306. // Compute new set of classes
  10307. var newStateClasses = this.baseClass.split(" ");
  10308. function multiply(modifier){
  10309. newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
  10310. }
  10311. if(!this.isLeftToRight()){
  10312. // For RTL mode we need to set an addition class like dijitTextBoxRtl.
  10313. multiply("Rtl");
  10314. }
  10315. if(this.checked){
  10316. multiply("Checked");
  10317. }
  10318. if(this.state){
  10319. multiply(this.state);
  10320. }
  10321. if(this.selected){
  10322. multiply("Selected");
  10323. }
  10324. if(this.disabled){
  10325. multiply("Disabled");
  10326. }else if(this.readOnly){
  10327. multiply("ReadOnly");
  10328. }else{
  10329. if(this.active){
  10330. multiply("Active");
  10331. }else if(this.hovering){
  10332. multiply("Hover");
  10333. }
  10334. }
  10335. if(this._focused){
  10336. multiply("Focused");
  10337. }
  10338. // Remove old state classes and add new ones.
  10339. // For performance concerns we only write into domNode.className once.
  10340. var tn = this.stateNode || this.domNode,
  10341. classHash = {}; // set of all classes (state and otherwise) for node
  10342. dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
  10343. if("_stateClasses" in this){
  10344. dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
  10345. }
  10346. dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
  10347. var newClasses = [];
  10348. for(var c in classHash){
  10349. newClasses.push(c);
  10350. }
  10351. tn.className = newClasses.join(" ");
  10352. this._stateClasses = newStateClasses;
  10353. },
  10354. _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
  10355. // summary:
  10356. // Track mouse/focus events on specified node and set CSS class on that node to indicate
  10357. // current state. Usually not called directly, but via cssStateNodes attribute.
  10358. // description:
  10359. // Given class=foo, will set the following CSS class on the node
  10360. // - fooActive: if the user is currently pressing down the mouse button while over the node
  10361. // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
  10362. // - fooFocus: if the node is focused
  10363. //
  10364. // Note that it won't set any classes if the widget is disabled.
  10365. // node: DomNode
  10366. // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
  10367. // is handled specially and automatically just by mixing in this class.
  10368. // clazz: String
  10369. // CSS class name (ex: dijitSliderUpArrow).
  10370. // Current state of node (initially false)
  10371. // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
  10372. var hovering=false, active=false, focused=false;
  10373. var self = this,
  10374. cn = dojo.hitch(this, "connect", node);
  10375. function setClass(){
  10376. var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
  10377. dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
  10378. dojo.toggleClass(node, clazz+"Active", active && !disabled);
  10379. dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
  10380. }
  10381. // Mouse
  10382. cn("onmouseenter", function(){
  10383. hovering = true;
  10384. setClass();
  10385. });
  10386. cn("onmouseleave", function(){
  10387. hovering = false;
  10388. active = false;
  10389. setClass();
  10390. });
  10391. cn("onmousedown", function(){
  10392. active = true;
  10393. setClass();
  10394. });
  10395. cn("onmouseup", function(){
  10396. active = false;
  10397. setClass();
  10398. });
  10399. // Focus
  10400. cn("onfocus", function(){
  10401. focused = true;
  10402. setClass();
  10403. });
  10404. cn("onblur", function(){
  10405. focused = false;
  10406. setClass();
  10407. });
  10408. // Just in case widget is enabled/disabled while it has focus/hover/active state.
  10409. // Maybe this is overkill.
  10410. this.watch("disabled", setClass);
  10411. this.watch("readOnly", setClass);
  10412. }
  10413. });
  10414. }
  10415. if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10416. dojo._hasResource["dijit.form._FormWidget"] = true;
  10417. dojo.provide("dijit.form._FormWidget");
  10418. dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  10419. {
  10420. // summary:
  10421. // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
  10422. // which can be children of a <form> node or a `dijit.form.Form` widget.
  10423. //
  10424. // description:
  10425. // Represents a single HTML element.
  10426. // All these widgets should have these attributes just like native HTML input elements.
  10427. // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
  10428. //
  10429. // They also share some common methods.
  10430. // name: [const] String
  10431. // Name used when submitting form; same as "name" attribute or plain HTML elements
  10432. name: "",
  10433. // alt: String
  10434. // Corresponds to the native HTML <input> element's attribute.
  10435. alt: "",
  10436. // value: String
  10437. // Corresponds to the native HTML <input> element's attribute.
  10438. value: "",
  10439. // type: String
  10440. // Corresponds to the native HTML <input> element's attribute.
  10441. type: "text",
  10442. // tabIndex: Integer
  10443. // Order fields are traversed when user hits the tab key
  10444. tabIndex: "0",
  10445. // disabled: Boolean
  10446. // Should this widget respond to user input?
  10447. // In markup, this is specified as "disabled='disabled'", or just "disabled".
  10448. disabled: false,
  10449. // intermediateChanges: Boolean
  10450. // Fires onChange for each value change or only on demand
  10451. intermediateChanges: false,
  10452. // scrollOnFocus: Boolean
  10453. // On focus, should this widget scroll into view?
  10454. scrollOnFocus: true,
  10455. // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
  10456. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  10457. value: "focusNode",
  10458. id: "focusNode",
  10459. tabIndex: "focusNode",
  10460. alt: "focusNode",
  10461. title: "focusNode"
  10462. }),
  10463. postMixInProperties: function(){
  10464. // Setup name=foo string to be referenced from the template (but only if a name has been specified)
  10465. // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
  10466. // Regarding escaping, see heading "Attribute values" in
  10467. // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
  10468. this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
  10469. this.inherited(arguments);
  10470. },
  10471. postCreate: function(){
  10472. this.inherited(arguments);
  10473. this.connect(this.domNode, "onmousedown", "_onMouseDown");
  10474. },
  10475. _setDisabledAttr: function(/*Boolean*/ value){
  10476. this._set("disabled", value);
  10477. dojo.attr(this.focusNode, 'disabled', value);
  10478. if(this.valueNode){
  10479. dojo.attr(this.valueNode, 'disabled', value);
  10480. }
  10481. dijit.setWaiState(this.focusNode, "disabled", value);
  10482. if(value){
  10483. // reset these, because after the domNode is disabled, we can no longer receive
  10484. // mouse related events, see #4200
  10485. this._set("hovering", false);
  10486. this._set("active", false);
  10487. // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
  10488. var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
  10489. dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
  10490. var node = this[attachPointName];
  10491. // complex code because tabIndex=-1 on a <div> doesn't work on FF
  10492. if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug
  10493. node.setAttribute('tabIndex', "-1");
  10494. }else{
  10495. node.removeAttribute('tabIndex');
  10496. }
  10497. }, this);
  10498. }else{
  10499. if(this.tabIndex != ""){
  10500. this.focusNode.setAttribute('tabIndex', this.tabIndex);
  10501. }
  10502. }
  10503. },
  10504. setDisabled: function(/*Boolean*/ disabled){
  10505. // summary:
  10506. // Deprecated. Use set('disabled', ...) instead.
  10507. dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
  10508. this.set('disabled', disabled);
  10509. },
  10510. _onFocus: function(e){
  10511. if(this.scrollOnFocus){
  10512. dojo.window.scrollIntoView(this.domNode);
  10513. }
  10514. this.inherited(arguments);
  10515. },
  10516. isFocusable: function(){
  10517. // summary:
  10518. // Tells if this widget is focusable or not. Used internally by dijit.
  10519. // tags:
  10520. // protected
  10521. return !this.disabled && this.focusNode && (dojo.style(this.domNode, "display") != "none");
  10522. },
  10523. focus: function(){
  10524. // summary:
  10525. // Put focus on this widget
  10526. if(!this.disabled){
  10527. dijit.focus(this.focusNode);
  10528. }
  10529. },
  10530. compare: function(/*anything*/ val1, /*anything*/ val2){
  10531. // summary:
  10532. // Compare 2 values (as returned by get('value') for this widget).
  10533. // tags:
  10534. // protected
  10535. if(typeof val1 == "number" && typeof val2 == "number"){
  10536. return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
  10537. }else if(val1 > val2){
  10538. return 1;
  10539. }else if(val1 < val2){
  10540. return -1;
  10541. }else{
  10542. return 0;
  10543. }
  10544. },
  10545. onChange: function(newValue){
  10546. // summary:
  10547. // Callback when this widget's value is changed.
  10548. // tags:
  10549. // callback
  10550. },
  10551. // _onChangeActive: [private] Boolean
  10552. // Indicates that changes to the value should call onChange() callback.
  10553. // This is false during widget initialization, to avoid calling onChange()
  10554. // when the initial value is set.
  10555. _onChangeActive: false,
  10556. _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  10557. // summary:
  10558. // Called when the value of the widget is set. Calls onChange() if appropriate
  10559. // newValue:
  10560. // the new value
  10561. // priorityChange:
  10562. // For a slider, for example, dragging the slider is priorityChange==false,
  10563. // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
  10564. // onChange is only called form priorityChange=true events.
  10565. // tags:
  10566. // private
  10567. if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
  10568. // this block executes not for a change, but during initialization,
  10569. // and is used to store away the original value (or for ToggleButton, the original checked state)
  10570. this._resetValue = this._lastValueReported = newValue;
  10571. }
  10572. this._pendingOnChange = this._pendingOnChange
  10573. || (typeof newValue != typeof this._lastValueReported)
  10574. || (this.compare(newValue, this._lastValueReported) != 0);
  10575. if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
  10576. this._lastValueReported = newValue;
  10577. this._pendingOnChange = false;
  10578. if(this._onChangeActive){
  10579. if(this._onChangeHandle){
  10580. clearTimeout(this._onChangeHandle);
  10581. }
  10582. // setTimout allows hidden value processing to run and
  10583. // also the onChange handler can safely adjust focus, etc
  10584. this._onChangeHandle = setTimeout(dojo.hitch(this,
  10585. function(){
  10586. this._onChangeHandle = null;
  10587. this.onChange(newValue);
  10588. }), 0); // try to collapse multiple onChange's fired faster than can be processed
  10589. }
  10590. }
  10591. },
  10592. create: function(){
  10593. // Overrides _Widget.create()
  10594. this.inherited(arguments);
  10595. this._onChangeActive = true;
  10596. },
  10597. destroy: function(){
  10598. if(this._onChangeHandle){ // destroy called before last onChange has fired
  10599. clearTimeout(this._onChangeHandle);
  10600. this.onChange(this._lastValueReported);
  10601. }
  10602. this.inherited(arguments);
  10603. },
  10604. setValue: function(/*String*/ value){
  10605. // summary:
  10606. // Deprecated. Use set('value', ...) instead.
  10607. dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
  10608. this.set('value', value);
  10609. },
  10610. getValue: function(){
  10611. // summary:
  10612. // Deprecated. Use get('value') instead.
  10613. dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
  10614. return this.get('value');
  10615. },
  10616. _onMouseDown: function(e){
  10617. // If user clicks on the button, even if the mouse is released outside of it,
  10618. // this button should get focus (to mimics native browser buttons).
  10619. // This is also needed on chrome because otherwise buttons won't get focus at all,
  10620. // which leads to bizarre focus restore on Dialog close etc.
  10621. if(!e.ctrlKey && dojo.mouseButtons.isLeft(e) && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
  10622. // Set a global event to handle mouseup, so it fires properly
  10623. // even if the cursor leaves this.domNode before the mouse up event.
  10624. var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
  10625. if (this.isFocusable()) {
  10626. this.focus();
  10627. }
  10628. this.disconnect(mouseUpConnector);
  10629. });
  10630. }
  10631. }
  10632. });
  10633. dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
  10634. {
  10635. // summary:
  10636. // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
  10637. // description:
  10638. // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
  10639. // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
  10640. // works as expected.
  10641. // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
  10642. // directly in the template as read by the parser in order to function. IE is known to specifically
  10643. // require the 'name' attribute at element creation time. See #8484, #8660.
  10644. // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
  10645. // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
  10646. // Seems like we really want value removed from attributeMap altogether
  10647. // (although there's no easy way to do that now)
  10648. // readOnly: Boolean
  10649. // Should this widget respond to user input?
  10650. // In markup, this is specified as "readOnly".
  10651. // Similar to disabled except readOnly form values are submitted.
  10652. readOnly: false,
  10653. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  10654. value: "",
  10655. readOnly: "focusNode"
  10656. }),
  10657. _setReadOnlyAttr: function(/*Boolean*/ value){
  10658. dojo.attr(this.focusNode, 'readOnly', value);
  10659. dijit.setWaiState(this.focusNode, "readonly", value);
  10660. this._set("readOnly", value);
  10661. },
  10662. postCreate: function(){
  10663. this.inherited(arguments);
  10664. if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ // IE won't stop the event with keypress
  10665. this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
  10666. }
  10667. // Update our reset value if it hasn't yet been set (because this.set()
  10668. // is only called when there *is* a value)
  10669. if(this._resetValue === undefined){
  10670. this._lastValueReported = this._resetValue = this.value;
  10671. }
  10672. },
  10673. _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  10674. // summary:
  10675. // Hook so set('value', value) works.
  10676. // description:
  10677. // Sets the value of the widget.
  10678. // If the value has changed, then fire onChange event, unless priorityChange
  10679. // is specified as null (or false?)
  10680. this._handleOnChange(newValue, priorityChange);
  10681. },
  10682. _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  10683. // summary:
  10684. // Called when the value of the widget has changed. Saves the new value in this.value,
  10685. // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
  10686. this._set("value", newValue);
  10687. this.inherited(arguments);
  10688. },
  10689. undo: function(){
  10690. // summary:
  10691. // Restore the value to the last value passed to onChange
  10692. this._setValueAttr(this._lastValueReported, false);
  10693. },
  10694. reset: function(){
  10695. // summary:
  10696. // Reset the widget's value to what it was at initialization time
  10697. this._hasBeenBlurred = false;
  10698. this._setValueAttr(this._resetValue, true);
  10699. },
  10700. _onKeyDown: function(e){
  10701. if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
  10702. var te;
  10703. if(dojo.isIE){
  10704. e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
  10705. te = document.createEventObject();
  10706. te.keyCode = dojo.keys.ESCAPE;
  10707. te.shiftKey = e.shiftKey;
  10708. e.srcElement.fireEvent('onkeypress', te);
  10709. }
  10710. }
  10711. },
  10712. _layoutHackIE7: function(){
  10713. // summary:
  10714. // Work around table sizing bugs on IE7 by forcing redraw
  10715. if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
  10716. var domNode = this.domNode;
  10717. var parent = domNode.parentNode;
  10718. var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
  10719. var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
  10720. var _this = this;
  10721. while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
  10722. (function ping(){
  10723. var disconnectHandle = _this.connect(parent, "onscroll",
  10724. function(e){
  10725. _this.disconnect(disconnectHandle); // only call once
  10726. pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
  10727. setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
  10728. }
  10729. );
  10730. })();
  10731. parent = parent.parentNode;
  10732. }
  10733. }
  10734. }
  10735. });
  10736. }
  10737. if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  10738. dojo._hasResource["dijit._HasDropDown"] = true;
  10739. dojo.provide("dijit._HasDropDown");
  10740. dojo.declare("dijit._HasDropDown",
  10741. null,
  10742. {
  10743. // summary:
  10744. // Mixin for widgets that need drop down ability.
  10745. // _buttonNode: [protected] DomNode
  10746. // The button/icon/node to click to display the drop down.
  10747. // Can be set via a dojoAttachPoint assignment.
  10748. // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
  10749. _buttonNode: null,
  10750. // _arrowWrapperNode: [protected] DomNode
  10751. // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
  10752. // on where the drop down is set to be positioned.
  10753. // Can be set via a dojoAttachPoint assignment.
  10754. // If missing, then _buttonNode will be used.
  10755. _arrowWrapperNode: null,
  10756. // _popupStateNode: [protected] DomNode
  10757. // The node to set the popupActive class on.
  10758. // Can be set via a dojoAttachPoint assignment.
  10759. // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
  10760. _popupStateNode: null,
  10761. // _aroundNode: [protected] DomNode
  10762. // The node to display the popup around.
  10763. // Can be set via a dojoAttachPoint assignment.
  10764. // If missing, then domNode will be used.
  10765. _aroundNode: null,
  10766. // dropDown: [protected] Widget
  10767. // The widget to display as a popup. This widget *must* be
  10768. // defined before the startup function is called.
  10769. dropDown: null,
  10770. // autoWidth: [protected] Boolean
  10771. // Set to true to make the drop down at least as wide as this
  10772. // widget. Set to false if the drop down should just be its
  10773. // default width
  10774. autoWidth: true,
  10775. // forceWidth: [protected] Boolean
  10776. // Set to true to make the drop down exactly as wide as this
  10777. // widget. Overrides autoWidth.
  10778. forceWidth: false,
  10779. // maxHeight: [protected] Integer
  10780. // The max height for our dropdown.
  10781. // Any dropdown taller than this will have scrollbars.
  10782. // Set to 0 for no max height, or -1 to limit height to available space in viewport
  10783. maxHeight: 0,
  10784. // dropDownPosition: [const] String[]
  10785. // This variable controls the position of the drop down.
  10786. // It's an array of strings with the following values:
  10787. //
  10788. // * before: places drop down to the left of the target node/widget, or to the right in
  10789. // the case of RTL scripts like Hebrew and Arabic
  10790. // * after: places drop down to the right of the target node/widget, or to the left in
  10791. // the case of RTL scripts like Hebrew and Arabic
  10792. // * above: drop down goes above target node
  10793. // * below: drop down goes below target node
  10794. //
  10795. // The list is positions is tried, in order, until a position is found where the drop down fits
  10796. // within the viewport.
  10797. //
  10798. dropDownPosition: ["below","above"],
  10799. // _stopClickEvents: Boolean
  10800. // When set to false, the click events will not be stopped, in
  10801. // case you want to use them in your subwidget
  10802. _stopClickEvents: true,
  10803. _onDropDownMouseDown: function(/*Event*/ e){
  10804. // summary:
  10805. // Callback when the user mousedown's on the arrow icon
  10806. if(this.disabled || this.readOnly){ return; }
  10807. // Prevent default to stop things like text selection, but don't stop propogation, so that:
  10808. // 1. TimeTextBox etc. can focusthe <input> on mousedown
  10809. // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
  10810. // 3. user defined onMouseDown handler fires
  10811. e.preventDefault();
  10812. this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
  10813. this.toggleDropDown();
  10814. },
  10815. _onDropDownMouseUp: function(/*Event?*/ e){
  10816. // summary:
  10817. // Callback when the user lifts their mouse after mouse down on the arrow icon.
  10818. // If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
  10819. // dropDown node. If the event is missing, then we are not
  10820. // a mouseup event.
  10821. //
  10822. // This is useful for the common mouse movement pattern
  10823. // with native browser <select> nodes:
  10824. // 1. mouse down on the select node (probably on the arrow)
  10825. // 2. move mouse to a menu item while holding down the mouse button
  10826. // 3. mouse up. this selects the menu item as though the user had clicked it.
  10827. if(e && this._docHandler){
  10828. this.disconnect(this._docHandler);
  10829. }
  10830. var dropDown = this.dropDown, overMenu = false;
  10831. if(e && this._opened){
  10832. // This code deals with the corner-case when the drop down covers the original widget,
  10833. // because it's so large. In that case mouse-up shouldn't select a value from the menu.
  10834. // Find out if our target is somewhere in our dropdown widget,
  10835. // but not over our _buttonNode (the clickable node)
  10836. var c = dojo.position(this._buttonNode, true);
  10837. if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
  10838. !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
  10839. var t = e.target;
  10840. while(t && !overMenu){
  10841. if(dojo.hasClass(t, "dijitPopup")){
  10842. overMenu = true;
  10843. }else{
  10844. t = t.parentNode;
  10845. }
  10846. }
  10847. if(overMenu){
  10848. t = e.target;
  10849. if(dropDown.onItemClick){
  10850. var menuItem;
  10851. while(t && !(menuItem = dijit.byNode(t))){
  10852. t = t.parentNode;
  10853. }
  10854. if(menuItem && menuItem.onClick && menuItem.getParent){
  10855. menuItem.getParent().onItemClick(menuItem, e);
  10856. }
  10857. }
  10858. return;
  10859. }
  10860. }
  10861. }
  10862. if(this._opened && dropDown.focus && dropDown.autoFocus !== false){
  10863. // Focus the dropdown widget - do it on a delay so that we
  10864. // don't steal our own focus.
  10865. window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
  10866. }
  10867. },
  10868. _onDropDownClick: function(/*Event*/ e){
  10869. // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
  10870. if(this._stopClickEvents){
  10871. dojo.stopEvent(e);
  10872. }
  10873. },
  10874. buildRendering: function(){
  10875. this.inherited(arguments);
  10876. this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
  10877. this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
  10878. // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
  10879. // based on where drop down will normally appear
  10880. var defaultPos = {
  10881. "after" : this.isLeftToRight() ? "Right" : "Left",
  10882. "before" : this.isLeftToRight() ? "Left" : "Right",
  10883. "above" : "Up",
  10884. "below" : "Down",
  10885. "left" : "Left",
  10886. "right" : "Right"
  10887. }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
  10888. dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
  10889. },
  10890. postCreate: function(){
  10891. // summary:
  10892. // set up nodes and connect our mouse and keypress events
  10893. this.inherited(arguments);
  10894. this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
  10895. this.connect(this._buttonNode, "onclick", "_onDropDownClick");
  10896. this.connect(this.focusNode, "onkeypress", "_onKey");
  10897. this.connect(this.focusNode, "onkeyup", "_onKeyUp");
  10898. },
  10899. destroy: function(){
  10900. if(this.dropDown){
  10901. // Destroy the drop down, unless it's already been destroyed. This can happen because
  10902. // the drop down is a direct child of <body> even though it's logically my child.
  10903. if(!this.dropDown._destroyed){
  10904. this.dropDown.destroyRecursive();
  10905. }
  10906. delete this.dropDown;
  10907. }
  10908. this.inherited(arguments);
  10909. },
  10910. _onKey: function(/*Event*/ e){
  10911. // summary:
  10912. // Callback when the user presses a key while focused on the button node
  10913. if(this.disabled || this.readOnly){ return; }
  10914. var d = this.dropDown, target = e.target;
  10915. if(d && this._opened && d.handleKey){
  10916. if(d.handleKey(e) === false){
  10917. /* false return code means that the drop down handled the key */
  10918. dojo.stopEvent(e);
  10919. return;
  10920. }
  10921. }
  10922. if(d && this._opened && e.charOrCode == dojo.keys.ESCAPE){
  10923. this.closeDropDown();
  10924. dojo.stopEvent(e);
  10925. }else if(!this._opened &&
  10926. (e.charOrCode == dojo.keys.DOWN_ARROW ||
  10927. ( (e.charOrCode == dojo.keys.ENTER || e.charOrCode == " ") &&
  10928. //ignore enter and space if the event is for a text input
  10929. ((target.tagName || "").toLowerCase() !== 'input' ||
  10930. (target.type && target.type.toLowerCase() !== 'text'))))){
  10931. // Toggle the drop down, but wait until keyup so that the drop down doesn't
  10932. // get a stray keyup event, or in the case of key-repeat (because user held
  10933. // down key for too long), stray keydown events
  10934. this._toggleOnKeyUp = true;
  10935. dojo.stopEvent(e);
  10936. }
  10937. },
  10938. _onKeyUp: function(){
  10939. if(this._toggleOnKeyUp){
  10940. delete this._toggleOnKeyUp;
  10941. this.toggleDropDown();
  10942. var d = this.dropDown; // drop down may not exist until toggleDropDown() call
  10943. if(d && d.focus){
  10944. setTimeout(dojo.hitch(d, "focus"), 1);
  10945. }
  10946. }
  10947. },
  10948. _onBlur: function(){
  10949. // summary:
  10950. // Called magically when focus has shifted away from this widget and it's dropdown
  10951. // Don't focus on button if the user has explicitly focused on something else (happens
  10952. // when user clicks another control causing the current popup to close)..
  10953. // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
  10954. // it when you display:none a node with focus.
  10955. var focusMe = dijit._curFocus && this.dropDown && dojo.isDescendant(dijit._curFocus, this.dropDown.domNode);
  10956. this.closeDropDown(focusMe);
  10957. this.inherited(arguments);
  10958. },
  10959. isLoaded: function(){
  10960. // summary:
  10961. // Returns whether or not the dropdown is loaded. This can
  10962. // be overridden in order to force a call to loadDropDown().
  10963. // tags:
  10964. // protected
  10965. return true;
  10966. },
  10967. loadDropDown: function(/* Function */ loadCallback){
  10968. // summary:
  10969. // Loads the data for the dropdown, and at some point, calls
  10970. // the given callback. This is basically a callback when the
  10971. // user presses the down arrow button to open the drop down.
  10972. // tags:
  10973. // protected
  10974. loadCallback();
  10975. },
  10976. toggleDropDown: function(){
  10977. // summary:
  10978. // Callback when the user presses the down arrow button or presses
  10979. // the down arrow key to open/close the drop down.
  10980. // Toggle the drop-down widget; if it is up, close it, if not, open it
  10981. // tags:
  10982. // protected
  10983. if(this.disabled || this.readOnly){ return; }
  10984. if(!this._opened){
  10985. // If we aren't loaded, load it first so there isn't a flicker
  10986. if(!this.isLoaded()){
  10987. this.loadDropDown(dojo.hitch(this, "openDropDown"));
  10988. return;
  10989. }else{
  10990. this.openDropDown();
  10991. }
  10992. }else{
  10993. this.closeDropDown();
  10994. }
  10995. },
  10996. openDropDown: function(){
  10997. // summary:
  10998. // Opens the dropdown for this widget. To be called only when this.dropDown
  10999. // has been created and is ready to display (ie, it's data is loaded).
  11000. // returns:
  11001. // return value of dijit.popup.open()
  11002. // tags:
  11003. // protected
  11004. var dropDown = this.dropDown,
  11005. ddNode = dropDown.domNode,
  11006. aroundNode = this._aroundNode || this.domNode,
  11007. self = this;
  11008. // Prepare our popup's height and honor maxHeight if it exists.
  11009. // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
  11010. // ie, dependent on how much space is available (BK)
  11011. if(!this._preparedNode){
  11012. this._preparedNode = true;
  11013. // Check if we have explicitly set width and height on the dropdown widget dom node
  11014. if(ddNode.style.width){
  11015. this._explicitDDWidth = true;
  11016. }
  11017. if(ddNode.style.height){
  11018. this._explicitDDHeight = true;
  11019. }
  11020. }
  11021. // Code for resizing dropdown (height limitation, or increasing width to match my width)
  11022. if(this.maxHeight || this.forceWidth || this.autoWidth){
  11023. var myStyle = {
  11024. display: "",
  11025. visibility: "hidden"
  11026. };
  11027. if(!this._explicitDDWidth){
  11028. myStyle.width = "";
  11029. }
  11030. if(!this._explicitDDHeight){
  11031. myStyle.height = "";
  11032. }
  11033. dojo.style(ddNode, myStyle);
  11034. // Figure out maximum height allowed (if there is a height restriction)
  11035. var maxHeight = this.maxHeight;
  11036. if(maxHeight == -1){
  11037. // limit height to space available in viewport either above or below my domNode
  11038. // (whichever side has more room)
  11039. var viewport = dojo.window.getBox(),
  11040. position = dojo.position(aroundNode, false);
  11041. maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
  11042. }
  11043. // Attach dropDown to DOM and make make visibility:hidden rather than display:none
  11044. // so we call startup() and also get the size
  11045. if(dropDown.startup && !dropDown._started){
  11046. dropDown.startup();
  11047. }
  11048. dijit.popup.moveOffScreen(dropDown);
  11049. // Get size of drop down, and determine if vertical scroll bar needed
  11050. var mb = dojo._getMarginSize(ddNode);
  11051. var overHeight = (maxHeight && mb.h > maxHeight);
  11052. dojo.style(ddNode, {
  11053. overflowX: "hidden",
  11054. overflowY: overHeight ? "auto" : "hidden"
  11055. });
  11056. if(overHeight){
  11057. mb.h = maxHeight;
  11058. if("w" in mb){
  11059. mb.w += 16; // room for vertical scrollbar
  11060. }
  11061. }else{
  11062. delete mb.h;
  11063. }
  11064. // Adjust dropdown width to match or be larger than my width
  11065. if(this.forceWidth){
  11066. mb.w = aroundNode.offsetWidth;
  11067. }else if(this.autoWidth){
  11068. mb.w = Math.max(mb.w, aroundNode.offsetWidth);
  11069. }else{
  11070. delete mb.w;
  11071. }
  11072. // And finally, resize the dropdown to calculated height and width
  11073. if(dojo.isFunction(dropDown.resize)){
  11074. dropDown.resize(mb);
  11075. }else{
  11076. dojo.marginBox(ddNode, mb);
  11077. }
  11078. }
  11079. var retVal = dijit.popup.open({
  11080. parent: this,
  11081. popup: dropDown,
  11082. around: aroundNode,
  11083. orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
  11084. onExecute: function(){
  11085. self.closeDropDown(true);
  11086. },
  11087. onCancel: function(){
  11088. self.closeDropDown(true);
  11089. },
  11090. onClose: function(){
  11091. dojo.attr(self._popupStateNode, "popupActive", false);
  11092. dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
  11093. self._opened = false;
  11094. }
  11095. });
  11096. dojo.attr(this._popupStateNode, "popupActive", "true");
  11097. dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
  11098. this._opened=true;
  11099. // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
  11100. return retVal;
  11101. },
  11102. closeDropDown: function(/*Boolean*/ focus){
  11103. // summary:
  11104. // Closes the drop down on this widget
  11105. // focus:
  11106. // If true, refocuses the button widget
  11107. // tags:
  11108. // protected
  11109. if(this._opened){
  11110. if(focus){ this.focus(); }
  11111. dijit.popup.close(this.dropDown);
  11112. this._opened = false;
  11113. }
  11114. }
  11115. }
  11116. );
  11117. }
  11118. if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  11119. dojo._hasResource["dijit.form.Button"] = true;
  11120. dojo.provide("dijit.form.Button");
  11121. dojo.declare("dijit.form.Button",
  11122. dijit.form._FormWidget,
  11123. {
  11124. // summary:
  11125. // Basically the same thing as a normal HTML button, but with special styling.
  11126. // description:
  11127. // Buttons can display a label, an icon, or both.
  11128. // A label should always be specified (through innerHTML) or the label
  11129. // attribute. It can be hidden via showLabel=false.
  11130. // example:
  11131. // | <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
  11132. //
  11133. // example:
  11134. // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
  11135. // | dojo.body().appendChild(button1.domNode);
  11136. // label: HTML String
  11137. // Text to display in button.
  11138. // If the label is hidden (showLabel=false) then and no title has
  11139. // been specified, then label is also set as title attribute of icon.
  11140. label: "",
  11141. // showLabel: Boolean
  11142. // Set this to true to hide the label text and display only the icon.
  11143. // (If showLabel=false then iconClass must be specified.)
  11144. // Especially useful for toolbars.
  11145. // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
  11146. //
  11147. // The exception case is for computers in high-contrast mode, where the label
  11148. // will still be displayed, since the icon doesn't appear.
  11149. showLabel: true,
  11150. // iconClass: String
  11151. // Class to apply to DOMNode in button to make it display an icon
  11152. iconClass: "",
  11153. // type: String
  11154. // Defines the type of button. "button", "submit", or "reset".
  11155. type: "button",
  11156. baseClass: "dijitButton",
  11157. 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"),
  11158. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  11159. value: "valueNode"
  11160. }),
  11161. _onClick: function(/*Event*/ e){
  11162. // summary:
  11163. // Internal function to handle click actions
  11164. if(this.disabled){
  11165. return false;
  11166. }
  11167. this._clicked(); // widget click actions
  11168. return this.onClick(e); // user click actions
  11169. },
  11170. _onButtonClick: function(/*Event*/ e){
  11171. // summary:
  11172. // Handler when the user activates the button portion.
  11173. if(this._onClick(e) === false){ // returning nothing is same as true
  11174. e.preventDefault(); // needed for checkbox
  11175. }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
  11176. for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
  11177. var widget=dijit.byNode(node);
  11178. if(widget && typeof widget._onSubmit == "function"){
  11179. widget._onSubmit(e);
  11180. break;
  11181. }
  11182. }
  11183. }else if(this.valueNode){
  11184. this.valueNode.click();
  11185. e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
  11186. }
  11187. },
  11188. buildRendering: function(){
  11189. this.inherited(arguments);
  11190. dojo.setSelectable(this.focusNode, false);
  11191. },
  11192. _fillContent: function(/*DomNode*/ source){
  11193. // Overrides _Templated._fillContent().
  11194. // If button label is specified as srcNodeRef.innerHTML rather than
  11195. // this.params.label, handle it here.
  11196. // TODO: remove the method in 2.0, parser will do it all for me
  11197. if(source && (!this.params || !("label" in this.params))){
  11198. this.set('label', source.innerHTML);
  11199. }
  11200. },
  11201. _setShowLabelAttr: function(val){
  11202. if(this.containerNode){
  11203. dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
  11204. }
  11205. this._set("showLabel", val);
  11206. },
  11207. onClick: function(/*Event*/ e){
  11208. // summary:
  11209. // Callback for when button is clicked.
  11210. // If type="submit", return true to perform submit, or false to cancel it.
  11211. // type:
  11212. // callback
  11213. return true; // Boolean
  11214. },
  11215. _clicked: function(/*Event*/ e){
  11216. // summary:
  11217. // Internal overridable function for when the button is clicked
  11218. },
  11219. setLabel: function(/*String*/ content){
  11220. // summary:
  11221. // Deprecated. Use set('label', ...) instead.
  11222. dojo.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
  11223. this.set("label", content);
  11224. },
  11225. _setLabelAttr: function(/*String*/ content){
  11226. // summary:
  11227. // Hook for set('label', ...) to work.
  11228. // description:
  11229. // Set the label (text) of the button; takes an HTML string.
  11230. this._set("label", content);
  11231. this.containerNode.innerHTML = content;
  11232. if(this.showLabel == false && !this.params.title){
  11233. this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
  11234. }
  11235. },
  11236. _setIconClassAttr: function(/*String*/ val){
  11237. // Custom method so that icon node is hidden when not in use, to avoid excess padding/margin
  11238. // appearing around it (even if it's a 0x0 sized <img> node)
  11239. var oldVal = this.iconClass || "dijitNoIcon",
  11240. newVal = val || "dijitNoIcon";
  11241. dojo.replaceClass(this.iconNode, newVal, oldVal);
  11242. this._set("iconClass", val);
  11243. }
  11244. });
  11245. dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
  11246. // summary:
  11247. // A button with a drop down
  11248. //
  11249. // example:
  11250. // | <button dojoType="dijit.form.DropDownButton" label="Hello world">
  11251. // | <div dojotype="dijit.Menu">...</div>
  11252. // | </button>
  11253. //
  11254. // example:
  11255. // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
  11256. // | dojo.body().appendChild(button1);
  11257. //
  11258. baseClass : "dijitDropDownButton",
  11259. 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"),
  11260. _fillContent: function(){
  11261. // Overrides Button._fillContent().
  11262. //
  11263. // My inner HTML contains both the button contents and a drop down widget, like
  11264. // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
  11265. // The first node is assumed to be the button content. The widget is the popup.
  11266. if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
  11267. //FIXME: figure out how to filter out the widget and use all remaining nodes as button
  11268. // content, not just nodes[0]
  11269. var nodes = dojo.query("*", this.srcNodeRef);
  11270. dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
  11271. // save pointer to srcNode so we can grab the drop down widget after it's instantiated
  11272. this.dropDownContainer = this.srcNodeRef;
  11273. }
  11274. },
  11275. startup: function(){
  11276. if(this._started){ return; }
  11277. // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
  11278. // make it invisible, and store a reference to pass to the popup code.
  11279. if(!this.dropDown && this.dropDownContainer){
  11280. var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
  11281. this.dropDown = dijit.byNode(dropDownNode);
  11282. delete this.dropDownContainer;
  11283. }
  11284. if(this.dropDown){
  11285. dijit.popup.hide(this.dropDown);
  11286. }
  11287. this.inherited(arguments);
  11288. },
  11289. isLoaded: function(){
  11290. // Returns whether or not we are loaded - if our dropdown has an href,
  11291. // then we want to check that.
  11292. var dropDown = this.dropDown;
  11293. return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
  11294. },
  11295. loadDropDown: function(){
  11296. // Loads our dropdown
  11297. var dropDown = this.dropDown;
  11298. if(!dropDown){ return; }
  11299. if(!this.isLoaded()){
  11300. var handler = dojo.connect(dropDown, "onLoad", this, function(){
  11301. dojo.disconnect(handler);
  11302. this.openDropDown();
  11303. });
  11304. dropDown.refresh();
  11305. }else{
  11306. this.openDropDown();
  11307. }
  11308. },
  11309. isFocusable: function(){
  11310. // Overridden so that focus is handled by the _HasDropDown mixin, not by
  11311. // the _FormWidget mixin.
  11312. return this.inherited(arguments) && !this._mouseDown;
  11313. }
  11314. });
  11315. dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
  11316. // summary:
  11317. // A combination button and drop-down button.
  11318. // Users can click one side to "press" the button, or click an arrow
  11319. // icon to display the drop down.
  11320. //
  11321. // example:
  11322. // | <button dojoType="dijit.form.ComboButton" onClick="...">
  11323. // | <span>Hello world</span>
  11324. // | <div dojoType="dijit.Menu">...</div>
  11325. // | </button>
  11326. //
  11327. // example:
  11328. // | var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
  11329. // | dojo.body().appendChild(button1.domNode);
  11330. //
  11331. 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"),
  11332. attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
  11333. id: "",
  11334. tabIndex: ["focusNode", "titleNode"],
  11335. title: "titleNode"
  11336. }),
  11337. // optionsTitle: String
  11338. // Text that describes the options menu (accessibility)
  11339. optionsTitle: "",
  11340. baseClass: "dijitComboButton",
  11341. // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
  11342. // mouse action over specified node
  11343. cssStateNodes: {
  11344. "buttonNode": "dijitButtonNode",
  11345. "titleNode": "dijitButtonContents",
  11346. "_popupStateNode": "dijitDownArrowButton"
  11347. },
  11348. _focusedNode: null,
  11349. _onButtonKeyPress: function(/*Event*/ evt){
  11350. // summary:
  11351. // Handler for right arrow key when focus is on left part of button
  11352. if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
  11353. dijit.focus(this._popupStateNode);
  11354. dojo.stopEvent(evt);
  11355. }
  11356. },
  11357. _onArrowKeyPress: function(/*Event*/ evt){
  11358. // summary:
  11359. // Handler for left arrow key when focus is on right part of button
  11360. if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
  11361. dijit.focus(this.titleNode);
  11362. dojo.stopEvent(evt);
  11363. }
  11364. },
  11365. focus: function(/*String*/ position){
  11366. // summary:
  11367. // Focuses this widget to according to position, if specified,
  11368. // otherwise on arrow node
  11369. // position:
  11370. // "start" or "end"
  11371. if(!this.disabled){
  11372. dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
  11373. }
  11374. }
  11375. });
  11376. dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
  11377. // summary:
  11378. // A button that can be in two states (checked or not).
  11379. // Can be base class for things like tabs or checkbox or radio buttons
  11380. baseClass: "dijitToggleButton",
  11381. // checked: Boolean
  11382. // Corresponds to the native HTML <input> element's attribute.
  11383. // In markup, specified as "checked='checked'" or just "checked".
  11384. // True if the button is depressed, or the checkbox is checked,
  11385. // or the radio button is selected, etc.
  11386. checked: false,
  11387. attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
  11388. checked:"focusNode"
  11389. }),
  11390. _clicked: function(/*Event*/ evt){
  11391. this.set('checked', !this.checked);
  11392. },
  11393. _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
  11394. this._set("checked", value);
  11395. dojo.attr(this.focusNode || this.domNode, "checked", value);
  11396. dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
  11397. this._handleOnChange(value, priorityChange);
  11398. },
  11399. setChecked: function(/*Boolean*/ checked){
  11400. // summary:
  11401. // Deprecated. Use set('checked', true/false) instead.
  11402. dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
  11403. this.set('checked', checked);
  11404. },
  11405. reset: function(){
  11406. // summary:
  11407. // Reset the widget's value to what it was at initialization time
  11408. this._hasBeenBlurred = false;
  11409. // set checked state to original setting
  11410. this.set('checked', this.params.checked || false);
  11411. }
  11412. });
  11413. }
  11414. if(!dojo._hasResource["dijit._editor._Plugin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  11415. dojo._hasResource["dijit._editor._Plugin"] = true;
  11416. dojo.provide("dijit._editor._Plugin");
  11417. dojo.declare("dijit._editor._Plugin", null, {
  11418. // summary
  11419. // Base class for a "plugin" to the editor, which is usually
  11420. // a single button on the Toolbar and some associated code
  11421. constructor: function(/*Object?*/args, /*DomNode?*/node){
  11422. this.params = args || {};
  11423. dojo.mixin(this, this.params);
  11424. this._connects=[];
  11425. this._attrPairNames = {};
  11426. },
  11427. // editor: [const] dijit.Editor
  11428. // Points to the parent editor
  11429. editor: null,
  11430. // iconClassPrefix: [const] String
  11431. // The CSS class name for the button node is formed from `iconClassPrefix` and `command`
  11432. iconClassPrefix: "dijitEditorIcon",
  11433. // button: dijit._Widget?
  11434. // Pointer to `dijit.form.Button` or other widget (ex: `dijit.form.FilteringSelect`)
  11435. // that is added to the toolbar to control this plugin.
  11436. // If not specified, will be created on initialization according to `buttonClass`
  11437. button: null,
  11438. // command: String
  11439. // String like "insertUnorderedList", "outdent", "justifyCenter", etc. that represents an editor command.
  11440. // Passed to editor.execCommand() if `useDefaultCommand` is true.
  11441. command: "",
  11442. // useDefaultCommand: Boolean
  11443. // If true, this plugin executes by calling Editor.execCommand() with the argument specified in `command`.
  11444. useDefaultCommand: true,
  11445. // buttonClass: Widget Class
  11446. // Class of widget (ex: dijit.form.Button or dijit.form.FilteringSelect)
  11447. // that is added to the toolbar to control this plugin.
  11448. // This is used to instantiate the button, unless `button` itself is specified directly.
  11449. buttonClass: dijit.form.Button,
  11450. // disabled: Boolean
  11451. // Flag to indicate if this plugin has been disabled and should do nothing
  11452. // helps control button state, among other things. Set via the setter api.
  11453. disabled: false,
  11454. getLabel: function(/*String*/key){
  11455. // summary:
  11456. // Returns the label to use for the button
  11457. // tags:
  11458. // private
  11459. return this.editor.commands[key]; // String
  11460. },
  11461. _initButton: function(){
  11462. // summary:
  11463. // Initialize the button or other widget that will control this plugin.
  11464. // This code only works for plugins controlling built-in commands in the editor.
  11465. // tags:
  11466. // protected extension
  11467. if(this.command.length){
  11468. var label = this.getLabel(this.command),
  11469. editor = this.editor,
  11470. className = this.iconClassPrefix+" "+this.iconClassPrefix + this.command.charAt(0).toUpperCase() + this.command.substr(1);
  11471. if(!this.button){
  11472. var props = dojo.mixin({
  11473. label: label,
  11474. dir: editor.dir,
  11475. lang: editor.lang,
  11476. showLabel: false,
  11477. iconClass: className,
  11478. dropDown: this.dropDown,
  11479. tabIndex: "-1"
  11480. }, this.params || {});
  11481. this.button = new this.buttonClass(props);
  11482. }
  11483. }
  11484. if(this.get("disabled") && this.button){
  11485. this.button.set("disabled", this.get("disabled"));
  11486. }
  11487. },
  11488. destroy: function(){
  11489. // summary:
  11490. // Destroy this plugin
  11491. dojo.forEach(this._connects, dojo.disconnect);
  11492. if(this.dropDown){
  11493. this.dropDown.destroyRecursive();
  11494. }
  11495. },
  11496. connect: function(o, f, tf){
  11497. // summary:
  11498. // Make a dojo.connect() that is automatically disconnected when this plugin is destroyed.
  11499. // Similar to `dijit._Widget.connect`.
  11500. // tags:
  11501. // protected
  11502. this._connects.push(dojo.connect(o, f, this, tf));
  11503. },
  11504. updateState: function(){
  11505. // summary:
  11506. // Change state of the plugin to respond to events in the editor.
  11507. // description:
  11508. // This is called on meaningful events in the editor, such as change of selection
  11509. // or caret position (but not simple typing of alphanumeric keys). It gives the
  11510. // plugin a chance to update the CSS of its button.
  11511. //
  11512. // For example, the "bold" plugin will highlight/unhighlight the bold button depending on whether the
  11513. // characters next to the caret are bold or not.
  11514. //
  11515. // Only makes sense when `useDefaultCommand` is true, as it calls Editor.queryCommandEnabled(`command`).
  11516. var e = this.editor,
  11517. c = this.command,
  11518. checked, enabled;
  11519. if(!e || !e.isLoaded || !c.length){ return; }
  11520. var disabled = this.get("disabled");
  11521. if(this.button){
  11522. try{
  11523. enabled = !disabled && e.queryCommandEnabled(c);
  11524. if(this.enabled !== enabled){
  11525. this.enabled = enabled;
  11526. this.button.set('disabled', !enabled);
  11527. }
  11528. if(typeof this.button.checked == 'boolean'){
  11529. checked = e.queryCommandState(c);
  11530. if(this.checked !== checked){
  11531. this.checked = checked;
  11532. this.button.set('checked', e.queryCommandState(c));
  11533. }
  11534. }
  11535. }catch(e){
  11536. console.log(e); // FIXME: we shouldn't have debug statements in our code. Log as an error?
  11537. }
  11538. }
  11539. },
  11540. setEditor: function(/*dijit.Editor*/ editor){
  11541. // summary:
  11542. // Tell the plugin which Editor it is associated with.
  11543. // TODO: refactor code to just pass editor to constructor.
  11544. // FIXME: detach from previous editor!!
  11545. this.editor = editor;
  11546. // FIXME: prevent creating this if we don't need to (i.e., editor can't handle our command)
  11547. this._initButton();
  11548. // Processing for buttons that execute by calling editor.execCommand()
  11549. if(this.button && this.useDefaultCommand){
  11550. if(this.editor.queryCommandAvailable(this.command)){
  11551. this.connect(this.button, "onClick",
  11552. dojo.hitch(this.editor, "execCommand", this.command, this.commandArg)
  11553. );
  11554. }else{
  11555. // hide button because editor doesn't support command (due to browser limitations)
  11556. this.button.domNode.style.display = "none";
  11557. }
  11558. }
  11559. this.connect(this.editor, "onNormalizedDisplayChanged", "updateState");
  11560. },
  11561. setToolbar: function(/*dijit.Toolbar*/ toolbar){
  11562. // summary:
  11563. // Tell the plugin to add it's controller widget (often a button)
  11564. // to the toolbar. Does nothing if there is no controller widget.
  11565. // TODO: refactor code to just pass toolbar to constructor.
  11566. if(this.button){
  11567. toolbar.addChild(this.button);
  11568. }
  11569. // console.debug("adding", this.button, "to:", toolbar);
  11570. },
  11571. set: function(/* attribute */ name, /* anything */ value){
  11572. // summary:
  11573. // Set a property on a plugin
  11574. // name:
  11575. // The property to set.
  11576. // value:
  11577. // The value to set in the property.
  11578. // description:
  11579. // Sets named properties on a plugin which may potentially be handled by a
  11580. // setter in the plugin.
  11581. // For example, if the plugin has a properties "foo"
  11582. // and "bar" and a method named "_setFooAttr", calling:
  11583. // | plugin.set("foo", "Howdy!");
  11584. // would be equivalent to writing:
  11585. // | plugin._setFooAttr("Howdy!");
  11586. // and:
  11587. // | plugin.set("bar", 3);
  11588. // would be equivalent to writing:
  11589. // | plugin.bar = 3;
  11590. //
  11591. // set() may also be called with a hash of name/value pairs, ex:
  11592. // | plugin.set({
  11593. // | foo: "Howdy",
  11594. // | bar: 3
  11595. // | })
  11596. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  11597. if(typeof name === "object"){
  11598. for(var x in name){
  11599. this.set(x, name[x]);
  11600. }
  11601. return this;
  11602. }
  11603. var names = this._getAttrNames(name);
  11604. if(this[names.s]){
  11605. // use the explicit setter
  11606. var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
  11607. }else{
  11608. this._set(name, value);
  11609. }
  11610. return result || this;
  11611. },
  11612. get: function(name){
  11613. // summary:
  11614. // Get a property from a plugin.
  11615. // name:
  11616. // The property to get.
  11617. // description:
  11618. // Get a named property from a plugin. The property may
  11619. // potentially be retrieved via a getter method. If no getter is defined, this
  11620. // just retrieves the object's property.
  11621. // For example, if the plugin has a properties "foo"
  11622. // and "bar" and a method named "_getFooAttr", calling:
  11623. // | plugin.get("foo");
  11624. // would be equivalent to writing:
  11625. // | plugin._getFooAttr();
  11626. // and:
  11627. // | plugin.get("bar");
  11628. // would be equivalent to writing:
  11629. // | plugin.bar;
  11630. var names = this._getAttrNames(name);
  11631. return this[names.g] ? this[names.g]() : this[name];
  11632. },
  11633. _setDisabledAttr: function(disabled){
  11634. // summary:
  11635. // Function to set the plugin state and call updateState to make sure the
  11636. // button is updated appropriately.
  11637. this.disabled = disabled;
  11638. this.updateState();
  11639. },
  11640. _getAttrNames: function(name){
  11641. // summary:
  11642. // Helper function for get() and set().
  11643. // Caches attribute name values so we don't do the string ops every time.
  11644. // tags:
  11645. // private
  11646. var apn = this._attrPairNames;
  11647. if(apn[name]){ return apn[name]; }
  11648. var uc = name.charAt(0).toUpperCase() + name.substr(1);
  11649. return (apn[name] = {
  11650. s: "_set"+uc+"Attr",
  11651. g: "_get"+uc+"Attr"
  11652. });
  11653. },
  11654. _set: function(/*String*/ name, /*anything*/ value){
  11655. // summary:
  11656. // Helper function to set new value for specified attribute
  11657. var oldValue = this[name];
  11658. this[name] = value;
  11659. }
  11660. });
  11661. }
  11662. if(!dojo._hasResource["dijit._editor.range"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  11663. dojo._hasResource["dijit._editor.range"] = true;
  11664. dojo.provide("dijit._editor.range");
  11665. dijit.range={};
  11666. dijit.range.getIndex=function(/*DomNode*/node, /*DomNode*/parent){
  11667. // dojo.profile.start("dijit.range.getIndex");
  11668. var ret=[], retR=[];
  11669. var stop = parent;
  11670. var onode = node;
  11671. var pnode, n;
  11672. while(node != stop){
  11673. var i = 0;
  11674. pnode = node.parentNode;
  11675. while((n=pnode.childNodes[i++])){
  11676. if(n === node){
  11677. --i;
  11678. break;
  11679. }
  11680. }
  11681. //if(i>=pnode.childNodes.length){
  11682. //dojo.debug("Error finding index of a node in dijit.range.getIndex");
  11683. //}
  11684. ret.unshift(i);
  11685. retR.unshift(i-pnode.childNodes.length);
  11686. node = pnode;
  11687. }
  11688. //normalized() can not be called so often to prevent
  11689. //invalidating selection/range, so we have to detect
  11690. //here that any text nodes in a row
  11691. if(ret.length > 0 && onode.nodeType == 3){
  11692. n = onode.previousSibling;
  11693. while(n && n.nodeType == 3){
  11694. ret[ret.length-1]--;
  11695. n = n.previousSibling;
  11696. }
  11697. n = onode.nextSibling;
  11698. while(n && n.nodeType == 3){
  11699. retR[retR.length-1]++;
  11700. n = n.nextSibling;
  11701. }
  11702. }
  11703. // dojo.profile.end("dijit.range.getIndex");
  11704. return {o: ret, r:retR};
  11705. }
  11706. dijit.range.getNode = function(/*Array*/index, /*DomNode*/parent){
  11707. if(!dojo.isArray(index) || index.length == 0){
  11708. return parent;
  11709. }
  11710. var node = parent;
  11711. // if(!node)debugger
  11712. dojo.every(index, function(i){
  11713. if(i >= 0 && i < node.childNodes.length){
  11714. node = node.childNodes[i];
  11715. }else{
  11716. node = null;
  11717. //console.debug('Error: can not find node with index',index,'under parent node',parent );
  11718. return false; //terminate dojo.every
  11719. }
  11720. return true; //carry on the every loop
  11721. });
  11722. return node;
  11723. }
  11724. dijit.range.getCommonAncestor = function(n1,n2,root){
  11725. root = root||n1.ownerDocument.body;
  11726. var getAncestors = function(n){
  11727. var as=[];
  11728. while(n){
  11729. as.unshift(n);
  11730. if(n !== root){
  11731. n = n.parentNode;
  11732. }else{
  11733. break;
  11734. }
  11735. }
  11736. return as;
  11737. };
  11738. var n1as = getAncestors(n1);
  11739. var n2as = getAncestors(n2);
  11740. var m = Math.min(n1as.length,n2as.length);
  11741. var com = n1as[0]; //at least, one element should be in the array: the root (BODY by default)
  11742. for(var i=1;i<m;i++){
  11743. if(n1as[i] === n2as[i]){
  11744. com = n1as[i]
  11745. }else{
  11746. break;
  11747. }
  11748. }
  11749. return com;
  11750. }
  11751. dijit.range.getAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
  11752. root = root || node.ownerDocument.body;
  11753. while(node && node !== root){
  11754. var name = node.nodeName.toUpperCase() ;
  11755. if(regex.test(name)){
  11756. return node;
  11757. }
  11758. node = node.parentNode;
  11759. }
  11760. return null;
  11761. }
  11762. dijit.range.BlockTagNames = /^(?:P|DIV|H1|H2|H3|H4|H5|H6|ADDRESS|PRE|OL|UL|LI|DT|DE)$/;
  11763. dijit.range.getBlockAncestor = function(/*DomNode*/node, /*RegEx?*/regex, /*DomNode?*/root){
  11764. root = root || node.ownerDocument.body;
  11765. regex = regex || dijit.range.BlockTagNames;
  11766. var block=null, blockContainer;
  11767. while(node && node !== root){
  11768. var name = node.nodeName.toUpperCase() ;
  11769. if(!block && regex.test(name)){
  11770. block = node;
  11771. }
  11772. if(!blockContainer && (/^(?:BODY|TD|TH|CAPTION)$/).test(name)){
  11773. blockContainer = node;
  11774. }
  11775. node = node.parentNode;
  11776. }
  11777. return {blockNode:block, blockContainer:blockContainer || node.ownerDocument.body};
  11778. }
  11779. dijit.range.atBeginningOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
  11780. var atBeginning = false;
  11781. var offsetAtBeginning = (offset == 0);
  11782. if(!offsetAtBeginning && node.nodeType == 3){ //if this is a text node, check whether the left part is all space
  11783. if(/^[\s\xA0]+$/.test(node.nodeValue.substr(0,offset))){
  11784. offsetAtBeginning = true;
  11785. }
  11786. }
  11787. if(offsetAtBeginning){
  11788. var cnode = node;
  11789. atBeginning = true;
  11790. while(cnode && cnode !== container){
  11791. if(cnode.previousSibling){
  11792. atBeginning = false;
  11793. break;
  11794. }
  11795. cnode = cnode.parentNode;
  11796. }
  11797. }
  11798. return atBeginning;
  11799. }
  11800. dijit.range.atEndOfContainer = function(/*DomNode*/container, /*DomNode*/node, /*Int*/offset){
  11801. var atEnd = false;
  11802. var offsetAtEnd = (offset == (node.length || node.childNodes.length));
  11803. if(!offsetAtEnd && node.nodeType == 3){ //if this is a text node, check whether the right part is all space
  11804. if(/^[\s\xA0]+$/.test(node.nodeValue.substr(offset))){
  11805. offsetAtEnd = true;
  11806. }
  11807. }
  11808. if(offsetAtEnd){
  11809. var cnode = node;
  11810. atEnd = true;
  11811. while(cnode && cnode !== container){
  11812. if(cnode.nextSibling){
  11813. atEnd = false;
  11814. break;
  11815. }
  11816. cnode = cnode.parentNode;
  11817. }
  11818. }
  11819. return atEnd;
  11820. }
  11821. dijit.range.adjacentNoneTextNode=function(startnode, next){
  11822. var node = startnode;
  11823. var len = (0-startnode.length) || 0;
  11824. var prop = next?'nextSibling':'previousSibling';
  11825. while(node){
  11826. if(node.nodeType!=3){
  11827. break;
  11828. }
  11829. len += node.length
  11830. node = node[prop];
  11831. }
  11832. return [node,len];
  11833. }
  11834. dijit.range._w3c = Boolean(window['getSelection']);
  11835. dijit.range.create = function(/*Window?*/win){
  11836. if(dijit.range._w3c){
  11837. return (win || dojo.global).document.createRange();
  11838. }else{//IE
  11839. return new dijit.range.W3CRange;
  11840. }
  11841. }
  11842. dijit.range.getSelection = function(/*Window*/win, /*Boolean?*/ignoreUpdate){
  11843. if(dijit.range._w3c){
  11844. return win.getSelection();
  11845. }else{//IE
  11846. var s = new dijit.range.ie.selection(win);
  11847. if(!ignoreUpdate){
  11848. s._getCurrentSelection();
  11849. }
  11850. return s;
  11851. }
  11852. }
  11853. if(!dijit.range._w3c){
  11854. dijit.range.ie={
  11855. cachedSelection: {},
  11856. selection: function(win){
  11857. this._ranges = [];
  11858. this.addRange = function(r, /*boolean*/internal){
  11859. this._ranges.push(r);
  11860. if(!internal){
  11861. r._select();
  11862. }
  11863. this.rangeCount = this._ranges.length;
  11864. };
  11865. this.removeAllRanges = function(){
  11866. //don't detach, the range may be used later
  11867. // for(var i=0;i<this._ranges.length;i++){
  11868. // this._ranges[i].detach();
  11869. // }
  11870. this._ranges = [];
  11871. this.rangeCount = 0;
  11872. };
  11873. var _initCurrentRange = function(){
  11874. var r = win.document.selection.createRange();
  11875. var type=win.document.selection.type.toUpperCase();
  11876. if(type == "CONTROL"){
  11877. //TODO: multiple range selection(?)
  11878. return new dijit.range.W3CRange(dijit.range.ie.decomposeControlRange(r));
  11879. }else{
  11880. return new dijit.range.W3CRange(dijit.range.ie.decomposeTextRange(r));
  11881. }
  11882. };
  11883. this.getRangeAt = function(i){
  11884. return this._ranges[i];
  11885. };
  11886. this._getCurrentSelection = function(){
  11887. this.removeAllRanges();
  11888. var r=_initCurrentRange();
  11889. if(r){
  11890. this.addRange(r, true);
  11891. }
  11892. };
  11893. },
  11894. decomposeControlRange: function(range){
  11895. var firstnode = range.item(0), lastnode = range.item(range.length-1);
  11896. var startContainer = firstnode.parentNode, endContainer = lastnode.parentNode;
  11897. var startOffset = dijit.range.getIndex(firstnode, startContainer).o;
  11898. var endOffset = dijit.range.getIndex(lastnode, endContainer).o+1;
  11899. return [startContainer, startOffset,endContainer, endOffset];
  11900. },
  11901. getEndPoint: function(range, end){
  11902. var atmrange = range.duplicate();
  11903. atmrange.collapse(!end);
  11904. var cmpstr = 'EndTo' + (end?'End':'Start');
  11905. var parentNode = atmrange.parentElement();
  11906. var startnode, startOffset, lastNode;
  11907. if(parentNode.childNodes.length>0){
  11908. dojo.every(parentNode.childNodes, function(node,i){
  11909. var calOffset;
  11910. if(node.nodeType != 3){
  11911. atmrange.moveToElementText(node);
  11912. if(atmrange.compareEndPoints(cmpstr,range) > 0){
  11913. //startnode = node.previousSibling;
  11914. if(lastNode && lastNode.nodeType == 3){
  11915. //where shall we put the start? in the text node or after?
  11916. startnode = lastNode;
  11917. calOffset = true;
  11918. }else{
  11919. startnode = parentNode;
  11920. startOffset = i;
  11921. return false;
  11922. }
  11923. }else{
  11924. if(i == parentNode.childNodes.length-1){
  11925. startnode = parentNode;
  11926. startOffset = parentNode.childNodes.length;
  11927. return false;
  11928. }
  11929. }
  11930. }else{
  11931. if(i == parentNode.childNodes.length-1){//at the end of this node
  11932. startnode = node;
  11933. calOffset = true;
  11934. }
  11935. }
  11936. // try{
  11937. if(calOffset && startnode){
  11938. var prevnode = dijit.range.adjacentNoneTextNode(startnode)[0];
  11939. if(prevnode){
  11940. startnode = prevnode.nextSibling;
  11941. }else{
  11942. startnode = parentNode.firstChild; //firstChild must be a text node
  11943. }
  11944. var prevnodeobj = dijit.range.adjacentNoneTextNode(startnode);
  11945. prevnode = prevnodeobj[0];
  11946. var lenoffset = prevnodeobj[1];
  11947. if(prevnode){
  11948. atmrange.moveToElementText(prevnode);
  11949. atmrange.collapse(false);
  11950. }else{
  11951. atmrange.moveToElementText(parentNode);
  11952. }
  11953. atmrange.setEndPoint(cmpstr, range);
  11954. startOffset = atmrange.text.length-lenoffset;
  11955. return false;
  11956. }
  11957. // }catch(e){ debugger }
  11958. lastNode = node;
  11959. return true;
  11960. });
  11961. }else{
  11962. startnode = parentNode;
  11963. startOffset = 0;
  11964. }
  11965. //if at the end of startnode and we are dealing with start container, then
  11966. //move the startnode to nextSibling if it is a text node
  11967. //TODO: do this for end container?
  11968. if(!end && startnode.nodeType == 1 && startOffset == startnode.childNodes.length){
  11969. var nextnode=startnode.nextSibling;
  11970. if(nextnode && nextnode.nodeType == 3){
  11971. startnode = nextnode;
  11972. startOffset = 0;
  11973. }
  11974. }
  11975. return [startnode, startOffset];
  11976. },
  11977. setEndPoint: function(range, container, offset){
  11978. //text node
  11979. var atmrange = range.duplicate(), node, len;
  11980. if(container.nodeType!=3){ //normal node
  11981. if(offset > 0){
  11982. node = container.childNodes[offset-1];
  11983. if(node){
  11984. if(node.nodeType == 3){
  11985. container = node;
  11986. offset = node.length;
  11987. //pass through
  11988. }else{
  11989. if(node.nextSibling && node.nextSibling.nodeType == 3){
  11990. container=node.nextSibling;
  11991. offset=0;
  11992. //pass through
  11993. }else{
  11994. atmrange.moveToElementText(node.nextSibling?node:container);
  11995. var parent = node.parentNode;
  11996. var tempNode = parent.insertBefore(node.ownerDocument.createTextNode(' '), node.nextSibling);
  11997. atmrange.collapse(false);
  11998. parent.removeChild(tempNode);
  11999. }
  12000. }
  12001. }
  12002. }else{
  12003. atmrange.moveToElementText(container);
  12004. atmrange.collapse(true);
  12005. }
  12006. }
  12007. if(container.nodeType == 3){
  12008. var prevnodeobj = dijit.range.adjacentNoneTextNode(container);
  12009. var prevnode = prevnodeobj[0];
  12010. len = prevnodeobj[1];
  12011. if(prevnode){
  12012. atmrange.moveToElementText(prevnode);
  12013. atmrange.collapse(false);
  12014. //if contentEditable is not inherit, the above collapse won't make the end point
  12015. //in the correctly position: it always has a -1 offset, so compensate it
  12016. if(prevnode.contentEditable!='inherit'){
  12017. len++;
  12018. }
  12019. }else{
  12020. atmrange.moveToElementText(container.parentNode);
  12021. atmrange.collapse(true);
  12022. }
  12023. offset += len;
  12024. if(offset>0){
  12025. if(atmrange.move('character',offset) != offset){
  12026. console.error('Error when moving!');
  12027. }
  12028. }
  12029. }
  12030. return atmrange;
  12031. },
  12032. decomposeTextRange: function(range){
  12033. var tmpary = dijit.range.ie.getEndPoint(range);
  12034. var startContainer = tmpary[0], startOffset = tmpary[1];
  12035. var endContainer = tmpary[0], endOffset = tmpary[1];
  12036. if(range.htmlText.length){
  12037. if(range.htmlText == range.text){ //in the same text node
  12038. endOffset = startOffset+range.text.length;
  12039. }else{
  12040. tmpary = dijit.range.ie.getEndPoint(range,true);
  12041. endContainer = tmpary[0], endOffset = tmpary[1];
  12042. // if(startContainer.tagName == "BODY"){
  12043. // startContainer = startContainer.firstChild;
  12044. // }
  12045. }
  12046. }
  12047. return [startContainer, startOffset, endContainer, endOffset];
  12048. },
  12049. setRange: function(range, startContainer,
  12050. startOffset, endContainer, endOffset, collapsed){
  12051. var start=dijit.range.ie.setEndPoint(range, startContainer, startOffset);
  12052. range.setEndPoint('StartToStart',start);
  12053. if(!collapsed){
  12054. var end=dijit.range.ie.setEndPoint(range, endContainer, endOffset);
  12055. }
  12056. range.setEndPoint('EndToEnd',end || start);
  12057. return range;
  12058. }
  12059. }
  12060. dojo.declare("dijit.range.W3CRange",null, {
  12061. constructor: function(){
  12062. if(arguments.length>0){
  12063. this.setStart(arguments[0][0],arguments[0][1]);
  12064. this.setEnd(arguments[0][2],arguments[0][3]);
  12065. }else{
  12066. this.commonAncestorContainer = null;
  12067. this.startContainer = null;
  12068. this.startOffset = 0;
  12069. this.endContainer = null;
  12070. this.endOffset = 0;
  12071. this.collapsed = true;
  12072. }
  12073. },
  12074. _updateInternal: function(){
  12075. if(this.startContainer !== this.endContainer){
  12076. this.commonAncestorContainer = dijit.range.getCommonAncestor(this.startContainer, this.endContainer);
  12077. }else{
  12078. this.commonAncestorContainer = this.startContainer;
  12079. }
  12080. this.collapsed = (this.startContainer === this.endContainer) && (this.startOffset == this.endOffset);
  12081. },
  12082. setStart: function(node, offset){
  12083. offset=parseInt(offset);
  12084. if(this.startContainer === node && this.startOffset == offset){
  12085. return;
  12086. }
  12087. delete this._cachedBookmark;
  12088. this.startContainer = node;
  12089. this.startOffset = offset;
  12090. if(!this.endContainer){
  12091. this.setEnd(node, offset);
  12092. }else{
  12093. this._updateInternal();
  12094. }
  12095. },
  12096. setEnd: function(node, offset){
  12097. offset=parseInt(offset);
  12098. if(this.endContainer === node && this.endOffset == offset){
  12099. return;
  12100. }
  12101. delete this._cachedBookmark;
  12102. this.endContainer = node;
  12103. this.endOffset = offset;
  12104. if(!this.startContainer){
  12105. this.setStart(node, offset);
  12106. }else{
  12107. this._updateInternal();
  12108. }
  12109. },
  12110. setStartAfter: function(node, offset){
  12111. this._setPoint('setStart', node, offset, 1);
  12112. },
  12113. setStartBefore: function(node, offset){
  12114. this._setPoint('setStart', node, offset, 0);
  12115. },
  12116. setEndAfter: function(node, offset){
  12117. this._setPoint('setEnd', node, offset, 1);
  12118. },
  12119. setEndBefore: function(node, offset){
  12120. this._setPoint('setEnd', node, offset, 0);
  12121. },
  12122. _setPoint: function(what, node, offset, ext){
  12123. var index = dijit.range.getIndex(node, node.parentNode).o;
  12124. this[what](node.parentNode, index.pop()+ext);
  12125. },
  12126. _getIERange: function(){
  12127. var r = (this._body || this.endContainer.ownerDocument.body).createTextRange();
  12128. dijit.range.ie.setRange(r, this.startContainer, this.startOffset, this.endContainer, this.endOffset, this.collapsed);
  12129. return r;
  12130. },
  12131. getBookmark: function(body){
  12132. this._getIERange();
  12133. return this._cachedBookmark;
  12134. },
  12135. _select: function(){
  12136. var r = this._getIERange();
  12137. r.select();
  12138. },
  12139. deleteContents: function(){
  12140. var r = this._getIERange();
  12141. r.pasteHTML('');
  12142. this.endContainer = this.startContainer;
  12143. this.endOffset = this.startOffset;
  12144. this.collapsed = true;
  12145. },
  12146. cloneRange: function(){
  12147. var r = new dijit.range.W3CRange([this.startContainer,this.startOffset,
  12148. this.endContainer,this.endOffset]);
  12149. r._body = this._body;
  12150. return r;
  12151. },
  12152. detach: function(){
  12153. this._body = null;
  12154. this.commonAncestorContainer = null;
  12155. this.startContainer = null;
  12156. this.startOffset = 0;
  12157. this.endContainer = null;
  12158. this.endOffset = 0;
  12159. this.collapsed = true;
  12160. }
  12161. });
  12162. } //if(!dijit.range._w3c)
  12163. }
  12164. if(!dojo._hasResource["dijit._editor.selection"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12165. dojo._hasResource["dijit._editor.selection"] = true;
  12166. dojo.provide("dijit._editor.selection");
  12167. dojo.getObject("_editor.selection", true, dijit);
  12168. // FIXME:
  12169. // all of these methods branch internally for IE. This is probably
  12170. // sub-optimal in terms of runtime performance. We should investigate the
  12171. // size difference for differentiating at definition time.
  12172. dojo.mixin(dijit._editor.selection, {
  12173. getType: function(){
  12174. // summary:
  12175. // Get the selection type (like dojo.doc.select.type in IE).
  12176. if(dojo.isIE < 9){
  12177. return dojo.doc.selection.type.toLowerCase();
  12178. }else{
  12179. var stype = "text";
  12180. // Check if the actual selection is a CONTROL (IMG, TABLE, HR, etc...).
  12181. var oSel;
  12182. try{
  12183. oSel = dojo.global.getSelection();
  12184. }catch(e){ /*squelch*/ }
  12185. if(oSel && oSel.rangeCount == 1){
  12186. var oRange = oSel.getRangeAt(0);
  12187. if( (oRange.startContainer == oRange.endContainer) &&
  12188. ((oRange.endOffset - oRange.startOffset) == 1) &&
  12189. (oRange.startContainer.nodeType != 3 /* text node*/)
  12190. ){
  12191. stype = "control";
  12192. }
  12193. }
  12194. return stype; //String
  12195. }
  12196. },
  12197. getSelectedText: function(){
  12198. // summary:
  12199. // Return the text (no html tags) included in the current selection or null if no text is selected
  12200. if(dojo.isIE < 9){
  12201. if(dijit._editor.selection.getType() == 'control'){
  12202. return null;
  12203. }
  12204. return dojo.doc.selection.createRange().text;
  12205. }else{
  12206. var selection = dojo.global.getSelection();
  12207. if(selection){
  12208. return selection.toString(); //String
  12209. }
  12210. }
  12211. return '';
  12212. },
  12213. getSelectedHtml: function(){
  12214. // summary:
  12215. // Return the html text of the current selection or null if unavailable
  12216. if(dojo.isIE < 9){
  12217. if(dijit._editor.selection.getType() == 'control'){
  12218. return null;
  12219. }
  12220. return dojo.doc.selection.createRange().htmlText;
  12221. }else{
  12222. var selection = dojo.global.getSelection();
  12223. if(selection && selection.rangeCount){
  12224. var i;
  12225. var html = "";
  12226. for(i = 0; i < selection.rangeCount; i++){
  12227. //Handle selections spanning ranges, such as Opera
  12228. var frag = selection.getRangeAt(i).cloneContents();
  12229. var div = dojo.doc.createElement("div");
  12230. div.appendChild(frag);
  12231. html += div.innerHTML;
  12232. }
  12233. return html; //String
  12234. }
  12235. return null;
  12236. }
  12237. },
  12238. getSelectedElement: function(){
  12239. // summary:
  12240. // Retrieves the selected element (if any), just in the case that
  12241. // a single element (object like and image or a table) is
  12242. // selected.
  12243. if(dijit._editor.selection.getType() == "control"){
  12244. if(dojo.isIE < 9){
  12245. var range = dojo.doc.selection.createRange();
  12246. if(range && range.item){
  12247. return dojo.doc.selection.createRange().item(0);
  12248. }
  12249. }else{
  12250. var selection = dojo.global.getSelection();
  12251. return selection.anchorNode.childNodes[ selection.anchorOffset ];
  12252. }
  12253. }
  12254. return null;
  12255. },
  12256. getParentElement: function(){
  12257. // summary:
  12258. // Get the parent element of the current selection
  12259. if(dijit._editor.selection.getType() == "control"){
  12260. var p = this.getSelectedElement();
  12261. if(p){ return p.parentNode; }
  12262. }else{
  12263. if(dojo.isIE < 9){
  12264. var r = dojo.doc.selection.createRange();
  12265. r.collapse(true);
  12266. return r.parentElement();
  12267. }else{
  12268. var selection = dojo.global.getSelection();
  12269. if(selection){
  12270. var node = selection.anchorNode;
  12271. while(node && (node.nodeType != 1)){ // not an element
  12272. node = node.parentNode;
  12273. }
  12274. return node;
  12275. }
  12276. }
  12277. }
  12278. return null;
  12279. },
  12280. hasAncestorElement: function(/*String*/tagName /* ... */){
  12281. // summary:
  12282. // Check whether current selection has a parent element which is
  12283. // of type tagName (or one of the other specified tagName)
  12284. // tagName: String
  12285. // The tag name to determine if it has an ancestor of.
  12286. return this.getAncestorElement.apply(this, arguments) != null; //Boolean
  12287. },
  12288. getAncestorElement: function(/*String*/tagName /* ... */){
  12289. // summary:
  12290. // Return the parent element of the current selection which is of
  12291. // type tagName (or one of the other specified tagName)
  12292. // tagName: String
  12293. // The tag name to determine if it has an ancestor of.
  12294. var node = this.getSelectedElement() || this.getParentElement();
  12295. return this.getParentOfType(node, arguments); //DOMNode
  12296. },
  12297. isTag: function(/*DomNode*/ node, /*String[]*/ tags){
  12298. // summary:
  12299. // Function to determine if a node is one of an array of tags.
  12300. // node:
  12301. // The node to inspect.
  12302. // tags:
  12303. // An array of tag name strings to check to see if the node matches.
  12304. if(node && node.tagName){
  12305. var _nlc = node.tagName.toLowerCase();
  12306. for(var i=0; i<tags.length; i++){
  12307. var _tlc = String(tags[i]).toLowerCase();
  12308. if(_nlc == _tlc){
  12309. return _tlc; // String
  12310. }
  12311. }
  12312. }
  12313. return "";
  12314. },
  12315. getParentOfType: function(/*DomNode*/ node, /*String[]*/ tags){
  12316. // summary:
  12317. // Function to locate a parent node that matches one of a set of tags
  12318. // node:
  12319. // The node to inspect.
  12320. // tags:
  12321. // An array of tag name strings to check to see if the node matches.
  12322. while(node){
  12323. if(this.isTag(node, tags).length){
  12324. return node; // DOMNode
  12325. }
  12326. node = node.parentNode;
  12327. }
  12328. return null;
  12329. },
  12330. collapse: function(/*Boolean*/beginning){
  12331. // summary:
  12332. // Function to collapse (clear), the current selection
  12333. // beginning: Boolean
  12334. // Boolean to indicate whether to collapse the cursor to the beginning of the selection or end.
  12335. if(window.getSelection){
  12336. var selection = dojo.global.getSelection();
  12337. if(selection.removeAllRanges){ // Mozilla
  12338. if(beginning){
  12339. selection.collapseToStart();
  12340. }else{
  12341. selection.collapseToEnd();
  12342. }
  12343. }else{ // Safari
  12344. // pulled from WebCore/ecma/kjs_window.cpp, line 2536
  12345. selection.collapse(beginning);
  12346. }
  12347. }else if(dojo.isIE){ // IE
  12348. var range = dojo.doc.selection.createRange();
  12349. range.collapse(beginning);
  12350. range.select();
  12351. }
  12352. },
  12353. remove: function(){
  12354. // summary:
  12355. // Function to delete the currently selected content from the document.
  12356. var sel = dojo.doc.selection;
  12357. if(dojo.isIE < 9){
  12358. if(sel.type.toLowerCase() != "none"){
  12359. sel.clear();
  12360. }
  12361. return sel; //Selection
  12362. }else{
  12363. sel = dojo.global.getSelection();
  12364. sel.deleteFromDocument();
  12365. return sel; //Selection
  12366. }
  12367. },
  12368. selectElementChildren: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  12369. // summary:
  12370. // clear previous selection and select the content of the node
  12371. // (excluding the node itself)
  12372. // element: DOMNode
  12373. // The element you wish to select the children content of.
  12374. // nochangefocus: Boolean
  12375. // Boolean to indicate if the foxus should change or not.
  12376. var win = dojo.global;
  12377. var doc = dojo.doc;
  12378. var range;
  12379. element = dojo.byId(element);
  12380. if(doc.selection && dojo.isIE < 9 && dojo.body().createTextRange){ // IE
  12381. range = element.ownerDocument.body.createTextRange();
  12382. range.moveToElementText(element);
  12383. if(!nochangefocus){
  12384. try{
  12385. range.select(); // IE throws an exception here if the widget is hidden. See #5439
  12386. }catch(e){ /* squelch */}
  12387. }
  12388. }else if(win.getSelection){
  12389. var selection = dojo.global.getSelection();
  12390. if(dojo.isOpera){
  12391. //Opera's selectAllChildren doesn't seem to work right
  12392. //against <body> nodes and possibly others ... so
  12393. //we use the W3C range API
  12394. if(selection.rangeCount){
  12395. range = selection.getRangeAt(0);
  12396. }else{
  12397. range = doc.createRange();
  12398. }
  12399. range.setStart(element, 0);
  12400. range.setEnd(element,(element.nodeType == 3)?element.length:element.childNodes.length);
  12401. selection.addRange(range);
  12402. }else{
  12403. selection.selectAllChildren(element);
  12404. }
  12405. }
  12406. },
  12407. selectElement: function(/*DomNode*/element,/*Boolean?*/nochangefocus){
  12408. // summary:
  12409. // clear previous selection and select element (including all its children)
  12410. // element: DOMNode
  12411. // The element to select.
  12412. // nochangefocus: Boolean
  12413. // Boolean indicating if the focus should be changed. IE only.
  12414. var range;
  12415. var doc = dojo.doc;
  12416. var win = dojo.global;
  12417. element = dojo.byId(element);
  12418. if(dojo.isIE < 9 && dojo.body().createTextRange){
  12419. try{
  12420. var tg = element.tagName ? element.tagName.toLowerCase() : "";
  12421. if(tg === "img" || tg === "table"){
  12422. range = dojo.body().createControlRange();
  12423. }else{
  12424. range = dojo.body().createRange();
  12425. }
  12426. range.addElement(element);
  12427. if(!nochangefocus){
  12428. range.select();
  12429. }
  12430. }catch(e){
  12431. this.selectElementChildren(element,nochangefocus);
  12432. }
  12433. }else if(dojo.global.getSelection){
  12434. var selection = win.getSelection();
  12435. range = doc.createRange();
  12436. if(selection.removeAllRanges){ // Mozilla
  12437. // FIXME: does this work on Safari?
  12438. if(dojo.isOpera){
  12439. //Opera works if you use the current range on
  12440. //the selection if present.
  12441. if(selection.getRangeAt(0)){
  12442. range = selection.getRangeAt(0);
  12443. }
  12444. }
  12445. range.selectNode(element);
  12446. selection.removeAllRanges();
  12447. selection.addRange(range);
  12448. }
  12449. }
  12450. },
  12451. inSelection: function(node){
  12452. // summary:
  12453. // This function determines if 'node' is
  12454. // in the current selection.
  12455. // tags:
  12456. // public
  12457. if(node){
  12458. var newRange;
  12459. var doc = dojo.doc;
  12460. var range;
  12461. if(dojo.global.getSelection){
  12462. //WC3
  12463. var sel = dojo.global.getSelection();
  12464. if(sel && sel.rangeCount > 0){
  12465. range = sel.getRangeAt(0);
  12466. }
  12467. if(range && range.compareBoundaryPoints && doc.createRange){
  12468. try{
  12469. newRange = doc.createRange();
  12470. newRange.setStart(node, 0);
  12471. if(range.compareBoundaryPoints(range.START_TO_END, newRange) === 1){
  12472. return true;
  12473. }
  12474. }catch(e){ /* squelch */}
  12475. }
  12476. }else if(doc.selection){
  12477. // Probably IE, so we can't use the range object as the pseudo
  12478. // range doesn't implement the boundry checking, we have to
  12479. // use IE specific crud.
  12480. range = doc.selection.createRange();
  12481. try{
  12482. newRange = node.ownerDocument.body.createControlRange();
  12483. if(newRange){
  12484. newRange.addElement(node);
  12485. }
  12486. }catch(e1){
  12487. try{
  12488. newRange = node.ownerDocument.body.createTextRange();
  12489. newRange.moveToElementText(node);
  12490. }catch(e2){/* squelch */}
  12491. }
  12492. if(range && newRange){
  12493. // We can finally compare similar to W3C
  12494. if(range.compareEndPoints("EndToStart", newRange) === 1){
  12495. return true;
  12496. }
  12497. }
  12498. }
  12499. }
  12500. return false; // boolean
  12501. }
  12502. });
  12503. }
  12504. if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12505. dojo._hasResource["dojo.data.util.sorter"] = true;
  12506. dojo.provide("dojo.data.util.sorter");
  12507. dojo.getObject("data.util.sorter", true, dojo);
  12508. dojo.data.util.sorter.basicComparator = function( /*anything*/ a,
  12509. /*anything*/ b){
  12510. // summary:
  12511. // Basic comparision function that compares if an item is greater or less than another item
  12512. // description:
  12513. // returns 1 if a > b, -1 if a < b, 0 if equal.
  12514. // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
  12515. // And compared to each other, null is equivalent to undefined.
  12516. //null is a problematic compare, so if null, we set to undefined.
  12517. //Makes the check logic simple, compact, and consistent
  12518. //And (null == undefined) === true, so the check later against null
  12519. //works for undefined and is less bytes.
  12520. var r = -1;
  12521. if(a === null){
  12522. a = undefined;
  12523. }
  12524. if(b === null){
  12525. b = undefined;
  12526. }
  12527. if(a == b){
  12528. r = 0;
  12529. }else if(a > b || a == null){
  12530. r = 1;
  12531. }
  12532. return r; //int {-1,0,1}
  12533. };
  12534. dojo.data.util.sorter.createSortFunction = function( /* attributes array */sortSpec,
  12535. /*dojo.data.core.Read*/ store){
  12536. // summary:
  12537. // Helper function to generate the sorting function based off the list of sort attributes.
  12538. // description:
  12539. // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
  12540. // it will look in the mapping for comparisons function for the attributes. If one is found, it will
  12541. // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
  12542. // Returns the sorting function for this particular list of attributes and sorting directions.
  12543. //
  12544. // sortSpec: array
  12545. // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
  12546. // The objects should be formatted as follows:
  12547. // {
  12548. // attribute: "attributeName-string" || attribute,
  12549. // descending: true|false; // Default is false.
  12550. // }
  12551. // store: object
  12552. // The datastore object to look up item values from.
  12553. //
  12554. var sortFunctions=[];
  12555. function createSortFunction(attr, dir, comp, s){
  12556. //Passing in comp and s (comparator and store), makes this
  12557. //function much faster.
  12558. return function(itemA, itemB){
  12559. var a = s.getValue(itemA, attr);
  12560. var b = s.getValue(itemB, attr);
  12561. return dir * comp(a,b); //int
  12562. };
  12563. }
  12564. var sortAttribute;
  12565. var map = store.comparatorMap;
  12566. var bc = dojo.data.util.sorter.basicComparator;
  12567. for(var i = 0; i < sortSpec.length; i++){
  12568. sortAttribute = sortSpec[i];
  12569. var attr = sortAttribute.attribute;
  12570. if(attr){
  12571. var dir = (sortAttribute.descending) ? -1 : 1;
  12572. var comp = bc;
  12573. if(map){
  12574. if(typeof attr !== "string" && ("toString" in attr)){
  12575. attr = attr.toString();
  12576. }
  12577. comp = map[attr] || bc;
  12578. }
  12579. sortFunctions.push(createSortFunction(attr,
  12580. dir, comp, store));
  12581. }
  12582. }
  12583. return function(rowA, rowB){
  12584. var i=0;
  12585. while(i < sortFunctions.length){
  12586. var ret = sortFunctions[i++](rowA, rowB);
  12587. if(ret !== 0){
  12588. return ret;//int
  12589. }
  12590. }
  12591. return 0; //int
  12592. }; // Function
  12593. };
  12594. }
  12595. if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12596. dojo._hasResource["dojo.data.util.simpleFetch"] = true;
  12597. dojo.provide("dojo.data.util.simpleFetch");
  12598. dojo.getObject("data.util.simpleFetch", true, dojo);
  12599. dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
  12600. // summary:
  12601. // The simpleFetch mixin is designed to serve as a set of function(s) that can
  12602. // be mixed into other datastore implementations to accelerate their development.
  12603. // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
  12604. // call by returning an array of all the found items that matched the query. The simpleFetch mixin
  12605. // is not designed to work for datastores that respond to a fetch() call by incrementally
  12606. // loading items, or sequentially loading partial batches of the result
  12607. // set. For datastores that mixin simpleFetch, simpleFetch
  12608. // implements a fetch method that automatically handles eight of the fetch()
  12609. // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
  12610. // The class mixing in simpleFetch should not implement fetch(),
  12611. // but should instead implement a _fetchItems() method. The _fetchItems()
  12612. // method takes three arguments, the keywordArgs object that was passed
  12613. // to fetch(), a callback function to be called when the result array is
  12614. // available, and an error callback to be called if something goes wrong.
  12615. // The _fetchItems() method should ignore any keywordArgs parameters for
  12616. // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
  12617. // The _fetchItems() method needs to correctly handle any other keywordArgs
  12618. // parameters, including the query parameter and any optional parameters
  12619. // (such as includeChildren). The _fetchItems() method should create an array of
  12620. // result items and pass it to the fetchHandler along with the original request object
  12621. // -- or, the _fetchItems() method may, if it wants to, create an new request object
  12622. // with other specifics about the request that are specific to the datastore and pass
  12623. // that as the request object to the handler.
  12624. //
  12625. // For more information on this specific function, see dojo.data.api.Read.fetch()
  12626. request = request || {};
  12627. if(!request.store){
  12628. request.store = this;
  12629. }
  12630. var self = this;
  12631. var _errorHandler = function(errorData, requestObject){
  12632. if(requestObject.onError){
  12633. var scope = requestObject.scope || dojo.global;
  12634. requestObject.onError.call(scope, errorData, requestObject);
  12635. }
  12636. };
  12637. var _fetchHandler = function(items, requestObject){
  12638. var oldAbortFunction = requestObject.abort || null;
  12639. var aborted = false;
  12640. var startIndex = requestObject.start?requestObject.start:0;
  12641. var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
  12642. requestObject.abort = function(){
  12643. aborted = true;
  12644. if(oldAbortFunction){
  12645. oldAbortFunction.call(requestObject);
  12646. }
  12647. };
  12648. var scope = requestObject.scope || dojo.global;
  12649. if(!requestObject.store){
  12650. requestObject.store = self;
  12651. }
  12652. if(requestObject.onBegin){
  12653. requestObject.onBegin.call(scope, items.length, requestObject);
  12654. }
  12655. if(requestObject.sort){
  12656. items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
  12657. }
  12658. if(requestObject.onItem){
  12659. for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
  12660. var item = items[i];
  12661. if(!aborted){
  12662. requestObject.onItem.call(scope, item, requestObject);
  12663. }
  12664. }
  12665. }
  12666. if(requestObject.onComplete && !aborted){
  12667. var subset = null;
  12668. if(!requestObject.onItem){
  12669. subset = items.slice(startIndex, endIndex);
  12670. }
  12671. requestObject.onComplete.call(scope, subset, requestObject);
  12672. }
  12673. };
  12674. this._fetchItems(request, _fetchHandler, _errorHandler);
  12675. return request; // Object
  12676. };
  12677. }
  12678. if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12679. dojo._hasResource["dojo.data.util.filter"] = true;
  12680. dojo.provide("dojo.data.util.filter");
  12681. dojo.getObject("data.util.filter", true, dojo);
  12682. dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
  12683. // summary:
  12684. // Helper function to convert a simple pattern to a regular expression for matching.
  12685. // description:
  12686. // Returns a regular expression object that conforms to the defined conversion rules.
  12687. // For example:
  12688. // ca* -> /^ca.*$/
  12689. // *ca* -> /^.*ca.*$/
  12690. // *c\*a* -> /^.*c\*a.*$/
  12691. // *c\*a?* -> /^.*c\*a..*$/
  12692. // and so on.
  12693. //
  12694. // pattern: string
  12695. // A simple matching pattern to convert that follows basic rules:
  12696. // * Means match anything, so ca* means match anything starting with ca
  12697. // ? Means match single character. So, b?b will match to bob and bab, and so on.
  12698. // \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
  12699. // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
  12700. // represented by \\ to be treated as an ordinary \ character instead of an escape.
  12701. //
  12702. // ignoreCase:
  12703. // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
  12704. // By default, it is assumed case sensitive.
  12705. var rxp = "^";
  12706. var c = null;
  12707. for(var i = 0; i < pattern.length; i++){
  12708. c = pattern.charAt(i);
  12709. switch(c){
  12710. case '\\':
  12711. rxp += c;
  12712. i++;
  12713. rxp += pattern.charAt(i);
  12714. break;
  12715. case '*':
  12716. rxp += ".*"; break;
  12717. case '?':
  12718. rxp += "."; break;
  12719. case '$':
  12720. case '^':
  12721. case '/':
  12722. case '+':
  12723. case '.':
  12724. case '|':
  12725. case '(':
  12726. case ')':
  12727. case '{':
  12728. case '}':
  12729. case '[':
  12730. case ']':
  12731. rxp += "\\"; //fallthrough
  12732. default:
  12733. rxp += c;
  12734. }
  12735. }
  12736. rxp += "$";
  12737. if(ignoreCase){
  12738. return new RegExp(rxp,"mi"); //RegExp
  12739. }else{
  12740. return new RegExp(rxp,"m"); //RegExp
  12741. }
  12742. };
  12743. }
  12744. if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  12745. dojo._hasResource["dijit.form.TextBox"] = true;
  12746. dojo.provide("dijit.form.TextBox");
  12747. dojo.declare(
  12748. "dijit.form.TextBox",
  12749. dijit.form._FormValueWidget,
  12750. {
  12751. // summary:
  12752. // A base class for textbox form inputs
  12753. // trim: Boolean
  12754. // Removes leading and trailing whitespace if true. Default is false.
  12755. trim: false,
  12756. // uppercase: Boolean
  12757. // Converts all characters to uppercase if true. Default is false.
  12758. uppercase: false,
  12759. // lowercase: Boolean
  12760. // Converts all characters to lowercase if true. Default is false.
  12761. lowercase: false,
  12762. // propercase: Boolean
  12763. // Converts the first character of each word to uppercase if true.
  12764. propercase: false,
  12765. // maxLength: String
  12766. // HTML INPUT tag maxLength declaration.
  12767. maxLength: "",
  12768. // selectOnClick: [const] Boolean
  12769. // If true, all text will be selected when focused with mouse
  12770. selectOnClick: false,
  12771. // placeHolder: String
  12772. // Defines a hint to help users fill out the input field (as defined in HTML 5).
  12773. // This should only contain plain text (no html markup).
  12774. placeHolder: "",
  12775. 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"),
  12776. _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
  12777. _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
  12778. baseClass: "dijitTextBox",
  12779. attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
  12780. maxLength: "focusNode"
  12781. }),
  12782. postMixInProperties: function(){
  12783. var type = this.type.toLowerCase();
  12784. if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
  12785. this.templateString = this._singleNodeTemplate;
  12786. }
  12787. this.inherited(arguments);
  12788. },
  12789. _setPlaceHolderAttr: function(v){
  12790. this._set("placeHolder", v);
  12791. if(!this._phspan){
  12792. this._attachPoints.push('_phspan');
  12793. /* dijitInputField class gives placeHolder same padding as the input field
  12794. * parent node already has dijitInputField class but it doesn't affect this <span>
  12795. * since it's position: absolute.
  12796. */
  12797. this._phspan = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
  12798. }
  12799. this._phspan.innerHTML="";
  12800. this._phspan.appendChild(document.createTextNode(v));
  12801. this._updatePlaceHolder();
  12802. },
  12803. _updatePlaceHolder: function(){
  12804. if(this._phspan){
  12805. this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
  12806. }
  12807. },
  12808. _getValueAttr: function(){
  12809. // summary:
  12810. // Hook so get('value') works as we like.
  12811. // description:
  12812. // For `dijit.form.TextBox` this basically returns the value of the <input>.
  12813. //
  12814. // For `dijit.form.MappedTextBox` subclasses, which have both
  12815. // a "displayed value" and a separate "submit value",
  12816. // This treats the "displayed value" as the master value, computing the
  12817. // submit value from it via this.parse().
  12818. return this.parse(this.get('displayedValue'), this.constraints);
  12819. },
  12820. _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  12821. // summary:
  12822. // Hook so set('value', ...) works.
  12823. //
  12824. // description:
  12825. // Sets the value of the widget to "value" which can be of
  12826. // any type as determined by the widget.
  12827. //
  12828. // value:
  12829. // The visual element value is also set to a corresponding,
  12830. // but not necessarily the same, value.
  12831. //
  12832. // formattedValue:
  12833. // If specified, used to set the visual element value,
  12834. // otherwise a computed visual value is used.
  12835. //
  12836. // priorityChange:
  12837. // If true, an onChange event is fired immediately instead of
  12838. // waiting for the next blur event.
  12839. var filteredValue;
  12840. if(value !== undefined){
  12841. // TODO: this is calling filter() on both the display value and the actual value.
  12842. // I added a comment to the filter() definition about this, but it should be changed.
  12843. filteredValue = this.filter(value);
  12844. if(typeof formattedValue != "string"){
  12845. if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
  12846. formattedValue = this.filter(this.format(filteredValue, this.constraints));
  12847. }else{ formattedValue = ''; }
  12848. }
  12849. }
  12850. if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
  12851. this.textbox.value = formattedValue;
  12852. this._set("displayedValue", this.get("displayedValue"));
  12853. }
  12854. this._updatePlaceHolder();
  12855. this.inherited(arguments, [filteredValue, priorityChange]);
  12856. },
  12857. // displayedValue: String
  12858. // For subclasses like ComboBox where the displayed value
  12859. // (ex: Kentucky) and the serialized value (ex: KY) are different,
  12860. // this represents the displayed value.
  12861. //
  12862. // Setting 'displayedValue' through set('displayedValue', ...)
  12863. // updates 'value', and vice-versa. Otherwise 'value' is updated
  12864. // from 'displayedValue' periodically, like onBlur etc.
  12865. //
  12866. // TODO: move declaration to MappedTextBox?
  12867. // Problem is that ComboBox references displayedValue,
  12868. // for benefit of FilteringSelect.
  12869. displayedValue: "",
  12870. getDisplayedValue: function(){
  12871. // summary:
  12872. // Deprecated. Use get('displayedValue') instead.
  12873. // tags:
  12874. // deprecated
  12875. dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
  12876. return this.get('displayedValue');
  12877. },
  12878. _getDisplayedValueAttr: function(){
  12879. // summary:
  12880. // Hook so get('displayedValue') works.
  12881. // description:
  12882. // Returns the displayed value (what the user sees on the screen),
  12883. // after filtering (ie, trimming spaces etc.).
  12884. //
  12885. // For some subclasses of TextBox (like ComboBox), the displayed value
  12886. // is different from the serialized value that's actually
  12887. // sent to the server (see dijit.form.ValidationTextBox.serialize)
  12888. // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
  12889. // this method
  12890. // TODO: this isn't really the displayed value when the user is typing
  12891. return this.filter(this.textbox.value);
  12892. },
  12893. setDisplayedValue: function(/*String*/ value){
  12894. // summary:
  12895. // Deprecated. Use set('displayedValue', ...) instead.
  12896. // tags:
  12897. // deprecated
  12898. dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
  12899. this.set('displayedValue', value);
  12900. },
  12901. _setDisplayedValueAttr: function(/*String*/ value){
  12902. // summary:
  12903. // Hook so set('displayedValue', ...) works.
  12904. // description:
  12905. // Sets the value of the visual element to the string "value".
  12906. // The widget value is also set to a corresponding,
  12907. // but not necessarily the same, value.
  12908. if(value === null || value === undefined){ value = '' }
  12909. else if(typeof value != "string"){ value = String(value) }
  12910. this.textbox.value = value;
  12911. // sets the serialized value to something corresponding to specified displayedValue
  12912. // (if possible), and also updates the textbox.value, for example converting "123"
  12913. // to "123.00"
  12914. this._setValueAttr(this.get('value'), undefined);
  12915. this._set("displayedValue", this.get('displayedValue'));
  12916. },
  12917. format: function(/*String*/ value, /*Object*/ constraints){
  12918. // summary:
  12919. // Replacable function to convert a value to a properly formatted string.
  12920. // tags:
  12921. // protected extension
  12922. return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
  12923. },
  12924. parse: function(/*String*/ value, /*Object*/ constraints){
  12925. // summary:
  12926. // Replacable function to convert a formatted string to a value
  12927. // tags:
  12928. // protected extension
  12929. return value; // String
  12930. },
  12931. _refreshState: function(){
  12932. // summary:
  12933. // After the user types some characters, etc., this method is
  12934. // called to check the field for validity etc. The base method
  12935. // in `dijit.form.TextBox` does nothing, but subclasses override.
  12936. // tags:
  12937. // protected
  12938. },
  12939. _onInput: function(e){
  12940. if(e && e.type && /key/i.test(e.type) && e.keyCode){
  12941. switch(e.keyCode){
  12942. case dojo.keys.SHIFT:
  12943. case dojo.keys.ALT:
  12944. case dojo.keys.CTRL:
  12945. case dojo.keys.TAB:
  12946. return;
  12947. }
  12948. }
  12949. if(this.intermediateChanges){
  12950. var _this = this;
  12951. // the setTimeout allows the key to post to the widget input box
  12952. setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
  12953. }
  12954. this._refreshState();
  12955. // In case someone is watch()'ing for changes to displayedValue
  12956. this._set("displayedValue", this.get("displayedValue"));
  12957. },
  12958. postCreate: function(){
  12959. if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
  12960. // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
  12961. setTimeout(dojo.hitch(this, function(){
  12962. var s = dojo.getComputedStyle(this.domNode);
  12963. if(s){
  12964. var ff = s.fontFamily;
  12965. if(ff){
  12966. var inputs = this.domNode.getElementsByTagName("INPUT");
  12967. if(inputs){
  12968. for(var i=0; i < inputs.length; i++){
  12969. inputs[i].style.fontFamily = ff;
  12970. }
  12971. }
  12972. }
  12973. }
  12974. }), 0);
  12975. }
  12976. // setting the value here is needed since value="" in the template causes "undefined"
  12977. // and setting in the DOM (instead of the JS object) helps with form reset actions
  12978. this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
  12979. this.inherited(arguments);
  12980. if(dojo.isMoz || dojo.isOpera){
  12981. this.connect(this.textbox, "oninput", "_onInput");
  12982. }else{
  12983. this.connect(this.textbox, "onkeydown", "_onInput");
  12984. this.connect(this.textbox, "onkeyup", "_onInput");
  12985. this.connect(this.textbox, "onpaste", "_onInput");
  12986. this.connect(this.textbox, "oncut", "_onInput");
  12987. }
  12988. },
  12989. _blankValue: '', // if the textbox is blank, what value should be reported
  12990. filter: function(val){
  12991. // summary:
  12992. // Auto-corrections (such as trimming) that are applied to textbox
  12993. // value on blur or form submit.
  12994. // description:
  12995. // For MappedTextBox subclasses, this is called twice
  12996. // - once with the display value
  12997. // - once the value as set/returned by set('value', ...)
  12998. // and get('value'), ex: a Number for NumberTextBox.
  12999. //
  13000. // In the latter case it does corrections like converting null to NaN. In
  13001. // the former case the NumberTextBox.filter() method calls this.inherited()
  13002. // to execute standard trimming code in TextBox.filter().
  13003. //
  13004. // TODO: break this into two methods in 2.0
  13005. //
  13006. // tags:
  13007. // protected extension
  13008. if(val === null){ return this._blankValue; }
  13009. if(typeof val != "string"){ return val; }
  13010. if(this.trim){
  13011. val = dojo.trim(val);
  13012. }
  13013. if(this.uppercase){
  13014. val = val.toUpperCase();
  13015. }
  13016. if(this.lowercase){
  13017. val = val.toLowerCase();
  13018. }
  13019. if(this.propercase){
  13020. val = val.replace(/[^\s]+/g, function(word){
  13021. return word.substring(0,1).toUpperCase() + word.substring(1);
  13022. });
  13023. }
  13024. return val;
  13025. },
  13026. _setBlurValue: function(){
  13027. this._setValueAttr(this.get('value'), true);
  13028. },
  13029. _onBlur: function(e){
  13030. if(this.disabled){ return; }
  13031. this._setBlurValue();
  13032. this.inherited(arguments);
  13033. if(this._selectOnClickHandle){
  13034. this.disconnect(this._selectOnClickHandle);
  13035. }
  13036. if(this.selectOnClick && dojo.isMoz){
  13037. this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
  13038. }
  13039. this._updatePlaceHolder();
  13040. },
  13041. _onFocus: function(/*String*/ by){
  13042. if(this.disabled || this.readOnly){ return; }
  13043. // Select all text on focus via click if nothing already selected.
  13044. // Since mouse-up will clear the selection need to defer selection until after mouse-up.
  13045. // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
  13046. if(this.selectOnClick && by == "mouse"){
  13047. this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
  13048. // Only select all text on first click; otherwise users would have no way to clear
  13049. // the selection.
  13050. this.disconnect(this._selectOnClickHandle);
  13051. // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
  13052. // and if not, then select all the text
  13053. var textIsNotSelected;
  13054. if(dojo.isIE){
  13055. var range = dojo.doc.selection.createRange();
  13056. var parent = range.parentElement();
  13057. textIsNotSelected = parent == this.textbox && range.text.length == 0;
  13058. }else{
  13059. textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
  13060. }
  13061. if(textIsNotSelected){
  13062. dijit.selectInputText(this.textbox);
  13063. }
  13064. });
  13065. }
  13066. this._updatePlaceHolder();
  13067. // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
  13068. // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
  13069. this.inherited(arguments);
  13070. this._refreshState();
  13071. },
  13072. reset: function(){
  13073. // Overrides dijit._FormWidget.reset().
  13074. // Additionally resets the displayed textbox value to ''
  13075. this.textbox.value = '';
  13076. this.inherited(arguments);
  13077. }
  13078. }
  13079. );
  13080. dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
  13081. // summary:
  13082. // Select text in the input element argument, from start (default 0), to stop (default end).
  13083. // TODO: use functions in _editor/selection.js?
  13084. var _window = dojo.global;
  13085. var _document = dojo.doc;
  13086. element = dojo.byId(element);
  13087. if(isNaN(start)){ start = 0; }
  13088. if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
  13089. dijit.focus(element);
  13090. if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
  13091. if(element.createTextRange){
  13092. var r = element.createTextRange();
  13093. r.collapse(true);
  13094. r.moveStart("character", -99999); // move to 0
  13095. r.moveStart("character", start); // delta from 0 is the correct position
  13096. r.moveEnd("character", stop-start);
  13097. r.select();
  13098. }
  13099. }else if(_window["getSelection"]){
  13100. if(element.setSelectionRange){
  13101. element.setSelectionRange(start, stop);
  13102. }
  13103. }
  13104. };
  13105. }
  13106. if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13107. dojo._hasResource["dijit.Tooltip"] = true;
  13108. dojo.provide("dijit.Tooltip");
  13109. dojo.declare(
  13110. "dijit._MasterTooltip",
  13111. [dijit._Widget, dijit._Templated],
  13112. {
  13113. // summary:
  13114. // Internal widget that holds the actual tooltip markup,
  13115. // which occurs once per page.
  13116. // Called by Tooltip widgets which are just containers to hold
  13117. // the markup
  13118. // tags:
  13119. // protected
  13120. // duration: Integer
  13121. // Milliseconds to fade in/fade out
  13122. duration: dijit.defaultDuration,
  13123. 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"),
  13124. postCreate: function(){
  13125. dojo.body().appendChild(this.domNode);
  13126. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  13127. // Setup fade-in and fade-out functions.
  13128. this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
  13129. this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
  13130. },
  13131. show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
  13132. // summary:
  13133. // Display tooltip w/specified contents to right of specified node
  13134. // (To left if there's no space on the right, or if rtl == true)
  13135. if(this.aroundNode && this.aroundNode === aroundNode){
  13136. return;
  13137. }
  13138. // reset width; it may have been set by orient() on a previous tooltip show()
  13139. this.domNode.width = "auto";
  13140. if(this.fadeOut.status() == "playing"){
  13141. // previous tooltip is being hidden; wait until the hide completes then show new one
  13142. this._onDeck=arguments;
  13143. return;
  13144. }
  13145. this.containerNode.innerHTML=innerHTML;
  13146. var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
  13147. // show it
  13148. dojo.style(this.domNode, "opacity", 0);
  13149. this.fadeIn.play();
  13150. this.isShowingNow = true;
  13151. this.aroundNode = aroundNode;
  13152. },
  13153. orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
  13154. // summary:
  13155. // Private function to set CSS for tooltip node based on which position it's in.
  13156. // This is called by the dijit popup code. It will also reduce the tooltip's
  13157. // width to whatever width is available
  13158. // tags:
  13159. // protected
  13160. this.connectorNode.style.top = ""; //reset to default
  13161. //Adjust the spaceAvailable width, without changing the spaceAvailable object
  13162. var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
  13163. node.className = "dijitTooltip " +
  13164. {
  13165. "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
  13166. "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
  13167. "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
  13168. "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
  13169. "BR-BL": "dijitTooltipRight",
  13170. "BL-BR": "dijitTooltipLeft"
  13171. }[aroundCorner + "-" + tooltipCorner];
  13172. // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
  13173. this.domNode.style.width = "auto";
  13174. var size = dojo.contentBox(this.domNode);
  13175. var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
  13176. var widthWasReduced = width < size.w;
  13177. this.domNode.style.width = width+"px";
  13178. //Adjust width for tooltips that have a really long word or a nowrap setting
  13179. if(widthWasReduced){
  13180. this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
  13181. var scrollWidth = this.containerNode.scrollWidth;
  13182. this.containerNode.style.overflow = "visible"; //change it back
  13183. if(scrollWidth > width){
  13184. scrollWidth = scrollWidth + dojo.style(this.domNode,"paddingLeft") + dojo.style(this.domNode,"paddingRight");
  13185. this.domNode.style.width = scrollWidth + "px";
  13186. }
  13187. }
  13188. // Reposition the tooltip connector.
  13189. if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
  13190. var mb = dojo.marginBox(node);
  13191. var tooltipConnectorHeight = this.connectorNode.offsetHeight;
  13192. if(mb.h > spaceAvailable.h){
  13193. // The tooltip starts at the top of the page and will extend past the aroundNode
  13194. var aroundNodePlacement = spaceAvailable.h - (aroundNodeCoords.h / 2) - (tooltipConnectorHeight / 2);
  13195. this.connectorNode.style.top = aroundNodePlacement + "px";
  13196. this.connectorNode.style.bottom = "";
  13197. }else{
  13198. // Align center of connector with center of aroundNode, except don't let bottom
  13199. // of connector extend below bottom of tooltip content, or top of connector
  13200. // extend past top of tooltip content
  13201. this.connectorNode.style.bottom = Math.min(
  13202. Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
  13203. mb.h - tooltipConnectorHeight) + "px";
  13204. this.connectorNode.style.top = "";
  13205. }
  13206. }else{
  13207. // reset the tooltip back to the defaults
  13208. this.connectorNode.style.top = "";
  13209. this.connectorNode.style.bottom = "";
  13210. }
  13211. return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
  13212. },
  13213. _onShow: function(){
  13214. // summary:
  13215. // Called at end of fade-in operation
  13216. // tags:
  13217. // protected
  13218. if(dojo.isIE){
  13219. // the arrow won't show up on a node w/an opacity filter
  13220. this.domNode.style.filter="";
  13221. }
  13222. },
  13223. hide: function(aroundNode){
  13224. // summary:
  13225. // Hide the tooltip
  13226. if(this._onDeck && this._onDeck[1] == aroundNode){
  13227. // this hide request is for a show() that hasn't even started yet;
  13228. // just cancel the pending show()
  13229. this._onDeck=null;
  13230. }else if(this.aroundNode === aroundNode){
  13231. // this hide request is for the currently displayed tooltip
  13232. this.fadeIn.stop();
  13233. this.isShowingNow = false;
  13234. this.aroundNode = null;
  13235. this.fadeOut.play();
  13236. }else{
  13237. // just ignore the call, it's for a tooltip that has already been erased
  13238. }
  13239. },
  13240. _onHide: function(){
  13241. // summary:
  13242. // Called at end of fade-out operation
  13243. // tags:
  13244. // protected
  13245. this.domNode.style.cssText=""; // to position offscreen again
  13246. this.containerNode.innerHTML="";
  13247. if(this._onDeck){
  13248. // a show request has been queued up; do it now
  13249. this.show.apply(this, this._onDeck);
  13250. this._onDeck=null;
  13251. }
  13252. }
  13253. }
  13254. );
  13255. dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
  13256. // summary:
  13257. // Display tooltip w/specified contents in specified position.
  13258. // See description of dijit.Tooltip.defaultPosition for details on position parameter.
  13259. // If position is not specified then dijit.Tooltip.defaultPosition is used.
  13260. if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
  13261. return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
  13262. };
  13263. dijit.hideTooltip = function(aroundNode){
  13264. // summary:
  13265. // Hide the tooltip
  13266. if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
  13267. return dijit._masterTT.hide(aroundNode);
  13268. };
  13269. dojo.declare(
  13270. "dijit.Tooltip",
  13271. dijit._Widget,
  13272. {
  13273. // summary:
  13274. // Pops up a tooltip (a help message) when you hover over a node.
  13275. // label: String
  13276. // Text to display in the tooltip.
  13277. // Specified as innerHTML when creating the widget from markup.
  13278. label: "",
  13279. // showDelay: Integer
  13280. // Number of milliseconds to wait after hovering over/focusing on the object, before
  13281. // the tooltip is displayed.
  13282. showDelay: 400,
  13283. // connectId: String|String[]
  13284. // Id of domNode(s) to attach the tooltip to.
  13285. // When user hovers over specified dom node, the tooltip will appear.
  13286. connectId: [],
  13287. // position: String[]
  13288. // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
  13289. position: [],
  13290. _setConnectIdAttr: function(/*String*/ newId){
  13291. // summary:
  13292. // Connect to node(s) (specified by id)
  13293. // Remove connections to old nodes (if there are any)
  13294. dojo.forEach(this._connections || [], function(nested){
  13295. dojo.forEach(nested, dojo.hitch(this, "disconnect"));
  13296. }, this);
  13297. // Make connections to nodes in newIds.
  13298. var ary = dojo.isArrayLike(newId) ? newId : (newId ? [newId] : []);
  13299. this._connections = dojo.map(ary, function(id){
  13300. var node = dojo.byId(id);
  13301. return node ? [
  13302. this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
  13303. this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
  13304. this.connect(node, "onfocus", "_onTargetFocus"),
  13305. this.connect(node, "onblur", "_onTargetBlur")
  13306. ] : [];
  13307. }, this);
  13308. this._set("connectId", newId);
  13309. this._connectIds = ary; // save as array
  13310. },
  13311. addTarget: function(/*DOMNODE || String*/ node){
  13312. // summary:
  13313. // Attach tooltip to specified node if it's not already connected
  13314. // TODO: remove in 2.0 and just use set("connectId", ...) interface
  13315. var id = node.id || node;
  13316. if(dojo.indexOf(this._connectIds, id) == -1){
  13317. this.set("connectId", this._connectIds.concat(id));
  13318. }
  13319. },
  13320. removeTarget: function(/*DOMNODE || String*/ node){
  13321. // summary:
  13322. // Detach tooltip from specified node
  13323. // TODO: remove in 2.0 and just use set("connectId", ...) interface
  13324. var id = node.id || node, // map from DOMNode back to plain id string
  13325. idx = dojo.indexOf(this._connectIds, id);
  13326. if(idx >= 0){
  13327. // remove id (modifies original this._connectIds but that's OK in this case)
  13328. this._connectIds.splice(idx, 1);
  13329. this.set("connectId", this._connectIds);
  13330. }
  13331. },
  13332. buildRendering: function(){
  13333. this.inherited(arguments);
  13334. dojo.addClass(this.domNode,"dijitTooltipData");
  13335. },
  13336. startup: function(){
  13337. this.inherited(arguments);
  13338. // If this tooltip was created in a template, or for some other reason the specified connectId[s]
  13339. // didn't exist during the widget's initialization, then connect now.
  13340. var ids = this.connectId;
  13341. dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
  13342. },
  13343. _onTargetMouseEnter: function(/*Event*/ e){
  13344. // summary:
  13345. // Handler for mouseenter event on the target node
  13346. // tags:
  13347. // private
  13348. this._onHover(e);
  13349. },
  13350. _onTargetMouseLeave: function(/*Event*/ e){
  13351. // summary:
  13352. // Handler for mouseleave event on the target node
  13353. // tags:
  13354. // private
  13355. this._onUnHover(e);
  13356. },
  13357. _onTargetFocus: function(/*Event*/ e){
  13358. // summary:
  13359. // Handler for focus event on the target node
  13360. // tags:
  13361. // private
  13362. this._focus = true;
  13363. this._onHover(e);
  13364. },
  13365. _onTargetBlur: function(/*Event*/ e){
  13366. // summary:
  13367. // Handler for blur event on the target node
  13368. // tags:
  13369. // private
  13370. this._focus = false;
  13371. this._onUnHover(e);
  13372. },
  13373. _onHover: function(/*Event*/ e){
  13374. // summary:
  13375. // Despite the name of this method, it actually handles both hover and focus
  13376. // events on the target node, setting a timer to show the tooltip.
  13377. // tags:
  13378. // private
  13379. if(!this._showTimer){
  13380. var target = e.target;
  13381. this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
  13382. }
  13383. },
  13384. _onUnHover: function(/*Event*/ e){
  13385. // summary:
  13386. // Despite the name of this method, it actually handles both mouseleave and blur
  13387. // events on the target node, hiding the tooltip.
  13388. // tags:
  13389. // private
  13390. // keep a tooltip open if the associated element still has focus (even though the
  13391. // mouse moved away)
  13392. if(this._focus){ return; }
  13393. if(this._showTimer){
  13394. clearTimeout(this._showTimer);
  13395. delete this._showTimer;
  13396. }
  13397. this.close();
  13398. },
  13399. open: function(/*DomNode*/ target){
  13400. // summary:
  13401. // Display the tooltip; usually not called directly.
  13402. // tags:
  13403. // private
  13404. if(this._showTimer){
  13405. clearTimeout(this._showTimer);
  13406. delete this._showTimer;
  13407. }
  13408. dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
  13409. this._connectNode = target;
  13410. this.onShow(target, this.position);
  13411. },
  13412. close: function(){
  13413. // summary:
  13414. // Hide the tooltip or cancel timer for show of tooltip
  13415. // tags:
  13416. // private
  13417. if(this._connectNode){
  13418. // if tooltip is currently shown
  13419. dijit.hideTooltip(this._connectNode);
  13420. delete this._connectNode;
  13421. this.onHide();
  13422. }
  13423. if(this._showTimer){
  13424. // if tooltip is scheduled to be shown (after a brief delay)
  13425. clearTimeout(this._showTimer);
  13426. delete this._showTimer;
  13427. }
  13428. },
  13429. onShow: function(target, position){
  13430. // summary:
  13431. // Called when the tooltip is shown
  13432. // tags:
  13433. // callback
  13434. },
  13435. onHide: function(){
  13436. // summary:
  13437. // Called when the tooltip is hidden
  13438. // tags:
  13439. // callback
  13440. },
  13441. uninitialize: function(){
  13442. this.close();
  13443. this.inherited(arguments);
  13444. }
  13445. }
  13446. );
  13447. // dijit.Tooltip.defaultPosition: String[]
  13448. // This variable controls the position of tooltips, if the position is not specified to
  13449. // the Tooltip widget or *TextBox widget itself. It's an array of strings with the following values:
  13450. //
  13451. // * before: places tooltip to the left of the target node/widget, or to the right in
  13452. // the case of RTL scripts like Hebrew and Arabic
  13453. // * after: places tooltip to the right of the target node/widget, or to the left in
  13454. // the case of RTL scripts like Hebrew and Arabic
  13455. // * above: tooltip goes above target node
  13456. // * below: tooltip goes below target node
  13457. //
  13458. // The list is positions is tried, in order, until a position is found where the tooltip fits
  13459. // within the viewport.
  13460. //
  13461. // Be careful setting this parameter. A value of "above" may work fine until the user scrolls
  13462. // the screen so that there's no room above the target node. Nodes with drop downs, like
  13463. // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
  13464. // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
  13465. // is only room below (or above) the target node, but not both.
  13466. dijit.Tooltip.defaultPosition = ["after", "before"];
  13467. }
  13468. if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13469. dojo._hasResource["dijit.form.ValidationTextBox"] = true;
  13470. dojo.provide("dijit.form.ValidationTextBox");
  13471. /*=====
  13472. dijit.form.ValidationTextBox.__Constraints = function(){
  13473. // locale: String
  13474. // locale used for validation, picks up value from this widget's lang attribute
  13475. // _flags_: anything
  13476. // various flags passed to regExpGen function
  13477. this.locale = "";
  13478. this._flags_ = "";
  13479. }
  13480. =====*/
  13481. dojo.declare(
  13482. "dijit.form.ValidationTextBox",
  13483. dijit.form.TextBox,
  13484. {
  13485. // summary:
  13486. // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
  13487. // tags:
  13488. // protected
  13489. 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"),
  13490. baseClass: "dijitTextBox dijitValidationTextBox",
  13491. // required: Boolean
  13492. // User is required to enter data into this field.
  13493. required: false,
  13494. // promptMessage: String
  13495. // If defined, display this hint string immediately on focus to the textbox, if empty.
  13496. // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
  13497. // Think of this like a tooltip that tells the user what to do, not an error message
  13498. // that tells the user what they've done wrong.
  13499. //
  13500. // Message disappears when user starts typing.
  13501. promptMessage: "",
  13502. // invalidMessage: String
  13503. // The message to display if value is invalid.
  13504. // The translated string value is read from the message file by default.
  13505. // Set to "" to use the promptMessage instead.
  13506. invalidMessage: "$_unset_$",
  13507. // missingMessage: String
  13508. // The message to display if value is empty and the field is required.
  13509. // The translated string value is read from the message file by default.
  13510. // Set to "" to use the invalidMessage instead.
  13511. missingMessage: "$_unset_$",
  13512. // message: String
  13513. // Currently error/prompt message.
  13514. // When using the default tooltip implementation, this will only be
  13515. // displayed when the field is focused.
  13516. message: "",
  13517. // constraints: dijit.form.ValidationTextBox.__Constraints
  13518. // user-defined object needed to pass parameters to the validator functions
  13519. constraints: {},
  13520. // regExp: [extension protected] String
  13521. // regular expression string used to validate the input
  13522. // Do not specify both regExp and regExpGen
  13523. regExp: ".*",
  13524. regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ constraints){
  13525. // summary:
  13526. // Overridable function used to generate regExp when dependent on constraints.
  13527. // Do not specify both regExp and regExpGen.
  13528. // tags:
  13529. // extension protected
  13530. return this.regExp; // String
  13531. },
  13532. // state: [readonly] String
  13533. // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
  13534. state: "",
  13535. // tooltipPosition: String[]
  13536. // See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
  13537. tooltipPosition: [],
  13538. _setValueAttr: function(){
  13539. // summary:
  13540. // Hook so set('value', ...) works.
  13541. this.inherited(arguments);
  13542. this.validate(this._focused);
  13543. },
  13544. validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
  13545. // summary:
  13546. // Overridable function used to validate the text input against the regular expression.
  13547. // tags:
  13548. // protected
  13549. return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
  13550. (!this.required || !this._isEmpty(value)) &&
  13551. (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
  13552. },
  13553. _isValidSubset: function(){
  13554. // summary:
  13555. // Returns true if the value is either already valid or could be made valid by appending characters.
  13556. // This is used for validation while the user [may be] still typing.
  13557. return this.textbox.value.search(this._partialre) == 0;
  13558. },
  13559. isValid: function(/*Boolean*/ isFocused){
  13560. // summary:
  13561. // Tests if value is valid.
  13562. // Can override with your own routine in a subclass.
  13563. // tags:
  13564. // protected
  13565. return this.validator(this.textbox.value, this.constraints);
  13566. },
  13567. _isEmpty: function(value){
  13568. // summary:
  13569. // Checks for whitespace
  13570. return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
  13571. },
  13572. getErrorMessage: function(/*Boolean*/ isFocused){
  13573. // summary:
  13574. // Return an error message to show if appropriate
  13575. // tags:
  13576. // protected
  13577. return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
  13578. },
  13579. getPromptMessage: function(/*Boolean*/ isFocused){
  13580. // summary:
  13581. // Return a hint message to show when widget is first focused
  13582. // tags:
  13583. // protected
  13584. return this.promptMessage; // String
  13585. },
  13586. _maskValidSubsetError: true,
  13587. validate: function(/*Boolean*/ isFocused){
  13588. // summary:
  13589. // Called by oninit, onblur, and onkeypress.
  13590. // description:
  13591. // Show missing or invalid messages if appropriate, and highlight textbox field.
  13592. // tags:
  13593. // protected
  13594. var message = "";
  13595. var isValid = this.disabled || this.isValid(isFocused);
  13596. if(isValid){ this._maskValidSubsetError = true; }
  13597. var isEmpty = this._isEmpty(this.textbox.value);
  13598. var isValidSubset = !isValid && isFocused && this._isValidSubset();
  13599. this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
  13600. dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
  13601. if(this.state == "Error"){
  13602. this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
  13603. message = this.getErrorMessage(isFocused);
  13604. }else if(this.state == "Incomplete"){
  13605. message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
  13606. this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
  13607. }else if(isEmpty){
  13608. message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
  13609. }
  13610. this.set("message", message);
  13611. return isValid;
  13612. },
  13613. displayMessage: function(/*String*/ message){
  13614. // summary:
  13615. // Overridable method to display validation errors/hints.
  13616. // By default uses a tooltip.
  13617. // tags:
  13618. // extension
  13619. dijit.hideTooltip(this.domNode);
  13620. if(message && this._focused){
  13621. dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
  13622. }
  13623. },
  13624. _refreshState: function(){
  13625. // Overrides TextBox._refreshState()
  13626. this.validate(this._focused);
  13627. this.inherited(arguments);
  13628. },
  13629. //////////// INITIALIZATION METHODS ///////////////////////////////////////
  13630. constructor: function(){
  13631. this.constraints = {};
  13632. },
  13633. _setConstraintsAttr: function(/*Object*/ constraints){
  13634. if(!constraints.locale && this.lang){
  13635. constraints.locale = this.lang;
  13636. }
  13637. this._set("constraints", constraints);
  13638. this._computePartialRE();
  13639. },
  13640. _computePartialRE: function(){
  13641. var p = this.regExpGen(this.constraints);
  13642. this.regExp = p;
  13643. var partialre = "";
  13644. // parse the regexp and produce a new regexp that matches valid subsets
  13645. // if the regexp is .* then there's no use in matching subsets since everything is valid
  13646. if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
  13647. function (re){
  13648. switch(re.charAt(0)){
  13649. case '{':
  13650. case '+':
  13651. case '?':
  13652. case '*':
  13653. case '^':
  13654. case '$':
  13655. case '|':
  13656. case '(':
  13657. partialre += re;
  13658. break;
  13659. case ")":
  13660. partialre += "|$)";
  13661. break;
  13662. default:
  13663. partialre += "(?:"+re+"|$)";
  13664. break;
  13665. }
  13666. }
  13667. );}
  13668. try{ // this is needed for now since the above regexp parsing needs more test verification
  13669. "".search(partialre);
  13670. }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
  13671. partialre = this.regExp;
  13672. console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
  13673. } // should never be here unless the original RE is bad or the parsing is bad
  13674. this._partialre = "^(?:" + partialre + ")$";
  13675. },
  13676. postMixInProperties: function(){
  13677. this.inherited(arguments);
  13678. this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
  13679. if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
  13680. if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
  13681. if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
  13682. if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
  13683. this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
  13684. },
  13685. _setDisabledAttr: function(/*Boolean*/ value){
  13686. this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
  13687. this._refreshState();
  13688. },
  13689. _setRequiredAttr: function(/*Boolean*/ value){
  13690. this._set("required", value);
  13691. dijit.setWaiState(this.focusNode, "required", value);
  13692. this._refreshState();
  13693. },
  13694. _setMessageAttr: function(/*String*/ message){
  13695. this._set("message", message);
  13696. this.displayMessage(message);
  13697. },
  13698. reset:function(){
  13699. // Overrides dijit.form.TextBox.reset() by also
  13700. // hiding errors about partial matches
  13701. this._maskValidSubsetError = true;
  13702. this.inherited(arguments);
  13703. },
  13704. _onBlur: function(){
  13705. // the message still exists but for back-compat, and to erase the tooltip
  13706. // (if the message is being displayed as a tooltip), call displayMessage('')
  13707. this.displayMessage('');
  13708. this.inherited(arguments);
  13709. }
  13710. }
  13711. );
  13712. dojo.declare(
  13713. "dijit.form.MappedTextBox",
  13714. dijit.form.ValidationTextBox,
  13715. {
  13716. // summary:
  13717. // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
  13718. // a visible formatted display value, and a serializable
  13719. // value in a hidden input field which is actually sent to the server.
  13720. // description:
  13721. // The visible display may
  13722. // be locale-dependent and interactive. The value sent to the server is stored in a hidden
  13723. // input field which uses the `name` attribute declared by the original widget. That value sent
  13724. // to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
  13725. // locale-neutral.
  13726. // tags:
  13727. // protected
  13728. postMixInProperties: function(){
  13729. this.inherited(arguments);
  13730. // we want the name attribute to go to the hidden <input>, not the displayed <input>,
  13731. // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
  13732. this.nameAttrSetting = "";
  13733. },
  13734. serialize: function(/*anything*/ val, /*Object?*/ options){
  13735. // summary:
  13736. // Overridable function used to convert the get('value') result to a canonical
  13737. // (non-localized) string. For example, will print dates in ISO format, and
  13738. // numbers the same way as they are represented in javascript.
  13739. // tags:
  13740. // protected extension
  13741. return val.toString ? val.toString() : ""; // String
  13742. },
  13743. toString: function(){
  13744. // summary:
  13745. // Returns widget as a printable string using the widget's value
  13746. // tags:
  13747. // protected
  13748. var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
  13749. return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
  13750. },
  13751. validate: function(){
  13752. // Overrides `dijit.form.TextBox.validate`
  13753. this.valueNode.value = this.toString();
  13754. return this.inherited(arguments);
  13755. },
  13756. buildRendering: function(){
  13757. // Overrides `dijit._Templated.buildRendering`
  13758. this.inherited(arguments);
  13759. // Create a hidden <input> node with the serialized value used for submit
  13760. // (as opposed to the displayed value).
  13761. // Passing in name as markup rather than calling dojo.create() with an attrs argument
  13762. // to make dojo.query(input[name=...]) work on IE. (see #8660)
  13763. this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
  13764. },
  13765. reset: function(){
  13766. // Overrides `dijit.form.ValidationTextBox.reset` to
  13767. // reset the hidden textbox value to ''
  13768. this.valueNode.value = '';
  13769. this.inherited(arguments);
  13770. }
  13771. }
  13772. );
  13773. /*=====
  13774. dijit.form.RangeBoundTextBox.__Constraints = function(){
  13775. // min: Number
  13776. // Minimum signed value. Default is -Infinity
  13777. // max: Number
  13778. // Maximum signed value. Default is +Infinity
  13779. this.min = min;
  13780. this.max = max;
  13781. }
  13782. =====*/
  13783. dojo.declare(
  13784. "dijit.form.RangeBoundTextBox",
  13785. dijit.form.MappedTextBox,
  13786. {
  13787. // summary:
  13788. // Base class for textbox form widgets which defines a range of valid values.
  13789. // rangeMessage: String
  13790. // The message to display if value is out-of-range
  13791. rangeMessage: "",
  13792. /*=====
  13793. // constraints: dijit.form.RangeBoundTextBox.__Constraints
  13794. constraints: {},
  13795. ======*/
  13796. rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
  13797. // summary:
  13798. // Overridable function used to validate the range of the numeric input value.
  13799. // tags:
  13800. // protected
  13801. return ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
  13802. ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
  13803. },
  13804. isInRange: function(/*Boolean*/ isFocused){
  13805. // summary:
  13806. // Tests if the value is in the min/max range specified in constraints
  13807. // tags:
  13808. // protected
  13809. return this.rangeCheck(this.get('value'), this.constraints);
  13810. },
  13811. _isDefinitelyOutOfRange: function(){
  13812. // summary:
  13813. // Returns true if the value is out of range and will remain
  13814. // out of range even if the user types more characters
  13815. var val = this.get('value');
  13816. var isTooLittle = false;
  13817. var isTooMuch = false;
  13818. if("min" in this.constraints){
  13819. var min = this.constraints.min;
  13820. min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
  13821. isTooLittle = (typeof min == "number") && min < 0;
  13822. }
  13823. if("max" in this.constraints){
  13824. var max = this.constraints.max;
  13825. max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
  13826. isTooMuch = (typeof max == "number") && max > 0;
  13827. }
  13828. return isTooLittle || isTooMuch;
  13829. },
  13830. _isValidSubset: function(){
  13831. // summary:
  13832. // Overrides `dijit.form.ValidationTextBox._isValidSubset`.
  13833. // Returns true if the input is syntactically valid, and either within
  13834. // range or could be made in range by more typing.
  13835. return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
  13836. },
  13837. isValid: function(/*Boolean*/ isFocused){
  13838. // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
  13839. return this.inherited(arguments) &&
  13840. ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
  13841. },
  13842. getErrorMessage: function(/*Boolean*/ isFocused){
  13843. // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
  13844. var v = this.get('value');
  13845. if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
  13846. return this.rangeMessage; // String
  13847. }
  13848. return this.inherited(arguments);
  13849. },
  13850. postMixInProperties: function(){
  13851. this.inherited(arguments);
  13852. if(!this.rangeMessage){
  13853. this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
  13854. this.rangeMessage = this.messages.rangeMessage;
  13855. }
  13856. },
  13857. _setConstraintsAttr: function(/*Object*/ constraints){
  13858. this.inherited(arguments);
  13859. if(this.focusNode){ // not set when called from postMixInProperties
  13860. if(this.constraints.min !== undefined){
  13861. dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
  13862. }else{
  13863. dijit.removeWaiState(this.focusNode, "valuemin");
  13864. }
  13865. if(this.constraints.max !== undefined){
  13866. dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
  13867. }else{
  13868. dijit.removeWaiState(this.focusNode, "valuemax");
  13869. }
  13870. }
  13871. },
  13872. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  13873. // summary:
  13874. // Hook so set('value', ...) works.
  13875. dijit.setWaiState(this.focusNode, "valuenow", value);
  13876. this.inherited(arguments);
  13877. }
  13878. }
  13879. );
  13880. }
  13881. if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  13882. dojo._hasResource["dijit.form.ComboBox"] = true;
  13883. dojo.provide("dijit.form.ComboBox");
  13884. dojo.declare(
  13885. "dijit.form.ComboBoxMixin",
  13886. dijit._HasDropDown,
  13887. {
  13888. // summary:
  13889. // Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
  13890. // description:
  13891. // All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
  13892. // tags:
  13893. // protected
  13894. // item: Object
  13895. // This is the item returned by the dojo.data.store implementation that
  13896. // provides the data for this ComboBox, it's the currently selected item.
  13897. item: null,
  13898. // pageSize: Integer
  13899. // Argument to data provider.
  13900. // Specifies number of search results per page (before hitting "next" button)
  13901. pageSize: Infinity,
  13902. // store: [const] Object
  13903. // Reference to data provider object used by this ComboBox
  13904. store: null,
  13905. // fetchProperties: Object
  13906. // Mixin to the dojo.data store's fetch.
  13907. // For example, to set the sort order of the ComboBox menu, pass:
  13908. // | { sort: [{attribute:"name",descending: true}] }
  13909. // To override the default queryOptions so that deep=false, do:
  13910. // | { queryOptions: {ignoreCase: true, deep: false} }
  13911. fetchProperties:{},
  13912. // query: Object
  13913. // A query that can be passed to 'store' to initially filter the items,
  13914. // before doing further filtering based on `searchAttr` and the key.
  13915. // Any reference to the `searchAttr` is ignored.
  13916. query: {},
  13917. // autoComplete: Boolean
  13918. // If user types in a partial string, and then tab out of the `<input>` box,
  13919. // automatically copy the first entry displayed in the drop down list to
  13920. // the `<input>` field
  13921. autoComplete: true,
  13922. // highlightMatch: String
  13923. // One of: "first", "all" or "none".
  13924. //
  13925. // If the ComboBox/FilteringSelect opens with the search results and the searched
  13926. // string can be found, it will be highlighted. If set to "all"
  13927. // then will probably want to change `queryExpr` parameter to '*${0}*'
  13928. //
  13929. // Highlighting is only performed when `labelType` is "text", so as to not
  13930. // interfere with any HTML markup an HTML label might contain.
  13931. highlightMatch: "first",
  13932. // searchDelay: Integer
  13933. // Delay in milliseconds between when user types something and we start
  13934. // searching based on that value
  13935. searchDelay: 100,
  13936. // searchAttr: String
  13937. // Search for items in the data store where this attribute (in the item)
  13938. // matches what the user typed
  13939. searchAttr: "name",
  13940. // labelAttr: String?
  13941. // The entries in the drop down list come from this attribute in the
  13942. // dojo.data items.
  13943. // If not specified, the searchAttr attribute is used instead.
  13944. labelAttr: "",
  13945. // labelType: String
  13946. // Specifies how to interpret the labelAttr in the data store items.
  13947. // Can be "html" or "text".
  13948. labelType: "text",
  13949. // queryExpr: String
  13950. // This specifies what query ComboBox/FilteringSelect sends to the data store,
  13951. // based on what the user has typed. Changing this expression will modify
  13952. // whether the drop down shows only exact matches, a "starting with" match,
  13953. // etc. Use it in conjunction with highlightMatch.
  13954. // dojo.data query expression pattern.
  13955. // `${0}` will be substituted for the user text.
  13956. // `*` is used for wildcards.
  13957. // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
  13958. queryExpr: "${0}*",
  13959. // ignoreCase: Boolean
  13960. // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
  13961. ignoreCase: true,
  13962. // hasDownArrow: Boolean
  13963. // Set this textbox to have a down arrow button, to display the drop down list.
  13964. // Defaults to true.
  13965. hasDownArrow: true,
  13966. 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"),
  13967. baseClass: "dijitTextBox dijitComboBox",
  13968. // dropDownClass: [protected extension] String
  13969. // Name of the dropdown widget class used to select a date/time.
  13970. // Subclasses should specify this.
  13971. dropDownClass: "dijit.form._ComboBoxMenu",
  13972. // Set classes like dijitDownArrowButtonHover depending on
  13973. // mouse action over button node
  13974. cssStateNodes: {
  13975. "_buttonNode": "dijitDownArrowButton"
  13976. },
  13977. // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
  13978. maxHeight: -1,
  13979. // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
  13980. _stopClickEvents: false,
  13981. _getCaretPos: function(/*DomNode*/ element){
  13982. // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
  13983. var pos = 0;
  13984. if(typeof(element.selectionStart) == "number"){
  13985. // FIXME: this is totally borked on Moz < 1.3. Any recourse?
  13986. pos = element.selectionStart;
  13987. }else if(dojo.isIE){
  13988. // in the case of a mouse click in a popup being handled,
  13989. // then the dojo.doc.selection is not the textarea, but the popup
  13990. // var r = dojo.doc.selection.createRange();
  13991. // hack to get IE 6 to play nice. What a POS browser.
  13992. var tr = dojo.doc.selection.createRange().duplicate();
  13993. var ntr = element.createTextRange();
  13994. tr.move("character",0);
  13995. ntr.move("character",0);
  13996. try{
  13997. // If control doesn't have focus, you get an exception.
  13998. // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
  13999. // There appears to be no workaround for this - googled for quite a while.
  14000. ntr.setEndPoint("EndToEnd", tr);
  14001. pos = String(ntr.text).replace(/\r/g,"").length;
  14002. }catch(e){
  14003. // If focus has shifted, 0 is fine for caret pos.
  14004. }
  14005. }
  14006. return pos;
  14007. },
  14008. _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
  14009. location = parseInt(location);
  14010. dijit.selectInputText(element, location, location);
  14011. },
  14012. _setDisabledAttr: function(/*Boolean*/ value){
  14013. // Additional code to set disabled state of ComboBox node.
  14014. // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
  14015. this.inherited(arguments);
  14016. dijit.setWaiState(this.domNode, "disabled", value);
  14017. },
  14018. _abortQuery: function(){
  14019. // stop in-progress query
  14020. if(this.searchTimer){
  14021. clearTimeout(this.searchTimer);
  14022. this.searchTimer = null;
  14023. }
  14024. if(this._fetchHandle){
  14025. if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
  14026. this._fetchHandle = null;
  14027. }
  14028. },
  14029. _onInput: function(/*Event*/ evt){
  14030. // summary:
  14031. // Handles paste events
  14032. if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
  14033. this.searchTimer = setTimeout(dojo.hitch(this, function(){
  14034. this._onKey({charOrCode: 229}); // fake IME key to cause a search
  14035. }), 100); // long delay that will probably be preempted by keyboard input
  14036. }
  14037. this.inherited(arguments);
  14038. },
  14039. _onKey: function(/*Event*/ evt){
  14040. // summary:
  14041. // Handles keyboard events
  14042. var key = evt.charOrCode;
  14043. // except for cutting/pasting case - ctrl + x/v
  14044. if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
  14045. return; // throw out weird key combinations and spurious events
  14046. }
  14047. var doSearch = false;
  14048. var pw = this.dropDown;
  14049. var dk = dojo.keys;
  14050. var highlighted = null;
  14051. this._prev_key_backspace = false;
  14052. this._abortQuery();
  14053. // _HasDropDown will do some of the work:
  14054. // 1. when drop down is not yet shown:
  14055. // - if user presses the down arrow key, call loadDropDown()
  14056. // 2. when drop down is already displayed:
  14057. // - on ESC key, call closeDropDown()
  14058. // - otherwise, call dropDown.handleKey() to process the keystroke
  14059. this.inherited(arguments);
  14060. if(this._opened){
  14061. highlighted = pw.getHighlightedOption();
  14062. }
  14063. switch(key){
  14064. case dk.PAGE_DOWN:
  14065. case dk.DOWN_ARROW:
  14066. case dk.PAGE_UP:
  14067. case dk.UP_ARROW:
  14068. // Keystroke caused ComboBox_menu to move to a different item.
  14069. // Copy new item to <input> box.
  14070. if(this._opened){
  14071. this._announceOption(highlighted);
  14072. }
  14073. dojo.stopEvent(evt);
  14074. break;
  14075. case dk.ENTER:
  14076. // prevent submitting form if user presses enter. Also
  14077. // prevent accepting the value if either Next or Previous
  14078. // are selected
  14079. if(highlighted){
  14080. // only stop event on prev/next
  14081. if(highlighted == pw.nextButton){
  14082. this._nextSearch(1);
  14083. dojo.stopEvent(evt);
  14084. break;
  14085. }else if(highlighted == pw.previousButton){
  14086. this._nextSearch(-1);
  14087. dojo.stopEvent(evt);
  14088. break;
  14089. }
  14090. }else{
  14091. // Update 'value' (ex: KY) according to currently displayed text
  14092. this._setBlurValue(); // set value if needed
  14093. this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
  14094. }
  14095. // default case:
  14096. // if enter pressed while drop down is open, or for FilteringSelect,
  14097. // if we are in the middle of a query to convert a directly typed in value to an item,
  14098. // prevent submit
  14099. if(this._opened || this._fetchHandle){
  14100. dojo.stopEvent(evt);
  14101. }
  14102. // fall through
  14103. case dk.TAB:
  14104. var newvalue = this.get('displayedValue');
  14105. // if the user had More Choices selected fall into the
  14106. // _onBlur handler
  14107. if(pw && (
  14108. newvalue == pw._messages["previousMessage"] ||
  14109. newvalue == pw._messages["nextMessage"])
  14110. ){
  14111. break;
  14112. }
  14113. if(highlighted){
  14114. this._selectOption();
  14115. }
  14116. if(this._opened){
  14117. this._lastQuery = null; // in case results come back later
  14118. this.closeDropDown();
  14119. }
  14120. break;
  14121. case ' ':
  14122. if(highlighted){
  14123. // user is effectively clicking a choice in the drop down menu
  14124. dojo.stopEvent(evt);
  14125. this._selectOption();
  14126. this.closeDropDown();
  14127. }else{
  14128. // user typed a space into the input box, treat as normal character
  14129. doSearch = true;
  14130. }
  14131. break;
  14132. case dk.DELETE:
  14133. case dk.BACKSPACE:
  14134. this._prev_key_backspace = true;
  14135. doSearch = true;
  14136. break;
  14137. default:
  14138. // Non char keys (F1-F12 etc..) shouldn't open list.
  14139. // Ascii characters and IME input (Chinese, Japanese etc.) should.
  14140. //IME input produces keycode == 229.
  14141. doSearch = typeof key == 'string' || key == 229;
  14142. }
  14143. if(doSearch){
  14144. // need to wait a tad before start search so that the event
  14145. // bubbles through DOM and we have value visible
  14146. this.item = undefined; // undefined means item needs to be set
  14147. this.searchTimer = setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
  14148. }
  14149. },
  14150. _autoCompleteText: function(/*String*/ text){
  14151. // summary:
  14152. // Fill in the textbox with the first item from the drop down
  14153. // list, and highlight the characters that were
  14154. // auto-completed. For example, if user typed "CA" and the
  14155. // drop down list appeared, the textbox would be changed to
  14156. // "California" and "ifornia" would be highlighted.
  14157. var fn = this.focusNode;
  14158. // IE7: clear selection so next highlight works all the time
  14159. dijit.selectInputText(fn, fn.value.length);
  14160. // does text autoComplete the value in the textbox?
  14161. var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
  14162. if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
  14163. var cpos = this._getCaretPos(fn);
  14164. // only try to extend if we added the last character at the end of the input
  14165. if((cpos+1) > fn.value.length){
  14166. // only add to input node as we would overwrite Capitalisation of chars
  14167. // actually, that is ok
  14168. fn.value = text;//.substr(cpos);
  14169. // visually highlight the autocompleted characters
  14170. dijit.selectInputText(fn, cpos);
  14171. }
  14172. }else{
  14173. // text does not autoComplete; replace the whole value and highlight
  14174. fn.value = text;
  14175. dijit.selectInputText(fn);
  14176. }
  14177. },
  14178. _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
  14179. // summary:
  14180. // Callback when a search completes.
  14181. // description:
  14182. // 1. generates drop-down list and calls _showResultList() to display it
  14183. // 2. if this result list is from user pressing "more choices"/"previous choices"
  14184. // then tell screen reader to announce new option
  14185. this._fetchHandle = null;
  14186. if( this.disabled ||
  14187. this.readOnly ||
  14188. (dataObject.query[this.searchAttr] != this._lastQuery)
  14189. ){
  14190. return;
  14191. }
  14192. var wasSelected = this.dropDown._highlighted_option && dojo.hasClass(this.dropDown._highlighted_option, "dijitMenuItemSelected");
  14193. this.dropDown.clearResultList();
  14194. if(!results.length && !this._maxOptions){ // if no results and not just the previous choices button
  14195. this.closeDropDown();
  14196. return;
  14197. }
  14198. // Fill in the textbox with the first item from the drop down list,
  14199. // and highlight the characters that were auto-completed. For
  14200. // example, if user typed "CA" and the drop down list appeared, the
  14201. // textbox would be changed to "California" and "ifornia" would be
  14202. // highlighted.
  14203. dataObject._maxOptions = this._maxOptions;
  14204. var nodes = this.dropDown.createOptions(
  14205. results,
  14206. dataObject,
  14207. dojo.hitch(this, "_getMenuLabelFromItem")
  14208. );
  14209. // show our list (only if we have content, else nothing)
  14210. this._showResultList();
  14211. // #4091:
  14212. // tell the screen reader that the paging callback finished by
  14213. // shouting the next choice
  14214. if(dataObject.direction){
  14215. if(1 == dataObject.direction){
  14216. this.dropDown.highlightFirstOption();
  14217. }else if(-1 == dataObject.direction){
  14218. this.dropDown.highlightLastOption();
  14219. }
  14220. if(wasSelected){
  14221. this._announceOption(this.dropDown.getHighlightedOption());
  14222. }
  14223. }else if(this.autoComplete && !this._prev_key_backspace
  14224. // when the user clicks the arrow button to show the full list,
  14225. // startSearch looks for "*".
  14226. // it does not make sense to autocomplete
  14227. // if they are just previewing the options available.
  14228. && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
  14229. this._announceOption(nodes[1]); // 1st real item
  14230. }
  14231. },
  14232. _showResultList: function(){
  14233. // summary:
  14234. // Display the drop down if not already displayed, or if it is displayed, then
  14235. // reposition it if necessary (reposition may be necessary if drop down's height changed).
  14236. this.closeDropDown(true);
  14237. // hide the tooltip
  14238. this.displayMessage("");
  14239. this.openDropDown();
  14240. dijit.setWaiState(this.domNode, "expanded", "true");
  14241. },
  14242. loadDropDown: function(/*Function*/ callback){
  14243. // Overrides _HasDropDown.loadDropDown().
  14244. // This is called when user has pressed button icon or pressed the down arrow key
  14245. // to open the drop down.
  14246. this._startSearchAll();
  14247. },
  14248. isLoaded: function(){
  14249. // signal to _HasDropDown that it needs to call loadDropDown() to load the
  14250. // drop down asynchronously before displaying it
  14251. return false;
  14252. },
  14253. closeDropDown: function(){
  14254. // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
  14255. // This method is the callback when the user types ESC or clicking
  14256. // the button icon while the drop down is open. It's also called by other code.
  14257. this._abortQuery();
  14258. if(this._opened){
  14259. this.inherited(arguments);
  14260. dijit.setWaiState(this.domNode, "expanded", "false");
  14261. dijit.removeWaiState(this.focusNode,"activedescendant");
  14262. }
  14263. },
  14264. _setBlurValue: function(){
  14265. // if the user clicks away from the textbox OR tabs away, set the
  14266. // value to the textbox value
  14267. // #4617:
  14268. // if value is now more choices or previous choices, revert
  14269. // the value
  14270. var newvalue = this.get('displayedValue');
  14271. var pw = this.dropDown;
  14272. if(pw && (
  14273. newvalue == pw._messages["previousMessage"] ||
  14274. newvalue == pw._messages["nextMessage"]
  14275. )
  14276. ){
  14277. this._setValueAttr(this._lastValueReported, true);
  14278. }else if(typeof this.item == "undefined"){
  14279. // Update 'value' (ex: KY) according to currently displayed text
  14280. this.item = null;
  14281. this.set('displayedValue', newvalue);
  14282. }else{
  14283. if(this.value != this._lastValueReported){
  14284. dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
  14285. }
  14286. this._refreshState();
  14287. }
  14288. },
  14289. _onBlur: function(){
  14290. // summary:
  14291. // Called magically when focus has shifted away from this widget and it's drop down
  14292. this.closeDropDown();
  14293. this.inherited(arguments);
  14294. },
  14295. _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  14296. // summary:
  14297. // Set the displayed valued in the input box, and the hidden value
  14298. // that gets submitted, based on a dojo.data store item.
  14299. // description:
  14300. // Users shouldn't call this function; they should be calling
  14301. // set('item', value)
  14302. // tags:
  14303. // private
  14304. if(!displayedValue){
  14305. displayedValue = this.store.getValue(item, this.searchAttr);
  14306. }
  14307. var value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
  14308. this._set("item", item);
  14309. dijit.form.ComboBox.superclass._setValueAttr.call(this, value, priorityChange, displayedValue);
  14310. },
  14311. _announceOption: function(/*Node*/ node){
  14312. // summary:
  14313. // a11y code that puts the highlighted option in the textbox.
  14314. // This way screen readers will know what is happening in the
  14315. // menu.
  14316. if(!node){
  14317. return;
  14318. }
  14319. // pull the text value from the item attached to the DOM node
  14320. var newValue;
  14321. if(node == this.dropDown.nextButton ||
  14322. node == this.dropDown.previousButton){
  14323. newValue = node.innerHTML;
  14324. this.item = undefined;
  14325. this.value = '';
  14326. }else{
  14327. newValue = this.store.getValue(node.item, this.searchAttr).toString();
  14328. this.set('item', node.item, false, newValue);
  14329. }
  14330. // get the text that the user manually entered (cut off autocompleted text)
  14331. this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
  14332. // set up ARIA activedescendant
  14333. dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
  14334. // autocomplete the rest of the option to announce change
  14335. this._autoCompleteText(newValue);
  14336. },
  14337. _selectOption: function(/*Event*/ evt){
  14338. // summary:
  14339. // Menu callback function, called when an item in the menu is selected.
  14340. if(evt){
  14341. this._announceOption(evt.target);
  14342. }
  14343. this.closeDropDown();
  14344. this._setCaretPos(this.focusNode, this.focusNode.value.length);
  14345. dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
  14346. },
  14347. _startSearchAll: function(){
  14348. this._startSearch('');
  14349. },
  14350. _startSearchFromInput: function(){
  14351. this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
  14352. },
  14353. _getQueryString: function(/*String*/ text){
  14354. return dojo.string.substitute(this.queryExpr, [text]);
  14355. },
  14356. _startSearch: function(/*String*/ key){
  14357. // summary:
  14358. // Starts a search for elements matching key (key=="" means to return all items),
  14359. // and calls _openResultList() when the search completes, to display the results.
  14360. if(!this.dropDown){
  14361. var popupId = this.id + "_popup",
  14362. dropDownConstructor = dojo.getObject(this.dropDownClass, false);
  14363. this.dropDown = new dropDownConstructor({
  14364. onChange: dojo.hitch(this, this._selectOption),
  14365. id: popupId,
  14366. dir: this.dir
  14367. });
  14368. dijit.removeWaiState(this.focusNode,"activedescendant");
  14369. dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
  14370. }
  14371. // create a new query to prevent accidentally querying for a hidden
  14372. // value from FilteringSelect's keyField
  14373. var query = dojo.clone(this.query); // #5970
  14374. this._lastInput = key; // Store exactly what was entered by the user.
  14375. this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
  14376. // #5970: set _lastQuery, *then* start the timeout
  14377. // otherwise, if the user types and the last query returns before the timeout,
  14378. // _lastQuery won't be set and their input gets rewritten
  14379. this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
  14380. this.searchTimer = null;
  14381. var fetch = {
  14382. queryOptions: {
  14383. ignoreCase: this.ignoreCase,
  14384. deep: true
  14385. },
  14386. query: query,
  14387. onBegin: dojo.hitch(this, "_setMaxOptions"),
  14388. onComplete: dojo.hitch(this, "_openResultList"),
  14389. onError: function(errText){
  14390. _this._fetchHandle = null;
  14391. console.error('dijit.form.ComboBox: ' + errText);
  14392. _this.closeDropDown();
  14393. },
  14394. start: 0,
  14395. count: this.pageSize
  14396. };
  14397. dojo.mixin(fetch, _this.fetchProperties);
  14398. this._fetchHandle = _this.store.fetch(fetch);
  14399. var nextSearch = function(dataObject, direction){
  14400. dataObject.start += dataObject.count*direction;
  14401. // #4091:
  14402. // tell callback the direction of the paging so the screen
  14403. // reader knows which menu option to shout
  14404. dataObject.direction = direction;
  14405. this._fetchHandle = this.store.fetch(dataObject);
  14406. this.focus();
  14407. };
  14408. this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
  14409. }, query, this), this.searchDelay);
  14410. },
  14411. _setMaxOptions: function(size, request){
  14412. this._maxOptions = size;
  14413. },
  14414. _getValueField: function(){
  14415. // summary:
  14416. // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
  14417. // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
  14418. return this.searchAttr;
  14419. },
  14420. //////////// INITIALIZATION METHODS ///////////////////////////////////////
  14421. constructor: function(){
  14422. this.query={};
  14423. this.fetchProperties={};
  14424. },
  14425. postMixInProperties: function(){
  14426. if(!this.store){
  14427. var srcNodeRef = this.srcNodeRef;
  14428. // if user didn't specify store, then assume there are option tags
  14429. this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
  14430. // if there is no value set and there is an option list, set
  14431. // the value to the first value to be consistent with native
  14432. // Select
  14433. // Firefox and Safari set value
  14434. // IE6 and Opera set selectedIndex, which is automatically set
  14435. // by the selected attribute of an option tag
  14436. // IE6 does not set value, Opera sets value = selectedIndex
  14437. if(!("value" in this.params)){
  14438. var item = (this.item = this.store.fetchSelectedItem());
  14439. if(item){
  14440. var valueField = this._getValueField();
  14441. this.value = this.store.getValue(item, valueField);
  14442. }
  14443. }
  14444. }
  14445. this.inherited(arguments);
  14446. },
  14447. postCreate: function(){
  14448. // summary:
  14449. // Subclasses must call this method from their postCreate() methods
  14450. // tags:
  14451. // protected
  14452. // find any associated label element and add to ComboBox node.
  14453. var label=dojo.query('label[for="'+this.id+'"]');
  14454. if(label.length){
  14455. label[0].id = (this.id+"_label");
  14456. dijit.setWaiState(this.domNode, "labelledby", label[0].id);
  14457. }
  14458. this.inherited(arguments);
  14459. },
  14460. _setHasDownArrowAttr: function(val){
  14461. this.hasDownArrow = val;
  14462. this._buttonNode.style.display = val ? "" : "none";
  14463. },
  14464. _getMenuLabelFromItem: function(/*Item*/ item){
  14465. var label = this.labelFunc(item, this.store),
  14466. labelType = this.labelType;
  14467. // If labelType is not "text" we don't want to screw any markup ot whatever.
  14468. if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
  14469. label = this.doHighlight(label, this._escapeHtml(this._lastInput));
  14470. labelType = "html";
  14471. }
  14472. return {html: labelType == "html", label: label};
  14473. },
  14474. doHighlight: function(/*String*/ label, /*String*/ find){
  14475. // summary:
  14476. // Highlights the string entered by the user in the menu. By default this
  14477. // highlights the first occurrence found. Override this method
  14478. // to implement your custom highlighting.
  14479. // tags:
  14480. // protected
  14481. var
  14482. // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
  14483. modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
  14484. i = this.queryExpr.indexOf("${0}");
  14485. find = dojo.regexp.escapeString(find); // escape regexp special chars
  14486. return this._escapeHtml(label).replace(
  14487. // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
  14488. new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
  14489. '<span class="dijitComboBoxHighlightMatch">$1</span>'
  14490. ); // returns String, (almost) valid HTML (entities encoded)
  14491. },
  14492. _escapeHtml: function(/*String*/ str){
  14493. // TODO Should become dojo.html.entities(), when exists use instead
  14494. // summary:
  14495. // Adds escape sequences for special characters in XML: &<>"'
  14496. str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
  14497. .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  14498. return str; // string
  14499. },
  14500. reset: function(){
  14501. // Overrides the _FormWidget.reset().
  14502. // Additionally reset the .item (to clean up).
  14503. this.item = null;
  14504. this.inherited(arguments);
  14505. },
  14506. labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
  14507. // summary:
  14508. // Computes the label to display based on the dojo.data store item.
  14509. // returns:
  14510. // The label that the ComboBox should display
  14511. // tags:
  14512. // private
  14513. // Use toString() because XMLStore returns an XMLItem whereas this
  14514. // method is expected to return a String (#9354)
  14515. return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
  14516. }
  14517. }
  14518. );
  14519. dojo.declare(
  14520. "dijit.form._ComboBoxMenu",
  14521. [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  14522. {
  14523. // summary:
  14524. // Focus-less menu for internal use in `dijit.form.ComboBox`
  14525. // tags:
  14526. // private
  14527. templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
  14528. +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' role='option'></li>"
  14529. +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' role='option'></li>"
  14530. +"</ul>",
  14531. // _messages: Object
  14532. // Holds "next" and "previous" text for paging buttons on drop down
  14533. _messages: null,
  14534. baseClass: "dijitComboBoxMenu",
  14535. postMixInProperties: function(){
  14536. this.inherited(arguments);
  14537. this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
  14538. },
  14539. buildRendering: function(){
  14540. this.inherited(arguments);
  14541. // fill in template with i18n messages
  14542. this.previousButton.innerHTML = this._messages["previousMessage"];
  14543. this.nextButton.innerHTML = this._messages["nextMessage"];
  14544. },
  14545. _setValueAttr: function(/*Object*/ value){
  14546. this.value = value;
  14547. this.onChange(value);
  14548. },
  14549. // stubs
  14550. onChange: function(/*Object*/ value){
  14551. // summary:
  14552. // Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
  14553. // Probably should be called onSelect.
  14554. // tags:
  14555. // callback
  14556. },
  14557. onPage: function(/*Number*/ direction){
  14558. // summary:
  14559. // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
  14560. // tags:
  14561. // callback
  14562. },
  14563. onClose: function(){
  14564. // summary:
  14565. // Callback from dijit.popup code to this widget, notifying it that it closed
  14566. // tags:
  14567. // private
  14568. this._blurOptionNode();
  14569. },
  14570. _createOption: function(/*Object*/ item, labelFunc){
  14571. // summary:
  14572. // Creates an option to appear on the popup menu subclassed by
  14573. // `dijit.form.FilteringSelect`.
  14574. var menuitem = dojo.create("li", {
  14575. "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
  14576. role: "option"
  14577. });
  14578. var labelObject = labelFunc(item);
  14579. if(labelObject.html){
  14580. menuitem.innerHTML = labelObject.label;
  14581. }else{
  14582. menuitem.appendChild(
  14583. dojo.doc.createTextNode(labelObject.label)
  14584. );
  14585. }
  14586. // #3250: in blank options, assign a normal height
  14587. if(menuitem.innerHTML == ""){
  14588. menuitem.innerHTML = "&nbsp;";
  14589. }
  14590. menuitem.item=item;
  14591. return menuitem;
  14592. },
  14593. createOptions: function(results, dataObject, labelFunc){
  14594. // summary:
  14595. // Fills in the items in the drop down list
  14596. // results:
  14597. // Array of dojo.data items
  14598. // dataObject:
  14599. // dojo.data store
  14600. // labelFunc:
  14601. // Function to produce a label in the drop down list from a dojo.data item
  14602. //this._dataObject=dataObject;
  14603. //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
  14604. // display "Previous . . ." button
  14605. this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
  14606. dojo.attr(this.previousButton, "id", this.id + "_prev");
  14607. // create options using _createOption function defined by parent
  14608. // ComboBox (or FilteringSelect) class
  14609. // #2309:
  14610. // iterate over cache nondestructively
  14611. dojo.forEach(results, function(item, i){
  14612. var menuitem = this._createOption(item, labelFunc);
  14613. dojo.attr(menuitem, "id", this.id + i);
  14614. this.domNode.insertBefore(menuitem, this.nextButton);
  14615. }, this);
  14616. // display "Next . . ." button
  14617. var displayMore = false;
  14618. //Try to determine if we should show 'more'...
  14619. if(dataObject._maxOptions && dataObject._maxOptions != -1){
  14620. if((dataObject.start + dataObject.count) < dataObject._maxOptions){
  14621. displayMore = true;
  14622. }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
  14623. //Weird return from a datastore, where a start + count > maxOptions
  14624. // implies maxOptions isn't really valid and we have to go into faking it.
  14625. //And more or less assume more if count == results.length
  14626. displayMore = true;
  14627. }
  14628. }else if(dataObject.count == results.length){
  14629. //Don't know the size, so we do the best we can based off count alone.
  14630. //So, if we have an exact match to count, assume more.
  14631. displayMore = true;
  14632. }
  14633. this.nextButton.style.display = displayMore ? "" : "none";
  14634. dojo.attr(this.nextButton,"id", this.id + "_next");
  14635. return this.domNode.childNodes;
  14636. },
  14637. clearResultList: function(){
  14638. // summary:
  14639. // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
  14640. while(this.domNode.childNodes.length>2){
  14641. this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
  14642. }
  14643. this._blurOptionNode();
  14644. },
  14645. _onMouseDown: function(/*Event*/ evt){
  14646. dojo.stopEvent(evt);
  14647. },
  14648. _onMouseUp: function(/*Event*/ evt){
  14649. if(evt.target === this.domNode || !this._highlighted_option){
  14650. // !this._highlighted_option check to prevent immediate selection when menu appears on top
  14651. // of <input>, see #9898. Note that _HasDropDown also has code to prevent this.
  14652. return;
  14653. }else if(evt.target == this.previousButton){
  14654. this._blurOptionNode();
  14655. this.onPage(-1);
  14656. }else if(evt.target == this.nextButton){
  14657. this._blurOptionNode();
  14658. this.onPage(1);
  14659. }else{
  14660. var tgt = evt.target;
  14661. // while the clicked node is inside the div
  14662. while(!tgt.item){
  14663. // recurse to the top
  14664. tgt = tgt.parentNode;
  14665. }
  14666. this._setValueAttr({ target: tgt }, true);
  14667. }
  14668. },
  14669. _onMouseOver: function(/*Event*/ evt){
  14670. if(evt.target === this.domNode){ return; }
  14671. var tgt = evt.target;
  14672. if(!(tgt == this.previousButton || tgt == this.nextButton)){
  14673. // while the clicked node is inside the div
  14674. while(!tgt.item){
  14675. // recurse to the top
  14676. tgt = tgt.parentNode;
  14677. }
  14678. }
  14679. this._focusOptionNode(tgt);
  14680. },
  14681. _onMouseOut: function(/*Event*/ evt){
  14682. if(evt.target === this.domNode){ return; }
  14683. this._blurOptionNode();
  14684. },
  14685. _focusOptionNode: function(/*DomNode*/ node){
  14686. // summary:
  14687. // Does the actual highlight.
  14688. if(this._highlighted_option != node){
  14689. this._blurOptionNode();
  14690. this._highlighted_option = node;
  14691. dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
  14692. }
  14693. },
  14694. _blurOptionNode: function(){
  14695. // summary:
  14696. // Removes highlight on highlighted option.
  14697. if(this._highlighted_option){
  14698. dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
  14699. this._highlighted_option = null;
  14700. }
  14701. },
  14702. _highlightNextOption: function(){
  14703. // summary:
  14704. // Highlight the item just below the current selection.
  14705. // If nothing selected, highlight first option.
  14706. // because each press of a button clears the menu,
  14707. // the highlighted option sometimes becomes detached from the menu!
  14708. // test to see if the option has a parent to see if this is the case.
  14709. if(!this.getHighlightedOption()){
  14710. var fc = this.domNode.firstChild;
  14711. this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
  14712. }else{
  14713. var ns = this._highlighted_option.nextSibling;
  14714. if(ns && ns.style.display != "none"){
  14715. this._focusOptionNode(ns);
  14716. }else{
  14717. this.highlightFirstOption();
  14718. }
  14719. }
  14720. // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
  14721. dojo.window.scrollIntoView(this._highlighted_option);
  14722. },
  14723. highlightFirstOption: function(){
  14724. // summary:
  14725. // Highlight the first real item in the list (not Previous Choices).
  14726. var first = this.domNode.firstChild;
  14727. var second = first.nextSibling;
  14728. this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
  14729. dojo.window.scrollIntoView(this._highlighted_option);
  14730. },
  14731. highlightLastOption: function(){
  14732. // summary:
  14733. // Highlight the last real item in the list (not More Choices).
  14734. this._focusOptionNode(this.domNode.lastChild.previousSibling);
  14735. dojo.window.scrollIntoView(this._highlighted_option);
  14736. },
  14737. _highlightPrevOption: function(){
  14738. // summary:
  14739. // Highlight the item just above the current selection.
  14740. // If nothing selected, highlight last option (if
  14741. // you select Previous and try to keep scrolling up the list).
  14742. if(!this.getHighlightedOption()){
  14743. var lc = this.domNode.lastChild;
  14744. this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
  14745. }else{
  14746. var ps = this._highlighted_option.previousSibling;
  14747. if(ps && ps.style.display != "none"){
  14748. this._focusOptionNode(ps);
  14749. }else{
  14750. this.highlightLastOption();
  14751. }
  14752. }
  14753. dojo.window.scrollIntoView(this._highlighted_option);
  14754. },
  14755. _page: function(/*Boolean*/ up){
  14756. // summary:
  14757. // Handles page-up and page-down keypresses
  14758. var scrollamount = 0;
  14759. var oldscroll = this.domNode.scrollTop;
  14760. var height = dojo.style(this.domNode, "height");
  14761. // if no item is highlighted, highlight the first option
  14762. if(!this.getHighlightedOption()){
  14763. this._highlightNextOption();
  14764. }
  14765. while(scrollamount<height){
  14766. if(up){
  14767. // stop at option 1
  14768. if(!this.getHighlightedOption().previousSibling ||
  14769. this._highlighted_option.previousSibling.style.display == "none"){
  14770. break;
  14771. }
  14772. this._highlightPrevOption();
  14773. }else{
  14774. // stop at last option
  14775. if(!this.getHighlightedOption().nextSibling ||
  14776. this._highlighted_option.nextSibling.style.display == "none"){
  14777. break;
  14778. }
  14779. this._highlightNextOption();
  14780. }
  14781. // going backwards
  14782. var newscroll=this.domNode.scrollTop;
  14783. scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
  14784. oldscroll=newscroll;
  14785. }
  14786. },
  14787. pageUp: function(){
  14788. // summary:
  14789. // Handles pageup keypress.
  14790. // TODO: just call _page directly from handleKey().
  14791. // tags:
  14792. // private
  14793. this._page(true);
  14794. },
  14795. pageDown: function(){
  14796. // summary:
  14797. // Handles pagedown keypress.
  14798. // TODO: just call _page directly from handleKey().
  14799. // tags:
  14800. // private
  14801. this._page(false);
  14802. },
  14803. getHighlightedOption: function(){
  14804. // summary:
  14805. // Returns the highlighted option.
  14806. var ho = this._highlighted_option;
  14807. return (ho && ho.parentNode) ? ho : null;
  14808. },
  14809. handleKey: function(evt){
  14810. // summary:
  14811. // Handle keystroke event forwarded from ComboBox, returning false if it's
  14812. // a keystroke I recognize and process, true otherwise.
  14813. switch(evt.charOrCode){
  14814. case dojo.keys.DOWN_ARROW:
  14815. this._highlightNextOption();
  14816. return false;
  14817. case dojo.keys.PAGE_DOWN:
  14818. this.pageDown();
  14819. return false;
  14820. case dojo.keys.UP_ARROW:
  14821. this._highlightPrevOption();
  14822. return false;
  14823. case dojo.keys.PAGE_UP:
  14824. this.pageUp();
  14825. return false;
  14826. default:
  14827. return true;
  14828. }
  14829. }
  14830. }
  14831. );
  14832. dojo.declare(
  14833. "dijit.form.ComboBox",
  14834. [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
  14835. {
  14836. // summary:
  14837. // Auto-completing text box, and base class for dijit.form.FilteringSelect.
  14838. //
  14839. // description:
  14840. // The drop down box's values are populated from an class called
  14841. // a data provider, which returns a list of values based on the characters
  14842. // that the user has typed into the input box.
  14843. // If OPTION tags are used as the data provider via markup,
  14844. // then the OPTION tag's child text node is used as the widget value
  14845. // when selected. The OPTION tag's value attribute is ignored.
  14846. // To set the default value when using OPTION tags, specify the selected
  14847. // attribute on 1 of the child OPTION tags.
  14848. //
  14849. // Some of the options to the ComboBox are actually arguments to the data
  14850. // provider.
  14851. _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  14852. // summary:
  14853. // Hook so set('value', value) works.
  14854. // description:
  14855. // Sets the value of the select.
  14856. this._set("item", null); // value not looked up in store
  14857. if(!value){ value = ''; } // null translates to blank
  14858. dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
  14859. }
  14860. }
  14861. );
  14862. dojo.declare("dijit.form._ComboBoxDataStore", null, {
  14863. // summary:
  14864. // Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
  14865. //
  14866. // description:
  14867. // Provides a store for inlined data like:
  14868. //
  14869. // | <select>
  14870. // | <option value="AL">Alabama</option>
  14871. // | ...
  14872. //
  14873. // Actually. just implements the subset of dojo.data.Read/Notification
  14874. // needed for ComboBox and FilteringSelect to work.
  14875. //
  14876. // Note that an item is just a pointer to the <option> DomNode.
  14877. constructor: function( /*DomNode*/ root){
  14878. this.root = root;
  14879. if(root.tagName != "SELECT" && root.firstChild){
  14880. root = dojo.query("select", root);
  14881. if(root.length > 0){ // SELECT is a child of srcNodeRef
  14882. root = root[0];
  14883. }else{ // no select, so create 1 to parent the option tags to define selectedIndex
  14884. this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
  14885. root = this.root.firstChild;
  14886. }
  14887. this.root = root;
  14888. }
  14889. dojo.query("> option", root).forEach(function(node){
  14890. // TODO: this was added in #3858 but unclear why/if it's needed; doesn't seem to be.
  14891. // If it is needed then can we just hide the select itself instead?
  14892. //node.style.display="none";
  14893. node.innerHTML = dojo.trim(node.innerHTML);
  14894. });
  14895. },
  14896. getValue: function( /*item*/ item,
  14897. /*attribute-name-string*/ attribute,
  14898. /*value?*/ defaultValue){
  14899. return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
  14900. },
  14901. isItemLoaded: function(/*anything*/ something){
  14902. return true;
  14903. },
  14904. getFeatures: function(){
  14905. return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
  14906. },
  14907. _fetchItems: function( /*Object*/ args,
  14908. /*Function*/ findCallback,
  14909. /*Function*/ errorCallback){
  14910. // summary:
  14911. // See dojo.data.util.simpleFetch.fetch()
  14912. if(!args.query){ args.query = {}; }
  14913. if(!args.query.name){ args.query.name = ""; }
  14914. if(!args.queryOptions){ args.queryOptions = {}; }
  14915. var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
  14916. items = dojo.query("> option", this.root).filter(function(option){
  14917. return (option.innerText || option.textContent || '').match(matcher);
  14918. } );
  14919. if(args.sort){
  14920. items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
  14921. }
  14922. findCallback(items, args);
  14923. },
  14924. close: function(/*dojo.data.api.Request || args || null*/ request){
  14925. return;
  14926. },
  14927. getLabel: function(/*item*/ item){
  14928. return item.innerHTML;
  14929. },
  14930. getIdentity: function(/*item*/ item){
  14931. return dojo.attr(item, "value");
  14932. },
  14933. fetchItemByIdentity: function(/*Object*/ args){
  14934. // summary:
  14935. // Given the identity of an item, this method returns the item that has
  14936. // that identity through the onItem callback.
  14937. // Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
  14938. //
  14939. // description:
  14940. // Given arguments like:
  14941. //
  14942. // | {identity: "CA", onItem: function(item){...}
  14943. //
  14944. // Call `onItem()` with the DOM node `<option value="CA">California</option>`
  14945. var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
  14946. args.onItem(item);
  14947. },
  14948. fetchSelectedItem: function(){
  14949. // summary:
  14950. // Get the option marked as selected, like `<option selected>`.
  14951. // Not part of dojo.data API.
  14952. var root = this.root,
  14953. si = root.selectedIndex;
  14954. return typeof si == "number"
  14955. ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
  14956. : null; // dojo.data.Item
  14957. }
  14958. });
  14959. //Mix in the simple fetch implementation to this class.
  14960. dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
  14961. }
  14962. if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  14963. dojo._hasResource["dijit.form.FilteringSelect"] = true;
  14964. dojo.provide("dijit.form.FilteringSelect");
  14965. dojo.declare(
  14966. "dijit.form.FilteringSelect",
  14967. [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
  14968. {
  14969. // summary:
  14970. // An enhanced version of the HTML SELECT tag, populated dynamically
  14971. //
  14972. // description:
  14973. // An enhanced version of the HTML SELECT tag, populated dynamically. It works
  14974. // very nicely with very large data sets because it can load and page data as needed.
  14975. // It also resembles ComboBox, but does not allow values outside of the provided ones.
  14976. // If OPTION tags are used as the data provider via markup, then the
  14977. // OPTION tag's child text node is used as the displayed value when selected
  14978. // while the OPTION tag's value attribute is used as the widget value on form submit.
  14979. // To set the default value when using OPTION tags, specify the selected
  14980. // attribute on 1 of the child OPTION tags.
  14981. //
  14982. // Similar features:
  14983. // - There is a drop down list of possible values.
  14984. // - You can only enter a value from the drop down list. (You can't
  14985. // enter an arbitrary value.)
  14986. // - The value submitted with the form is the hidden value (ex: CA),
  14987. // not the displayed value a.k.a. label (ex: California)
  14988. //
  14989. // Enhancements over plain HTML version:
  14990. // - If you type in some text then it will filter down the list of
  14991. // possible values in the drop down list.
  14992. // - List can be specified either as a static list or via a javascript
  14993. // function (that can get the list from a server)
  14994. // required: Boolean
  14995. // True (default) if user is required to enter a value into this field.
  14996. required: true,
  14997. _lastDisplayedValue: "",
  14998. _isValidSubset: function(){
  14999. return this._opened;
  15000. },
  15001. isValid: function(){
  15002. // Overrides ValidationTextBox.isValid()
  15003. return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
  15004. },
  15005. _refreshState: function(){
  15006. if(!this.searchTimer){ // state will be refreshed after results are returned
  15007. this.inherited(arguments);
  15008. }
  15009. },
  15010. _callbackSetLabel: function(
  15011. /*Array*/ result,
  15012. /*Object*/ dataObject,
  15013. /*Boolean?*/ priorityChange){
  15014. // summary:
  15015. // Callback from dojo.data after lookup of user entered value finishes
  15016. // setValue does a synchronous lookup,
  15017. // so it calls _callbackSetLabel directly,
  15018. // and so does not pass dataObject
  15019. // still need to test against _lastQuery in case it came too late
  15020. if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
  15021. return;
  15022. }
  15023. if(!result.length){
  15024. //#3268: don't modify display value on bad input
  15025. //#3285: change CSS to indicate error
  15026. this.valueNode.value = "";
  15027. dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
  15028. this._set("item", null);
  15029. this.validate(this._focused);
  15030. }else{
  15031. this.set('item', result[0], priorityChange);
  15032. }
  15033. },
  15034. _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
  15035. // Callback when a data store query completes.
  15036. // Overrides ComboBox._openResultList()
  15037. // #3285: tap into search callback to see if user's query resembles a match
  15038. if(dataObject.query[this.searchAttr] != this._lastQuery){
  15039. return;
  15040. }
  15041. dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
  15042. if(this.item === undefined){ // item == undefined for keyboard search
  15043. // If the search returned no items that means that the user typed
  15044. // in something invalid (and they can't make it valid by typing more characters),
  15045. // so flag the FilteringSelect as being in an invalid state
  15046. this.validate(true);
  15047. }
  15048. },
  15049. _getValueAttr: function(){
  15050. // summary:
  15051. // Hook for get('value') to work.
  15052. // don't get the textbox value but rather the previously set hidden value.
  15053. // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
  15054. return this.valueNode.value;
  15055. },
  15056. _getValueField: function(){
  15057. // Overrides ComboBox._getValueField()
  15058. return "value";
  15059. },
  15060. _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
  15061. // summary:
  15062. // Hook so set('value', value) works.
  15063. // description:
  15064. // Sets the value of the select.
  15065. // Also sets the label to the corresponding value by reverse lookup.
  15066. if(!this._onChangeActive){ priorityChange = null; }
  15067. this._lastQuery = value;
  15068. if(value === null || value === ''){
  15069. this._setDisplayedValueAttr('', priorityChange);
  15070. return;
  15071. }
  15072. //#3347: fetchItemByIdentity if no keyAttr specified
  15073. var self = this;
  15074. this.store.fetchItemByIdentity({
  15075. identity: value,
  15076. onItem: function(item){
  15077. self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
  15078. }
  15079. });
  15080. },
  15081. _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
  15082. // summary:
  15083. // Set the displayed valued in the input box, and the hidden value
  15084. // that gets submitted, based on a dojo.data store item.
  15085. // description:
  15086. // Users shouldn't call this function; they should be calling
  15087. // set('item', value)
  15088. // tags:
  15089. // private
  15090. this.inherited(arguments);
  15091. this.valueNode.value = this.value;
  15092. this._lastDisplayedValue = this.textbox.value;
  15093. },
  15094. _getDisplayQueryString: function(/*String*/ text){
  15095. return text.replace(/([\\\*\?])/g, "\\$1");
  15096. },
  15097. _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
  15098. // summary:
  15099. // Hook so set('displayedValue', label) works.
  15100. // description:
  15101. // Sets textbox to display label. Also performs reverse lookup
  15102. // to set the hidden value. label should corresponding to item.searchAttr.
  15103. if(label == null){ label = ''; }
  15104. // This is called at initialization along with every custom setter.
  15105. // Usually (or always?) the call can be ignored. If it needs to be
  15106. // processed then at least make sure that the XHR request doesn't trigger an onChange()
  15107. // event, even if it returns after creation has finished
  15108. if(!this._created){
  15109. if(!("displayedValue" in this.params)){
  15110. return;
  15111. }
  15112. priorityChange = false;
  15113. }
  15114. // Do a reverse lookup to map the specified displayedValue to the hidden value.
  15115. // Note that if there's a custom labelFunc() this code
  15116. if(this.store){
  15117. this.closeDropDown();
  15118. var query = dojo.clone(this.query); // #6196: populate query with user-specifics
  15119. // escape meta characters of dojo.data.util.filter.patternToRegExp().
  15120. this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
  15121. // If the label is not valid, the callback will never set it,
  15122. // so the last valid value will get the warning textbox. Set the
  15123. // textbox value now so that the impending warning will make
  15124. // sense to the user
  15125. this.textbox.value = label;
  15126. this._lastDisplayedValue = label;
  15127. this._set("displayedValue", label); // for watch("displayedValue") notification
  15128. var _this = this;
  15129. var fetch = {
  15130. query: query,
  15131. queryOptions: {
  15132. ignoreCase: this.ignoreCase,
  15133. deep: true
  15134. },
  15135. onComplete: function(result, dataObject){
  15136. _this._fetchHandle = null;
  15137. dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
  15138. },
  15139. onError: function(errText){
  15140. _this._fetchHandle = null;
  15141. console.error('dijit.form.FilteringSelect: ' + errText);
  15142. dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
  15143. }
  15144. };
  15145. dojo.mixin(fetch, this.fetchProperties);
  15146. this._fetchHandle = this.store.fetch(fetch);
  15147. }
  15148. },
  15149. undo: function(){
  15150. this.set('displayedValue', this._lastDisplayedValue);
  15151. }
  15152. }
  15153. );
  15154. }
  15155. if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  15156. dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
  15157. dojo.provide("dojo.data.ItemFileReadStore");
  15158. dojo.declare("dojo.data.ItemFileReadStore", null,{
  15159. // summary:
  15160. // The ItemFileReadStore implements the dojo.data.api.Read API and reads
  15161. // data from JSON files that have contents in this format --
  15162. // { items: [
  15163. // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
  15164. // { name:'Fozzie Bear', wears:['hat', 'tie']},
  15165. // { name:'Miss Piggy', pets:'Foo-Foo'}
  15166. // ]}
  15167. // Note that it can also contain an 'identifer' property that specified which attribute on the items
  15168. // in the array of items that acts as the unique identifier for that item.
  15169. //
  15170. constructor: function(/* Object */ keywordParameters){
  15171. // summary: constructor
  15172. // keywordParameters: {url: String}
  15173. // keywordParameters: {data: jsonObject}
  15174. // keywordParameters: {typeMap: object)
  15175. // The structure of the typeMap object is as follows:
  15176. // {
  15177. // type0: function || object,
  15178. // type1: function || object,
  15179. // ...
  15180. // typeN: function || object
  15181. // }
  15182. // Where if it is a function, it is assumed to be an object constructor that takes the
  15183. // value of _value as the initialization parameters. If it is an object, then it is assumed
  15184. // to be an object of general form:
  15185. // {
  15186. // type: function, //constructor.
  15187. // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
  15188. // }
  15189. this._arrayOfAllItems = [];
  15190. this._arrayOfTopLevelItems = [];
  15191. this._loadFinished = false;
  15192. this._jsonFileUrl = keywordParameters.url;
  15193. this._ccUrl = keywordParameters.url;
  15194. this.url = keywordParameters.url;
  15195. this._jsonData = keywordParameters.data;
  15196. this.data = null;
  15197. this._datatypeMap = keywordParameters.typeMap || {};
  15198. if(!this._datatypeMap['Date']){
  15199. //If no default mapping for dates, then set this as default.
  15200. //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
  15201. //of generically representing dates.
  15202. this._datatypeMap['Date'] = {
  15203. type: Date,
  15204. deserialize: function(value){
  15205. return dojo.date.stamp.fromISOString(value);
  15206. }
  15207. };
  15208. }
  15209. this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
  15210. this._itemsByIdentity = null;
  15211. this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
  15212. this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
  15213. this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
  15214. this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
  15215. this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
  15216. this._queuedFetches = [];
  15217. if(keywordParameters.urlPreventCache !== undefined){
  15218. this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
  15219. }
  15220. if(keywordParameters.hierarchical !== undefined){
  15221. this.hierarchical = keywordParameters.hierarchical?true:false;
  15222. }
  15223. if(keywordParameters.clearOnClose){
  15224. this.clearOnClose = true;
  15225. }
  15226. if("failOk" in keywordParameters){
  15227. this.failOk = keywordParameters.failOk?true:false;
  15228. }
  15229. },
  15230. url: "", // use "" rather than undefined for the benefit of the parser (#3539)
  15231. //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
  15232. //when clearOnClose and close is used.
  15233. _ccUrl: "",
  15234. data: null, // define this so that the parser can populate it
  15235. typeMap: null, //Define so parser can populate.
  15236. //Parameter to allow users to specify if a close call should force a reload or not.
  15237. //By default, it retains the old behavior of not clearing if close is called. But
  15238. //if set true, the store will be reset to default state. Note that by doing this,
  15239. //all item handles will become invalid and a new fetch must be issued.
  15240. clearOnClose: false,
  15241. //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
  15242. //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
  15243. //Added for tracker: #6072
  15244. urlPreventCache: false,
  15245. //Parameter for specifying that it is OK for the xhrGet call to fail silently.
  15246. failOk: false,
  15247. //Parameter to indicate to process data from the url as hierarchical
  15248. //(data items can contain other data items in js form). Default is true
  15249. //for backwards compatibility. False means only root items are processed
  15250. //as items, all child objects outside of type-mapped objects and those in
  15251. //specific reference format, are left straight JS data objects.
  15252. hierarchical: true,
  15253. _assertIsItem: function(/* item */ item){
  15254. // summary:
  15255. // This function tests whether the item passed in is indeed an item in the store.
  15256. // item:
  15257. // The item to test for being contained by the store.
  15258. if(!this.isItem(item)){
  15259. throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
  15260. }
  15261. },
  15262. _assertIsAttribute: function(/* attribute-name-string */ attribute){
  15263. // summary:
  15264. // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
  15265. // attribute:
  15266. // The attribute to test for being contained by the store.
  15267. if(typeof attribute !== "string"){
  15268. throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
  15269. }
  15270. },
  15271. getValue: function( /* item */ item,
  15272. /* attribute-name-string */ attribute,
  15273. /* value? */ defaultValue){
  15274. // summary:
  15275. // See dojo.data.api.Read.getValue()
  15276. var values = this.getValues(item, attribute);
  15277. return (values.length > 0)?values[0]:defaultValue; // mixed
  15278. },
  15279. getValues: function(/* item */ item,
  15280. /* attribute-name-string */ attribute){
  15281. // summary:
  15282. // See dojo.data.api.Read.getValues()
  15283. this._assertIsItem(item);
  15284. this._assertIsAttribute(attribute);
  15285. // Clone it before returning. refs: #10474
  15286. return (item[attribute] || []).slice(0); // Array
  15287. },
  15288. getAttributes: function(/* item */ item){
  15289. // summary:
  15290. // See dojo.data.api.Read.getAttributes()
  15291. this._assertIsItem(item);
  15292. var attributes = [];
  15293. for(var key in item){
  15294. // Save off only the real item attributes, not the special id marks for O(1) isItem.
  15295. if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
  15296. attributes.push(key);
  15297. }
  15298. }
  15299. return attributes; // Array
  15300. },
  15301. hasAttribute: function( /* item */ item,
  15302. /* attribute-name-string */ attribute){
  15303. // summary:
  15304. // See dojo.data.api.Read.hasAttribute()
  15305. this._assertIsItem(item);
  15306. this._assertIsAttribute(attribute);
  15307. return (attribute in item);
  15308. },
  15309. containsValue: function(/* item */ item,
  15310. /* attribute-name-string */ attribute,
  15311. /* anything */ value){
  15312. // summary:
  15313. // See dojo.data.api.Read.containsValue()
  15314. var regexp = undefined;
  15315. if(typeof value === "string"){
  15316. regexp = dojo.data.util.filter.patternToRegExp(value, false);
  15317. }
  15318. return this._containsValue(item, attribute, value, regexp); //boolean.
  15319. },
  15320. _containsValue: function( /* item */ item,
  15321. /* attribute-name-string */ attribute,
  15322. /* anything */ value,
  15323. /* RegExp?*/ regexp){
  15324. // summary:
  15325. // Internal function for looking at the values contained by the item.
  15326. // description:
  15327. // Internal function for looking at the values contained by the item. This
  15328. // function allows for denoting if the comparison should be case sensitive for
  15329. // strings or not (for handling filtering cases where string case should not matter)
  15330. //
  15331. // item:
  15332. // The data item to examine for attribute values.
  15333. // attribute:
  15334. // The attribute to inspect.
  15335. // value:
  15336. // The value to match.
  15337. // regexp:
  15338. // Optional regular expression generated off value if value was of string type to handle wildcarding.
  15339. // If present and attribute values are string, then it can be used for comparison instead of 'value'
  15340. return dojo.some(this.getValues(item, attribute), function(possibleValue){
  15341. if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
  15342. if(possibleValue.toString().match(regexp)){
  15343. return true; // Boolean
  15344. }
  15345. }else if(value === possibleValue){
  15346. return true; // Boolean
  15347. }
  15348. });
  15349. },
  15350. isItem: function(/* anything */ something){
  15351. // summary:
  15352. // See dojo.data.api.Read.isItem()
  15353. if(something && something[this._storeRefPropName] === this){
  15354. if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
  15355. return true;
  15356. }
  15357. }
  15358. return false; // Boolean
  15359. },
  15360. isItemLoaded: function(/* anything */ something){
  15361. // summary:
  15362. // See dojo.data.api.Read.isItemLoaded()
  15363. return this.isItem(something); //boolean
  15364. },
  15365. loadItem: function(/* object */ keywordArgs){
  15366. // summary:
  15367. // See dojo.data.api.Read.loadItem()
  15368. this._assertIsItem(keywordArgs.item);
  15369. },
  15370. getFeatures: function(){
  15371. // summary:
  15372. // See dojo.data.api.Read.getFeatures()
  15373. return this._features; //Object
  15374. },
  15375. getLabel: function(/* item */ item){
  15376. // summary:
  15377. // See dojo.data.api.Read.getLabel()
  15378. if(this._labelAttr && this.isItem(item)){
  15379. return this.getValue(item,this._labelAttr); //String
  15380. }
  15381. return undefined; //undefined
  15382. },
  15383. getLabelAttributes: function(/* item */ item){
  15384. // summary:
  15385. // See dojo.data.api.Read.getLabelAttributes()
  15386. if(this._labelAttr){
  15387. return [this._labelAttr]; //array
  15388. }
  15389. return null; //null
  15390. },
  15391. _fetchItems: function( /* Object */ keywordArgs,
  15392. /* Function */ findCallback,
  15393. /* Function */ errorCallback){
  15394. // summary:
  15395. // See dojo.data.util.simpleFetch.fetch()
  15396. var self = this,
  15397. filter = function(requestArgs, arrayOfItems){
  15398. var items = [],
  15399. i, key;
  15400. if(requestArgs.query){
  15401. var value,
  15402. ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
  15403. //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
  15404. //same value for each item examined. Much more efficient.
  15405. var regexpList = {};
  15406. for(key in requestArgs.query){
  15407. value = requestArgs.query[key];
  15408. if(typeof value === "string"){
  15409. regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
  15410. }else if(value instanceof RegExp){
  15411. regexpList[key] = value;
  15412. }
  15413. }
  15414. for(i = 0; i < arrayOfItems.length; ++i){
  15415. var match = true;
  15416. var candidateItem = arrayOfItems[i];
  15417. if(candidateItem === null){
  15418. match = false;
  15419. }else{
  15420. for(key in requestArgs.query){
  15421. value = requestArgs.query[key];
  15422. if(!self._containsValue(candidateItem, key, value, regexpList[key])){
  15423. match = false;
  15424. }
  15425. }
  15426. }
  15427. if(match){
  15428. items.push(candidateItem);
  15429. }
  15430. }
  15431. findCallback(items, requestArgs);
  15432. }else{
  15433. // We want a copy to pass back in case the parent wishes to sort the array.
  15434. // We shouldn't allow resort of the internal list, so that multiple callers
  15435. // can get lists and sort without affecting each other. We also need to
  15436. // filter out any null values that have been left as a result of deleteItem()
  15437. // calls in ItemFileWriteStore.
  15438. for(i = 0; i < arrayOfItems.length; ++i){
  15439. var item = arrayOfItems[i];
  15440. if(item !== null){
  15441. items.push(item);
  15442. }
  15443. }
  15444. findCallback(items, requestArgs);
  15445. }
  15446. };
  15447. if(this._loadFinished){
  15448. filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
  15449. }else{
  15450. //Do a check on the JsonFileUrl and crosscheck it.
  15451. //If it doesn't match the cross-check, it needs to be updated
  15452. //This allows for either url or _jsonFileUrl to he changed to
  15453. //reset the store load location. Done this way for backwards
  15454. //compatibility. People use _jsonFileUrl (even though officially
  15455. //private.
  15456. if(this._jsonFileUrl !== this._ccUrl){
  15457. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  15458. "To change the url, set the url property of the store," +
  15459. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  15460. this._ccUrl = this._jsonFileUrl;
  15461. this.url = this._jsonFileUrl;
  15462. }else if(this.url !== this._ccUrl){
  15463. this._jsonFileUrl = this.url;
  15464. this._ccUrl = this.url;
  15465. }
  15466. //See if there was any forced reset of data.
  15467. if(this.data != null){
  15468. this._jsonData = this.data;
  15469. this.data = null;
  15470. }
  15471. if(this._jsonFileUrl){
  15472. //If fetches come in before the loading has finished, but while
  15473. //a load is in progress, we have to defer the fetching to be
  15474. //invoked in the callback.
  15475. if(this._loadInProgress){
  15476. this._queuedFetches.push({args: keywordArgs, filter: filter});
  15477. }else{
  15478. this._loadInProgress = true;
  15479. var getArgs = {
  15480. url: self._jsonFileUrl,
  15481. handleAs: "json-comment-optional",
  15482. preventCache: this.urlPreventCache,
  15483. failOk: this.failOk
  15484. };
  15485. var getHandler = dojo.xhrGet(getArgs);
  15486. getHandler.addCallback(function(data){
  15487. try{
  15488. self._getItemsFromLoadedData(data);
  15489. self._loadFinished = true;
  15490. self._loadInProgress = false;
  15491. filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
  15492. self._handleQueuedFetches();
  15493. }catch(e){
  15494. self._loadFinished = true;
  15495. self._loadInProgress = false;
  15496. errorCallback(e, keywordArgs);
  15497. }
  15498. });
  15499. getHandler.addErrback(function(error){
  15500. self._loadInProgress = false;
  15501. errorCallback(error, keywordArgs);
  15502. });
  15503. //Wire up the cancel to abort of the request
  15504. //This call cancel on the deferred if it hasn't been called
  15505. //yet and then will chain to the simple abort of the
  15506. //simpleFetch keywordArgs
  15507. var oldAbort = null;
  15508. if(keywordArgs.abort){
  15509. oldAbort = keywordArgs.abort;
  15510. }
  15511. keywordArgs.abort = function(){
  15512. var df = getHandler;
  15513. if(df && df.fired === -1){
  15514. df.cancel();
  15515. df = null;
  15516. }
  15517. if(oldAbort){
  15518. oldAbort.call(keywordArgs);
  15519. }
  15520. };
  15521. }
  15522. }else if(this._jsonData){
  15523. try{
  15524. this._loadFinished = true;
  15525. this._getItemsFromLoadedData(this._jsonData);
  15526. this._jsonData = null;
  15527. filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
  15528. }catch(e){
  15529. errorCallback(e, keywordArgs);
  15530. }
  15531. }else{
  15532. errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
  15533. }
  15534. }
  15535. },
  15536. _handleQueuedFetches: function(){
  15537. // summary:
  15538. // Internal function to execute delayed request in the store.
  15539. //Execute any deferred fetches now.
  15540. if(this._queuedFetches.length > 0){
  15541. for(var i = 0; i < this._queuedFetches.length; i++){
  15542. var fData = this._queuedFetches[i],
  15543. delayedQuery = fData.args,
  15544. delayedFilter = fData.filter;
  15545. if(delayedFilter){
  15546. delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
  15547. }else{
  15548. this.fetchItemByIdentity(delayedQuery);
  15549. }
  15550. }
  15551. this._queuedFetches = [];
  15552. }
  15553. },
  15554. _getItemsArray: function(/*object?*/queryOptions){
  15555. // summary:
  15556. // Internal function to determine which list of items to search over.
  15557. // queryOptions: The query options parameter, if any.
  15558. if(queryOptions && queryOptions.deep){
  15559. return this._arrayOfAllItems;
  15560. }
  15561. return this._arrayOfTopLevelItems;
  15562. },
  15563. close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
  15564. // summary:
  15565. // See dojo.data.api.Read.close()
  15566. if(this.clearOnClose &&
  15567. this._loadFinished &&
  15568. !this._loadInProgress){
  15569. //Reset all internalsback to default state. This will force a reload
  15570. //on next fetch. This also checks that the data or url param was set
  15571. //so that the store knows it can get data. Without one of those being set,
  15572. //the next fetch will trigger an error.
  15573. if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
  15574. (this.url == "" || this.url == null)
  15575. ) && this.data == null){
  15576. console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " +
  15577. " information has not been provided." +
  15578. " Please set 'url' or 'data' to the appropriate value before" +
  15579. " the next fetch");
  15580. }
  15581. this._arrayOfAllItems = [];
  15582. this._arrayOfTopLevelItems = [];
  15583. this._loadFinished = false;
  15584. this._itemsByIdentity = null;
  15585. this._loadInProgress = false;
  15586. this._queuedFetches = [];
  15587. }
  15588. },
  15589. _getItemsFromLoadedData: function(/* Object */ dataObject){
  15590. // summary:
  15591. // Function to parse the loaded data into item format and build the internal items array.
  15592. // description:
  15593. // Function to parse the loaded data into item format and build the internal items array.
  15594. //
  15595. // dataObject:
  15596. // The JS data object containing the raw data to convery into item format.
  15597. //
  15598. // returns: array
  15599. // Array of items in store item format.
  15600. // First, we define a couple little utility functions...
  15601. var addingArrays = false,
  15602. self = this;
  15603. function valueIsAnItem(/* anything */ aValue){
  15604. // summary:
  15605. // Given any sort of value that could be in the raw json data,
  15606. // return true if we should interpret the value as being an
  15607. // item itself, rather than a literal value or a reference.
  15608. // example:
  15609. // | false == valueIsAnItem("Kermit");
  15610. // | false == valueIsAnItem(42);
  15611. // | false == valueIsAnItem(new Date());
  15612. // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
  15613. // | false == valueIsAnItem({_reference:'Kermit'});
  15614. // | true == valueIsAnItem({name:'Kermit', color:'green'});
  15615. // | true == valueIsAnItem({iggy:'pop'});
  15616. // | true == valueIsAnItem({foo:42});
  15617. var isItem = (
  15618. (aValue !== null) &&
  15619. (typeof aValue === "object") &&
  15620. (!dojo.isArray(aValue) || addingArrays) &&
  15621. (!dojo.isFunction(aValue)) &&
  15622. (aValue.constructor == Object || dojo.isArray(aValue)) &&
  15623. (typeof aValue._reference === "undefined") &&
  15624. (typeof aValue._type === "undefined") &&
  15625. (typeof aValue._value === "undefined") &&
  15626. self.hierarchical
  15627. );
  15628. return isItem;
  15629. }
  15630. function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
  15631. self._arrayOfAllItems.push(anItem);
  15632. for(var attribute in anItem){
  15633. var valueForAttribute = anItem[attribute];
  15634. if(valueForAttribute){
  15635. if(dojo.isArray(valueForAttribute)){
  15636. var valueArray = valueForAttribute;
  15637. for(var k = 0; k < valueArray.length; ++k){
  15638. var singleValue = valueArray[k];
  15639. if(valueIsAnItem(singleValue)){
  15640. addItemAndSubItemsToArrayOfAllItems(singleValue);
  15641. }
  15642. }
  15643. }else{
  15644. if(valueIsAnItem(valueForAttribute)){
  15645. addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
  15646. }
  15647. }
  15648. }
  15649. }
  15650. }
  15651. this._labelAttr = dataObject.label;
  15652. // We need to do some transformations to convert the data structure
  15653. // that we read from the file into a format that will be convenient
  15654. // to work with in memory.
  15655. // Step 1: Walk through the object hierarchy and build a list of all items
  15656. var i,
  15657. item;
  15658. this._arrayOfAllItems = [];
  15659. this._arrayOfTopLevelItems = dataObject.items;
  15660. for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
  15661. item = this._arrayOfTopLevelItems[i];
  15662. if(dojo.isArray(item)){
  15663. addingArrays = true;
  15664. }
  15665. addItemAndSubItemsToArrayOfAllItems(item);
  15666. item[this._rootItemPropName]=true;
  15667. }
  15668. // Step 2: Walk through all the attribute values of all the items,
  15669. // and replace single values with arrays. For example, we change this:
  15670. // { name:'Miss Piggy', pets:'Foo-Foo'}
  15671. // into this:
  15672. // { name:['Miss Piggy'], pets:['Foo-Foo']}
  15673. //
  15674. // We also store the attribute names so we can validate our store
  15675. // reference and item id special properties for the O(1) isItem
  15676. var allAttributeNames = {},
  15677. key;
  15678. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  15679. item = this._arrayOfAllItems[i];
  15680. for(key in item){
  15681. if(key !== this._rootItemPropName){
  15682. var value = item[key];
  15683. if(value !== null){
  15684. if(!dojo.isArray(value)){
  15685. item[key] = [value];
  15686. }
  15687. }else{
  15688. item[key] = [null];
  15689. }
  15690. }
  15691. allAttributeNames[key]=key;
  15692. }
  15693. }
  15694. // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
  15695. // This should go really fast, it will generally never even run the loop.
  15696. while(allAttributeNames[this._storeRefPropName]){
  15697. this._storeRefPropName += "_";
  15698. }
  15699. while(allAttributeNames[this._itemNumPropName]){
  15700. this._itemNumPropName += "_";
  15701. }
  15702. while(allAttributeNames[this._reverseRefMap]){
  15703. this._reverseRefMap += "_";
  15704. }
  15705. // Step 4: Some data files specify an optional 'identifier', which is
  15706. // the name of an attribute that holds the identity of each item.
  15707. // If this data file specified an identifier attribute, then build a
  15708. // hash table of items keyed by the identity of the items.
  15709. var arrayOfValues;
  15710. var identifier = dataObject.identifier;
  15711. if(identifier){
  15712. this._itemsByIdentity = {};
  15713. this._features['dojo.data.api.Identity'] = identifier;
  15714. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  15715. item = this._arrayOfAllItems[i];
  15716. arrayOfValues = item[identifier];
  15717. var identity = arrayOfValues[0];
  15718. if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
  15719. this._itemsByIdentity[identity] = item;
  15720. }else{
  15721. if(this._jsonFileUrl){
  15722. 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 + "]");
  15723. }else if(this._jsonData){
  15724. 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 + "]");
  15725. }
  15726. }
  15727. }
  15728. }else{
  15729. this._features['dojo.data.api.Identity'] = Number;
  15730. }
  15731. // Step 5: Walk through all the items, and set each item's properties
  15732. // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
  15733. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  15734. item = this._arrayOfAllItems[i];
  15735. item[this._storeRefPropName] = this;
  15736. item[this._itemNumPropName] = i;
  15737. }
  15738. // Step 6: We walk through all the attribute values of all the items,
  15739. // looking for type/value literals and item-references.
  15740. //
  15741. // We replace item-references with pointers to items. For example, we change:
  15742. // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  15743. // into this:
  15744. // { name:['Kermit'], friends:[miss_piggy] }
  15745. // (where miss_piggy is the object representing the 'Miss Piggy' item).
  15746. //
  15747. // We replace type/value pairs with typed-literals. For example, we change:
  15748. // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
  15749. // into this:
  15750. // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
  15751. //
  15752. // We also generate the associate map for all items for the O(1) isItem function.
  15753. for(i = 0; i < this._arrayOfAllItems.length; ++i){
  15754. item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  15755. for(key in item){
  15756. arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
  15757. for(var j = 0; j < arrayOfValues.length; ++j){
  15758. value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
  15759. if(value !== null && typeof value == "object"){
  15760. if(("_type" in value) && ("_value" in value)){
  15761. var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
  15762. var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
  15763. if(!mappingObj){
  15764. throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
  15765. }else if(dojo.isFunction(mappingObj)){
  15766. arrayOfValues[j] = new mappingObj(value._value);
  15767. }else if(dojo.isFunction(mappingObj.deserialize)){
  15768. arrayOfValues[j] = mappingObj.deserialize(value._value);
  15769. }else{
  15770. throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
  15771. }
  15772. }
  15773. if(value._reference){
  15774. var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
  15775. if(!dojo.isObject(referenceDescription)){
  15776. // example: 'Miss Piggy'
  15777. // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
  15778. arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
  15779. }else{
  15780. // example: {name:'Miss Piggy'}
  15781. // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
  15782. for(var k = 0; k < this._arrayOfAllItems.length; ++k){
  15783. var candidateItem = this._arrayOfAllItems[k],
  15784. found = true;
  15785. for(var refKey in referenceDescription){
  15786. if(candidateItem[refKey] != referenceDescription[refKey]){
  15787. found = false;
  15788. }
  15789. }
  15790. if(found){
  15791. arrayOfValues[j] = candidateItem;
  15792. }
  15793. }
  15794. }
  15795. if(this.referenceIntegrity){
  15796. var refItem = arrayOfValues[j];
  15797. if(this.isItem(refItem)){
  15798. this._addReferenceToMap(refItem, item, key);
  15799. }
  15800. }
  15801. }else if(this.isItem(value)){
  15802. //It's a child item (not one referenced through _reference).
  15803. //We need to treat this as a referenced item, so it can be cleaned up
  15804. //in a write store easily.
  15805. if(this.referenceIntegrity){
  15806. this._addReferenceToMap(value, item, key);
  15807. }
  15808. }
  15809. }
  15810. }
  15811. }
  15812. }
  15813. },
  15814. _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
  15815. // summary:
  15816. // Method to add an reference map entry for an item and attribute.
  15817. // description:
  15818. // Method to add an reference map entry for an item and attribute. //
  15819. // refItem:
  15820. // The item that is referenced.
  15821. // parentItem:
  15822. // The item that holds the new reference to refItem.
  15823. // attribute:
  15824. // The attribute on parentItem that contains the new reference.
  15825. //Stub function, does nothing. Real processing is in ItemFileWriteStore.
  15826. },
  15827. getIdentity: function(/* item */ item){
  15828. // summary:
  15829. // See dojo.data.api.Identity.getIdentity()
  15830. var identifier = this._features['dojo.data.api.Identity'];
  15831. if(identifier === Number){
  15832. return item[this._itemNumPropName]; // Number
  15833. }else{
  15834. var arrayOfValues = item[identifier];
  15835. if(arrayOfValues){
  15836. return arrayOfValues[0]; // Object || String
  15837. }
  15838. }
  15839. return null; // null
  15840. },
  15841. fetchItemByIdentity: function(/* Object */ keywordArgs){
  15842. // summary:
  15843. // See dojo.data.api.Identity.fetchItemByIdentity()
  15844. // Hasn't loaded yet, we have to trigger the load.
  15845. var item,
  15846. scope;
  15847. if(!this._loadFinished){
  15848. var self = this;
  15849. //Do a check on the JsonFileUrl and crosscheck it.
  15850. //If it doesn't match the cross-check, it needs to be updated
  15851. //This allows for either url or _jsonFileUrl to he changed to
  15852. //reset the store load location. Done this way for backwards
  15853. //compatibility. People use _jsonFileUrl (even though officially
  15854. //private.
  15855. if(this._jsonFileUrl !== this._ccUrl){
  15856. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  15857. "To change the url, set the url property of the store," +
  15858. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  15859. this._ccUrl = this._jsonFileUrl;
  15860. this.url = this._jsonFileUrl;
  15861. }else if(this.url !== this._ccUrl){
  15862. this._jsonFileUrl = this.url;
  15863. this._ccUrl = this.url;
  15864. }
  15865. //See if there was any forced reset of data.
  15866. if(this.data != null && this._jsonData == null){
  15867. this._jsonData = this.data;
  15868. this.data = null;
  15869. }
  15870. if(this._jsonFileUrl){
  15871. if(this._loadInProgress){
  15872. this._queuedFetches.push({args: keywordArgs});
  15873. }else{
  15874. this._loadInProgress = true;
  15875. var getArgs = {
  15876. url: self._jsonFileUrl,
  15877. handleAs: "json-comment-optional",
  15878. preventCache: this.urlPreventCache,
  15879. failOk: this.failOk
  15880. };
  15881. var getHandler = dojo.xhrGet(getArgs);
  15882. getHandler.addCallback(function(data){
  15883. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  15884. try{
  15885. self._getItemsFromLoadedData(data);
  15886. self._loadFinished = true;
  15887. self._loadInProgress = false;
  15888. item = self._getItemByIdentity(keywordArgs.identity);
  15889. if(keywordArgs.onItem){
  15890. keywordArgs.onItem.call(scope, item);
  15891. }
  15892. self._handleQueuedFetches();
  15893. }catch(error){
  15894. self._loadInProgress = false;
  15895. if(keywordArgs.onError){
  15896. keywordArgs.onError.call(scope, error);
  15897. }
  15898. }
  15899. });
  15900. getHandler.addErrback(function(error){
  15901. self._loadInProgress = false;
  15902. if(keywordArgs.onError){
  15903. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  15904. keywordArgs.onError.call(scope, error);
  15905. }
  15906. });
  15907. }
  15908. }else if(this._jsonData){
  15909. // Passed in data, no need to xhr.
  15910. self._getItemsFromLoadedData(self._jsonData);
  15911. self._jsonData = null;
  15912. self._loadFinished = true;
  15913. item = self._getItemByIdentity(keywordArgs.identity);
  15914. if(keywordArgs.onItem){
  15915. scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  15916. keywordArgs.onItem.call(scope, item);
  15917. }
  15918. }
  15919. }else{
  15920. // Already loaded. We can just look it up and call back.
  15921. item = this._getItemByIdentity(keywordArgs.identity);
  15922. if(keywordArgs.onItem){
  15923. scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  15924. keywordArgs.onItem.call(scope, item);
  15925. }
  15926. }
  15927. },
  15928. _getItemByIdentity: function(/* Object */ identity){
  15929. // summary:
  15930. // Internal function to look an item up by its identity map.
  15931. var item = null;
  15932. if(this._itemsByIdentity &&
  15933. Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
  15934. item = this._itemsByIdentity[identity];
  15935. }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
  15936. item = this._arrayOfAllItems[identity];
  15937. }
  15938. if(item === undefined){
  15939. item = null;
  15940. }
  15941. return item; // Object
  15942. },
  15943. getIdentityAttributes: function(/* item */ item){
  15944. // summary:
  15945. // See dojo.data.api.Identity.getIdentityAttributes()
  15946. var identifier = this._features['dojo.data.api.Identity'];
  15947. if(identifier === Number){
  15948. // If (identifier === Number) it means getIdentity() just returns
  15949. // an integer item-number for each item. The dojo.data.api.Identity
  15950. // spec says we need to return null if the identity is not composed
  15951. // of attributes
  15952. return null; // null
  15953. }else{
  15954. return [identifier]; // Array
  15955. }
  15956. },
  15957. _forceLoad: function(){
  15958. // summary:
  15959. // Internal function to force a load of the store if it hasn't occurred yet. This is required
  15960. // for specific functions to work properly.
  15961. var self = this;
  15962. //Do a check on the JsonFileUrl and crosscheck it.
  15963. //If it doesn't match the cross-check, it needs to be updated
  15964. //This allows for either url or _jsonFileUrl to he changed to
  15965. //reset the store load location. Done this way for backwards
  15966. //compatibility. People use _jsonFileUrl (even though officially
  15967. //private.
  15968. if(this._jsonFileUrl !== this._ccUrl){
  15969. dojo.deprecated("dojo.data.ItemFileReadStore: ",
  15970. "To change the url, set the url property of the store," +
  15971. " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
  15972. this._ccUrl = this._jsonFileUrl;
  15973. this.url = this._jsonFileUrl;
  15974. }else if(this.url !== this._ccUrl){
  15975. this._jsonFileUrl = this.url;
  15976. this._ccUrl = this.url;
  15977. }
  15978. //See if there was any forced reset of data.
  15979. if(this.data != null){
  15980. this._jsonData = this.data;
  15981. this.data = null;
  15982. }
  15983. if(this._jsonFileUrl){
  15984. var getArgs = {
  15985. url: this._jsonFileUrl,
  15986. handleAs: "json-comment-optional",
  15987. preventCache: this.urlPreventCache,
  15988. failOk: this.failOk,
  15989. sync: true
  15990. };
  15991. var getHandler = dojo.xhrGet(getArgs);
  15992. getHandler.addCallback(function(data){
  15993. try{
  15994. //Check to be sure there wasn't another load going on concurrently
  15995. //So we don't clobber data that comes in on it. If there is a load going on
  15996. //then do not save this data. It will potentially clobber current data.
  15997. //We mainly wanted to sync/wait here.
  15998. //TODO: Revisit the loading scheme of this store to improve multi-initial
  15999. //request handling.
  16000. if(self._loadInProgress !== true && !self._loadFinished){
  16001. self._getItemsFromLoadedData(data);
  16002. self._loadFinished = true;
  16003. }else if(self._loadInProgress){
  16004. //Okay, we hit an error state we can't recover from. A forced load occurred
  16005. //while an async load was occurring. Since we cannot block at this point, the best
  16006. //that can be managed is to throw an error.
  16007. throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");
  16008. }
  16009. }catch(e){
  16010. console.log(e);
  16011. throw e;
  16012. }
  16013. });
  16014. getHandler.addErrback(function(error){
  16015. throw error;
  16016. });
  16017. }else if(this._jsonData){
  16018. self._getItemsFromLoadedData(self._jsonData);
  16019. self._jsonData = null;
  16020. self._loadFinished = true;
  16021. }
  16022. }
  16023. });
  16024. //Mix in the simple fetch implementation to this class.
  16025. dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
  16026. }
  16027. if(!dojo._hasResource["dijit._editor.plugins.FontChoice"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16028. dojo._hasResource["dijit._editor.plugins.FontChoice"] = true;
  16029. dojo.provide("dijit._editor.plugins.FontChoice");
  16030. dojo.declare("dijit._editor.plugins._FontDropDown",
  16031. [dijit._Widget, dijit._Templated],{
  16032. // summary:
  16033. // Base class for widgets that contains a label (like "Font:")
  16034. // and a FilteringSelect drop down to pick a value.
  16035. // Used as Toolbar entry.
  16036. // label: [public] String
  16037. // The label to apply to this particular FontDropDown.
  16038. label: "",
  16039. // widgetsInTemplate: [public] boolean
  16040. // Over-ride denoting the template has widgets to parse.
  16041. widgetsInTemplate: true,
  16042. // plainText: [public] boolean
  16043. // Flag to indicate that the returned label should be plain text
  16044. // instead of an example.
  16045. plainText: false,
  16046. // templateString: [public] String
  16047. // The template used to construct the labeled dropdown.
  16048. templateString:
  16049. "<span style='white-space: nowrap' class='dijit dijitReset dijitInline'>" +
  16050. "<label class='dijitLeft dijitInline' for='${selectId}'>${label}</label>" +
  16051. "<input dojoType='dijit.form.FilteringSelect' required='false' labelType='html' labelAttr='label' searchAttr='name' " +
  16052. "tabIndex='-1' id='${selectId}' dojoAttachPoint='select' value=''/>" +
  16053. "</span>",
  16054. postMixInProperties: function(){
  16055. // summary:
  16056. // Over-ride to set specific properties.
  16057. this.inherited(arguments);
  16058. this.strings = dojo.i18n.getLocalization("dijit._editor", "FontChoice");
  16059. // Set some substitution variables used in the template
  16060. this.label = this.strings[this.command];
  16061. this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
  16062. this.selectId = this.id + "_select";
  16063. this.inherited(arguments);
  16064. },
  16065. postCreate: function(){
  16066. // summary:
  16067. // Over-ride for the default postCreate action
  16068. // This establishes the filtering selects and the like.
  16069. // Initialize the list of items in the drop down by creating data store with items like:
  16070. // {value: 1, name: "xx-small", label: "<font size=1>xx-small</font-size>" }
  16071. var items = dojo.map(this.values, function(value){
  16072. var name = this.strings[value] || value;
  16073. return {
  16074. label: this.getLabel(value, name),
  16075. name: name,
  16076. value: value
  16077. };
  16078. }, this);
  16079. this.select.store = new dojo.data.ItemFileReadStore({
  16080. data: {
  16081. identifier: "value",
  16082. items: items
  16083. }
  16084. });
  16085. this.select.set("value", "", false);
  16086. this.disabled = this.select.get("disabled");
  16087. },
  16088. _setValueAttr: function(value, priorityChange){
  16089. // summary:
  16090. // Over-ride for the default action of setting the
  16091. // widget value, maps the input to known values
  16092. // value: Object|String
  16093. // The value to set in the select.
  16094. // priorityChange:
  16095. // Optional parameter used to tell the select whether or not to fire
  16096. // onChange event.
  16097. //if the value is not a permitted value, just set empty string to prevent showing the warning icon
  16098. priorityChange = priorityChange !== false?true:false;
  16099. this.select.set('value', dojo.indexOf(this.values,value) < 0 ? "" : value, priorityChange);
  16100. if(!priorityChange){
  16101. // Clear the last state in case of updateState calls. Ref: #10466
  16102. this.select._lastValueReported=null;
  16103. }
  16104. },
  16105. _getValueAttr: function(){
  16106. // summary:
  16107. // Allow retreiving the value from the composite select on
  16108. // call to button.get("value");
  16109. return this.select.get('value');
  16110. },
  16111. focus: function(){
  16112. // summary:
  16113. // Over-ride for focus control of this widget. Delegates focus down to the
  16114. // filtering select.
  16115. this.select.focus();
  16116. },
  16117. _setDisabledAttr: function(value){
  16118. // summary:
  16119. // Over-ride for the button's 'disabled' attribute so that it can be
  16120. // disabled programmatically.
  16121. // Save off ths disabled state so the get retrieves it correctly
  16122. //without needing to have a function proxy it.
  16123. this.disabled = value;
  16124. this.select.set("disabled", value);
  16125. }
  16126. });
  16127. dojo.declare("dijit._editor.plugins._FontNameDropDown", dijit._editor.plugins._FontDropDown, {
  16128. // summary:
  16129. // Dropdown to select a font; goes in editor toolbar.
  16130. // generic: Boolean
  16131. // Use generic (web standard) font names
  16132. generic: false,
  16133. // command: [public] String
  16134. // The editor 'command' implemented by this plugin.
  16135. command: "fontName",
  16136. postMixInProperties: function(){
  16137. // summary:
  16138. // Over-ride for the default posr mixin control
  16139. if(!this.values){
  16140. this.values = this.generic ?
  16141. ["serif", "sans-serif", "monospace", "cursive", "fantasy"] : // CSS font-family generics
  16142. ["Arial", "Times New Roman", "Comic Sans MS", "Courier New"];
  16143. }
  16144. this.inherited(arguments);
  16145. },
  16146. getLabel: function(value, name){
  16147. // summary:
  16148. // Function used to generate the labels of the format dropdown
  16149. // will return a formatted, or plain label based on the value
  16150. // of the plainText option.
  16151. // value: String
  16152. // The 'insert value' associated with a name
  16153. // name: String
  16154. // The text name of the value
  16155. if(this.plainText){
  16156. return name;
  16157. }else{
  16158. return "<div style='font-family: "+value+"'>" + name + "</div>";
  16159. }
  16160. },
  16161. _setValueAttr: function(value, priorityChange){
  16162. // summary:
  16163. // Over-ride for the default action of setting the
  16164. // widget value, maps the input to known values
  16165. priorityChange = priorityChange !== false?true:false;
  16166. if(this.generic){
  16167. var map = {
  16168. "Arial": "sans-serif",
  16169. "Helvetica": "sans-serif",
  16170. "Myriad": "sans-serif",
  16171. "Times": "serif",
  16172. "Times New Roman": "serif",
  16173. "Comic Sans MS": "cursive",
  16174. "Apple Chancery": "cursive",
  16175. "Courier": "monospace",
  16176. "Courier New": "monospace",
  16177. "Papyrus": "fantasy",
  16178. "Estrangelo Edessa": "cursive",
  16179. "Gabriola": "fantasy"
  16180. };
  16181. value = map[value] || value;
  16182. }
  16183. this.inherited(arguments, [value, priorityChange]);
  16184. }
  16185. });
  16186. dojo.declare("dijit._editor.plugins._FontSizeDropDown", dijit._editor.plugins._FontDropDown, {
  16187. // summary:
  16188. // Dropdown to select a font size; goes in editor toolbar.
  16189. // command: [public] String
  16190. // The editor 'command' implemented by this plugin.
  16191. command: "fontSize",
  16192. // values: [public] Number[]
  16193. // The HTML font size values supported by this plugin
  16194. values: [1,2,3,4,5,6,7], // sizes according to the old HTML FONT SIZE
  16195. getLabel: function(value, name){
  16196. // summary:
  16197. // Function used to generate the labels of the format dropdown
  16198. // will return a formatted, or plain label based on the value
  16199. // of the plainText option.
  16200. // We're stuck using the deprecated FONT tag to correspond
  16201. // with the size measurements used by the editor
  16202. // value: String
  16203. // The 'insert value' associated with a name
  16204. // name: String
  16205. // The text name of the value
  16206. if(this.plainText){
  16207. return name;
  16208. }else{
  16209. return "<font size=" + value + "'>" + name + "</font>";
  16210. }
  16211. },
  16212. _setValueAttr: function(value, priorityChange){
  16213. // summary:
  16214. // Over-ride for the default action of setting the
  16215. // widget value, maps the input to known values
  16216. priorityChange = priorityChange !== false?true:false;
  16217. if(value.indexOf && value.indexOf("px") != -1){
  16218. var pixels = parseInt(value, 10);
  16219. value = {10:1, 13:2, 16:3, 18:4, 24:5, 32:6, 48:7}[pixels] || value;
  16220. }
  16221. this.inherited(arguments, [value, priorityChange]);
  16222. }
  16223. });
  16224. dojo.declare("dijit._editor.plugins._FormatBlockDropDown", dijit._editor.plugins._FontDropDown, {
  16225. // summary:
  16226. // Dropdown to select a format (like paragraph or heading); goes in editor toolbar.
  16227. // command: [public] String
  16228. // The editor 'command' implemented by this plugin.
  16229. command: "formatBlock",
  16230. // values: [public] Array
  16231. // The HTML format tags supported by this plugin
  16232. values: ["noFormat", "p", "h1", "h2", "h3", "pre"],
  16233. postCreate: function(){
  16234. // Init and set the default value to no formatting. Update state will adjust it
  16235. // as needed.
  16236. this.inherited(arguments);
  16237. this.set("value", "noFormat", false);
  16238. },
  16239. getLabel: function(value, name){
  16240. // summary:
  16241. // Function used to generate the labels of the format dropdown
  16242. // will return a formatted, or plain label based on the value
  16243. // of the plainText option.
  16244. // value: String
  16245. // The 'insert value' associated with a name
  16246. // name: String
  16247. // The text name of the value
  16248. if(this.plainText || value == "noFormat"){
  16249. return name;
  16250. }else{
  16251. return "<" + value + ">" + name + "</" + value + ">";
  16252. }
  16253. },
  16254. _execCommand: function(editor, command, choice){
  16255. // summary:
  16256. // Over-ride for default exec-command label.
  16257. // Allows us to treat 'none' as special.
  16258. if(choice === "noFormat"){
  16259. var start;
  16260. var end;
  16261. var sel = dijit.range.getSelection(editor.window);
  16262. if(sel && sel.rangeCount > 0){
  16263. var range = sel.getRangeAt(0);
  16264. var node, tag;
  16265. if(range){
  16266. start = range.startContainer;
  16267. end = range.endContainer;
  16268. // find containing nodes of start/end.
  16269. while(start && start !== editor.editNode &&
  16270. start !== editor.document.body &&
  16271. start.nodeType !== 1){
  16272. start = start.parentNode;
  16273. }
  16274. while(end && end !== editor.editNode &&
  16275. end !== editor.document.body &&
  16276. end.nodeType !== 1){
  16277. end = end.parentNode;
  16278. }
  16279. var processChildren = dojo.hitch(this, function(node, array){
  16280. if(node.childNodes && node.childNodes.length){
  16281. var i;
  16282. for(i = 0; i < node.childNodes.length; i++){
  16283. var c = node.childNodes[i];
  16284. if(c.nodeType == 1){
  16285. if(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [c])){
  16286. var tag = c.tagName? c.tagName.toLowerCase(): "";
  16287. if(dojo.indexOf(this.values, tag) !== -1){
  16288. array.push(c);
  16289. }
  16290. processChildren(c,array);
  16291. }
  16292. }
  16293. }
  16294. }
  16295. });
  16296. var unformatNodes = dojo.hitch(this, function(nodes){
  16297. // summary:
  16298. // Internal function to clear format nodes.
  16299. // nodes:
  16300. // The array of nodes to strip formatting from.
  16301. if(nodes && nodes.length){
  16302. editor.beginEditing();
  16303. while(nodes.length){
  16304. this._removeFormat(editor, nodes.pop());
  16305. }
  16306. editor.endEditing();
  16307. }
  16308. });
  16309. var clearNodes = [];
  16310. if(start == end){
  16311. //Contained within the same block, may be collapsed, but who cares, see if we
  16312. // have a block element to remove.
  16313. var block;
  16314. node = start;
  16315. while(node && node !== editor.editNode && node !== editor.document.body){
  16316. if(node.nodeType == 1){
  16317. tag = node.tagName? node.tagName.toLowerCase(): "";
  16318. if(dojo.indexOf(this.values, tag) !== -1){
  16319. block = node;
  16320. break;
  16321. }
  16322. }
  16323. node = node.parentNode;
  16324. }
  16325. //Also look for all child nodes in the selection that may need to be
  16326. //cleared of formatting
  16327. processChildren(start, clearNodes);
  16328. if(block) { clearNodes = [block].concat(clearNodes); }
  16329. unformatNodes(clearNodes);
  16330. }else{
  16331. // Probably a multi select, so we have to process it. Whee.
  16332. node = start;
  16333. while(dojo.withGlobal(editor.window, "inSelection", dijit._editor.selection, [node])){
  16334. if(node.nodeType == 1){
  16335. tag = node.tagName? node.tagName.toLowerCase(): "";
  16336. if(dojo.indexOf(this.values, tag) !== -1){
  16337. clearNodes.push(node);
  16338. }
  16339. processChildren(node,clearNodes);
  16340. }
  16341. node = node.nextSibling;
  16342. }
  16343. unformatNodes(clearNodes);
  16344. }
  16345. editor.onDisplayChanged();
  16346. }
  16347. }
  16348. }else{
  16349. editor.execCommand(command, choice);
  16350. }
  16351. },
  16352. _removeFormat: function(editor, node){
  16353. // summary:
  16354. // function to remove the block format node.
  16355. // node:
  16356. // The block format node to remove (and leave the contents behind)
  16357. if(editor.customUndo){
  16358. // So of course IE doesn't work right with paste-overs.
  16359. // We have to do this manually, which is okay since IE already uses
  16360. // customUndo and we turned it on for WebKit. WebKit pasted funny,
  16361. // so couldn't use the execCommand approach
  16362. while(node.firstChild){
  16363. dojo.place(node.firstChild, node, "before");
  16364. }
  16365. node.parentNode.removeChild(node);
  16366. }else{
  16367. // Everyone else works fine this way, a paste-over and is native
  16368. // undo friendly.
  16369. dojo.withGlobal(editor.window,
  16370. "selectElementChildren", dijit._editor.selection, [node]);
  16371. var html = dojo.withGlobal(editor.window,
  16372. "getSelectedHtml", dijit._editor.selection, [null]);
  16373. dojo.withGlobal(editor.window,
  16374. "selectElement", dijit._editor.selection, [node]);
  16375. editor.execCommand("inserthtml", html||"");
  16376. }
  16377. }
  16378. });
  16379. // TODO: for 2.0, split into FontChoice plugin into three separate classes,
  16380. // one for each command (and change registry below)
  16381. dojo.declare("dijit._editor.plugins.FontChoice", dijit._editor._Plugin,{
  16382. // summary:
  16383. // This plugin provides three drop downs for setting style in the editor
  16384. // (font, font size, and format block), as controlled by command.
  16385. //
  16386. // description:
  16387. // The commands provided by this plugin are:
  16388. //
  16389. // * fontName
  16390. // | Provides a drop down to select from a list of font names
  16391. // * fontSize
  16392. // | Provides a drop down to select from a list of font sizes
  16393. // * formatBlock
  16394. // | Provides a drop down to select from a list of block styles
  16395. // |
  16396. //
  16397. // which can easily be added to an editor by including one or more of the above commands
  16398. // in the `plugins` attribute as follows:
  16399. //
  16400. // | plugins="['fontName','fontSize',...]"
  16401. //
  16402. // It is possible to override the default dropdown list by providing an Array for the `custom` property when
  16403. // instantiating this plugin, e.g.
  16404. //
  16405. // | plugins="[{name:'dijit._editor.plugins.FontChoice', command:'fontName', custom:['Verdana','Myriad','Garamond']},...]"
  16406. //
  16407. // Alternatively, for `fontName` only, `generic:true` may be specified to provide a dropdown with
  16408. // [CSS generic font families](http://www.w3.org/TR/REC-CSS2/fonts.html#generic-font-families)
  16409. //
  16410. // Note that the editor is often unable to properly handle font styling information defined outside
  16411. // the context of the current editor instance, such as pre-populated HTML.
  16412. // useDefaultCommand: [protected] booleam
  16413. // Override _Plugin.useDefaultCommand...
  16414. // processing is handled by this plugin, not by dijit.Editor.
  16415. useDefaultCommand: false,
  16416. _initButton: function(){
  16417. // summary:
  16418. // Overrides _Plugin._initButton(), to initialize the FilteringSelect+label in toolbar,
  16419. // rather than a simple button.
  16420. // tags:
  16421. // protected
  16422. // Create the widget to go into the toolbar (the so-called "button")
  16423. var clazz = {
  16424. fontName: dijit._editor.plugins._FontNameDropDown,
  16425. fontSize: dijit._editor.plugins._FontSizeDropDown,
  16426. formatBlock: dijit._editor.plugins._FormatBlockDropDown
  16427. }[this.command],
  16428. params = this.params;
  16429. // For back-compat reasons support setting custom values via "custom" parameter
  16430. // rather than "values" parameter
  16431. if(this.params.custom){
  16432. params.values = this.params.custom;
  16433. }
  16434. var editor = this.editor;
  16435. this.button = new clazz(dojo.delegate({dir: editor.dir, lang: editor.lang}, params));
  16436. // Reflect changes to the drop down in the editor
  16437. this.connect(this.button.select, "onChange", function(choice){
  16438. // User invoked change, since all internal updates set priorityChange to false and will
  16439. // not trigger an onChange event.
  16440. this.editor.focus();
  16441. if(this.command == "fontName" && choice.indexOf(" ") != -1){ choice = "'" + choice + "'"; }
  16442. // Invoke, the editor already normalizes commands called through its
  16443. // execCommand.
  16444. if(this.button._execCommand){
  16445. this.button._execCommand(this.editor, this.command, choice);
  16446. }else{
  16447. this.editor.execCommand(this.command, choice);
  16448. }
  16449. });
  16450. },
  16451. updateState: function(){
  16452. // summary:
  16453. // Overrides _Plugin.updateState(). This controls updating the menu
  16454. // options to the right values on state changes in the document (that trigger a
  16455. // test of the actions.)
  16456. // It set value of drop down in toolbar to reflect font/font size/format block
  16457. // of text at current caret position.
  16458. // tags:
  16459. // protected
  16460. var _e = this.editor;
  16461. var _c = this.command;
  16462. if(!_e || !_e.isLoaded || !_c.length){ return; }
  16463. if(this.button){
  16464. var disabled = this.get("disabled");
  16465. this.button.set("disabled", disabled);
  16466. if(disabled){ return; }
  16467. var value;
  16468. try{
  16469. value = _e.queryCommandValue(_c) || "";
  16470. }catch(e){
  16471. //Firefox may throw error above if the editor is just loaded, ignore it
  16472. value = "";
  16473. }
  16474. // strip off single quotes, if any
  16475. var quoted = dojo.isString(value) && value.match(/'([^']*)'/);
  16476. if(quoted){ value = quoted[1]; }
  16477. if(_c === "formatBlock"){
  16478. if(!value || value == "p"){
  16479. // Some browsers (WebKit) doesn't actually get the tag info right.
  16480. // and IE returns paragraph when in a DIV!, so incorrect a lot,
  16481. // so we have double-check it.
  16482. value = null;
  16483. var elem;
  16484. // Try to find the current element where the caret is.
  16485. var sel = dijit.range.getSelection(this.editor.window);
  16486. if(sel && sel.rangeCount > 0){
  16487. var range = sel.getRangeAt(0);
  16488. if(range){
  16489. elem = range.endContainer;
  16490. }
  16491. }
  16492. // Okay, now see if we can find one of the formatting types we're in.
  16493. while(elem && elem !== _e.editNode && elem !== _e.document){
  16494. var tg = elem.tagName?elem.tagName.toLowerCase():"";
  16495. if(tg && dojo.indexOf(this.button.values, tg) > -1){
  16496. value = tg;
  16497. break;
  16498. }
  16499. elem = elem.parentNode;
  16500. }
  16501. if(!value){
  16502. // Still no value, so lets select 'none'.
  16503. value = "noFormat";
  16504. }
  16505. }else{
  16506. // Check that the block format is one allowed, if not,
  16507. // null it so that it gets set to empty.
  16508. if(dojo.indexOf(this.button.values, value) < 0){
  16509. value = "noFormat";
  16510. }
  16511. }
  16512. }
  16513. if(value !== this.button.get("value")){
  16514. // Set the value, but denote it is not a priority change, so no
  16515. // onchange fires.
  16516. this.button.set('value', value, false);
  16517. }
  16518. }
  16519. }
  16520. });
  16521. // Register this plugin.
  16522. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  16523. if(o.plugin){ return; }
  16524. switch(o.args.name){
  16525. case "fontName": case "fontSize": case "formatBlock":
  16526. o.plugin = new dijit._editor.plugins.FontChoice({
  16527. command: o.args.name,
  16528. plainText: o.args.plainText?o.args.plainText:false
  16529. });
  16530. }
  16531. });
  16532. }
  16533. if(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16534. dojo._hasResource["dijit._Contained"] = true;
  16535. dojo.provide("dijit._Contained");
  16536. dojo.declare("dijit._Contained",
  16537. null,
  16538. {
  16539. // summary:
  16540. // Mixin for widgets that are children of a container widget
  16541. //
  16542. // example:
  16543. // | // make a basic custom widget that knows about it's parents
  16544. // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
  16545. getParent: function(){
  16546. // summary:
  16547. // Returns the parent widget of this widget, assuming the parent
  16548. // specifies isContainer
  16549. var parent = dijit.getEnclosingWidget(this.domNode.parentNode);
  16550. return parent && parent.isContainer ? parent : null;
  16551. },
  16552. _getSibling: function(/*String*/ which){
  16553. // summary:
  16554. // Returns next or previous sibling
  16555. // which:
  16556. // Either "next" or "previous"
  16557. // tags:
  16558. // private
  16559. var node = this.domNode;
  16560. do{
  16561. node = node[which+"Sibling"];
  16562. }while(node && node.nodeType != 1);
  16563. return node && dijit.byNode(node); // dijit._Widget
  16564. },
  16565. getPreviousSibling: function(){
  16566. // summary:
  16567. // Returns null if this is the first child of the parent,
  16568. // otherwise returns the next element sibling to the "left".
  16569. return this._getSibling("previous"); // dijit._Widget
  16570. },
  16571. getNextSibling: function(){
  16572. // summary:
  16573. // Returns null if this is the last child of the parent,
  16574. // otherwise returns the next element sibling to the "right".
  16575. return this._getSibling("next"); // dijit._Widget
  16576. },
  16577. getIndexInParent: function(){
  16578. // summary:
  16579. // Returns the index of this widget within its container parent.
  16580. // It returns -1 if the parent does not exist, or if the parent
  16581. // is not a dijit._Container
  16582. var p = this.getParent();
  16583. if(!p || !p.getIndexOfChild){
  16584. return -1; // int
  16585. }
  16586. return p.getIndexOfChild(this); // int
  16587. }
  16588. }
  16589. );
  16590. }
  16591. if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16592. dojo._hasResource["dijit.layout._LayoutWidget"] = true;
  16593. dojo.provide("dijit.layout._LayoutWidget");
  16594. dojo.declare("dijit.layout._LayoutWidget",
  16595. [dijit._Widget, dijit._Container, dijit._Contained],
  16596. {
  16597. // summary:
  16598. // Base class for a _Container widget which is responsible for laying out its children.
  16599. // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
  16600. // baseClass: [protected extension] String
  16601. // This class name is applied to the widget's domNode
  16602. // and also may be used to generate names for sub nodes,
  16603. // for example dijitTabContainer-content.
  16604. baseClass: "dijitLayoutContainer",
  16605. // isLayoutContainer: [protected] Boolean
  16606. // Indicates that this widget is going to call resize() on its
  16607. // children widgets, setting their size, when they become visible.
  16608. isLayoutContainer: true,
  16609. buildRendering: function(){
  16610. this.inherited(arguments);
  16611. dojo.addClass(this.domNode, "dijitContainer");
  16612. },
  16613. startup: function(){
  16614. // summary:
  16615. // Called after all the widgets have been instantiated and their
  16616. // dom nodes have been inserted somewhere under dojo.doc.body.
  16617. //
  16618. // Widgets should override this method to do any initialization
  16619. // dependent on other widgets existing, and then call
  16620. // this superclass method to finish things off.
  16621. //
  16622. // startup() in subclasses shouldn't do anything
  16623. // size related because the size of the widget hasn't been set yet.
  16624. if(this._started){ return; }
  16625. // Need to call inherited first - so that child widgets get started
  16626. // up correctly
  16627. this.inherited(arguments);
  16628. // If I am a not being controlled by a parent layout widget...
  16629. var parent = this.getParent && this.getParent()
  16630. if(!(parent && parent.isLayoutContainer)){
  16631. // Do recursive sizing and layout of all my descendants
  16632. // (passing in no argument to resize means that it has to glean the size itself)
  16633. this.resize();
  16634. // Since my parent isn't a layout container, and my style *may be* width=height=100%
  16635. // or something similar (either set directly or via a CSS class),
  16636. // monitor when my size changes so that I can re-layout.
  16637. // For browsers where I can't directly monitor when my size changes,
  16638. // monitor when the viewport changes size, which *may* indicate a size change for me.
  16639. this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
  16640. // Using function(){} closure to ensure no arguments to resize.
  16641. this.resize();
  16642. });
  16643. }
  16644. },
  16645. resize: function(changeSize, resultSize){
  16646. // summary:
  16647. // Call this to resize a widget, or after its size has changed.
  16648. // description:
  16649. // Change size mode:
  16650. // When changeSize is specified, changes the marginBox of this widget
  16651. // and forces it to relayout its contents accordingly.
  16652. // changeSize may specify height, width, or both.
  16653. //
  16654. // If resultSize is specified it indicates the size the widget will
  16655. // become after changeSize has been applied.
  16656. //
  16657. // Notification mode:
  16658. // When changeSize is null, indicates that the caller has already changed
  16659. // the size of the widget, or perhaps it changed because the browser
  16660. // window was resized. Tells widget to relayout its contents accordingly.
  16661. //
  16662. // If resultSize is also specified it indicates the size the widget has
  16663. // become.
  16664. //
  16665. // In either mode, this method also:
  16666. // 1. Sets this._borderBox and this._contentBox to the new size of
  16667. // the widget. Queries the current domNode size if necessary.
  16668. // 2. Calls layout() to resize contents (and maybe adjust child widgets).
  16669. //
  16670. // changeSize: Object?
  16671. // Sets the widget to this margin-box size and position.
  16672. // May include any/all of the following properties:
  16673. // | {w: int, h: int, l: int, t: int}
  16674. //
  16675. // resultSize: Object?
  16676. // The margin-box size of this widget after applying changeSize (if
  16677. // changeSize is specified). If caller knows this size and
  16678. // passes it in, we don't need to query the browser to get the size.
  16679. // | {w: int, h: int}
  16680. var node = this.domNode;
  16681. // set margin box size, unless it wasn't specified, in which case use current size
  16682. if(changeSize){
  16683. dojo.marginBox(node, changeSize);
  16684. // set offset of the node
  16685. if(changeSize.t){ node.style.top = changeSize.t + "px"; }
  16686. if(changeSize.l){ node.style.left = changeSize.l + "px"; }
  16687. }
  16688. // If either height or width wasn't specified by the user, then query node for it.
  16689. // But note that setting the margin box and then immediately querying dimensions may return
  16690. // inaccurate results, so try not to depend on it.
  16691. var mb = resultSize || {};
  16692. dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
  16693. if( !("h" in mb) || !("w" in mb) ){
  16694. mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values
  16695. }
  16696. // Compute and save the size of my border box and content box
  16697. // (w/out calling dojo.contentBox() since that may fail if size was recently set)
  16698. var cs = dojo.getComputedStyle(node);
  16699. var me = dojo._getMarginExtents(node, cs);
  16700. var be = dojo._getBorderExtents(node, cs);
  16701. var bb = (this._borderBox = {
  16702. w: mb.w - (me.w + be.w),
  16703. h: mb.h - (me.h + be.h)
  16704. });
  16705. var pe = dojo._getPadExtents(node, cs);
  16706. this._contentBox = {
  16707. l: dojo._toPixelValue(node, cs.paddingLeft),
  16708. t: dojo._toPixelValue(node, cs.paddingTop),
  16709. w: bb.w - pe.w,
  16710. h: bb.h - pe.h
  16711. };
  16712. // Callback for widget to adjust size of its children
  16713. this.layout();
  16714. },
  16715. layout: function(){
  16716. // summary:
  16717. // Widgets override this method to size and position their contents/children.
  16718. // When this is called this._contentBox is guaranteed to be set (see resize()).
  16719. //
  16720. // This is called after startup(), and also when the widget's size has been
  16721. // changed.
  16722. // tags:
  16723. // protected extension
  16724. },
  16725. _setupChild: function(/*dijit._Widget*/child){
  16726. // summary:
  16727. // Common setup for initial children and children which are added after startup
  16728. // tags:
  16729. // protected extension
  16730. var cls = this.baseClass + "-child "
  16731. + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
  16732. dojo.addClass(child.domNode, cls);
  16733. },
  16734. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  16735. // Overrides _Container.addChild() to call _setupChild()
  16736. this.inherited(arguments);
  16737. if(this._started){
  16738. this._setupChild(child);
  16739. }
  16740. },
  16741. removeChild: function(/*dijit._Widget*/ child){
  16742. // Overrides _Container.removeChild() to remove class added by _setupChild()
  16743. var cls = this.baseClass + "-child"
  16744. + (child.baseClass ?
  16745. " " + this.baseClass + "-" + child.baseClass : "");
  16746. dojo.removeClass(child.domNode, cls);
  16747. this.inherited(arguments);
  16748. }
  16749. }
  16750. );
  16751. dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
  16752. // summary:
  16753. // Given the margin-box size of a node, return its content box size.
  16754. // Functions like dojo.contentBox() but is more reliable since it doesn't have
  16755. // to wait for the browser to compute sizes.
  16756. var cs = dojo.getComputedStyle(node);
  16757. var me = dojo._getMarginExtents(node, cs);
  16758. var pb = dojo._getPadBorderExtents(node, cs);
  16759. return {
  16760. l: dojo._toPixelValue(node, cs.paddingLeft),
  16761. t: dojo._toPixelValue(node, cs.paddingTop),
  16762. w: mb.w - (me.w + pb.w),
  16763. h: mb.h - (me.h + pb.h)
  16764. };
  16765. };
  16766. (function(){
  16767. var capitalize = function(word){
  16768. return word.substring(0,1).toUpperCase() + word.substring(1);
  16769. };
  16770. var size = function(widget, dim){
  16771. // size the child
  16772. var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
  16773. // record child's size
  16774. if(newSize){
  16775. // if the child returned it's new size then use that
  16776. dojo.mixin(widget, newSize);
  16777. }else{
  16778. // otherwise, call marginBox(), but favor our own numbers when we have them.
  16779. // the browser lies sometimes
  16780. dojo.mixin(widget, dojo.marginBox(widget.domNode));
  16781. dojo.mixin(widget, dim);
  16782. }
  16783. };
  16784. dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
  16785. /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
  16786. // summary
  16787. // Layout a bunch of child dom nodes within a parent dom node
  16788. // container:
  16789. // parent node
  16790. // dim:
  16791. // {l, t, w, h} object specifying dimensions of container into which to place children
  16792. // children:
  16793. // an array of Widgets or at least objects containing:
  16794. // * domNode: pointer to DOM node to position
  16795. // * region or layoutAlign: position to place DOM node
  16796. // * resize(): (optional) method to set size of node
  16797. // * id: (optional) Id of widgets, referenced from resize object, below.
  16798. // changedRegionId:
  16799. // If specified, the slider for the region with the specified id has been dragged, and thus
  16800. // the region's height or width should be adjusted according to changedRegionSize
  16801. // changedRegionSize:
  16802. // See changedRegionId.
  16803. // copy dim because we are going to modify it
  16804. dim = dojo.mixin({}, dim);
  16805. dojo.addClass(container, "dijitLayoutContainer");
  16806. // Move "client" elements to the end of the array for layout. a11y dictates that the author
  16807. // needs to be able to put them in the document in tab-order, but this algorithm requires that
  16808. // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
  16809. children = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
  16810. .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
  16811. // set positions/sizes
  16812. dojo.forEach(children, function(child){
  16813. var elm = child.domNode,
  16814. pos = (child.region || child.layoutAlign);
  16815. // set elem to upper left corner of unused space; may move it later
  16816. var elmStyle = elm.style;
  16817. elmStyle.left = dim.l+"px";
  16818. elmStyle.top = dim.t+"px";
  16819. elmStyle.position = "absolute";
  16820. dojo.addClass(elm, "dijitAlign" + capitalize(pos));
  16821. // Size adjustments to make to this child widget
  16822. var sizeSetting = {};
  16823. // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
  16824. // panes and width adjustment for left/right align panes.
  16825. if(changedRegionId && changedRegionId == child.id){
  16826. sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
  16827. }
  16828. // set size && adjust record of remaining space.
  16829. // note that setting the width of a <div> may affect its height.
  16830. if(pos == "top" || pos == "bottom"){
  16831. sizeSetting.w = dim.w;
  16832. size(child, sizeSetting);
  16833. dim.h -= child.h;
  16834. if(pos == "top"){
  16835. dim.t += child.h;
  16836. }else{
  16837. elmStyle.top = dim.t + dim.h + "px";
  16838. }
  16839. }else if(pos == "left" || pos == "right"){
  16840. sizeSetting.h = dim.h;
  16841. size(child, sizeSetting);
  16842. dim.w -= child.w;
  16843. if(pos == "left"){
  16844. dim.l += child.w;
  16845. }else{
  16846. elmStyle.left = dim.l + dim.w + "px";
  16847. }
  16848. }else if(pos == "client" || pos == "center"){
  16849. size(child, dim);
  16850. }
  16851. });
  16852. };
  16853. })();
  16854. }
  16855. if(!dojo._hasResource["dijit.layout._ContentPaneResizeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  16856. dojo._hasResource["dijit.layout._ContentPaneResizeMixin"] = true;
  16857. dojo.provide("dijit.layout._ContentPaneResizeMixin");
  16858. dojo.declare("dijit.layout._ContentPaneResizeMixin", null, {
  16859. // summary:
  16860. // Resize() functionality of ContentPane. If there's a single layout widget
  16861. // child then it will call resize() with the same dimensions as the ContentPane.
  16862. // Otherwise just calls resize on each child.
  16863. //
  16864. // Also implements basic startup() functionality, where starting the parent
  16865. // will start the children
  16866. // doLayout: Boolean
  16867. // - false - don't adjust size of children
  16868. // - true - if there is a single visible child widget, set it's size to
  16869. // however big the ContentPane is
  16870. doLayout: true,
  16871. // isContainer: [protected] Boolean
  16872. // Indicates that this widget acts as a "parent" to the descendant widgets.
  16873. // When the parent is started it will call startup() on the child widgets.
  16874. // See also `isLayoutContainer`.
  16875. isContainer: true,
  16876. // isLayoutContainer: [protected] Boolean
  16877. // Indicates that this widget will call resize() on it's child widgets
  16878. // when they become visible.
  16879. isLayoutContainer: true,
  16880. _startChildren: function(){
  16881. // summary:
  16882. // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
  16883. // This starts all the widgets
  16884. dojo.forEach(this.getChildren(), function(child){
  16885. child.startup();
  16886. child._started = true;
  16887. });
  16888. },
  16889. startup: function(){
  16890. // summary:
  16891. // See `dijit.layout._LayoutWidget.startup` for description.
  16892. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  16893. // the same API.
  16894. if(this._started){ return; }
  16895. var parent = dijit._Contained.prototype.getParent.call(this);
  16896. this._childOfLayoutWidget = parent && parent.isLayoutContainer;
  16897. // I need to call resize() on my child/children (when I become visible), unless
  16898. // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
  16899. this._needLayout = !this._childOfLayoutWidget;
  16900. this.inherited(arguments);
  16901. this._startChildren();
  16902. if(this._isShown()){
  16903. this._onShow();
  16904. }
  16905. if(!this._childOfLayoutWidget){
  16906. // If my parent isn't a layout container, since my style *may be* width=height=100%
  16907. // or something similar (either set directly or via a CSS class),
  16908. // monitor when my size changes so that I can re-layout.
  16909. // For browsers where I can't directly monitor when my size changes,
  16910. // monitor when the viewport changes size, which *may* indicate a size change for me.
  16911. this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
  16912. // Using function(){} closure to ensure no arguments to resize.
  16913. this._needLayout = !this._childOfLayoutWidget;
  16914. this.resize();
  16915. });
  16916. }
  16917. },
  16918. _checkIfSingleChild: function(){
  16919. // summary:
  16920. // Test if we have exactly one visible widget as a child,
  16921. // and if so assume that we are a container for that widget,
  16922. // and should propagate startup() and resize() calls to it.
  16923. // Skips over things like data stores since they aren't visible.
  16924. var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
  16925. return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
  16926. }),
  16927. childWidgetNodes = childNodes.filter(function(node){
  16928. return dojo.hasAttr(node, "data-dojo-type") || dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
  16929. }),
  16930. candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
  16931. return widget && widget.domNode && widget.resize;
  16932. });
  16933. if(
  16934. // all child nodes are widgets
  16935. childNodes.length == childWidgetNodes.length &&
  16936. // all but one are invisible (like dojo.data)
  16937. candidateWidgets.length == 1
  16938. ){
  16939. this._singleChild = candidateWidgets[0];
  16940. }else{
  16941. delete this._singleChild;
  16942. }
  16943. // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
  16944. dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
  16945. },
  16946. resize: function(changeSize, resultSize){
  16947. // summary:
  16948. // See `dijit.layout._LayoutWidget.resize` for description.
  16949. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  16950. // the same API.
  16951. // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
  16952. // never called, so resize() is our trigger to do the initial href download (see [20099]).
  16953. // However, don't load href for closed TitlePanes.
  16954. if(!this._wasShown && this.open !== false){
  16955. this._onShow();
  16956. }
  16957. this._resizeCalled = true;
  16958. this._scheduleLayout(changeSize, resultSize);
  16959. },
  16960. _scheduleLayout: function(changeSize, resultSize){
  16961. // summary:
  16962. // Resize myself, and call resize() on each of my child layout widgets, either now
  16963. // (if I'm currently visible) or when I become visible
  16964. if(this._isShown()){
  16965. this._layout(changeSize, resultSize);
  16966. }else{
  16967. this._needLayout = true;
  16968. this._changeSize = changeSize;
  16969. this._resultSize = resultSize;
  16970. }
  16971. },
  16972. _layout: function(changeSize, resultSize){
  16973. // summary:
  16974. // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
  16975. // Also, since I am a Container widget, each of my children expects me to
  16976. // call resize() or layout() on them.
  16977. //
  16978. // Should be called on initialization and also whenever we get new content
  16979. // (from an href, or from set('content', ...))... but deferred until
  16980. // the ContentPane is visible
  16981. // Set margin box size, unless it wasn't specified, in which case use current size.
  16982. if(changeSize){
  16983. dojo.marginBox(this.domNode, changeSize);
  16984. }
  16985. // Compute content box size of containerNode in case we [later] need to size our single child.
  16986. var cn = this.containerNode;
  16987. if(cn === this.domNode){
  16988. // If changeSize or resultSize was passed to this method and this.containerNode ==
  16989. // this.domNode then we can compute the content-box size without querying the node,
  16990. // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
  16991. var mb = resultSize || {};
  16992. dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
  16993. if(!("h" in mb) || !("w" in mb)){
  16994. mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
  16995. }
  16996. this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
  16997. }else{
  16998. this._contentBox = dojo.contentBox(cn);
  16999. }
  17000. this._layoutChildren();
  17001. delete this._needLayout;
  17002. },
  17003. _layoutChildren: function(){
  17004. // Call _checkIfSingleChild() again in case app has manually mucked w/the content
  17005. // of the ContentPane (rather than changing it through the set("content", ...) API.
  17006. if(this.doLayout){
  17007. this._checkIfSingleChild();
  17008. }
  17009. if(this._singleChild && this._singleChild.resize){
  17010. var cb = this._contentBox || dojo.contentBox(this.containerNode);
  17011. // note: if widget has padding this._contentBox will have l and t set,
  17012. // but don't pass them to resize() or it will doubly-offset the child
  17013. this._singleChild.resize({w: cb.w, h: cb.h});
  17014. }else{
  17015. // All my child widgets are independently sized (rather than matching my size),
  17016. // but I still need to call resize() on each child to make it layout.
  17017. dojo.forEach(this.getChildren(), function(widget){
  17018. if(widget.resize){
  17019. widget.resize();
  17020. }
  17021. });
  17022. }
  17023. },
  17024. _isShown: function(){
  17025. // summary:
  17026. // Returns true if the content is currently shown.
  17027. // description:
  17028. // If I am a child of a layout widget then it actually returns true if I've ever been visible,
  17029. // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
  17030. // tree every call, and at least solves the performance problem on page load by deferring loading
  17031. // hidden ContentPanes until they are first shown
  17032. if(this._childOfLayoutWidget){
  17033. // If we are TitlePane, etc - we return that only *IF* we've been resized
  17034. if(this._resizeCalled && "open" in this){
  17035. return this.open;
  17036. }
  17037. return this._resizeCalled;
  17038. }else if("open" in this){
  17039. return this.open; // for TitlePane, etc.
  17040. }else{
  17041. var node = this.domNode, parent = this.domNode.parentNode;
  17042. return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden") &&
  17043. parent && parent.style && (parent.style.display != 'none');
  17044. }
  17045. },
  17046. _onShow: function(){
  17047. // summary:
  17048. // Called when the ContentPane is made visible
  17049. // description:
  17050. // For a plain ContentPane, this is called on initialization, from startup().
  17051. // If the ContentPane is a hidden pane of a TabContainer etc., then it's
  17052. // called whenever the pane is made visible.
  17053. //
  17054. // Does layout/resize of child widget(s)
  17055. // Need to keep track of whether ContentPane has been shown (which is different than
  17056. // whether or not it's currently visible).
  17057. this._wasShown = true;
  17058. if(this._needLayout){
  17059. // If a layout has been scheduled for when we become visible, do it now
  17060. this._layout(this._changeSize, this._resultSize);
  17061. }
  17062. this.inherited(arguments);
  17063. }
  17064. });
  17065. }
  17066. if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17067. dojo._hasResource["dojo.html"] = true;
  17068. dojo.provide("dojo.html");
  17069. dojo.getObject("html", true, dojo);
  17070. // the parser might be needed..
  17071. (function(){ // private scope, sort of a namespace
  17072. // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
  17073. var idCounter = 0,
  17074. d = dojo;
  17075. dojo.html._secureForInnerHtml = function(/*String*/ cont){
  17076. // summary:
  17077. // removes !DOCTYPE and title elements from the html string.
  17078. //
  17079. // khtml is picky about dom faults, you can't attach a style or <title> node as child of body
  17080. // must go into head, so we need to cut out those tags
  17081. // cont:
  17082. // An html string for insertion into the dom
  17083. //
  17084. return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
  17085. };
  17086. /*====
  17087. dojo.html._emptyNode = function(node){
  17088. // summary:
  17089. // removes all child nodes from the given node
  17090. // node: DOMNode
  17091. // the parent element
  17092. };
  17093. =====*/
  17094. dojo.html._emptyNode = dojo.empty;
  17095. dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
  17096. // summary:
  17097. // inserts the given content into the given node
  17098. // node:
  17099. // the parent element
  17100. // content:
  17101. // the content to be set on the parent element.
  17102. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  17103. // always empty
  17104. d.empty(node);
  17105. if(cont) {
  17106. if(typeof cont == "string") {
  17107. cont = d._toDom(cont, node.ownerDocument);
  17108. }
  17109. if(!cont.nodeType && d.isArrayLike(cont)) {
  17110. // handle as enumerable, but it may shrink as we enumerate it
  17111. for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
  17112. d.place( cont[i], node, "last");
  17113. }
  17114. } else {
  17115. // pass nodes, documentFragments and unknowns through to dojo.place
  17116. d.place(cont, node, "last");
  17117. }
  17118. }
  17119. // return DomNode
  17120. return node;
  17121. };
  17122. // we wrap up the content-setting operation in a object
  17123. dojo.declare("dojo.html._ContentSetter", null,
  17124. {
  17125. // node: DomNode|String
  17126. // An node which will be the parent element that we set content into
  17127. node: "",
  17128. // content: String|DomNode|DomNode[]
  17129. // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
  17130. content: "",
  17131. // id: String?
  17132. // Usually only used internally, and auto-generated with each instance
  17133. id: "",
  17134. // cleanContent: Boolean
  17135. // Should the content be treated as a full html document,
  17136. // and the real content stripped of <html>, <body> wrapper before injection
  17137. cleanContent: false,
  17138. // extractContent: Boolean
  17139. // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
  17140. extractContent: false,
  17141. // parseContent: Boolean
  17142. // Should the node by passed to the parser after the new content is set
  17143. parseContent: false,
  17144. // parserScope: String
  17145. // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
  17146. // will search for data-dojo-type (or dojoType). For backwards compatibility
  17147. // reasons defaults to dojo._scopeName (which is "dojo" except when
  17148. // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  17149. parserScope: dojo._scopeName,
  17150. // startup: Boolean
  17151. // Start the child widgets after parsing them. Only obeyed if parseContent is true.
  17152. startup: true,
  17153. // lifecyle methods
  17154. constructor: function(/* Object */params, /* String|DomNode */node){
  17155. // summary:
  17156. // Provides a configurable, extensible object to wrap the setting on content on a node
  17157. // call the set() method to actually set the content..
  17158. // the original params are mixed directly into the instance "this"
  17159. dojo.mixin(this, params || {});
  17160. // give precedence to params.node vs. the node argument
  17161. // and ensure its a node, not an id string
  17162. node = this.node = dojo.byId( this.node || node );
  17163. if(!this.id){
  17164. this.id = [
  17165. "Setter",
  17166. (node) ? node.id || node.tagName : "",
  17167. idCounter++
  17168. ].join("_");
  17169. }
  17170. },
  17171. set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
  17172. // summary:
  17173. // front-end to the set-content sequence
  17174. // cont:
  17175. // An html string, node or enumerable list of nodes for insertion into the dom
  17176. // If not provided, the object's content property will be used
  17177. if(undefined !== cont){
  17178. this.content = cont;
  17179. }
  17180. // in the re-use scenario, set needs to be able to mixin new configuration
  17181. if(params){
  17182. this._mixin(params);
  17183. }
  17184. this.onBegin();
  17185. this.setContent();
  17186. this.onEnd();
  17187. return this.node;
  17188. },
  17189. setContent: function(){
  17190. // summary:
  17191. // sets the content on the node
  17192. var node = this.node;
  17193. if(!node) {
  17194. // can't proceed
  17195. throw new Error(this.declaredClass + ": setContent given no node");
  17196. }
  17197. try{
  17198. node = dojo.html._setNodeContent(node, this.content);
  17199. }catch(e){
  17200. // check if a domfault occurs when we are appending this.errorMessage
  17201. // like for instance if domNode is a UL and we try append a DIV
  17202. // FIXME: need to allow the user to provide a content error message string
  17203. var errMess = this.onContentError(e);
  17204. try{
  17205. node.innerHTML = errMess;
  17206. }catch(e){
  17207. console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
  17208. }
  17209. }
  17210. // always put back the node for the next method
  17211. this.node = node; // DomNode
  17212. },
  17213. empty: function() {
  17214. // summary
  17215. // cleanly empty out existing content
  17216. // destroy any widgets from a previous run
  17217. // NOTE: if you dont want this you'll need to empty
  17218. // the parseResults array property yourself to avoid bad things happenning
  17219. if(this.parseResults && this.parseResults.length) {
  17220. dojo.forEach(this.parseResults, function(w) {
  17221. if(w.destroy){
  17222. w.destroy();
  17223. }
  17224. });
  17225. delete this.parseResults;
  17226. }
  17227. // this is fast, but if you know its already empty or safe, you could
  17228. // override empty to skip this step
  17229. dojo.html._emptyNode(this.node);
  17230. },
  17231. onBegin: function(){
  17232. // summary
  17233. // Called after instantiation, but before set();
  17234. // It allows modification of any of the object properties
  17235. // - including the node and content provided - before the set operation actually takes place
  17236. // This default implementation checks for cleanContent and extractContent flags to
  17237. // optionally pre-process html string content
  17238. var cont = this.content;
  17239. if(dojo.isString(cont)){
  17240. if(this.cleanContent){
  17241. cont = dojo.html._secureForInnerHtml(cont);
  17242. }
  17243. if(this.extractContent){
  17244. var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
  17245. if(match){ cont = match[1]; }
  17246. }
  17247. }
  17248. // clean out the node and any cruft associated with it - like widgets
  17249. this.empty();
  17250. this.content = cont;
  17251. return this.node; /* DomNode */
  17252. },
  17253. onEnd: function(){
  17254. // summary
  17255. // Called after set(), when the new content has been pushed into the node
  17256. // It provides an opportunity for post-processing before handing back the node to the caller
  17257. // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
  17258. if(this.parseContent){
  17259. // populates this.parseResults if you need those..
  17260. this._parse();
  17261. }
  17262. return this.node; /* DomNode */
  17263. },
  17264. tearDown: function(){
  17265. // summary
  17266. // manually reset the Setter instance if its being re-used for example for another set()
  17267. // description
  17268. // tearDown() is not called automatically.
  17269. // In normal use, the Setter instance properties are simply allowed to fall out of scope
  17270. // but the tearDown method can be called to explicitly reset this instance.
  17271. delete this.parseResults;
  17272. delete this.node;
  17273. delete this.content;
  17274. },
  17275. onContentError: function(err){
  17276. return "Error occured setting content: " + err;
  17277. },
  17278. _mixin: function(params){
  17279. // mix properties/methods into the instance
  17280. // TODO: the intention with tearDown is to put the Setter's state
  17281. // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
  17282. // so we could do something here to move the original properties aside for later restoration
  17283. var empty = {}, key;
  17284. for(key in params){
  17285. if(key in empty){ continue; }
  17286. // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
  17287. // .. but history shows we'll almost always guess wrong
  17288. this[key] = params[key];
  17289. }
  17290. },
  17291. _parse: function(){
  17292. // summary:
  17293. // runs the dojo parser over the node contents, storing any results in this.parseResults
  17294. // Any errors resulting from parsing are passed to _onError for handling
  17295. var rootNode = this.node;
  17296. try{
  17297. // store the results (widgets, whatever) for potential retrieval
  17298. var inherited = {};
  17299. dojo.forEach(["dir", "lang", "textDir"], function(name){
  17300. if(this[name]){
  17301. inherited[name] = this[name];
  17302. }
  17303. }, this);
  17304. this.parseResults = dojo.parser.parse({
  17305. rootNode: rootNode,
  17306. noStart: !this.startup,
  17307. inherited: inherited,
  17308. scope: this.parserScope
  17309. });
  17310. }catch(e){
  17311. this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
  17312. }
  17313. },
  17314. _onError: function(type, err, consoleText){
  17315. // summary:
  17316. // shows user the string that is returned by on[type]Error
  17317. // overide/implement on[type]Error and return your own string to customize
  17318. var errText = this['on' + type + 'Error'].call(this, err);
  17319. if(consoleText){
  17320. console.error(consoleText, err);
  17321. }else if(errText){ // a empty string won't change current content
  17322. dojo.html._setNodeContent(this.node, errText, true);
  17323. }
  17324. }
  17325. }); // end dojo.declare()
  17326. dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
  17327. // summary:
  17328. // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
  17329. // may be a better choice for simple HTML insertion.
  17330. // description:
  17331. // Unless you need to use the params capabilities of this method, you should use
  17332. // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
  17333. // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
  17334. // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
  17335. // or the other capabilities as defined by the params object for this method.
  17336. // node:
  17337. // the parent element that will receive the content
  17338. // cont:
  17339. // the content to be set on the parent element.
  17340. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  17341. // params:
  17342. // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
  17343. // example:
  17344. // A safe string/node/nodelist content replacement/injection with hooks for extension
  17345. // Example Usage:
  17346. // dojo.html.set(node, "some string");
  17347. // dojo.html.set(node, contentNode, {options});
  17348. // dojo.html.set(node, myNode.childNodes, {options});
  17349. if(undefined == cont){
  17350. console.warn("dojo.html.set: no cont argument provided, using empty string");
  17351. cont = "";
  17352. }
  17353. if(!params){
  17354. // simple and fast
  17355. return dojo.html._setNodeContent(node, cont, true);
  17356. }else{
  17357. // more options but slower
  17358. // note the arguments are reversed in order, to match the convention for instantiation via the parser
  17359. var op = new dojo.html._ContentSetter(dojo.mixin(
  17360. params,
  17361. { content: cont, node: node }
  17362. ));
  17363. return op.set();
  17364. }
  17365. };
  17366. })();
  17367. }
  17368. if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17369. dojo._hasResource["dijit.layout.ContentPane"] = true;
  17370. dojo.provide("dijit.layout.ContentPane");
  17371. dojo.declare(
  17372. "dijit.layout.ContentPane", [dijit._Widget, dijit.layout._ContentPaneResizeMixin],
  17373. {
  17374. // summary:
  17375. // A widget containing an HTML fragment, specified inline
  17376. // or by uri. Fragment may include widgets.
  17377. //
  17378. // description:
  17379. // This widget embeds a document fragment in the page, specified
  17380. // either by uri, javascript generated markup or DOM reference.
  17381. // Any widgets within this content are instantiated and managed,
  17382. // but laid out according to the HTML structure. Unlike IFRAME,
  17383. // ContentPane embeds a document fragment as would be found
  17384. // inside the BODY tag of a full HTML document. It should not
  17385. // contain the HTML, HEAD, or BODY tags.
  17386. // For more advanced functionality with scripts and
  17387. // stylesheets, see dojox.layout.ContentPane. This widget may be
  17388. // used stand alone or as a base class for other widgets.
  17389. // ContentPane is useful as a child of other layout containers
  17390. // such as BorderContainer or TabContainer, but note that those
  17391. // widgets can contain any widget as a child.
  17392. //
  17393. // example:
  17394. // Some quick samples:
  17395. // To change the innerHTML: cp.set('content', '<b>new content</b>')
  17396. //
  17397. // Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection))
  17398. //
  17399. // To do an ajax update: cp.set('href', url)
  17400. // href: String
  17401. // The href of the content that displays now.
  17402. // Set this at construction if you want to load data externally when the
  17403. // pane is shown. (Set preload=true to load it immediately.)
  17404. // Changing href after creation doesn't have any effect; Use set('href', ...);
  17405. href: "",
  17406. /*=====
  17407. // content: String || DomNode || NodeList || dijit._Widget
  17408. // The innerHTML of the ContentPane.
  17409. // Note that the initialization parameter / argument to set("content", ...)
  17410. // can be a String, DomNode, Nodelist, or _Widget.
  17411. content: "",
  17412. =====*/
  17413. // extractContent: Boolean
  17414. // Extract visible content from inside of <body> .... </body>.
  17415. // I.e., strip <html> and <head> (and it's contents) from the href
  17416. extractContent: false,
  17417. // parseOnLoad: Boolean
  17418. // Parse content and create the widgets, if any.
  17419. parseOnLoad: true,
  17420. // parserScope: String
  17421. // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
  17422. // will search for data-dojo-type (or dojoType). For backwards compatibility
  17423. // reasons defaults to dojo._scopeName (which is "dojo" except when
  17424. // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
  17425. parserScope: dojo._scopeName,
  17426. // preventCache: Boolean
  17427. // Prevent caching of data from href's by appending a timestamp to the href.
  17428. preventCache: false,
  17429. // preload: Boolean
  17430. // Force load of data on initialization even if pane is hidden.
  17431. preload: false,
  17432. // refreshOnShow: Boolean
  17433. // Refresh (re-download) content when pane goes from hidden to shown
  17434. refreshOnShow: false,
  17435. // loadingMessage: String
  17436. // Message that shows while downloading
  17437. loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
  17438. // errorMessage: String
  17439. // Message that shows if an error occurs
  17440. errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
  17441. // isLoaded: [readonly] Boolean
  17442. // True if the ContentPane has data in it, either specified
  17443. // during initialization (via href or inline content), or set
  17444. // via set('content', ...) / set('href', ...)
  17445. //
  17446. // False if it doesn't have any content, or if ContentPane is
  17447. // still in the process of downloading href.
  17448. isLoaded: false,
  17449. baseClass: "dijitContentPane",
  17450. // ioArgs: Object
  17451. // Parameters to pass to xhrGet() request, for example:
  17452. // | <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
  17453. ioArgs: {},
  17454. // onLoadDeferred: [readonly] dojo.Deferred
  17455. // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
  17456. // Calling onLoadDeferred.addCallback() or addErrback() registers your
  17457. // callback to be called only once, when the prior set('href', ...) call or
  17458. // the initial href parameter to the constructor finishes loading.
  17459. //
  17460. // This is different than an onLoad() handler which gets called any time any href
  17461. // or content is loaded.
  17462. onLoadDeferred: null,
  17463. // Override _Widget's attributeMap because we don't want the title attribute (used to specify
  17464. // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
  17465. // entire pane.
  17466. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  17467. title: []
  17468. }),
  17469. // Flag to parser that I'll parse my contents, so it shouldn't.
  17470. stopParser: true,
  17471. // template: [private] Boolean
  17472. // Flag from the parser that this ContentPane is inside a template
  17473. // so the contents are pre-parsed.
  17474. // (TODO: this declaration can be commented out in 2.0)
  17475. template: false,
  17476. create: function(params, srcNodeRef){
  17477. // Convert a srcNodeRef argument into a content parameter, so that the original contents are
  17478. // processed in the same way as contents set via set("content", ...), calling the parser etc.
  17479. // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
  17480. if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
  17481. var df = dojo.doc.createDocumentFragment();
  17482. srcNodeRef = dojo.byId(srcNodeRef)
  17483. while(srcNodeRef.firstChild){
  17484. df.appendChild(srcNodeRef.firstChild);
  17485. }
  17486. params = dojo.delegate(params, {content: df});
  17487. }
  17488. this.inherited(arguments, [params, srcNodeRef]);
  17489. },
  17490. postMixInProperties: function(){
  17491. this.inherited(arguments);
  17492. var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
  17493. this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
  17494. this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
  17495. },
  17496. buildRendering: function(){
  17497. this.inherited(arguments);
  17498. // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
  17499. // For subclasses of ContentPane that do have a template, does nothing.
  17500. if(!this.containerNode){
  17501. this.containerNode = this.domNode;
  17502. }
  17503. // remove the title attribute so it doesn't show up when hovering
  17504. // over a node (TODO: remove in 2.0, no longer needed after #11490)
  17505. this.domNode.title = "";
  17506. if(!dojo.attr(this.domNode,"role")){
  17507. dijit.setWaiRole(this.domNode, "group");
  17508. }
  17509. },
  17510. _startChildren: function(){
  17511. // summary:
  17512. // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
  17513. // This starts all the widgets
  17514. this.inherited(arguments);
  17515. // And this catches stuff like dojo.dnd.Source
  17516. if(this._contentSetter){
  17517. dojo.forEach(this._contentSetter.parseResults, function(obj){
  17518. if(!obj._started && !obj._destroyed && dojo.isFunction(obj.startup)){
  17519. obj.startup();
  17520. obj._started = true;
  17521. }
  17522. }, this);
  17523. }
  17524. },
  17525. setHref: function(/*String|Uri*/ href){
  17526. // summary:
  17527. // Deprecated. Use set('href', ...) instead.
  17528. dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
  17529. return this.set("href", href);
  17530. },
  17531. _setHrefAttr: function(/*String|Uri*/ href){
  17532. // summary:
  17533. // Hook so set("href", ...) works.
  17534. // description:
  17535. // Reset the (external defined) content of this pane and replace with new url
  17536. // Note: It delays the download until widget is shown if preload is false.
  17537. // href:
  17538. // url to the page you want to get, must be within the same domain as your mainpage
  17539. // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
  17540. this.cancel();
  17541. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  17542. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  17543. this._set("href", href);
  17544. // _setHrefAttr() is called during creation and by the user, after creation.
  17545. // Assuming preload == false, only in the second case do we actually load the URL;
  17546. // otherwise it's done in startup(), and only if this widget is shown.
  17547. if(this.preload || (this._created && this._isShown())){
  17548. this._load();
  17549. }else{
  17550. // Set flag to indicate that href needs to be loaded the next time the
  17551. // ContentPane is made visible
  17552. this._hrefChanged = true;
  17553. }
  17554. return this.onLoadDeferred; // dojo.Deferred
  17555. },
  17556. setContent: function(/*String|DomNode|Nodelist*/data){
  17557. // summary:
  17558. // Deprecated. Use set('content', ...) instead.
  17559. dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
  17560. this.set("content", data);
  17561. },
  17562. _setContentAttr: function(/*String|DomNode|Nodelist*/data){
  17563. // summary:
  17564. // Hook to make set("content", ...) work.
  17565. // Replaces old content with data content, include style classes from old content
  17566. // data:
  17567. // the new Content may be String, DomNode or NodeList
  17568. //
  17569. // if data is a NodeList (or an array of nodes) nodes are copied
  17570. // so you can import nodes from another document implicitly
  17571. // clear href so we can't run refresh and clear content
  17572. // refresh should only work if we downloaded the content
  17573. this._set("href", "");
  17574. // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
  17575. this.cancel();
  17576. // Even though user is just setting content directly, still need to define an onLoadDeferred
  17577. // because the _onLoadHandler() handler is still getting called from setContent()
  17578. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  17579. if(this._created){
  17580. // For back-compat reasons, call onLoad() for set('content', ...)
  17581. // calls but not for content specified in srcNodeRef (ie: <div dojoType=ContentPane>...</div>)
  17582. // or as initialization parameter (ie: new ContentPane({content: ...})
  17583. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  17584. }
  17585. this._setContent(data || "");
  17586. this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
  17587. return this.onLoadDeferred; // dojo.Deferred
  17588. },
  17589. _getContentAttr: function(){
  17590. // summary:
  17591. // Hook to make get("content") work
  17592. return this.containerNode.innerHTML;
  17593. },
  17594. cancel: function(){
  17595. // summary:
  17596. // Cancels an in-flight download of content
  17597. if(this._xhrDfd && (this._xhrDfd.fired == -1)){
  17598. this._xhrDfd.cancel();
  17599. }
  17600. delete this._xhrDfd; // garbage collect
  17601. this.onLoadDeferred = null;
  17602. },
  17603. uninitialize: function(){
  17604. if(this._beingDestroyed){
  17605. this.cancel();
  17606. }
  17607. this.inherited(arguments);
  17608. },
  17609. destroyRecursive: function(/*Boolean*/ preserveDom){
  17610. // summary:
  17611. // Destroy the ContentPane and its contents
  17612. // if we have multiple controllers destroying us, bail after the first
  17613. if(this._beingDestroyed){
  17614. return;
  17615. }
  17616. this.inherited(arguments);
  17617. },
  17618. _onShow: function(){
  17619. // summary:
  17620. // Called when the ContentPane is made visible
  17621. // description:
  17622. // For a plain ContentPane, this is called on initialization, from startup().
  17623. // If the ContentPane is a hidden pane of a TabContainer etc., then it's
  17624. // called whenever the pane is made visible.
  17625. //
  17626. // Does necessary processing, including href download and layout/resize of
  17627. // child widget(s)
  17628. this.inherited(arguments);
  17629. if(this.href){
  17630. if(!this._xhrDfd && // if there's an href that isn't already being loaded
  17631. (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
  17632. ){
  17633. return this.refresh(); // If child has an href, promise that fires when the load is complete
  17634. }
  17635. }
  17636. },
  17637. refresh: function(){
  17638. // summary:
  17639. // [Re]download contents of href and display
  17640. // description:
  17641. // 1. cancels any currently in-flight requests
  17642. // 2. posts "loading..." message
  17643. // 3. sends XHR to download new data
  17644. // Cancel possible prior in-flight request
  17645. this.cancel();
  17646. this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
  17647. this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
  17648. this._load();
  17649. return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
  17650. },
  17651. _load: function(){
  17652. // summary:
  17653. // Load/reload the href specified in this.href
  17654. // display loading message
  17655. this._setContent(this.onDownloadStart(), true);
  17656. var self = this;
  17657. var getArgs = {
  17658. preventCache: (this.preventCache || this.refreshOnShow),
  17659. url: this.href,
  17660. handleAs: "text"
  17661. };
  17662. if(dojo.isObject(this.ioArgs)){
  17663. dojo.mixin(getArgs, this.ioArgs);
  17664. }
  17665. var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
  17666. hand.addCallback(function(html){
  17667. try{
  17668. self._isDownloaded = true;
  17669. self._setContent(html, false);
  17670. self.onDownloadEnd();
  17671. }catch(err){
  17672. self._onError('Content', err); // onContentError
  17673. }
  17674. delete self._xhrDfd;
  17675. return html;
  17676. });
  17677. hand.addErrback(function(err){
  17678. if(!hand.canceled){
  17679. // show error message in the pane
  17680. self._onError('Download', err); // onDownloadError
  17681. }
  17682. delete self._xhrDfd;
  17683. return err;
  17684. });
  17685. // Remove flag saying that a load is needed
  17686. delete this._hrefChanged;
  17687. },
  17688. _onLoadHandler: function(data){
  17689. // summary:
  17690. // This is called whenever new content is being loaded
  17691. this._set("isLoaded", true);
  17692. try{
  17693. this.onLoadDeferred.callback(data);
  17694. }catch(e){
  17695. console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
  17696. }
  17697. },
  17698. _onUnloadHandler: function(){
  17699. // summary:
  17700. // This is called whenever the content is being unloaded
  17701. this._set("isLoaded", false);
  17702. try{
  17703. this.onUnload();
  17704. }catch(e){
  17705. console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
  17706. }
  17707. },
  17708. destroyDescendants: function(){
  17709. // summary:
  17710. // Destroy all the widgets inside the ContentPane and empty containerNode
  17711. // Make sure we call onUnload (but only when the ContentPane has real content)
  17712. if(this.isLoaded){
  17713. this._onUnloadHandler();
  17714. }
  17715. // Even if this.isLoaded == false there might still be a "Loading..." message
  17716. // to erase, so continue...
  17717. // For historical reasons we need to delete all widgets under this.containerNode,
  17718. // even ones that the user has created manually.
  17719. var setter = this._contentSetter;
  17720. dojo.forEach(this.getChildren(), function(widget){
  17721. if(widget.destroyRecursive){
  17722. widget.destroyRecursive();
  17723. }
  17724. });
  17725. if(setter){
  17726. // Most of the widgets in setter.parseResults have already been destroyed, but
  17727. // things like Menu that have been moved to <body> haven't yet
  17728. dojo.forEach(setter.parseResults, function(widget){
  17729. if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
  17730. widget.destroyRecursive();
  17731. }
  17732. });
  17733. delete setter.parseResults;
  17734. }
  17735. // And then clear away all the DOM nodes
  17736. dojo.html._emptyNode(this.containerNode);
  17737. // Delete any state information we have about current contents
  17738. delete this._singleChild;
  17739. },
  17740. _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
  17741. // summary:
  17742. // Insert the content into the container node
  17743. // first get rid of child widgets
  17744. this.destroyDescendants();
  17745. // dojo.html.set will take care of the rest of the details
  17746. // we provide an override for the error handling to ensure the widget gets the errors
  17747. // configure the setter instance with only the relevant widget instance properties
  17748. // NOTE: unless we hook into attr, or provide property setters for each property,
  17749. // we need to re-configure the ContentSetter with each use
  17750. var setter = this._contentSetter;
  17751. if(! (setter && setter instanceof dojo.html._ContentSetter)){
  17752. setter = this._contentSetter = new dojo.html._ContentSetter({
  17753. node: this.containerNode,
  17754. _onError: dojo.hitch(this, this._onError),
  17755. onContentError: dojo.hitch(this, function(e){
  17756. // fires if a domfault occurs when we are appending this.errorMessage
  17757. // like for instance if domNode is a UL and we try append a DIV
  17758. var errMess = this.onContentError(e);
  17759. try{
  17760. this.containerNode.innerHTML = errMess;
  17761. }catch(e){
  17762. console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
  17763. }
  17764. })/*,
  17765. _onError */
  17766. });
  17767. };
  17768. var setterParams = dojo.mixin({
  17769. cleanContent: this.cleanContent,
  17770. extractContent: this.extractContent,
  17771. parseContent: this.parseOnLoad,
  17772. parserScope: this.parserScope,
  17773. startup: false,
  17774. dir: this.dir,
  17775. lang: this.lang
  17776. }, this._contentSetterParams || {});
  17777. setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
  17778. // setter params must be pulled afresh from the ContentPane each time
  17779. delete this._contentSetterParams;
  17780. if(this.doLayout){
  17781. this._checkIfSingleChild();
  17782. }
  17783. if(!isFakeContent){
  17784. if(this._started){
  17785. // Startup each top level child widget (and they will start their children, recursively)
  17786. this._startChildren();
  17787. // Call resize() on each of my child layout widgets,
  17788. // or resize() on my single child layout widget...
  17789. // either now (if I'm currently visible) or when I become visible
  17790. this._scheduleLayout();
  17791. }
  17792. this._onLoadHandler(cont);
  17793. }
  17794. },
  17795. _onError: function(type, err, consoleText){
  17796. this.onLoadDeferred.errback(err);
  17797. // shows user the string that is returned by on[type]Error
  17798. // override on[type]Error and return your own string to customize
  17799. var errText = this['on' + type + 'Error'].call(this, err);
  17800. if(consoleText){
  17801. console.error(consoleText, err);
  17802. }else if(errText){// a empty string won't change current content
  17803. this._setContent(errText, true);
  17804. }
  17805. },
  17806. // EVENT's, should be overide-able
  17807. onLoad: function(data){
  17808. // summary:
  17809. // Event hook, is called after everything is loaded and widgetified
  17810. // tags:
  17811. // callback
  17812. },
  17813. onUnload: function(){
  17814. // summary:
  17815. // Event hook, is called before old content is cleared
  17816. // tags:
  17817. // callback
  17818. },
  17819. onDownloadStart: function(){
  17820. // summary:
  17821. // Called before download starts.
  17822. // description:
  17823. // The string returned by this function will be the html
  17824. // that tells the user we are loading something.
  17825. // Override with your own function if you want to change text.
  17826. // tags:
  17827. // extension
  17828. return this.loadingMessage;
  17829. },
  17830. onContentError: function(/*Error*/ error){
  17831. // summary:
  17832. // Called on DOM faults, require faults etc. in content.
  17833. //
  17834. // In order to display an error message in the pane, return
  17835. // the error message from this method, as an HTML string.
  17836. //
  17837. // By default (if this method is not overriden), it returns
  17838. // nothing, so the error message is just printed to the console.
  17839. // tags:
  17840. // extension
  17841. },
  17842. onDownloadError: function(/*Error*/ error){
  17843. // summary:
  17844. // Called when download error occurs.
  17845. //
  17846. // In order to display an error message in the pane, return
  17847. // the error message from this method, as an HTML string.
  17848. //
  17849. // Default behavior (if this method is not overriden) is to display
  17850. // the error message inside the pane.
  17851. // tags:
  17852. // extension
  17853. return this.errorMessage;
  17854. },
  17855. onDownloadEnd: function(){
  17856. // summary:
  17857. // Called when download is finished.
  17858. // tags:
  17859. // callback
  17860. }
  17861. });
  17862. }
  17863. if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  17864. dojo._hasResource["dijit.form._FormMixin"] = true;
  17865. dojo.provide("dijit.form._FormMixin");
  17866. dojo.declare("dijit.form._FormMixin", null, {
  17867. // summary:
  17868. // Mixin for containers of form widgets (i.e. widgets that represent a single value
  17869. // and can be children of a <form> node or dijit.form.Form widget)
  17870. // description:
  17871. // Can extract all the form widgets
  17872. // values and combine them into a single javascript object, or alternately
  17873. // take such an object and set the values for all the contained
  17874. // form widgets
  17875. /*=====
  17876. // value: Object
  17877. // Name/value hash for each child widget with a name and value.
  17878. // Child widgets without names are not part of the hash.
  17879. //
  17880. // If there are multiple child widgets w/the same name, value is an array,
  17881. // unless they are radio buttons in which case value is a scalar (since only
  17882. // one radio button can be checked at a time).
  17883. //
  17884. // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
  17885. //
  17886. // Example:
  17887. // | { name: "John Smith", interests: ["sports", "movies"] }
  17888. =====*/
  17889. // state: [readonly] String
  17890. // Will be "Error" if one or more of the child widgets has an invalid value,
  17891. // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
  17892. // which indicates that the form is ready to be submitted.
  17893. state: "",
  17894. // TODO:
  17895. // * Repeater
  17896. // * better handling for arrays. Often form elements have names with [] like
  17897. // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
  17898. //
  17899. //
  17900. reset: function(){
  17901. dojo.forEach(this.getDescendants(), function(widget){
  17902. if(widget.reset){
  17903. widget.reset();
  17904. }
  17905. });
  17906. },
  17907. validate: function(){
  17908. // summary:
  17909. // returns if the form is valid - same as isValid - but
  17910. // provides a few additional (ui-specific) features.
  17911. // 1 - it will highlight any sub-widgets that are not
  17912. // valid
  17913. // 2 - it will call focus() on the first invalid
  17914. // sub-widget
  17915. var didFocus = false;
  17916. return dojo.every(dojo.map(this.getDescendants(), function(widget){
  17917. // Need to set this so that "required" widgets get their
  17918. // state set.
  17919. widget._hasBeenBlurred = true;
  17920. var valid = widget.disabled || !widget.validate || widget.validate();
  17921. if(!valid && !didFocus){
  17922. // Set focus of the first non-valid widget
  17923. dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
  17924. widget.focus();
  17925. didFocus = true;
  17926. }
  17927. return valid;
  17928. }), function(item){ return item; });
  17929. },
  17930. setValues: function(val){
  17931. dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
  17932. return this.set('value', val);
  17933. },
  17934. _setValueAttr: function(/*Object*/ obj){
  17935. // summary:
  17936. // Fill in form values from according to an Object (in the format returned by get('value'))
  17937. // generate map from name --> [list of widgets with that name]
  17938. var map = { };
  17939. dojo.forEach(this.getDescendants(), function(widget){
  17940. if(!widget.name){ return; }
  17941. var entry = map[widget.name] || (map[widget.name] = [] );
  17942. entry.push(widget);
  17943. });
  17944. for(var name in map){
  17945. if(!map.hasOwnProperty(name)){
  17946. continue;
  17947. }
  17948. var widgets = map[name], // array of widgets w/this name
  17949. values = dojo.getObject(name, false, obj); // list of values for those widgets
  17950. if(values === undefined){
  17951. continue;
  17952. }
  17953. if(!dojo.isArray(values)){
  17954. values = [ values ];
  17955. }
  17956. if(typeof widgets[0].checked == 'boolean'){
  17957. // for checkbox/radio, values is a list of which widgets should be checked
  17958. dojo.forEach(widgets, function(w, i){
  17959. w.set('value', dojo.indexOf(values, w.value) != -1);
  17960. });
  17961. }else if(widgets[0].multiple){
  17962. // it takes an array (e.g. multi-select)
  17963. widgets[0].set('value', values);
  17964. }else{
  17965. // otherwise, values is a list of values to be assigned sequentially to each widget
  17966. dojo.forEach(widgets, function(w, i){
  17967. w.set('value', values[i]);
  17968. });
  17969. }
  17970. }
  17971. /***
  17972. * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
  17973. dojo.forEach(this.containerNode.elements, function(element){
  17974. if(element.name == ''){return}; // like "continue"
  17975. var namePath = element.name.split(".");
  17976. var myObj=obj;
  17977. var name=namePath[namePath.length-1];
  17978. for(var j=1,len2=namePath.length;j<len2;++j){
  17979. var p=namePath[j - 1];
  17980. // repeater support block
  17981. var nameA=p.split("[");
  17982. if(nameA.length > 1){
  17983. if(typeof(myObj[nameA[0]]) == "undefined"){
  17984. myObj[nameA[0]]=[ ];
  17985. } // if
  17986. nameIndex=parseInt(nameA[1]);
  17987. if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
  17988. myObj[nameA[0]][nameIndex] = { };
  17989. }
  17990. myObj=myObj[nameA[0]][nameIndex];
  17991. continue;
  17992. } // repeater support ends
  17993. if(typeof(myObj[p]) == "undefined"){
  17994. myObj=undefined;
  17995. break;
  17996. };
  17997. myObj=myObj[p];
  17998. }
  17999. if(typeof(myObj) == "undefined"){
  18000. return; // like "continue"
  18001. }
  18002. if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
  18003. return; // like "continue"
  18004. }
  18005. // TODO: widget values (just call set('value', ...) on the widget)
  18006. // TODO: maybe should call dojo.getNodeProp() instead
  18007. switch(element.type){
  18008. case "checkbox":
  18009. element.checked = (name in myObj) &&
  18010. dojo.some(myObj[name], function(val){ return val == element.value; });
  18011. break;
  18012. case "radio":
  18013. element.checked = (name in myObj) && myObj[name] == element.value;
  18014. break;
  18015. case "select-multiple":
  18016. element.selectedIndex=-1;
  18017. dojo.forEach(element.options, function(option){
  18018. option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
  18019. });
  18020. break;
  18021. case "select-one":
  18022. element.selectedIndex="0";
  18023. dojo.forEach(element.options, function(option){
  18024. option.selected = option.value == myObj[name];
  18025. });
  18026. break;
  18027. case "hidden":
  18028. case "text":
  18029. case "textarea":
  18030. case "password":
  18031. element.value = myObj[name] || "";
  18032. break;
  18033. }
  18034. });
  18035. */
  18036. // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
  18037. // which I am monitoring.
  18038. },
  18039. getValues: function(){
  18040. dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
  18041. return this.get('value');
  18042. },
  18043. _getValueAttr: function(){
  18044. // summary:
  18045. // Returns Object representing form values. See description of `value` for details.
  18046. // description:
  18047. // The value is updated into this.value every time a child has an onChange event,
  18048. // so in the common case this function could just return this.value. However,
  18049. // that wouldn't work when:
  18050. //
  18051. // 1. User presses return key to submit a form. That doesn't fire an onchange event,
  18052. // and even if it did it would come too late due to the setTimout(..., 0) in _handleOnChange()
  18053. //
  18054. // 2. app for some reason calls this.get("value") while the user is typing into a
  18055. // form field. Not sure if that case needs to be supported or not.
  18056. // get widget values
  18057. var obj = { };
  18058. dojo.forEach(this.getDescendants(), function(widget){
  18059. var name = widget.name;
  18060. if(!name || widget.disabled){ return; }
  18061. // Single value widget (checkbox, radio, or plain <input> type widget)
  18062. var value = widget.get('value');
  18063. // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
  18064. if(typeof widget.checked == 'boolean'){
  18065. if(/Radio/.test(widget.declaredClass)){
  18066. // radio button
  18067. if(value !== false){
  18068. dojo.setObject(name, value, obj);
  18069. }else{
  18070. // give radio widgets a default of null
  18071. value = dojo.getObject(name, false, obj);
  18072. if(value === undefined){
  18073. dojo.setObject(name, null, obj);
  18074. }
  18075. }
  18076. }else{
  18077. // checkbox/toggle button
  18078. var ary=dojo.getObject(name, false, obj);
  18079. if(!ary){
  18080. ary=[];
  18081. dojo.setObject(name, ary, obj);
  18082. }
  18083. if(value !== false){
  18084. ary.push(value);
  18085. }
  18086. }
  18087. }else{
  18088. var prev=dojo.getObject(name, false, obj);
  18089. if(typeof prev != "undefined"){
  18090. if(dojo.isArray(prev)){
  18091. prev.push(value);
  18092. }else{
  18093. dojo.setObject(name, [prev, value], obj);
  18094. }
  18095. }else{
  18096. // unique name
  18097. dojo.setObject(name, value, obj);
  18098. }
  18099. }
  18100. });
  18101. /***
  18102. * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
  18103. * but it doesn't understand [] notation, presumably)
  18104. var obj = { };
  18105. dojo.forEach(this.containerNode.elements, function(elm){
  18106. if(!elm.name) {
  18107. return; // like "continue"
  18108. }
  18109. var namePath = elm.name.split(".");
  18110. var myObj=obj;
  18111. var name=namePath[namePath.length-1];
  18112. for(var j=1,len2=namePath.length;j<len2;++j){
  18113. var nameIndex = null;
  18114. var p=namePath[j - 1];
  18115. var nameA=p.split("[");
  18116. if(nameA.length > 1){
  18117. if(typeof(myObj[nameA[0]]) == "undefined"){
  18118. myObj[nameA[0]]=[ ];
  18119. } // if
  18120. nameIndex=parseInt(nameA[1]);
  18121. if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
  18122. myObj[nameA[0]][nameIndex] = { };
  18123. }
  18124. } else if(typeof(myObj[nameA[0]]) == "undefined"){
  18125. myObj[nameA[0]] = { }
  18126. } // if
  18127. if(nameA.length == 1){
  18128. myObj=myObj[nameA[0]];
  18129. } else{
  18130. myObj=myObj[nameA[0]][nameIndex];
  18131. } // if
  18132. } // for
  18133. if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
  18134. if(name == name.split("[")[0]){
  18135. myObj[name]=elm.value;
  18136. } else{
  18137. // can not set value when there is no name
  18138. }
  18139. } else if(elm.type == "checkbox" && elm.checked){
  18140. if(typeof(myObj[name]) == 'undefined'){
  18141. myObj[name]=[ ];
  18142. }
  18143. myObj[name].push(elm.value);
  18144. } else if(elm.type == "select-multiple"){
  18145. if(typeof(myObj[name]) == 'undefined'){
  18146. myObj[name]=[ ];
  18147. }
  18148. for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
  18149. if(elm.options[jdx].selected){
  18150. myObj[name].push(elm.options[jdx].value);
  18151. }
  18152. }
  18153. } // if
  18154. name=undefined;
  18155. }); // forEach
  18156. ***/
  18157. return obj;
  18158. },
  18159. isValid: function(){
  18160. // summary:
  18161. // Returns true if all of the widgets are valid.
  18162. // Deprecated, will be removed in 2.0. Use get("state") instead.
  18163. return this.state == "";
  18164. },
  18165. onValidStateChange: function(isValid){
  18166. // summary:
  18167. // Stub function to connect to if you want to do something
  18168. // (like disable/enable a submit button) when the valid
  18169. // state changes on the form as a whole.
  18170. //
  18171. // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
  18172. },
  18173. _getState: function(){
  18174. // summary:
  18175. // Compute what this.state should be based on state of children
  18176. var states = dojo.map(this._descendants, function(w){
  18177. return w.get("state") || "";
  18178. });
  18179. return dojo.indexOf(states, "Error") >= 0 ? "Error" :
  18180. dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
  18181. },
  18182. disconnectChildren: function(){
  18183. // summary:
  18184. // Remove connections to monitor changes to children's value, error state, and disabled state,
  18185. // in order to update Form.value and Form.state.
  18186. dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect"));
  18187. dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); });
  18188. },
  18189. connectChildren: function(/*Boolean*/ inStartup){
  18190. // summary:
  18191. // Setup connections to monitor changes to children's value, error state, and disabled state,
  18192. // in order to update Form.value and Form.state.
  18193. //
  18194. // You can call this function directly, ex. in the event that you
  18195. // programmatically add a widget to the form *after* the form has been
  18196. // initialized.
  18197. var _this = this;
  18198. // Remove old connections, if any
  18199. this.disconnectChildren();
  18200. this._descendants = this.getDescendants();
  18201. // (Re)set this.value and this.state. Send watch() notifications but not on startup.
  18202. var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set");
  18203. set("value", this.get("value"));
  18204. set("state", this._getState());
  18205. // Monitor changes to error state and disabled state in order to update
  18206. // Form.state
  18207. var conns = (this._childConnections = []),
  18208. watches = (this._childWatches = []);
  18209. dojo.forEach(dojo.filter(this._descendants,
  18210. function(item){ return item.validate; }
  18211. ),
  18212. function(widget){
  18213. // We are interested in whenever the widget changes validity state - or
  18214. // whenever the disabled attribute on that widget is changed.
  18215. dojo.forEach(["state", "disabled"], function(attr){
  18216. watches.push(widget.watch(attr, function(attr, oldVal, newVal){
  18217. _this.set("state", _this._getState());
  18218. }));
  18219. });
  18220. });
  18221. // And monitor calls to child.onChange so we can update this.value
  18222. var onChange = function(){
  18223. // summary:
  18224. // Called when child's value or disabled state changes
  18225. // Use setTimeout() to collapse value changes in multiple children into a single
  18226. // update to my value. Multiple updates will occur on:
  18227. // 1. Form.set()
  18228. // 2. Form.reset()
  18229. // 3. user selecting a radio button (which will de-select another radio button,
  18230. // causing two onChange events)
  18231. if(_this._onChangeDelayTimer){
  18232. clearTimeout(_this._onChangeDelayTimer);
  18233. }
  18234. _this._onChangeDelayTimer = setTimeout(function(){
  18235. delete _this._onChangeDelayTimer;
  18236. _this._set("value", _this.get("value"));
  18237. }, 10);
  18238. };
  18239. dojo.forEach(
  18240. dojo.filter(this._descendants, function(item){ return item.onChange; } ),
  18241. function(widget){
  18242. // When a child widget's value changes,
  18243. // the efficient thing to do is to just update that one attribute in this.value,
  18244. // but that gets a little complicated when a checkbox is checked/unchecked
  18245. // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
  18246. // Doing simple thing for now.
  18247. conns.push(_this.connect(widget, "onChange", onChange));
  18248. // Disabling/enabling a child widget should remove it's value from this.value.
  18249. // Again, this code could be more efficient, doing simple thing for now.
  18250. watches.push(widget.watch("disabled", onChange));
  18251. }
  18252. );
  18253. },
  18254. startup: function(){
  18255. this.inherited(arguments);
  18256. // Initialize value and valid/invalid state tracking. Needs to be done in startup()
  18257. // so that children are initialized.
  18258. this.connectChildren(true);
  18259. // Make state change call onValidStateChange(), will be removed in 2.0
  18260. this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
  18261. },
  18262. destroy: function(){
  18263. this.disconnectChildren();
  18264. this.inherited(arguments);
  18265. }
  18266. });
  18267. }
  18268. if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18269. dojo._hasResource["dijit._DialogMixin"] = true;
  18270. dojo.provide("dijit._DialogMixin");
  18271. dojo.declare("dijit._DialogMixin", null,
  18272. {
  18273. // summary:
  18274. // This provides functions useful to Dialog and TooltipDialog
  18275. attributeMap: dijit._Widget.prototype.attributeMap,
  18276. execute: function(/*Object*/ formContents){
  18277. // summary:
  18278. // Callback when the user hits the submit button.
  18279. // Override this method to handle Dialog execution.
  18280. // description:
  18281. // After the user has pressed the submit button, the Dialog
  18282. // first calls onExecute() to notify the container to hide the
  18283. // dialog and restore focus to wherever it used to be.
  18284. //
  18285. // *Then* this method is called.
  18286. // type:
  18287. // callback
  18288. },
  18289. onCancel: function(){
  18290. // summary:
  18291. // Called when user has pressed the Dialog's cancel button, to notify container.
  18292. // description:
  18293. // Developer shouldn't override or connect to this method;
  18294. // it's a private communication device between the TooltipDialog
  18295. // and the thing that opened it (ex: `dijit.form.DropDownButton`)
  18296. // type:
  18297. // protected
  18298. },
  18299. onExecute: function(){
  18300. // summary:
  18301. // Called when user has pressed the dialog's OK button, to notify container.
  18302. // description:
  18303. // Developer shouldn't override or connect to this method;
  18304. // it's a private communication device between the TooltipDialog
  18305. // and the thing that opened it (ex: `dijit.form.DropDownButton`)
  18306. // type:
  18307. // protected
  18308. },
  18309. _onSubmit: function(){
  18310. // summary:
  18311. // Callback when user hits submit button
  18312. // type:
  18313. // protected
  18314. this.onExecute(); // notify container that we are about to execute
  18315. this.execute(this.get('value'));
  18316. },
  18317. _getFocusItems: function(){
  18318. // summary:
  18319. // Finds focusable items in dialog,
  18320. // and sets this._firstFocusItem and this._lastFocusItem
  18321. // tags:
  18322. // protected
  18323. var elems = dijit._getTabNavigable(this.containerNode);
  18324. this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
  18325. this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
  18326. }
  18327. }
  18328. );
  18329. }
  18330. if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18331. dojo._hasResource["dijit.TooltipDialog"] = true;
  18332. dojo.provide("dijit.TooltipDialog");
  18333. dojo.declare(
  18334. "dijit.TooltipDialog",
  18335. [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
  18336. {
  18337. // summary:
  18338. // Pops up a dialog that appears like a Tooltip
  18339. // title: String
  18340. // Description of tooltip dialog (required for a11y)
  18341. title: "",
  18342. // doLayout: [protected] Boolean
  18343. // Don't change this parameter from the default value.
  18344. // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
  18345. // is never a child of a layout container, nor can you specify the size of
  18346. // TooltipDialog in order to control the size of an inner widget.
  18347. doLayout: false,
  18348. // autofocus: Boolean
  18349. // A Toggle to modify the default focus behavior of a Dialog, which
  18350. // is to focus on the first dialog element after opening the dialog.
  18351. // False will disable autofocusing. Default: true
  18352. autofocus: true,
  18353. // baseClass: [protected] String
  18354. // The root className to use for the various states of this widget
  18355. baseClass: "dijitTooltipDialog",
  18356. // _firstFocusItem: [private] [readonly] DomNode
  18357. // The pointer to the first focusable node in the dialog.
  18358. // Set by `dijit._DialogMixin._getFocusItems`.
  18359. _firstFocusItem: null,
  18360. // _lastFocusItem: [private] [readonly] DomNode
  18361. // The pointer to which node has focus prior to our dialog.
  18362. // Set by `dijit._DialogMixin._getFocusItems`.
  18363. _lastFocusItem: null,
  18364. 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"),
  18365. _setTitleAttr: function(/*String*/ title){
  18366. this.containerNode.title = title;
  18367. this._set("title", title)
  18368. },
  18369. postCreate: function(){
  18370. this.inherited(arguments);
  18371. this.connect(this.containerNode, "onkeypress", "_onKey");
  18372. },
  18373. orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
  18374. // summary:
  18375. // Configure widget to be displayed in given position relative to the button.
  18376. // This is called from the dijit.popup code, and should not be called
  18377. // directly.
  18378. // tags:
  18379. // protected
  18380. var newC = "dijitTooltipAB" + (corner.charAt(1) == 'L' ? "Left" : "Right")
  18381. + " dijitTooltip"
  18382. + (corner.charAt(0) == 'T' ? "Below" : "Above");
  18383. dojo.replaceClass(this.domNode, newC, this._currentOrientClass || "");
  18384. this._currentOrientClass = newC;
  18385. },
  18386. focus: function(){
  18387. // summary:
  18388. // Focus on first field
  18389. this._getFocusItems(this.containerNode);
  18390. dijit.focus(this._firstFocusItem);
  18391. },
  18392. onOpen: function(/*Object*/ pos){
  18393. // summary:
  18394. // Called when dialog is displayed.
  18395. // This is called from the dijit.popup code, and should not be called directly.
  18396. // tags:
  18397. // protected
  18398. this.orient(this.domNode,pos.aroundCorner, pos.corner);
  18399. this._onShow(); // lazy load trigger
  18400. },
  18401. onClose: function(){
  18402. // summary:
  18403. // Called when dialog is hidden.
  18404. // This is called from the dijit.popup code, and should not be called directly.
  18405. // tags:
  18406. // protected
  18407. this.onHide();
  18408. },
  18409. _onKey: function(/*Event*/ evt){
  18410. // summary:
  18411. // Handler for keyboard events
  18412. // description:
  18413. // Keep keyboard focus in dialog; close dialog on escape key
  18414. // tags:
  18415. // private
  18416. var node = evt.target;
  18417. var dk = dojo.keys;
  18418. if(evt.charOrCode === dk.TAB){
  18419. this._getFocusItems(this.containerNode);
  18420. }
  18421. var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
  18422. if(evt.charOrCode == dk.ESCAPE){
  18423. // Use setTimeout to avoid crash on IE, see #10396.
  18424. setTimeout(dojo.hitch(this, "onCancel"), 0);
  18425. dojo.stopEvent(evt);
  18426. }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
  18427. if(!singleFocusItem){
  18428. dijit.focus(this._lastFocusItem); // send focus to last item in dialog
  18429. }
  18430. dojo.stopEvent(evt);
  18431. }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
  18432. if(!singleFocusItem){
  18433. dijit.focus(this._firstFocusItem); // send focus to first item in dialog
  18434. }
  18435. dojo.stopEvent(evt);
  18436. }else if(evt.charOrCode === dk.TAB){
  18437. // we want the browser's default tab handling to move focus
  18438. // but we don't want the tab to propagate upwards
  18439. evt.stopPropagation();
  18440. }
  18441. }
  18442. }
  18443. );
  18444. }
  18445. if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18446. dojo._hasResource["dijit.form.DropDownButton"] = true;
  18447. dojo.provide("dijit.form.DropDownButton");
  18448. }
  18449. if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18450. dojo._hasResource["dijit.form._FormSelectWidget"] = true;
  18451. dojo.provide("dijit.form._FormSelectWidget");
  18452. /*=====
  18453. dijit.form.__SelectOption = function(){
  18454. // value: String
  18455. // The value of the option. Setting to empty (or missing) will
  18456. // place a separator at that location
  18457. // label: String
  18458. // The label for our option. It can contain html tags.
  18459. // selected: Boolean
  18460. // Whether or not we are a selected option
  18461. // disabled: Boolean
  18462. // Whether or not this specific option is disabled
  18463. this.value = value;
  18464. this.label = label;
  18465. this.selected = selected;
  18466. this.disabled = disabled;
  18467. }
  18468. =====*/
  18469. dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
  18470. // summary:
  18471. // Extends _FormValueWidget in order to provide "select-specific"
  18472. // values - i.e., those values that are unique to <select> elements.
  18473. // This also provides the mechanism for reading the elements from
  18474. // a store, if desired.
  18475. // multiple: [const] Boolean
  18476. // Whether or not we are multi-valued
  18477. multiple: false,
  18478. // options: dijit.form.__SelectOption[]
  18479. // The set of options for our select item. Roughly corresponds to
  18480. // the html <option> tag.
  18481. options: null,
  18482. // store: dojo.data.api.Identity
  18483. // A store which, at the very least impelements dojo.data.api.Identity
  18484. // to use for getting our list of options - rather than reading them
  18485. // from the <option> html tags.
  18486. store: null,
  18487. // query: object
  18488. // A query to use when fetching items from our store
  18489. query: null,
  18490. // queryOptions: object
  18491. // Query options to use when fetching from the store
  18492. queryOptions: null,
  18493. // onFetch: Function
  18494. // A callback to do with an onFetch - but before any items are actually
  18495. // iterated over (i.e. to filter even futher what you want to add)
  18496. onFetch: null,
  18497. // sortByLabel: Boolean
  18498. // Flag to sort the options returned from a store by the label of
  18499. // the store.
  18500. sortByLabel: true,
  18501. // loadChildrenOnOpen: Boolean
  18502. // By default loadChildren is called when the items are fetched from the
  18503. // store. This property allows delaying loadChildren (and the creation
  18504. // of the options/menuitems) until the user clicks the button to open the
  18505. // dropdown.
  18506. loadChildrenOnOpen: false,
  18507. getOptions: function(/*anything*/ valueOrIdx){
  18508. // summary:
  18509. // Returns a given option (or options).
  18510. // valueOrIdx:
  18511. // If passed in as a string, that string is used to look up the option
  18512. // in the array of options - based on the value property.
  18513. // (See dijit.form.__SelectOption).
  18514. //
  18515. // If passed in a number, then the option with the given index (0-based)
  18516. // within this select will be returned.
  18517. //
  18518. // If passed in a dijit.form.__SelectOption, the same option will be
  18519. // returned if and only if it exists within this select.
  18520. //
  18521. // If passed an array, then an array will be returned with each element
  18522. // in the array being looked up.
  18523. //
  18524. // If not passed a value, then all options will be returned
  18525. //
  18526. // returns:
  18527. // The option corresponding with the given value or index. null
  18528. // is returned if any of the following are true:
  18529. // - A string value is passed in which doesn't exist
  18530. // - An index is passed in which is outside the bounds of the array of options
  18531. // - A dijit.form.__SelectOption is passed in which is not a part of the select
  18532. // NOTE: the compare for passing in a dijit.form.__SelectOption checks
  18533. // if the value property matches - NOT if the exact option exists
  18534. // NOTE: if passing in an array, null elements will be placed in the returned
  18535. // array when a value is not found.
  18536. var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
  18537. if(lookupValue === undefined){
  18538. return opts; // dijit.form.__SelectOption[]
  18539. }
  18540. if(dojo.isArray(lookupValue)){
  18541. return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
  18542. }
  18543. if(dojo.isObject(valueOrIdx)){
  18544. // We were passed an option - so see if it's in our array (directly),
  18545. // and if it's not, try and find it by value.
  18546. if(!dojo.some(this.options, function(o, idx){
  18547. if(o === lookupValue ||
  18548. (o.value && o.value === lookupValue.value)){
  18549. lookupValue = idx;
  18550. return true;
  18551. }
  18552. return false;
  18553. })){
  18554. lookupValue = -1;
  18555. }
  18556. }
  18557. if(typeof lookupValue == "string"){
  18558. for(var i=0; i<l; i++){
  18559. if(opts[i].value === lookupValue){
  18560. lookupValue = i;
  18561. break;
  18562. }
  18563. }
  18564. }
  18565. if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
  18566. return this.options[lookupValue] // dijit.form.__SelectOption
  18567. }
  18568. return null; // null
  18569. },
  18570. addOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ option){
  18571. // summary:
  18572. // Adds an option or options to the end of the select. If value
  18573. // of the option is empty or missing, a separator is created instead.
  18574. // Passing in an array of options will yield slightly better performance
  18575. // since the children are only loaded once.
  18576. if(!dojo.isArray(option)){ option = [option]; }
  18577. dojo.forEach(option, function(i){
  18578. if(i && dojo.isObject(i)){
  18579. this.options.push(i);
  18580. }
  18581. }, this);
  18582. this._loadChildren();
  18583. },
  18584. removeOption: function(/*String|dijit.form.__SelectOption|Number|Array*/ valueOrIdx){
  18585. // summary:
  18586. // Removes the given option or options. You can remove by string
  18587. // (in which case the value is removed), number (in which case the
  18588. // index in the options array is removed), or select option (in
  18589. // which case, the select option with a matching value is removed).
  18590. // You can also pass in an array of those values for a slightly
  18591. // better performance since the children are only loaded once.
  18592. if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
  18593. var oldOpts = this.getOptions(valueOrIdx);
  18594. dojo.forEach(oldOpts, function(i){
  18595. // We can get null back in our array - if our option was not found. In
  18596. // that case, we don't want to blow up...
  18597. if(i){
  18598. this.options = dojo.filter(this.options, function(node, idx){
  18599. return (node.value !== i.value || node.label !== i.label);
  18600. });
  18601. this._removeOptionItem(i);
  18602. }
  18603. }, this);
  18604. this._loadChildren();
  18605. },
  18606. updateOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ newOption){
  18607. // summary:
  18608. // Updates the values of the given option. The option to update
  18609. // is matched based on the value of the entered option. Passing
  18610. // in an array of new options will yeild better performance since
  18611. // the children will only be loaded once.
  18612. if(!dojo.isArray(newOption)){ newOption = [newOption]; }
  18613. dojo.forEach(newOption, function(i){
  18614. var oldOpt = this.getOptions(i), k;
  18615. if(oldOpt){
  18616. for(k in i){ oldOpt[k] = i[k]; }
  18617. }
  18618. }, this);
  18619. this._loadChildren();
  18620. },
  18621. setStore: function(/*dojo.data.api.Identity*/ store,
  18622. /*anything?*/ selectedValue,
  18623. /*Object?*/ fetchArgs){
  18624. // summary:
  18625. // Sets the store you would like to use with this select widget.
  18626. // The selected value is the value of the new store to set. This
  18627. // function returns the original store, in case you want to reuse
  18628. // it or something.
  18629. // store: dojo.data.api.Identity
  18630. // The store you would like to use - it MUST implement Identity,
  18631. // and MAY implement Notification.
  18632. // selectedValue: anything?
  18633. // The value that this widget should set itself to *after* the store
  18634. // has been loaded
  18635. // fetchArgs: Object?
  18636. // The arguments that will be passed to the store's fetch() function
  18637. var oStore = this.store;
  18638. fetchArgs = fetchArgs || {};
  18639. if(oStore !== store){
  18640. // Our store has changed, so update our notifications
  18641. dojo.forEach(this._notifyConnections || [], dojo.disconnect);
  18642. delete this._notifyConnections;
  18643. if(store && store.getFeatures()["dojo.data.api.Notification"]){
  18644. this._notifyConnections = [
  18645. dojo.connect(store, "onNew", this, "_onNewItem"),
  18646. dojo.connect(store, "onDelete", this, "_onDeleteItem"),
  18647. dojo.connect(store, "onSet", this, "_onSetItem")
  18648. ];
  18649. }
  18650. this._set("store", store);
  18651. }
  18652. // Turn off change notifications while we make all these changes
  18653. this._onChangeActive = false;
  18654. // Remove existing options (if there are any)
  18655. if(this.options && this.options.length){
  18656. this.removeOption(this.options);
  18657. }
  18658. // Add our new options
  18659. if(store){
  18660. this._loadingStore = true;
  18661. store.fetch(dojo.delegate(fetchArgs, {
  18662. onComplete: function(items, opts){
  18663. if(this.sortByLabel && !fetchArgs.sort && items.length){
  18664. items.sort(dojo.data.util.sorter.createSortFunction([{
  18665. attribute: store.getLabelAttributes(items[0])[0]
  18666. }], store));
  18667. }
  18668. if(fetchArgs.onFetch){
  18669. items = fetchArgs.onFetch.call(this, items, opts);
  18670. }
  18671. // TODO: Add these guys as a batch, instead of separately
  18672. dojo.forEach(items, function(i){
  18673. this._addOptionForItem(i);
  18674. }, this);
  18675. // Set our value (which might be undefined), and then tweak
  18676. // it to send a change event with the real value
  18677. this._loadingStore = false;
  18678. this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
  18679. delete this._pendingValue;
  18680. if(!this.loadChildrenOnOpen){
  18681. this._loadChildren();
  18682. }else{
  18683. this._pseudoLoadChildren(items);
  18684. }
  18685. this._fetchedWith = opts;
  18686. this._lastValueReported = this.multiple ? [] : null;
  18687. this._onChangeActive = true;
  18688. this.onSetStore();
  18689. this._handleOnChange(this.value);
  18690. },
  18691. scope: this
  18692. }));
  18693. }else{
  18694. delete this._fetchedWith;
  18695. }
  18696. return oStore; // dojo.data.api.Identity
  18697. },
  18698. // TODO: implement set() and watch() for store and query, although not sure how to handle
  18699. // setting them individually rather than together (as in setStore() above)
  18700. _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
  18701. // summary:
  18702. // set the value of the widget.
  18703. // If a string is passed, then we set our value from looking it up.
  18704. if(this._loadingStore){
  18705. // Our store is loading - so save our value, and we'll set it when
  18706. // we're done
  18707. this._pendingValue = newValue;
  18708. return;
  18709. }
  18710. var opts = this.getOptions() || [];
  18711. if(!dojo.isArray(newValue)){
  18712. newValue = [newValue];
  18713. }
  18714. dojo.forEach(newValue, function(i, idx){
  18715. if(!dojo.isObject(i)){
  18716. i = i + "";
  18717. }
  18718. if(typeof i === "string"){
  18719. newValue[idx] = dojo.filter(opts, function(node){
  18720. return node.value === i;
  18721. })[0] || {value: "", label: ""};
  18722. }
  18723. }, this);
  18724. // Make sure some sane default is set
  18725. newValue = dojo.filter(newValue, function(i){ return i && i.value; });
  18726. if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
  18727. newValue[0] = opts[0];
  18728. }
  18729. dojo.forEach(opts, function(i){
  18730. i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
  18731. });
  18732. var val = dojo.map(newValue, function(i){ return i.value; }),
  18733. disp = dojo.map(newValue, function(i){ return i.label; });
  18734. this._set("value", this.multiple ? val : val[0]);
  18735. this._setDisplay(this.multiple ? disp : disp[0]);
  18736. this._updateSelection();
  18737. this._handleOnChange(this.value, priorityChange);
  18738. },
  18739. _getDisplayedValueAttr: function(){
  18740. // summary:
  18741. // returns the displayed value of the widget
  18742. var val = this.get("value");
  18743. if(!dojo.isArray(val)){
  18744. val = [val];
  18745. }
  18746. var ret = dojo.map(this.getOptions(val), function(v){
  18747. if(v && "label" in v){
  18748. return v.label;
  18749. }else if(v){
  18750. return v.value;
  18751. }
  18752. return null;
  18753. }, this);
  18754. return this.multiple ? ret : ret[0];
  18755. },
  18756. _loadChildren: function(){
  18757. // summary:
  18758. // Loads the children represented by this widget's options.
  18759. // reset the menu to make it populatable on the next click
  18760. if(this._loadingStore){ return; }
  18761. dojo.forEach(this._getChildren(), function(child){
  18762. child.destroyRecursive();
  18763. });
  18764. // Add each menu item
  18765. dojo.forEach(this.options, this._addOptionItem, this);
  18766. // Update states
  18767. this._updateSelection();
  18768. },
  18769. _updateSelection: function(){
  18770. // summary:
  18771. // Sets the "selected" class on the item for styling purposes
  18772. this._set("value", this._getValueFromOpts());
  18773. var val = this.value;
  18774. if(!dojo.isArray(val)){
  18775. val = [val];
  18776. }
  18777. if(val && val[0]){
  18778. dojo.forEach(this._getChildren(), function(child){
  18779. var isSelected = dojo.some(val, function(v){
  18780. return child.option && (v === child.option.value);
  18781. });
  18782. dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
  18783. dijit.setWaiState(child.domNode, "selected", isSelected);
  18784. }, this);
  18785. }
  18786. },
  18787. _getValueFromOpts: function(){
  18788. // summary:
  18789. // Returns the value of the widget by reading the options for
  18790. // the selected flag
  18791. var opts = this.getOptions() || [];
  18792. if(!this.multiple && opts.length){
  18793. // Mirror what a select does - choose the first one
  18794. var opt = dojo.filter(opts, function(i){
  18795. return i.selected;
  18796. })[0];
  18797. if(opt && opt.value){
  18798. return opt.value
  18799. }else{
  18800. opts[0].selected = true;
  18801. return opts[0].value;
  18802. }
  18803. }else if(this.multiple){
  18804. // Set value to be the sum of all selected
  18805. return dojo.map(dojo.filter(opts, function(i){
  18806. return i.selected;
  18807. }), function(i){
  18808. return i.value;
  18809. }) || [];
  18810. }
  18811. return "";
  18812. },
  18813. // Internal functions to call when we have store notifications come in
  18814. _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
  18815. if(!parentInfo || !parentInfo.parent){
  18816. // Only add it if we are top-level
  18817. this._addOptionForItem(item);
  18818. }
  18819. },
  18820. _onDeleteItem: function(/*item*/ item){
  18821. var store = this.store;
  18822. this.removeOption(store.getIdentity(item));
  18823. },
  18824. _onSetItem: function(/*item*/ item){
  18825. this.updateOption(this._getOptionObjForItem(item));
  18826. },
  18827. _getOptionObjForItem: function(item){
  18828. // summary:
  18829. // Returns an option object based off the given item. The "value"
  18830. // of the option item will be the identity of the item, the "label"
  18831. // of the option will be the label of the item. If the item contains
  18832. // children, the children value of the item will be set
  18833. var store = this.store, label = store.getLabel(item),
  18834. value = (label ? store.getIdentity(item) : null);
  18835. return {value: value, label: label, item:item}; // dijit.form.__SelectOption
  18836. },
  18837. _addOptionForItem: function(/*item*/ item){
  18838. // summary:
  18839. // Creates (and adds) the option for the given item
  18840. var store = this.store;
  18841. if(!store.isItemLoaded(item)){
  18842. // We are not loaded - so let's load it and add later
  18843. store.loadItem({item: item, onComplete: function(i){
  18844. this._addOptionForItem(item);
  18845. },
  18846. scope: this});
  18847. return;
  18848. }
  18849. var newOpt = this._getOptionObjForItem(item);
  18850. this.addOption(newOpt);
  18851. },
  18852. constructor: function(/*Object*/ keywordArgs){
  18853. // summary:
  18854. // Saves off our value, if we have an initial one set so we
  18855. // can use it if we have a store as well (see startup())
  18856. this._oValue = (keywordArgs || {}).value || null;
  18857. },
  18858. buildRendering: function(){
  18859. this.inherited(arguments);
  18860. dojo.setSelectable(this.focusNode, false);
  18861. },
  18862. _fillContent: function(){
  18863. // summary:
  18864. // Loads our options and sets up our dropdown correctly. We
  18865. // don't want any content, so we don't call any inherit chain
  18866. // function.
  18867. var opts = this.options;
  18868. if(!opts){
  18869. opts = this.options = this.srcNodeRef ? dojo.query(">",
  18870. this.srcNodeRef).map(function(node){
  18871. if(node.getAttribute("type") === "separator"){
  18872. return { value: "", label: "", selected: false, disabled: false };
  18873. }
  18874. return {
  18875. value: (node.getAttribute("data-" + dojo._scopeName + "-value") || node.getAttribute("value")),
  18876. label: String(node.innerHTML),
  18877. // FIXME: disabled and selected are not valid on complex markup children (which is why we're
  18878. // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
  18879. // decide before 1.6
  18880. selected: node.getAttribute("selected") || false,
  18881. disabled: node.getAttribute("disabled") || false
  18882. };
  18883. }, this) : [];
  18884. }
  18885. if(!this.value){
  18886. this._set("value", this._getValueFromOpts());
  18887. }else if(this.multiple && typeof this.value == "string"){
  18888. this._set("value", this.value.split(","));
  18889. }
  18890. },
  18891. postCreate: function(){
  18892. // summary:
  18893. // sets up our event handling that we need for functioning
  18894. // as a select
  18895. this.inherited(arguments);
  18896. // Make our event connections for updating state
  18897. this.connect(this, "onChange", "_updateSelection");
  18898. this.connect(this, "startup", "_loadChildren");
  18899. this._setValueAttr(this.value, null);
  18900. },
  18901. startup: function(){
  18902. // summary:
  18903. // Connects in our store, if we have one defined
  18904. this.inherited(arguments);
  18905. var store = this.store, fetchArgs = {};
  18906. dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
  18907. if(this[i]){
  18908. fetchArgs[i] = this[i];
  18909. }
  18910. delete this[i];
  18911. }, this);
  18912. if(store && store.getFeatures()["dojo.data.api.Identity"]){
  18913. // Temporarily set our store to null so that it will get set
  18914. // and connected appropriately
  18915. this.store = null;
  18916. this.setStore(store, this._oValue, fetchArgs);
  18917. }
  18918. },
  18919. destroy: function(){
  18920. // summary:
  18921. // Clean up our connections
  18922. dojo.forEach(this._notifyConnections || [], dojo.disconnect);
  18923. this.inherited(arguments);
  18924. },
  18925. _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
  18926. // summary:
  18927. // User-overridable function which, for the given option, adds an
  18928. // item to the select. If the option doesn't have a value, then a
  18929. // separator is added in that place. Make sure to store the option
  18930. // in the created option widget.
  18931. },
  18932. _removeOptionItem: function(/*dijit.form.__SelectOption*/ option){
  18933. // summary:
  18934. // User-overridable function which, for the given option, removes
  18935. // its item from the select.
  18936. },
  18937. _setDisplay: function(/*String or String[]*/ newDisplay){
  18938. // summary:
  18939. // Overridable function which will set the display for the
  18940. // widget. newDisplay is either a string (in the case of
  18941. // single selects) or array of strings (in the case of multi-selects)
  18942. },
  18943. _getChildren: function(){
  18944. // summary:
  18945. // Overridable function to return the children that this widget contains.
  18946. return [];
  18947. },
  18948. _getSelectedOptionsAttr: function(){
  18949. // summary:
  18950. // hooks into this.attr to provide a mechanism for getting the
  18951. // option items for the current value of the widget.
  18952. return this.getOptions(this.get("value"));
  18953. },
  18954. _pseudoLoadChildren: function(/*item[]*/ items){
  18955. // summary:
  18956. // a function that will "fake" loading children, if needed, and
  18957. // if we have set to not load children until the widget opens.
  18958. // items:
  18959. // An array of items that will be loaded, when needed
  18960. },
  18961. onSetStore: function(){
  18962. // summary:
  18963. // a function that can be connected to in order to receive a
  18964. // notification that the store has finished loading and all options
  18965. // from that store are available
  18966. }
  18967. });
  18968. }
  18969. if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  18970. dojo._hasResource["dijit._KeyNavContainer"] = true;
  18971. dojo.provide("dijit._KeyNavContainer");
  18972. dojo.declare("dijit._KeyNavContainer",
  18973. dijit._Container,
  18974. {
  18975. // summary:
  18976. // A _Container with keyboard navigation of its children.
  18977. // description:
  18978. // To use this mixin, call connectKeyNavHandlers() in
  18979. // postCreate() and call startupKeyNavChildren() in startup().
  18980. // It provides normalized keyboard and focusing code for Container
  18981. // widgets.
  18982. /*=====
  18983. // focusedChild: [protected] Widget
  18984. // The currently focused child widget, or null if there isn't one
  18985. focusedChild: null,
  18986. =====*/
  18987. // tabIndex: Integer
  18988. // Tab index of the container; same as HTML tabIndex attribute.
  18989. // Note then when user tabs into the container, focus is immediately
  18990. // moved to the first item in the container.
  18991. tabIndex: "0",
  18992. _keyNavCodes: {},
  18993. connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
  18994. // summary:
  18995. // Call in postCreate() to attach the keyboard handlers
  18996. // to the container.
  18997. // preKeyCodes: dojo.keys[]
  18998. // Key codes for navigating to the previous child.
  18999. // nextKeyCodes: dojo.keys[]
  19000. // Key codes for navigating to the next child.
  19001. // tags:
  19002. // protected
  19003. var keyCodes = (this._keyNavCodes = {});
  19004. var prev = dojo.hitch(this, this.focusPrev);
  19005. var next = dojo.hitch(this, this.focusNext);
  19006. dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
  19007. dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
  19008. keyCodes[dojo.keys.HOME] = dojo.hitch(this, "focusFirstChild");
  19009. keyCodes[dojo.keys.END] = dojo.hitch(this, "focusLastChild");
  19010. this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
  19011. this.connect(this.domNode, "onfocus", "_onContainerFocus");
  19012. },
  19013. startupKeyNavChildren: function(){
  19014. // summary:
  19015. // Call in startup() to set child tabindexes to -1
  19016. // tags:
  19017. // protected
  19018. dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
  19019. },
  19020. addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
  19021. // summary:
  19022. // Add a child to our _Container
  19023. dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
  19024. this._startupChild(widget);
  19025. },
  19026. focus: function(){
  19027. // summary:
  19028. // Default focus() implementation: focus the first child.
  19029. this.focusFirstChild();
  19030. },
  19031. focusFirstChild: function(){
  19032. // summary:
  19033. // Focus the first focusable child in the container.
  19034. // tags:
  19035. // protected
  19036. var child = this._getFirstFocusableChild();
  19037. if(child){ // edge case: Menu could be empty or hidden
  19038. this.focusChild(child);
  19039. }
  19040. },
  19041. focusLastChild: function(){
  19042. // summary:
  19043. // Focus the last focusable child in the container.
  19044. // tags:
  19045. // protected
  19046. var child = this._getLastFocusableChild();
  19047. if(child){ // edge case: Menu could be empty or hidden
  19048. this.focusChild(child);
  19049. }
  19050. },
  19051. focusNext: function(){
  19052. // summary:
  19053. // Focus the next widget
  19054. // tags:
  19055. // protected
  19056. var child = this._getNextFocusableChild(this.focusedChild, 1);
  19057. this.focusChild(child);
  19058. },
  19059. focusPrev: function(){
  19060. // summary:
  19061. // Focus the last focusable node in the previous widget
  19062. // (ex: go to the ComboButton icon section rather than button section)
  19063. // tags:
  19064. // protected
  19065. var child = this._getNextFocusableChild(this.focusedChild, -1);
  19066. this.focusChild(child, true);
  19067. },
  19068. focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
  19069. // summary:
  19070. // Focus widget.
  19071. // widget:
  19072. // Reference to container's child widget
  19073. // last:
  19074. // If true and if widget has multiple focusable nodes, focus the
  19075. // last one instead of the first one
  19076. // tags:
  19077. // protected
  19078. if(this.focusedChild && widget !== this.focusedChild){
  19079. this._onChildBlur(this.focusedChild);
  19080. }
  19081. widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
  19082. widget.focus(last ? "end" : "start");
  19083. this._set("focusedChild", widget);
  19084. },
  19085. _startupChild: function(/*dijit._Widget*/ widget){
  19086. // summary:
  19087. // Setup for each child widget
  19088. // description:
  19089. // Sets tabIndex=-1 on each child, so that the tab key will
  19090. // leave the container rather than visiting each child.
  19091. // tags:
  19092. // private
  19093. widget.set("tabIndex", "-1");
  19094. this.connect(widget, "_onFocus", function(){
  19095. // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
  19096. widget.set("tabIndex", this.tabIndex);
  19097. });
  19098. this.connect(widget, "_onBlur", function(){
  19099. widget.set("tabIndex", "-1");
  19100. });
  19101. },
  19102. _onContainerFocus: function(evt){
  19103. // summary:
  19104. // Handler for when the container gets focus
  19105. // description:
  19106. // Initially the container itself has a tabIndex, but when it gets
  19107. // focus, switch focus to first child...
  19108. // tags:
  19109. // private
  19110. // Note that we can't use _onFocus() because switching focus from the
  19111. // _onFocus() handler confuses the focus.js code
  19112. // (because it causes _onFocusNode() to be called recursively)
  19113. // focus bubbles on Firefox,
  19114. // so just make sure that focus has really gone to the container
  19115. if(evt.target !== this.domNode){ return; }
  19116. this.focusFirstChild();
  19117. // and then set the container's tabIndex to -1,
  19118. // (don't remove as that breaks Safari 4)
  19119. // so that tab or shift-tab will go to the fields after/before
  19120. // the container, rather than the container itself
  19121. dojo.attr(this.domNode, "tabIndex", "-1");
  19122. },
  19123. _onBlur: function(evt){
  19124. // When focus is moved away the container, and its descendant (popup) widgets,
  19125. // then restore the container's tabIndex so that user can tab to it again.
  19126. // Note that using _onBlur() so that this doesn't happen when focus is shifted
  19127. // to one of my child widgets (typically a popup)
  19128. if(this.tabIndex){
  19129. dojo.attr(this.domNode, "tabIndex", this.tabIndex);
  19130. }
  19131. this.inherited(arguments);
  19132. },
  19133. _onContainerKeypress: function(evt){
  19134. // summary:
  19135. // When a key is pressed, if it's an arrow key etc. then
  19136. // it's handled here.
  19137. // tags:
  19138. // private
  19139. if(evt.ctrlKey || evt.altKey){ return; }
  19140. var func = this._keyNavCodes[evt.charOrCode];
  19141. if(func){
  19142. func();
  19143. dojo.stopEvent(evt);
  19144. }
  19145. },
  19146. _onChildBlur: function(/*dijit._Widget*/ widget){
  19147. // summary:
  19148. // Called when focus leaves a child widget to go
  19149. // to a sibling widget.
  19150. // tags:
  19151. // protected
  19152. },
  19153. _getFirstFocusableChild: function(){
  19154. // summary:
  19155. // Returns first child that can be focused
  19156. return this._getNextFocusableChild(null, 1); // dijit._Widget
  19157. },
  19158. _getLastFocusableChild: function(){
  19159. // summary:
  19160. // Returns last child that can be focused
  19161. return this._getNextFocusableChild(null, -1); // dijit._Widget
  19162. },
  19163. _getNextFocusableChild: function(child, dir){
  19164. // summary:
  19165. // Returns the next or previous focusable child, compared
  19166. // to "child"
  19167. // child: Widget
  19168. // The current widget
  19169. // dir: Integer
  19170. // * 1 = after
  19171. // * -1 = before
  19172. if(child){
  19173. child = this._getSiblingOfChild(child, dir);
  19174. }
  19175. var children = this.getChildren();
  19176. for(var i=0; i < children.length; i++){
  19177. if(!child){
  19178. child = children[(dir>0) ? 0 : (children.length-1)];
  19179. }
  19180. if(child.isFocusable()){
  19181. return child; // dijit._Widget
  19182. }
  19183. child = this._getSiblingOfChild(child, dir);
  19184. }
  19185. // no focusable child found
  19186. return null; // dijit._Widget
  19187. }
  19188. }
  19189. );
  19190. }
  19191. if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19192. dojo._hasResource["dijit.MenuItem"] = true;
  19193. dojo.provide("dijit.MenuItem");
  19194. dojo.declare("dijit.MenuItem",
  19195. [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
  19196. {
  19197. // summary:
  19198. // A line item in a Menu Widget
  19199. // Make 3 columns
  19200. // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
  19201. 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"),
  19202. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  19203. label: { node: "containerNode", type: "innerHTML" },
  19204. iconClass: { node: "iconNode", type: "class" }
  19205. }),
  19206. baseClass: "dijitMenuItem",
  19207. // label: String
  19208. // Menu text
  19209. label: '',
  19210. // iconClass: String
  19211. // Class to apply to DOMNode to make it display an icon.
  19212. iconClass: "",
  19213. // accelKey: String
  19214. // Text for the accelerator (shortcut) key combination.
  19215. // Note that although Menu can display accelerator keys there
  19216. // is no infrastructure to actually catch and execute these
  19217. // accelerators.
  19218. accelKey: "",
  19219. // disabled: Boolean
  19220. // If true, the menu item is disabled.
  19221. // If false, the menu item is enabled.
  19222. disabled: false,
  19223. _fillContent: function(/*DomNode*/ source){
  19224. // If button label is specified as srcNodeRef.innerHTML rather than
  19225. // this.params.label, handle it here.
  19226. if(source && !("label" in this.params)){
  19227. this.set('label', source.innerHTML);
  19228. }
  19229. },
  19230. buildRendering: function(){
  19231. this.inherited(arguments);
  19232. var label = this.id+"_text";
  19233. dojo.attr(this.containerNode, "id", label);
  19234. if(this.accelKeyNode){
  19235. dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
  19236. label += " " + this.id + "_accel";
  19237. }
  19238. dijit.setWaiState(this.domNode, "labelledby", label);
  19239. dojo.setSelectable(this.domNode, false);
  19240. },
  19241. _onHover: function(){
  19242. // summary:
  19243. // Handler when mouse is moved onto menu item
  19244. // tags:
  19245. // protected
  19246. this.getParent().onItemHover(this);
  19247. },
  19248. _onUnhover: function(){
  19249. // summary:
  19250. // Handler when mouse is moved off of menu item,
  19251. // possibly to a child menu, or maybe to a sibling
  19252. // menuitem or somewhere else entirely.
  19253. // tags:
  19254. // protected
  19255. // if we are unhovering the currently selected item
  19256. // then unselect it
  19257. this.getParent().onItemUnhover(this);
  19258. // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
  19259. // FF and IE don't generate an onmouseout event for the MenuItem.
  19260. // So, help out _CssStateMixin in this case.
  19261. this._set("hovering", false);
  19262. },
  19263. _onClick: function(evt){
  19264. // summary:
  19265. // Internal handler for click events on MenuItem.
  19266. // tags:
  19267. // private
  19268. this.getParent().onItemClick(this, evt);
  19269. dojo.stopEvent(evt);
  19270. },
  19271. onClick: function(/*Event*/ evt){
  19272. // summary:
  19273. // User defined function to handle clicks
  19274. // tags:
  19275. // callback
  19276. },
  19277. focus: function(){
  19278. // summary:
  19279. // Focus on this MenuItem
  19280. try{
  19281. if(dojo.isIE == 8){
  19282. // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
  19283. this.containerNode.focus();
  19284. }
  19285. dijit.focus(this.focusNode);
  19286. }catch(e){
  19287. // this throws on IE (at least) in some scenarios
  19288. }
  19289. },
  19290. _onFocus: function(){
  19291. // summary:
  19292. // This is called by the focus manager when focus
  19293. // goes to this MenuItem or a child menu.
  19294. // tags:
  19295. // protected
  19296. this._setSelected(true);
  19297. this.getParent()._onItemFocus(this);
  19298. this.inherited(arguments);
  19299. },
  19300. _setSelected: function(selected){
  19301. // summary:
  19302. // Indicate that this node is the currently selected one
  19303. // tags:
  19304. // private
  19305. /***
  19306. * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
  19307. * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
  19308. * That's not supposed to happen, but the problem is:
  19309. * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
  19310. * points to the parent Menu, bypassing the parent MenuItem... thus the
  19311. * MenuItem is not in the chain of active widgets and gets a premature call to
  19312. * _onBlur()
  19313. */
  19314. dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
  19315. },
  19316. setLabel: function(/*String*/ content){
  19317. // summary:
  19318. // Deprecated. Use set('label', ...) instead.
  19319. // tags:
  19320. // deprecated
  19321. dojo.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
  19322. this.set("label", content);
  19323. },
  19324. setDisabled: function(/*Boolean*/ disabled){
  19325. // summary:
  19326. // Deprecated. Use set('disabled', bool) instead.
  19327. // tags:
  19328. // deprecated
  19329. dojo.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
  19330. this.set('disabled', disabled);
  19331. },
  19332. _setDisabledAttr: function(/*Boolean*/ value){
  19333. // summary:
  19334. // Hook for attr('disabled', ...) to work.
  19335. // Enable or disable this menu item.
  19336. dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
  19337. this._set("disabled", value);
  19338. },
  19339. _setAccelKeyAttr: function(/*String*/ value){
  19340. // summary:
  19341. // Hook for attr('accelKey', ...) to work.
  19342. // Set accelKey on this menu item.
  19343. this.accelKeyNode.style.display=value?"":"none";
  19344. this.accelKeyNode.innerHTML=value;
  19345. //have to use colSpan to make it work in IE
  19346. dojo.attr(this.containerNode,'colSpan',value?"1":"2");
  19347. this._set("accelKey", value);
  19348. }
  19349. });
  19350. }
  19351. if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19352. dojo._hasResource["dijit.PopupMenuItem"] = true;
  19353. dojo.provide("dijit.PopupMenuItem");
  19354. dojo.declare("dijit.PopupMenuItem",
  19355. dijit.MenuItem,
  19356. {
  19357. _fillContent: function(){
  19358. // summary:
  19359. // When Menu is declared in markup, this code gets the menu label and
  19360. // the popup widget from the srcNodeRef.
  19361. // description:
  19362. // srcNodeRefinnerHTML contains both the menu item text and a popup widget
  19363. // The first part holds the menu item text and the second part is the popup
  19364. // example:
  19365. // | <div dojoType="dijit.PopupMenuItem">
  19366. // | <span>pick me</span>
  19367. // | <popup> ... </popup>
  19368. // | </div>
  19369. // tags:
  19370. // protected
  19371. if(this.srcNodeRef){
  19372. var nodes = dojo.query("*", this.srcNodeRef);
  19373. dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
  19374. // save pointer to srcNode so we can grab the drop down widget after it's instantiated
  19375. this.dropDownContainer = this.srcNodeRef;
  19376. }
  19377. },
  19378. startup: function(){
  19379. if(this._started){ return; }
  19380. this.inherited(arguments);
  19381. // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
  19382. // land now. move it to dojo.doc.body.
  19383. if(!this.popup){
  19384. var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
  19385. this.popup = dijit.byNode(node);
  19386. }
  19387. dojo.body().appendChild(this.popup.domNode);
  19388. this.popup.startup();
  19389. this.popup.domNode.style.display="none";
  19390. if(this.arrowWrapper){
  19391. dojo.style(this.arrowWrapper, "visibility", "");
  19392. }
  19393. dijit.setWaiState(this.focusNode, "haspopup", "true");
  19394. },
  19395. destroyDescendants: function(){
  19396. if(this.popup){
  19397. // Destroy the popup, unless it's already been destroyed. This can happen because
  19398. // the popup is a direct child of <body> even though it's logically my child.
  19399. if(!this.popup._destroyed){
  19400. this.popup.destroyRecursive();
  19401. }
  19402. delete this.popup;
  19403. }
  19404. this.inherited(arguments);
  19405. }
  19406. });
  19407. }
  19408. if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19409. dojo._hasResource["dijit.CheckedMenuItem"] = true;
  19410. dojo.provide("dijit.CheckedMenuItem");
  19411. dojo.declare("dijit.CheckedMenuItem",
  19412. dijit.MenuItem,
  19413. {
  19414. // summary:
  19415. // A checkbox-like menu item for toggling on and off
  19416. 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"),
  19417. // checked: Boolean
  19418. // Our checked state
  19419. checked: false,
  19420. _setCheckedAttr: function(/*Boolean*/ checked){
  19421. // summary:
  19422. // Hook so attr('checked', bool) works.
  19423. // Sets the class and state for the check box.
  19424. dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
  19425. dijit.setWaiState(this.domNode, "checked", checked);
  19426. this._set("checked", checked);
  19427. },
  19428. onChange: function(/*Boolean*/ checked){
  19429. // summary:
  19430. // User defined function to handle check/uncheck events
  19431. // tags:
  19432. // callback
  19433. },
  19434. _onClick: function(/*Event*/ e){
  19435. // summary:
  19436. // Clicking this item just toggles its state
  19437. // tags:
  19438. // private
  19439. if(!this.disabled){
  19440. this.set("checked", !this.checked);
  19441. this.onChange(this.checked);
  19442. }
  19443. this.inherited(arguments);
  19444. }
  19445. });
  19446. }
  19447. if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19448. dojo._hasResource["dijit.MenuSeparator"] = true;
  19449. dojo.provide("dijit.MenuSeparator");
  19450. dojo.declare("dijit.MenuSeparator",
  19451. [dijit._Widget, dijit._Templated, dijit._Contained],
  19452. {
  19453. // summary:
  19454. // A line between two menu items
  19455. 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"),
  19456. buildRendering: function(){
  19457. this.inherited(arguments);
  19458. dojo.setSelectable(this.domNode, false);
  19459. },
  19460. isFocusable: function(){
  19461. // summary:
  19462. // Override to always return false
  19463. // tags:
  19464. // protected
  19465. return false; // Boolean
  19466. }
  19467. });
  19468. }
  19469. if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  19470. dojo._hasResource["dijit.Menu"] = true;
  19471. dojo.provide("dijit.Menu");
  19472. // "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
  19473. dojo.declare("dijit._MenuBase",
  19474. [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
  19475. {
  19476. // summary:
  19477. // Base class for Menu and MenuBar
  19478. // parentMenu: [readonly] Widget
  19479. // pointer to menu that displayed me
  19480. parentMenu: null,
  19481. // popupDelay: Integer
  19482. // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
  19483. popupDelay: 500,
  19484. startup: function(){
  19485. if(this._started){ return; }
  19486. dojo.forEach(this.getChildren(), function(child){ child.startup(); });
  19487. this.startupKeyNavChildren();
  19488. this.inherited(arguments);
  19489. },
  19490. onExecute: function(){
  19491. // summary:
  19492. // Attach point for notification about when a menu item has been executed.
  19493. // This is an internal mechanism used for Menus to signal to their parent to
  19494. // close them, because they are about to execute the onClick handler. In
  19495. // general developers should not attach to or override this method.
  19496. // tags:
  19497. // protected
  19498. },
  19499. onCancel: function(/*Boolean*/ closeAll){
  19500. // summary:
  19501. // Attach point for notification about when the user cancels the current menu
  19502. // This is an internal mechanism used for Menus to signal to their parent to
  19503. // close them. In general developers should not attach to or override this method.
  19504. // tags:
  19505. // protected
  19506. },
  19507. _moveToPopup: function(/*Event*/ evt){
  19508. // summary:
  19509. // This handles the right arrow key (left arrow key on RTL systems),
  19510. // which will either open a submenu, or move to the next item in the
  19511. // ancestor MenuBar
  19512. // tags:
  19513. // private
  19514. if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
  19515. this.focusedChild._onClick(evt);
  19516. }else{
  19517. var topMenu = this._getTopMenu();
  19518. if(topMenu && topMenu._isMenuBar){
  19519. topMenu.focusNext();
  19520. }
  19521. }
  19522. },
  19523. _onPopupHover: function(/*Event*/ evt){
  19524. // summary:
  19525. // This handler is called when the mouse moves over the popup.
  19526. // tags:
  19527. // private
  19528. // if the mouse hovers over a menu popup that is in pending-close state,
  19529. // then stop the close operation.
  19530. // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
  19531. if(this.currentPopup && this.currentPopup._pendingClose_timer){
  19532. var parentMenu = this.currentPopup.parentMenu;
  19533. // highlight the parent menu item pointing to this popup
  19534. if(parentMenu.focusedChild){
  19535. parentMenu.focusedChild._setSelected(false);
  19536. }
  19537. parentMenu.focusedChild = this.currentPopup.from_item;
  19538. parentMenu.focusedChild._setSelected(true);
  19539. // cancel the pending close
  19540. this._stopPendingCloseTimer(this.currentPopup);
  19541. }
  19542. },
  19543. onItemHover: function(/*MenuItem*/ item){
  19544. // summary:
  19545. // Called when cursor is over a MenuItem.
  19546. // tags:
  19547. // protected
  19548. // Don't do anything unless user has "activated" the menu by:
  19549. // 1) clicking it
  19550. // 2) opening it from a parent menu (which automatically focuses it)
  19551. if(this.isActive){
  19552. this.focusChild(item);
  19553. if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
  19554. this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
  19555. }
  19556. }
  19557. // if the user is mixing mouse and keyboard navigation,
  19558. // then the menu may not be active but a menu item has focus,
  19559. // but it's not the item that the mouse just hovered over.
  19560. // To avoid both keyboard and mouse selections, use the latest.
  19561. if(this.focusedChild){
  19562. this.focusChild(item);
  19563. }
  19564. this._hoveredChild = item;
  19565. },
  19566. _onChildBlur: function(item){
  19567. // summary:
  19568. // Called when a child MenuItem becomes inactive because focus
  19569. // has been removed from the MenuItem *and* it's descendant menus.
  19570. // tags:
  19571. // private
  19572. this._stopPopupTimer();
  19573. item._setSelected(false);
  19574. // Close all popups that are open and descendants of this menu
  19575. var itemPopup = item.popup;
  19576. if(itemPopup){
  19577. this._stopPendingCloseTimer(itemPopup);
  19578. itemPopup._pendingClose_timer = setTimeout(function(){
  19579. itemPopup._pendingClose_timer = null;
  19580. if(itemPopup.parentMenu){
  19581. itemPopup.parentMenu.currentPopup = null;
  19582. }
  19583. dijit.popup.close(itemPopup); // this calls onClose
  19584. }, this.popupDelay);
  19585. }
  19586. },
  19587. onItemUnhover: function(/*MenuItem*/ item){
  19588. // summary:
  19589. // Callback fires when mouse exits a MenuItem
  19590. // tags:
  19591. // protected
  19592. if(this.isActive){
  19593. this._stopPopupTimer();
  19594. }
  19595. if(this._hoveredChild == item){ this._hoveredChild = null; }
  19596. },
  19597. _stopPopupTimer: function(){
  19598. // summary:
  19599. // Cancels the popup timer because the user has stop hovering
  19600. // on the MenuItem, etc.
  19601. // tags:
  19602. // private
  19603. if(this.hover_timer){
  19604. clearTimeout(this.hover_timer);
  19605. this.hover_timer = null;
  19606. }
  19607. },
  19608. _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
  19609. // summary:
  19610. // Cancels the pending-close timer because the close has been preempted
  19611. // tags:
  19612. // private
  19613. if(popup._pendingClose_timer){
  19614. clearTimeout(popup._pendingClose_timer);
  19615. popup._pendingClose_timer = null;
  19616. }
  19617. },
  19618. _stopFocusTimer: function(){
  19619. // summary:
  19620. // Cancels the pending-focus timer because the menu was closed before focus occured
  19621. // tags:
  19622. // private
  19623. if(this._focus_timer){
  19624. clearTimeout(this._focus_timer);
  19625. this._focus_timer = null;
  19626. }
  19627. },
  19628. _getTopMenu: function(){
  19629. // summary:
  19630. // Returns the top menu in this chain of Menus
  19631. // tags:
  19632. // private
  19633. for(var top=this; top.parentMenu; top=top.parentMenu);
  19634. return top;
  19635. },
  19636. onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
  19637. // summary:
  19638. // Handle clicks on an item.
  19639. // tags:
  19640. // private
  19641. // this can't be done in _onFocus since the _onFocus events occurs asynchronously
  19642. if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
  19643. this._markActive();
  19644. }
  19645. this.focusChild(item);
  19646. if(item.disabled){ return false; }
  19647. if(item.popup){
  19648. this._openPopup();
  19649. }else{
  19650. // before calling user defined handler, close hierarchy of menus
  19651. // and restore focus to place it was when menu was opened
  19652. this.onExecute();
  19653. // user defined handler for click
  19654. item.onClick(evt);
  19655. }
  19656. },
  19657. _openPopup: function(){
  19658. // summary:
  19659. // Open the popup to the side of/underneath the current menu item
  19660. // tags:
  19661. // protected
  19662. this._stopPopupTimer();
  19663. var from_item = this.focusedChild;
  19664. if(!from_item){ return; } // the focused child lost focus since the timer was started
  19665. var popup = from_item.popup;
  19666. if(popup.isShowingNow){ return; }
  19667. if(this.currentPopup){
  19668. this._stopPendingCloseTimer(this.currentPopup);
  19669. dijit.popup.close(this.currentPopup);
  19670. }
  19671. popup.parentMenu = this;
  19672. popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
  19673. var self = this;
  19674. dijit.popup.open({
  19675. parent: this,
  19676. popup: popup,
  19677. around: from_item.domNode,
  19678. orient: this._orient || (this.isLeftToRight() ?
  19679. {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
  19680. {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
  19681. onCancel: function(){ // called when the child menu is canceled
  19682. // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
  19683. // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
  19684. self.focusChild(from_item); // put focus back on my node
  19685. self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
  19686. from_item._setSelected(true); // oops, _cleanUp() deselected the item
  19687. self.focusedChild = from_item; // and unset focusedChild
  19688. },
  19689. onExecute: dojo.hitch(this, "_cleanUp")
  19690. });
  19691. this.currentPopup = popup;
  19692. // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
  19693. popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
  19694. if(popup.focus){
  19695. // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
  19696. // if the cursor happens to collide with the popup, it will generate an onmouseover event
  19697. // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
  19698. // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
  19699. popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
  19700. this._focus_timer = null;
  19701. this.focus();
  19702. }), 0);
  19703. }
  19704. },
  19705. _markActive: function(){
  19706. // summary:
  19707. // Mark this menu's state as active.
  19708. // Called when this Menu gets focus from:
  19709. // 1) clicking it (mouse or via space/arrow key)
  19710. // 2) being opened by a parent menu.
  19711. // This is not called just from mouse hover.
  19712. // Focusing a menu via TAB does NOT automatically set isActive
  19713. // since TAB is a navigation operation and not a selection one.
  19714. // For Windows apps, pressing the ALT key focuses the menubar
  19715. // menus (similar to TAB navigation) but the menu is not active
  19716. // (ie no dropdown) until an item is clicked.
  19717. this.isActive = true;
  19718. dojo.replaceClass(this.domNode, "dijitMenuActive", "dijitMenuPassive");
  19719. },
  19720. onOpen: function(/*Event*/ e){
  19721. // summary:
  19722. // Callback when this menu is opened.
  19723. // This is called by the popup manager as notification that the menu
  19724. // was opened.
  19725. // tags:
  19726. // private
  19727. this.isShowingNow = true;
  19728. this._markActive();
  19729. },
  19730. _markInactive: function(){
  19731. // summary:
  19732. // Mark this menu's state as inactive.
  19733. this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
  19734. dojo.replaceClass(this.domNode, "dijitMenuPassive", "dijitMenuActive");
  19735. },
  19736. onClose: function(){
  19737. // summary:
  19738. // Callback when this menu is closed.
  19739. // This is called by the popup manager as notification that the menu
  19740. // was closed.
  19741. // tags:
  19742. // private
  19743. this._stopFocusTimer();
  19744. this._markInactive();
  19745. this.isShowingNow = false;
  19746. this.parentMenu = null;
  19747. },
  19748. _closeChild: function(){
  19749. // summary:
  19750. // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
  19751. // tags:
  19752. // private
  19753. this._stopPopupTimer();
  19754. var fromItem = this.focusedChild && this.focusedChild.from_item;
  19755. if(this.currentPopup){
  19756. // If focus is on my child menu then move focus to me,
  19757. // because IE doesn't like it when you display:none a node with focus
  19758. if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
  19759. this.focusedChild.focusNode.focus();
  19760. }
  19761. // Close all popups that are open and descendants of this menu
  19762. dijit.popup.close(this.currentPopup);
  19763. this.currentPopup = null;
  19764. }
  19765. if(this.focusedChild){ // unhighlight the focused item
  19766. this.focusedChild._setSelected(false);
  19767. this.focusedChild._onUnhover();
  19768. this.focusedChild = null;
  19769. }
  19770. },
  19771. _onItemFocus: function(/*MenuItem*/ item){
  19772. // summary:
  19773. // Called when child of this Menu gets focus from:
  19774. // 1) clicking it
  19775. // 2) tabbing into it
  19776. // 3) being opened by a parent menu.
  19777. // This is not called just from mouse hover.
  19778. if(this._hoveredChild && this._hoveredChild != item){
  19779. this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
  19780. }
  19781. },
  19782. _onBlur: function(){
  19783. // summary:
  19784. // Called when focus is moved away from this Menu and it's submenus.
  19785. // tags:
  19786. // protected
  19787. this._cleanUp();
  19788. this.inherited(arguments);
  19789. },
  19790. _cleanUp: function(){
  19791. // summary:
  19792. // Called when the user is done with this menu. Closes hierarchy of menus.
  19793. // tags:
  19794. // private
  19795. this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
  19796. if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
  19797. this._markInactive();
  19798. }
  19799. }
  19800. });
  19801. dojo.declare("dijit.Menu",
  19802. dijit._MenuBase,
  19803. {
  19804. // summary
  19805. // A context menu you can assign to multiple elements
  19806. // TODO: most of the code in here is just for context menu (right-click menu)
  19807. // support. In retrospect that should have been a separate class (dijit.ContextMenu).
  19808. // Split them for 2.0
  19809. constructor: function(){
  19810. this._bindings = [];
  19811. },
  19812. 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"),
  19813. baseClass: "dijitMenu",
  19814. // targetNodeIds: [const] String[]
  19815. // Array of dom node ids of nodes to attach to.
  19816. // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
  19817. targetNodeIds: [],
  19818. // contextMenuForWindow: [const] Boolean
  19819. // If true, right clicking anywhere on the window will cause this context menu to open.
  19820. // If false, must specify targetNodeIds.
  19821. contextMenuForWindow: false,
  19822. // leftClickToOpen: [const] Boolean
  19823. // If true, menu will open on left click instead of right click, similiar to a file menu.
  19824. leftClickToOpen: false,
  19825. // refocus: Boolean
  19826. // When this menu closes, re-focus the element which had focus before it was opened.
  19827. refocus: true,
  19828. postCreate: function(){
  19829. if(this.contextMenuForWindow){
  19830. this.bindDomNode(dojo.body());
  19831. }else{
  19832. // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
  19833. // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
  19834. // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
  19835. dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
  19836. }
  19837. var k = dojo.keys, l = this.isLeftToRight();
  19838. this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
  19839. this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
  19840. this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
  19841. },
  19842. _onKeyPress: function(/*Event*/ evt){
  19843. // summary:
  19844. // Handle keyboard based menu navigation.
  19845. // tags:
  19846. // protected
  19847. if(evt.ctrlKey || evt.altKey){ return; }
  19848. switch(evt.charOrCode){
  19849. case this._openSubMenuKey:
  19850. this._moveToPopup(evt);
  19851. dojo.stopEvent(evt);
  19852. break;
  19853. case this._closeSubMenuKey:
  19854. if(this.parentMenu){
  19855. if(this.parentMenu._isMenuBar){
  19856. this.parentMenu.focusPrev();
  19857. }else{
  19858. this.onCancel(false);
  19859. }
  19860. }else{
  19861. dojo.stopEvent(evt);
  19862. }
  19863. break;
  19864. }
  19865. },
  19866. // thanks burstlib!
  19867. _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
  19868. // summary:
  19869. // Returns the window reference of the passed iframe
  19870. // tags:
  19871. // private
  19872. var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
  19873. // Moz. TODO: is this available when defaultView isn't?
  19874. this._iframeContentDocument(iframe_el)['__parent__'] ||
  19875. (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
  19876. return win; // Window
  19877. },
  19878. _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
  19879. // summary:
  19880. // Returns a reference to the document object inside iframe_el
  19881. // tags:
  19882. // protected
  19883. var doc = iframe_el.contentDocument // W3
  19884. || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
  19885. || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
  19886. || null;
  19887. return doc; // HTMLDocument
  19888. },
  19889. bindDomNode: function(/*String|DomNode*/ node){
  19890. // summary:
  19891. // Attach menu to given node
  19892. node = dojo.byId(node);
  19893. var cn; // Connect node
  19894. // Support context menus on iframes. Rather than binding to the iframe itself we need
  19895. // to bind to the <body> node inside the iframe.
  19896. if(node.tagName.toLowerCase() == "iframe"){
  19897. var iframe = node,
  19898. win = this._iframeContentWindow(iframe);
  19899. cn = dojo.withGlobal(win, dojo.body);
  19900. }else{
  19901. // To capture these events at the top level, attach to <html>, not <body>.
  19902. // Otherwise right-click context menu just doesn't work.
  19903. cn = (node == dojo.body() ? dojo.doc.documentElement : node);
  19904. }
  19905. // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
  19906. var binding = {
  19907. node: node,
  19908. iframe: iframe
  19909. };
  19910. // Save info about binding in _bindings[], and make node itself record index(+1) into
  19911. // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
  19912. // start with a number, which fails on FF/safari.
  19913. dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
  19914. // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
  19915. // loading yet, in which case we need to wait for the onload event first, and then connect
  19916. // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
  19917. // we need to monitor keyboard events in addition to the oncontextmenu event.
  19918. var doConnects = dojo.hitch(this, function(cn){
  19919. return [
  19920. // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
  19921. // rather than shift-F10?
  19922. dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
  19923. // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
  19924. dojo.stopEvent(evt);
  19925. this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
  19926. }),
  19927. dojo.connect(cn, "onkeydown", this, function(evt){
  19928. if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
  19929. dojo.stopEvent(evt);
  19930. this._scheduleOpen(evt.target, iframe); // no coords - open near target node
  19931. }
  19932. })
  19933. ];
  19934. });
  19935. binding.connects = cn ? doConnects(cn) : [];
  19936. if(iframe){
  19937. // Setup handler to [re]bind to the iframe when the contents are initially loaded,
  19938. // and every time the contents change.
  19939. // Need to do this b/c we are actually binding to the iframe's <body> node.
  19940. // Note: can't use dojo.connect(), see #9609.
  19941. binding.onloadHandler = dojo.hitch(this, function(){
  19942. // want to remove old connections, but IE throws exceptions when trying to
  19943. // access the <body> node because it's already gone, or at least in a state of limbo
  19944. var win = this._iframeContentWindow(iframe);
  19945. cn = dojo.withGlobal(win, dojo.body);
  19946. binding.connects = doConnects(cn);
  19947. });
  19948. if(iframe.addEventListener){
  19949. iframe.addEventListener("load", binding.onloadHandler, false);
  19950. }else{
  19951. iframe.attachEvent("onload", binding.onloadHandler);
  19952. }
  19953. }
  19954. },
  19955. unBindDomNode: function(/*String|DomNode*/ nodeName){
  19956. // summary:
  19957. // Detach menu from given node
  19958. var node;
  19959. try{
  19960. node = dojo.byId(nodeName);
  19961. }catch(e){
  19962. // On IE the dojo.byId() call will get an exception if the attach point was
  19963. // the <body> node of an <iframe> that has since been reloaded (and thus the
  19964. // <body> node is in a limbo state of destruction.
  19965. return;
  19966. }
  19967. // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
  19968. var attrName = "_dijitMenu" + this.id;
  19969. if(node && dojo.hasAttr(node, attrName)){
  19970. var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
  19971. dojo.forEach(b.connects, dojo.disconnect);
  19972. // Remove listener for iframe onload events
  19973. var iframe = b.iframe;
  19974. if(iframe){
  19975. if(iframe.removeEventListener){
  19976. iframe.removeEventListener("load", b.onloadHandler, false);
  19977. }else{
  19978. iframe.detachEvent("onload", b.onloadHandler);
  19979. }
  19980. }
  19981. dojo.removeAttr(node, attrName);
  19982. delete this._bindings[bid];
  19983. }
  19984. },
  19985. _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
  19986. // summary:
  19987. // Set timer to display myself. Using a timer rather than displaying immediately solves
  19988. // two problems:
  19989. //
  19990. // 1. IE: without the delay, focus work in "open" causes the system
  19991. // context menu to appear in spite of stopEvent.
  19992. //
  19993. // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
  19994. // even after a dojo.stopEvent(e). (Shift-F10 on windows doesn't generate the
  19995. // oncontextmenu event.)
  19996. if(!this._openTimer){
  19997. this._openTimer = setTimeout(dojo.hitch(this, function(){
  19998. delete this._openTimer;
  19999. this._openMyself({
  20000. target: target,
  20001. iframe: iframe,
  20002. coords: coords
  20003. });
  20004. }), 1);
  20005. }
  20006. },
  20007. _openMyself: function(args){
  20008. // summary:
  20009. // Internal function for opening myself when the user does a right-click or something similar.
  20010. // args:
  20011. // This is an Object containing:
  20012. // * target:
  20013. // The node that is being clicked
  20014. // * iframe:
  20015. // If an <iframe> is being clicked, iframe points to that iframe
  20016. // * coords:
  20017. // Put menu at specified x/y position in viewport, or if iframe is
  20018. // specified, then relative to iframe.
  20019. //
  20020. // _openMyself() formerly took the event object, and since various code references
  20021. // evt.target (after connecting to _openMyself()), using an Object for parameters
  20022. // (so that old code still works).
  20023. var target = args.target,
  20024. iframe = args.iframe,
  20025. coords = args.coords;
  20026. // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
  20027. // then near the node the menu is assigned to.
  20028. if(coords){
  20029. if(iframe){
  20030. // Specified coordinates are on <body> node of an <iframe>, convert to match main document
  20031. var od = target.ownerDocument,
  20032. ifc = dojo.position(iframe, true),
  20033. win = this._iframeContentWindow(iframe),
  20034. scroll = dojo.withGlobal(win, "_docScroll", dojo);
  20035. var cs = dojo.getComputedStyle(iframe),
  20036. tp = dojo._toPixelValue,
  20037. left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
  20038. top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
  20039. coords.x += ifc.x + left - scroll.x;
  20040. coords.y += ifc.y + top - scroll.y;
  20041. }
  20042. }else{
  20043. coords = dojo.position(target, true);
  20044. coords.x += 10;
  20045. coords.y += 10;
  20046. }
  20047. var self=this;
  20048. var savedFocus = dijit.getFocus(this);
  20049. function closeAndRestoreFocus(){
  20050. // user has clicked on a menu or popup
  20051. if(self.refocus){
  20052. dijit.focus(savedFocus);
  20053. }
  20054. dijit.popup.close(self);
  20055. }
  20056. dijit.popup.open({
  20057. popup: this,
  20058. x: coords.x,
  20059. y: coords.y,
  20060. onExecute: closeAndRestoreFocus,
  20061. onCancel: closeAndRestoreFocus,
  20062. orient: this.isLeftToRight() ? 'L' : 'R'
  20063. });
  20064. this.focus();
  20065. this._onBlur = function(){
  20066. this.inherited('_onBlur', arguments);
  20067. // Usually the parent closes the child widget but if this is a context
  20068. // menu then there is no parent
  20069. dijit.popup.close(this);
  20070. // don't try to restore focus; user has clicked another part of the screen
  20071. // and set focus there
  20072. };
  20073. },
  20074. uninitialize: function(){
  20075. dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
  20076. this.inherited(arguments);
  20077. }
  20078. }
  20079. );
  20080. }
  20081. if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20082. dojo._hasResource["dijit.form.Select"] = true;
  20083. dojo.provide("dijit.form.Select");
  20084. dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
  20085. // summary:
  20086. // An internally-used menu for dropdown that allows us a vertical scrollbar
  20087. buildRendering: function(){
  20088. // summary:
  20089. // Stub in our own changes, so that our domNode is not a table
  20090. // otherwise, we won't respond correctly to heights/overflows
  20091. this.inherited(arguments);
  20092. var o = (this.menuTableNode = this.domNode);
  20093. var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
  20094. if(o.parentNode){
  20095. o.parentNode.replaceChild(n, o);
  20096. }
  20097. dojo.removeClass(o, "dijitMenuTable");
  20098. n.className = o.className + " dijitSelectMenu";
  20099. o.className = "dijitReset dijitMenuTable";
  20100. dijit.setWaiRole(o,"listbox");
  20101. dijit.setWaiRole(n,"presentation");
  20102. n.appendChild(o);
  20103. },
  20104. postCreate: function(){
  20105. // summary:
  20106. // stop mousemove from selecting text on IE to be consistent with other browsers
  20107. this.inherited(arguments);
  20108. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  20109. },
  20110. resize: function(/*Object*/ mb){
  20111. // summary:
  20112. // Overridden so that we are able to handle resizing our
  20113. // internal widget. Note that this is not a "full" resize
  20114. // implementation - it only works correctly if you pass it a
  20115. // marginBox.
  20116. //
  20117. // mb: Object
  20118. // The margin box to set this dropdown to.
  20119. if(mb){
  20120. dojo.marginBox(this.domNode, mb);
  20121. if("w" in mb){
  20122. // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
  20123. // 100% is safer than a pixel value because there may be a scroll bar with
  20124. // browser/OS specific width.
  20125. this.menuTableNode.style.width = "100%";
  20126. }
  20127. }
  20128. }
  20129. });
  20130. dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
  20131. // summary:
  20132. // This is a "styleable" select box - it is basically a DropDownButton which
  20133. // can take a <select> as its input.
  20134. baseClass: "dijitSelect",
  20135. 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"),
  20136. // attributeMap: Object
  20137. // Add in our style to be applied to the focus node
  20138. attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
  20139. // required: Boolean
  20140. // Can be true or false, default is false.
  20141. required: false,
  20142. // state: String
  20143. // Shows current state (ie, validation result) of input (Normal, Warning, or Error)
  20144. state: "",
  20145. // message: String
  20146. // Currently displayed error/prompt message
  20147. message: "",
  20148. // tooltipPosition: String[]
  20149. // See description of dijit.Tooltip.defaultPosition for details on this parameter.
  20150. tooltipPosition: [],
  20151. // emptyLabel: string
  20152. // What to display in an "empty" dropdown
  20153. emptyLabel: "&nbsp;",
  20154. // _isLoaded: Boolean
  20155. // Whether or not we have been loaded
  20156. _isLoaded: false,
  20157. // _childrenLoaded: Boolean
  20158. // Whether or not our children have been loaded
  20159. _childrenLoaded: false,
  20160. _fillContent: function(){
  20161. // summary:
  20162. // Set the value to be the first, or the selected index
  20163. this.inherited(arguments);
  20164. // set value from selected option
  20165. if(this.options.length && !this.value && this.srcNodeRef){
  20166. var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
  20167. this.value = this.options[si >= 0 ? si : 0].value;
  20168. }
  20169. // Create the dropDown widget
  20170. this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
  20171. dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
  20172. },
  20173. _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
  20174. // summary:
  20175. // For the given option, return the menu item that should be
  20176. // used to display it. This can be overridden as needed
  20177. if(!option.value && !option.label){
  20178. // We are a separator (no label set for it)
  20179. return new dijit.MenuSeparator();
  20180. }else{
  20181. // Just a regular menu option
  20182. var click = dojo.hitch(this, "_setValueAttr", option);
  20183. var item = new dijit.MenuItem({
  20184. option: option,
  20185. label: option.label || this.emptyLabel,
  20186. onClick: click,
  20187. disabled: option.disabled || false
  20188. });
  20189. dijit.setWaiRole(item.focusNode, "listitem");
  20190. return item;
  20191. }
  20192. },
  20193. _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
  20194. // summary:
  20195. // For the given option, add an option to our dropdown.
  20196. // If the option doesn't have a value, then a separator is added
  20197. // in that place.
  20198. if(this.dropDown){
  20199. this.dropDown.addChild(this._getMenuItemForOption(option));
  20200. }
  20201. },
  20202. _getChildren: function(){
  20203. if(!this.dropDown){
  20204. return [];
  20205. }
  20206. return this.dropDown.getChildren();
  20207. },
  20208. _loadChildren: function(/*Boolean*/ loadMenuItems){
  20209. // summary:
  20210. // Resets the menu and the length attribute of the button - and
  20211. // ensures that the label is appropriately set.
  20212. // loadMenuItems: Boolean
  20213. // actually loads the child menu items - we only do this when we are
  20214. // populating for showing the dropdown.
  20215. if(loadMenuItems === true){
  20216. // this.inherited destroys this.dropDown's child widgets (MenuItems).
  20217. // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
  20218. // issues later in _setSelected). (see #10296)
  20219. if(this.dropDown){
  20220. delete this.dropDown.focusedChild;
  20221. }
  20222. if(this.options.length){
  20223. this.inherited(arguments);
  20224. }else{
  20225. // Drop down menu is blank but add one blank entry just so something appears on the screen
  20226. // to let users know that they are no choices (mimicing native select behavior)
  20227. dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
  20228. var item = new dijit.MenuItem({label: "&nbsp;"});
  20229. this.dropDown.addChild(item);
  20230. }
  20231. }else{
  20232. this._updateSelection();
  20233. }
  20234. this._isLoaded = false;
  20235. this._childrenLoaded = true;
  20236. if(!this._loadingStore){
  20237. // Don't call this if we are loading - since we will handle it later
  20238. this._setValueAttr(this.value);
  20239. }
  20240. },
  20241. _setValueAttr: function(value){
  20242. this.inherited(arguments);
  20243. dojo.attr(this.valueNode, "value", this.get("value"));
  20244. },
  20245. _setDisplay: function(/*String*/ newDisplay){
  20246. // summary:
  20247. // sets the display for the given value (or values)
  20248. var lbl = newDisplay || this.emptyLabel;
  20249. this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
  20250. dijit.setWaiState(this.focusNode, "valuetext", lbl);
  20251. },
  20252. validate: function(/*Boolean*/ isFocused){
  20253. // summary:
  20254. // Called by oninit, onblur, and onkeypress.
  20255. // description:
  20256. // Show missing or invalid messages if appropriate, and highlight textbox field.
  20257. // Used when a select is initially set to no value and the user is required to
  20258. // set the value.
  20259. var isValid = this.isValid(isFocused);
  20260. this._set("state", isValid ? "" : "Error");
  20261. dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
  20262. var message = isValid ? "" : this._missingMsg;
  20263. if(this.message !== message){
  20264. this._set("message", message);
  20265. dijit.hideTooltip(this.domNode);
  20266. if(message){
  20267. dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
  20268. }
  20269. }
  20270. return isValid;
  20271. },
  20272. isValid: function(/*Boolean*/ isFocused){
  20273. // summary:
  20274. // Whether or not this is a valid value. The only way a Select
  20275. // can be invalid is when it's required but nothing is selected.
  20276. return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
  20277. },
  20278. reset: function(){
  20279. // summary:
  20280. // Overridden so that the state will be cleared.
  20281. this.inherited(arguments);
  20282. dijit.hideTooltip(this.domNode);
  20283. this._set("state", "");
  20284. this._set("message", "")
  20285. },
  20286. postMixInProperties: function(){
  20287. // summary:
  20288. // set the missing message
  20289. this.inherited(arguments);
  20290. this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
  20291. this.lang).missingMessage;
  20292. },
  20293. postCreate: function(){
  20294. // summary:
  20295. // stop mousemove from selecting text on IE to be consistent with other browsers
  20296. this.inherited(arguments);
  20297. this.connect(this.domNode, "onmousemove", dojo.stopEvent);
  20298. },
  20299. _setStyleAttr: function(/*String||Object*/ value){
  20300. this.inherited(arguments);
  20301. dojo.toggleClass(this.domNode, this.baseClass + "FixedWidth", !!this.tableNode.style.width);
  20302. },
  20303. isLoaded: function(){
  20304. return this._isLoaded;
  20305. },
  20306. loadDropDown: function(/*Function*/ loadCallback){
  20307. // summary:
  20308. // populates the menu
  20309. this._loadChildren(true);
  20310. this._isLoaded = true;
  20311. loadCallback();
  20312. },
  20313. closeDropDown: function(){
  20314. // overriding _HasDropDown.closeDropDown()
  20315. this.inherited(arguments);
  20316. if(this.dropDown && this.dropDown.menuTableNode){
  20317. // Erase possible width: 100% setting from _SelectMenu.resize().
  20318. // Leaving it would interfere with the next openDropDown() call, which
  20319. // queries the natural size of the drop down.
  20320. this.dropDown.menuTableNode.style.width = "";
  20321. }
  20322. },
  20323. uninitialize: function(preserveDom){
  20324. if(this.dropDown && !this.dropDown._destroyed){
  20325. this.dropDown.destroyRecursive(preserveDom);
  20326. delete this.dropDown;
  20327. }
  20328. this.inherited(arguments);
  20329. }
  20330. });
  20331. }
  20332. if(!dojo._hasResource["dijit._editor.plugins.LinkDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20333. dojo._hasResource["dijit._editor.plugins.LinkDialog"] = true;
  20334. dojo.provide("dijit._editor.plugins.LinkDialog");
  20335. dojo.declare("dijit._editor.plugins.LinkDialog", dijit._editor._Plugin, {
  20336. // summary:
  20337. // This plugin provides the basis for an 'anchor' (link) dialog and an extension of it
  20338. // provides the image link dialog.
  20339. //
  20340. // description:
  20341. // The command provided by this plugin is:
  20342. // * createLink
  20343. // Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton
  20344. // (which triggers a TooltipDialog).
  20345. buttonClass: dijit.form.DropDownButton,
  20346. // Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.
  20347. useDefaultCommand: false,
  20348. // urlRegExp: [protected] String
  20349. // Used for validating input as correct URL. While file:// urls are not terribly
  20350. // useful, they are technically valid.
  20351. 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/]*)?(?:#.*)?)?)?",
  20352. // emailRegExp: [protected] String
  20353. // Used for validating input as correct email address. Taken from dojox.validate
  20354. emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +
  20355. "((?:(?:[\\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.
  20356. // htmlTemplate: [protected] String
  20357. // String used for templating the HTML to insert at the desired point.
  20358. htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +
  20359. " target=\"${targetSelect}\"" +
  20360. ">${textInput}</a>",
  20361. // tag: [protected] String
  20362. // Tag used for the link type.
  20363. tag: "a",
  20364. // _hostRxp [private] RegExp
  20365. // Regular expression used to validate url fragments (ip address, hostname, etc)
  20366. _hostRxp: new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
  20367. // _userAtRxp [private] RegExp
  20368. // Regular expression used to validate e-mail address fragment.
  20369. _userAtRxp: new RegExp("^([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+@", "i"),
  20370. // linkDialogTemplate: [protected] String
  20371. // Template for contents of TooltipDialog to pick URL
  20372. linkDialogTemplate: [
  20373. "<table><tr><td>",
  20374. "<label for='${id}_urlInput'>${url}</label>",
  20375. "</td><td>",
  20376. "<input dojoType='dijit.form.ValidationTextBox' required='true' " +
  20377. "id='${id}_urlInput' name='urlInput' intermediateChanges='true'/>",
  20378. "</td></tr><tr><td>",
  20379. "<label for='${id}_textInput'>${text}</label>",
  20380. "</td><td>",
  20381. "<input dojoType='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +
  20382. "name='textInput' intermediateChanges='true'/>",
  20383. "</td></tr><tr><td>",
  20384. "<label for='${id}_targetSelect'>${target}</label>",
  20385. "</td><td>",
  20386. "<select id='${id}_targetSelect' name='targetSelect' dojoType='dijit.form.Select'>",
  20387. "<option selected='selected' value='_self'>${currentWindow}</option>",
  20388. "<option value='_blank'>${newWindow}</option>",
  20389. "<option value='_top'>${topWindow}</option>",
  20390. "<option value='_parent'>${parentWindow}</option>",
  20391. "</select>",
  20392. "</td></tr><tr><td colspan='2'>",
  20393. "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  20394. "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  20395. "</td></tr></table>"
  20396. ].join(""),
  20397. _initButton: function(){
  20398. // Override _Plugin._initButton() to initialize DropDownButton and TooltipDialog.
  20399. var _this = this;
  20400. this.tag = this.command == 'insertImage' ? 'img' : 'a';
  20401. var messages = dojo.mixin(dojo.i18n.getLocalization("dijit", "common", this.lang),
  20402. dojo.i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));
  20403. var dropDown = (this.dropDown = new dijit.TooltipDialog({
  20404. title: messages[this.command + "Title"],
  20405. execute: dojo.hitch(this, "setValue"),
  20406. onOpen: function(){
  20407. _this._onOpenDialog();
  20408. dijit.TooltipDialog.prototype.onOpen.apply(this, arguments);
  20409. },
  20410. onCancel: function(){
  20411. setTimeout(dojo.hitch(_this, "_onCloseDialog"),0);
  20412. }
  20413. }));
  20414. messages.urlRegExp = this.urlRegExp;
  20415. messages.id = dijit.getUniqueId(this.editor.id);
  20416. this._uniqueId = messages.id;
  20417. this._setContent(dropDown.title +
  20418. "<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +
  20419. dojo.string.substitute(this.linkDialogTemplate, messages));
  20420. dropDown.startup();
  20421. this._urlInput = dijit.byId(this._uniqueId + "_urlInput");
  20422. this._textInput = dijit.byId(this._uniqueId + "_textInput");
  20423. this._setButton = dijit.byId(this._uniqueId + "_setButton");
  20424. this.connect(dijit.byId(this._uniqueId + "_cancelButton"), "onClick", function(){
  20425. this.dropDown.onCancel();
  20426. });
  20427. if(this._urlInput){
  20428. this.connect(this._urlInput, "onChange", "_checkAndFixInput");
  20429. }
  20430. if(this._textInput){
  20431. this.connect(this._textInput, "onChange", "_checkAndFixInput");
  20432. }
  20433. // Build up the dual check for http/https/file:, and mailto formats.
  20434. this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");
  20435. this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");
  20436. this._urlInput.isValid = dojo.hitch(this, function(){
  20437. // Function over-ride of isValid to test if the input matches a url or a mailto style link.
  20438. var value = this._urlInput.get("value");
  20439. return this._urlRegExp.test(value) || this._emailRegExp.test(value);
  20440. });
  20441. this._connectTagEvents();
  20442. this.inherited(arguments);
  20443. },
  20444. _checkAndFixInput: function(){
  20445. // summary:
  20446. // A function to listen for onChange events and test the input contents
  20447. // for valid information, such as valid urls with http/https/ftp and if
  20448. // not present, try and guess if the input url is relative or not, and if
  20449. // not, append http:// to it. Also validates other fields as determined by
  20450. // the internal _isValid function.
  20451. var self = this;
  20452. var url = this._urlInput.get("value");
  20453. var fixupUrl = function(url){
  20454. var appendHttp = false;
  20455. var appendMailto = false;
  20456. if(url && url.length > 1){
  20457. url = dojo.trim(url);
  20458. if(url.indexOf("mailto:") !== 0){
  20459. if(url.indexOf("/") > 0){
  20460. if(url.indexOf("://") === -1){
  20461. // Check that it doesn't start with / or ./, which would
  20462. // imply 'target server relativeness'
  20463. if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){
  20464. if(self._hostRxp.test(url)){
  20465. appendHttp = true;
  20466. }
  20467. }
  20468. }
  20469. }else if(self._userAtRxp.test(url)){
  20470. // If it looks like a foo@, append a mailto.
  20471. appendMailto = true;
  20472. }
  20473. }
  20474. }
  20475. if(appendHttp){
  20476. self._urlInput.set("value", "http://" + url);
  20477. }
  20478. if(appendMailto){
  20479. self._urlInput.set("value", "mailto:" + url);
  20480. }
  20481. self._setButton.set("disabled", !self._isValid());
  20482. };
  20483. if(this._delayedCheck){
  20484. clearTimeout(this._delayedCheck);
  20485. this._delayedCheck = null;
  20486. }
  20487. this._delayedCheck = setTimeout(function(){
  20488. fixupUrl(url);
  20489. }, 250);
  20490. },
  20491. _connectTagEvents: function(){
  20492. // summary:
  20493. // Over-ridable function that connects tag specific events.
  20494. this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  20495. this.connect(this.editor.editNode, "ondblclick", this._onDblClick);
  20496. }));
  20497. },
  20498. _isValid: function(){
  20499. // summary:
  20500. // Internal function to allow validating of the inputs
  20501. // for a link to determine if set should be disabled or not
  20502. // tags:
  20503. // protected
  20504. return this._urlInput.isValid() && this._textInput.isValid();
  20505. },
  20506. _setContent: function(staticPanel){
  20507. // summary:
  20508. // Helper for _initButton above. Not sure why it's a separate method.
  20509. this.dropDown.set({
  20510. parserScope: "dojo", // make parser search for dojoType/data-dojo-type even if page is multi-version
  20511. content: staticPanel
  20512. });
  20513. },
  20514. _checkValues: function(args){
  20515. // summary:
  20516. // Function to check the values in args and 'fix' them up as needed.
  20517. // args: Object
  20518. // Content being set.
  20519. // tags:
  20520. // protected
  20521. if(args && args.urlInput){
  20522. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  20523. }
  20524. return args;
  20525. },
  20526. setValue: function(args){
  20527. // summary:
  20528. // Callback from the dialog when user presses "set" button.
  20529. // tags:
  20530. // private
  20531. //TODO: prevent closing popup if the text is empty
  20532. this._onCloseDialog();
  20533. if(dojo.isIE < 9){ //see #4151
  20534. var sel = dijit.range.getSelection(this.editor.window);
  20535. var range = sel.getRangeAt(0);
  20536. var a = range.endContainer;
  20537. if(a.nodeType === 3){
  20538. // Text node, may be the link contents, so check parent.
  20539. // This plugin doesn't really support nested HTML elements
  20540. // in the link, it assumes all link content is text.
  20541. a = a.parentNode;
  20542. }
  20543. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  20544. // Stll nothing, one last thing to try on IE, as it might be 'img'
  20545. // and thus considered a control.
  20546. a = dojo.withGlobal(this.editor.window,
  20547. "getSelectedElement", dijit._editor.selection, [this.tag]);
  20548. }
  20549. if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){
  20550. // Okay, we do have a match. IE, for some reason, sometimes pastes before
  20551. // instead of removing the targetted paste-over element, so we unlink the
  20552. // old one first. If we do not the <a> tag remains, but it has no content,
  20553. // so isn't readily visible (but is wrong for the action).
  20554. if(this.editor.queryCommandEnabled("unlink")){
  20555. // Select all the link childent, then unlink. The following insert will
  20556. // then replace the selected text.
  20557. dojo.withGlobal(this.editor.window,
  20558. "selectElementChildren", dijit._editor.selection, [a]);
  20559. this.editor.execCommand("unlink");
  20560. }
  20561. }
  20562. }
  20563. // make sure values are properly escaped, etc.
  20564. args = this._checkValues(args);
  20565. this.editor.execCommand('inserthtml',
  20566. dojo.string.substitute(this.htmlTemplate, args));
  20567. // IE sometimes leaves a blank link, so we need to fix it up.
  20568. // Go ahead and do this for everyone just to avoid blank links
  20569. // in the page.
  20570. dojo.query("a", this.editor.document).forEach(function(a){
  20571. if(!a.innerHTML && !domAttr.has(a, "name")){
  20572. // Remove empty anchors that do not have "name" set.
  20573. // Empty ones with a name set could be a hidden hash
  20574. // anchor.
  20575. a.parentNode.removeChild(a);
  20576. }
  20577. }, this);
  20578. },
  20579. _onCloseDialog: function(){
  20580. // summary:
  20581. // Handler for close event on the dialog
  20582. this.editor.focus();
  20583. },
  20584. _getCurrentValues: function(a){
  20585. // summary:
  20586. // Over-ride for getting the values to set in the dropdown.
  20587. // a:
  20588. // The anchor/link to process for data for the dropdown.
  20589. // tags:
  20590. // protected
  20591. var url, text, target;
  20592. if(a && a.tagName.toLowerCase() === this.tag){
  20593. url = a.getAttribute('_djrealurl') || a.getAttribute('href');
  20594. target = a.getAttribute('target') || "_self";
  20595. text = a.textContent || a.innerText;
  20596. dojo.withGlobal(this.editor.window, "selectElement", dijit._editor.selection, [a, true]);
  20597. }else{
  20598. text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
  20599. }
  20600. return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;
  20601. },
  20602. _onOpenDialog: function(){
  20603. // summary:
  20604. // Handler for when the dialog is opened.
  20605. // If the caret is currently in a URL then populate the URL's info into the dialog.
  20606. var a;
  20607. if(dojo.isIE < 9){
  20608. // IE is difficult to select the element in, using the range unified
  20609. // API seems to work reasonably well.
  20610. var sel = dijit.range.getSelection(this.editor.window);
  20611. var range = sel.getRangeAt(0);
  20612. a = range.endContainer;
  20613. if(a.nodeType === 3){
  20614. // Text node, may be the link contents, so check parent.
  20615. // This plugin doesn't really support nested HTML elements
  20616. // in the link, it assumes all link content is text.
  20617. a = a.parentNode;
  20618. }
  20619. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  20620. // Stll nothing, one last thing to try on IE, as it might be 'img'
  20621. // and thus considered a control.
  20622. a = dojo.withGlobal(this.editor.window,
  20623. "getSelectedElement", dijit._editor.selection, [this.tag]);
  20624. }
  20625. }else{
  20626. a = dojo.withGlobal(this.editor.window,
  20627. "getAncestorElement", dijit._editor.selection, [this.tag]);
  20628. }
  20629. this.dropDown.reset();
  20630. this._setButton.set("disabled", true);
  20631. this.dropDown.set("value", this._getCurrentValues(a));
  20632. },
  20633. _onDblClick: function(e){
  20634. // summary:
  20635. // Function to define a behavior on double clicks on the element
  20636. // type this dialog edits to select it and pop up the editor
  20637. // dialog.
  20638. // e: Object
  20639. // The double-click event.
  20640. // tags:
  20641. // protected.
  20642. if(e && e.target){
  20643. var t = e.target;
  20644. var tg = t.tagName? t.tagName.toLowerCase() : "";
  20645. if(tg === this.tag && dojo.attr(t,"href")){
  20646. dojo.withGlobal(this.editor.window,
  20647. "selectElement",
  20648. dijit._editor.selection, [t]);
  20649. this.editor.onDisplayChanged();
  20650. setTimeout(dojo.hitch(this, function(){
  20651. // Focus shift outside the event handler.
  20652. // IE doesn't like focus changes in event handles.
  20653. this.button.set("disabled", false);
  20654. this.button.openDropDown();
  20655. }), 10);
  20656. }
  20657. }
  20658. }
  20659. });
  20660. dojo.declare("dijit._editor.plugins.ImgLinkDialog", [dijit._editor.plugins.LinkDialog], {
  20661. // summary:
  20662. // This plugin extends LinkDialog and adds in a plugin for handling image links.
  20663. // provides the image link dialog.
  20664. //
  20665. // description:
  20666. // The command provided by this plugin is:
  20667. // * insertImage
  20668. // linkDialogTemplate: [protected] String
  20669. // Over-ride for template since img dialog doesn't need target that anchor tags may.
  20670. linkDialogTemplate: [
  20671. "<table><tr><td>",
  20672. "<label for='${id}_urlInput'>${url}</label>",
  20673. "</td><td>",
  20674. "<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +
  20675. "required='true' id='${id}_urlInput' name='urlInput' intermediateChanges='true'/>",
  20676. "</td></tr><tr><td>",
  20677. "<label for='${id}_textInput'>${text}</label>",
  20678. "</td><td>",
  20679. "<input dojoType='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +
  20680. "name='textInput' intermediateChanges='true'/>",
  20681. "</td></tr><tr><td>",
  20682. "</td><td>",
  20683. "</td></tr><tr><td colspan='2'>",
  20684. "<button dojoType='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  20685. "<button dojoType='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  20686. "</td></tr></table>"
  20687. ].join(""),
  20688. // htmlTemplate: [protected] String
  20689. // String used for templating the <img> HTML to insert at the desired point.
  20690. htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",
  20691. // tag: [protected] String
  20692. // Tag used for the link type (img).
  20693. tag: "img",
  20694. _getCurrentValues: function(img){
  20695. // summary:
  20696. // Over-ride for getting the values to set in the dropdown.
  20697. // a:
  20698. // The anchor/link to process for data for the dropdown.
  20699. // tags:
  20700. // protected
  20701. var url, text;
  20702. if(img && img.tagName.toLowerCase() === this.tag){
  20703. url = img.getAttribute('_djrealurl') || img.getAttribute('src');
  20704. text = img.getAttribute('alt');
  20705. dojo.withGlobal(this.editor.window,
  20706. "selectElement", dijit._editor.selection, [img, true]);
  20707. }else{
  20708. text = dojo.withGlobal(this.editor.window, dijit._editor.selection.getSelectedText);
  20709. }
  20710. return {urlInput: url || '', textInput: text || ''}; //Object;
  20711. },
  20712. _isValid: function(){
  20713. // summary:
  20714. // Over-ride for images. You can have alt text of blank, it is valid.
  20715. // tags:
  20716. // protected
  20717. return this._urlInput.isValid();
  20718. },
  20719. _connectTagEvents: function(){
  20720. // summary:
  20721. // Over-ridable function that connects tag specific events.
  20722. this.inherited(arguments);
  20723. this.editor.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  20724. // Use onmousedown instead of onclick. Seems that IE eats the first onclick
  20725. // to wrap it in a selector box, then the second one acts as onclick. See #10420
  20726. this.connect(this.editor.editNode, "onmousedown", this._selectTag);
  20727. }));
  20728. },
  20729. _selectTag: function(e){
  20730. // summary:
  20731. // A simple event handler that lets me select an image if it is clicked on.
  20732. // makes it easier to select images in a standard way across browsers. Otherwise
  20733. // selecting an image for edit becomes difficult.
  20734. // e: Event
  20735. // The mousedown event.
  20736. // tags:
  20737. // private
  20738. if(e && e.target){
  20739. var t = e.target;
  20740. var tg = t.tagName? t.tagName.toLowerCase() : "";
  20741. if(tg === this.tag){
  20742. dojo.withGlobal(this.editor.window,
  20743. "selectElement",
  20744. dijit._editor.selection, [t]);
  20745. }
  20746. }
  20747. },
  20748. _checkValues: function(args){
  20749. // summary:
  20750. // Function to check the values in args and 'fix' them up as needed
  20751. // (special characters in the url or alt text)
  20752. // args: Object
  20753. // Content being set.
  20754. // tags:
  20755. // protected
  20756. if(args && args.urlInput){
  20757. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  20758. }
  20759. if(args && args.textInput){
  20760. args.textInput = args.textInput.replace(/"/g, "&quot;");
  20761. }
  20762. return args;
  20763. },
  20764. _onDblClick: function(e){
  20765. // summary:
  20766. // Function to define a behavior on double clicks on the element
  20767. // type this dialog edits to select it and pop up the editor
  20768. // dialog.
  20769. // e: Object
  20770. // The double-click event.
  20771. // tags:
  20772. // protected.
  20773. if(e && e.target){
  20774. var t = e.target;
  20775. var tg = t.tagName? t.tagName.toLowerCase() : "";
  20776. if(tg === this.tag && dojo.attr(t,"src")){
  20777. dojo.withGlobal(this.editor.window,
  20778. "selectElement",
  20779. dijit._editor.selection, [t]);
  20780. this.editor.onDisplayChanged();
  20781. setTimeout(dojo.hitch(this, function(){
  20782. // Focus shift outside the event handler.
  20783. // IE doesn't like focus changes in event handles.
  20784. this.button.set("disabled", false);
  20785. this.button.openDropDown();
  20786. }), 10);
  20787. }
  20788. }
  20789. }
  20790. });
  20791. // Register this plugin.
  20792. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  20793. if(o.plugin){ return; }
  20794. switch(o.args.name){
  20795. case "createLink":
  20796. o.plugin = new dijit._editor.plugins.LinkDialog({command: o.args.name});
  20797. break;
  20798. case "insertImage":
  20799. o.plugin = new dijit._editor.plugins.ImgLinkDialog({command: o.args.name});
  20800. break;
  20801. }
  20802. });
  20803. }
  20804. if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  20805. dojo._hasResource["dojo.colors"] = true;
  20806. dojo.provide("dojo.colors");
  20807. dojo.getObject("colors", true, dojo);
  20808. //TODO: this module appears to break naming conventions
  20809. /*=====
  20810. dojo.colors = {
  20811. // summary: Color utilities
  20812. }
  20813. =====*/
  20814. (function(){
  20815. // this is a standard conversion prescribed by the CSS3 Color Module
  20816. var hue2rgb = function(m1, m2, h){
  20817. if(h < 0){ ++h; }
  20818. if(h > 1){ --h; }
  20819. var h6 = 6 * h;
  20820. if(h6 < 1){ return m1 + (m2 - m1) * h6; }
  20821. if(2 * h < 1){ return m2; }
  20822. if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
  20823. return m1;
  20824. };
  20825. dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
  20826. // summary:
  20827. // get rgb(a) array from css-style color declarations
  20828. // description:
  20829. // this function can handle all 4 CSS3 Color Module formats: rgb,
  20830. // rgba, hsl, hsla, including rgb(a) with percentage values.
  20831. var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
  20832. if(m){
  20833. var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
  20834. if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
  20835. var r = c[0];
  20836. if(r.charAt(r.length - 1) == "%"){
  20837. // 3 rgb percentage values
  20838. a = dojo.map(c, function(x){
  20839. return parseFloat(x) * 2.56;
  20840. });
  20841. if(l == 4){ a[3] = c[3]; }
  20842. return dojo.colorFromArray(a, obj); // dojo.Color
  20843. }
  20844. return dojo.colorFromArray(c, obj); // dojo.Color
  20845. }
  20846. if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
  20847. // normalize hsl values
  20848. var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
  20849. S = parseFloat(c[1]) / 100,
  20850. L = parseFloat(c[2]) / 100,
  20851. // calculate rgb according to the algorithm
  20852. // recommended by the CSS3 Color Module
  20853. m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
  20854. m1 = 2 * L - m2;
  20855. a = [
  20856. hue2rgb(m1, m2, H + 1 / 3) * 256,
  20857. hue2rgb(m1, m2, H) * 256,
  20858. hue2rgb(m1, m2, H - 1 / 3) * 256,
  20859. 1
  20860. ];
  20861. if(l == 4){ a[3] = c[3]; }
  20862. return dojo.colorFromArray(a, obj); // dojo.Color
  20863. }
  20864. }
  20865. return null; // dojo.Color
  20866. };
  20867. var confine = function(c, low, high){
  20868. // summary:
  20869. // sanitize a color component by making sure it is a number,
  20870. // and clamping it to valid values
  20871. c = Number(c);
  20872. return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
  20873. };
  20874. dojo.Color.prototype.sanitize = function(){
  20875. // summary: makes sure that the object has correct attributes
  20876. var t = this;
  20877. t.r = Math.round(confine(t.r, 0, 255));
  20878. t.g = Math.round(confine(t.g, 0, 255));
  20879. t.b = Math.round(confine(t.b, 0, 255));
  20880. t.a = confine(t.a, 0, 1);
  20881. return this; // dojo.Color
  20882. };
  20883. })();
  20884. dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
  20885. // summary: creates a greyscale color with an optional alpha
  20886. return dojo.colorFromArray([g, g, g, a]);
  20887. };
  20888. // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
  20889. dojo.mixin(dojo.Color.named, {
  20890. aliceblue: [240,248,255],
  20891. antiquewhite: [250,235,215],
  20892. aquamarine: [127,255,212],
  20893. azure: [240,255,255],
  20894. beige: [245,245,220],
  20895. bisque: [255,228,196],
  20896. blanchedalmond: [255,235,205],
  20897. blueviolet: [138,43,226],
  20898. brown: [165,42,42],
  20899. burlywood: [222,184,135],
  20900. cadetblue: [95,158,160],
  20901. chartreuse: [127,255,0],
  20902. chocolate: [210,105,30],
  20903. coral: [255,127,80],
  20904. cornflowerblue: [100,149,237],
  20905. cornsilk: [255,248,220],
  20906. crimson: [220,20,60],
  20907. cyan: [0,255,255],
  20908. darkblue: [0,0,139],
  20909. darkcyan: [0,139,139],
  20910. darkgoldenrod: [184,134,11],
  20911. darkgray: [169,169,169],
  20912. darkgreen: [0,100,0],
  20913. darkgrey: [169,169,169],
  20914. darkkhaki: [189,183,107],
  20915. darkmagenta: [139,0,139],
  20916. darkolivegreen: [85,107,47],
  20917. darkorange: [255,140,0],
  20918. darkorchid: [153,50,204],
  20919. darkred: [139,0,0],
  20920. darksalmon: [233,150,122],
  20921. darkseagreen: [143,188,143],
  20922. darkslateblue: [72,61,139],
  20923. darkslategray: [47,79,79],
  20924. darkslategrey: [47,79,79],
  20925. darkturquoise: [0,206,209],
  20926. darkviolet: [148,0,211],
  20927. deeppink: [255,20,147],
  20928. deepskyblue: [0,191,255],
  20929. dimgray: [105,105,105],
  20930. dimgrey: [105,105,105],
  20931. dodgerblue: [30,144,255],
  20932. firebrick: [178,34,34],
  20933. floralwhite: [255,250,240],
  20934. forestgreen: [34,139,34],
  20935. gainsboro: [220,220,220],
  20936. ghostwhite: [248,248,255],
  20937. gold: [255,215,0],
  20938. goldenrod: [218,165,32],
  20939. greenyellow: [173,255,47],
  20940. grey: [128,128,128],
  20941. honeydew: [240,255,240],
  20942. hotpink: [255,105,180],
  20943. indianred: [205,92,92],
  20944. indigo: [75,0,130],
  20945. ivory: [255,255,240],
  20946. khaki: [240,230,140],
  20947. lavender: [230,230,250],
  20948. lavenderblush: [255,240,245],
  20949. lawngreen: [124,252,0],
  20950. lemonchiffon: [255,250,205],
  20951. lightblue: [173,216,230],
  20952. lightcoral: [240,128,128],
  20953. lightcyan: [224,255,255],
  20954. lightgoldenrodyellow: [250,250,210],
  20955. lightgray: [211,211,211],
  20956. lightgreen: [144,238,144],
  20957. lightgrey: [211,211,211],
  20958. lightpink: [255,182,193],
  20959. lightsalmon: [255,160,122],
  20960. lightseagreen: [32,178,170],
  20961. lightskyblue: [135,206,250],
  20962. lightslategray: [119,136,153],
  20963. lightslategrey: [119,136,153],
  20964. lightsteelblue: [176,196,222],
  20965. lightyellow: [255,255,224],
  20966. limegreen: [50,205,50],
  20967. linen: [250,240,230],
  20968. magenta: [255,0,255],
  20969. mediumaquamarine: [102,205,170],
  20970. mediumblue: [0,0,205],
  20971. mediumorchid: [186,85,211],
  20972. mediumpurple: [147,112,219],
  20973. mediumseagreen: [60,179,113],
  20974. mediumslateblue: [123,104,238],
  20975. mediumspringgreen: [0,250,154],
  20976. mediumturquoise: [72,209,204],
  20977. mediumvioletred: [199,21,133],
  20978. midnightblue: [25,25,112],
  20979. mintcream: [245,255,250],
  20980. mistyrose: [255,228,225],
  20981. moccasin: [255,228,181],
  20982. navajowhite: [255,222,173],
  20983. oldlace: [253,245,230],
  20984. olivedrab: [107,142,35],
  20985. orange: [255,165,0],
  20986. orangered: [255,69,0],
  20987. orchid: [218,112,214],
  20988. palegoldenrod: [238,232,170],
  20989. palegreen: [152,251,152],
  20990. paleturquoise: [175,238,238],
  20991. palevioletred: [219,112,147],
  20992. papayawhip: [255,239,213],
  20993. peachpuff: [255,218,185],
  20994. peru: [205,133,63],
  20995. pink: [255,192,203],
  20996. plum: [221,160,221],
  20997. powderblue: [176,224,230],
  20998. rosybrown: [188,143,143],
  20999. royalblue: [65,105,225],
  21000. saddlebrown: [139,69,19],
  21001. salmon: [250,128,114],
  21002. sandybrown: [244,164,96],
  21003. seagreen: [46,139,87],
  21004. seashell: [255,245,238],
  21005. sienna: [160,82,45],
  21006. skyblue: [135,206,235],
  21007. slateblue: [106,90,205],
  21008. slategray: [112,128,144],
  21009. slategrey: [112,128,144],
  21010. snow: [255,250,250],
  21011. springgreen: [0,255,127],
  21012. steelblue: [70,130,180],
  21013. tan: [210,180,140],
  21014. thistle: [216,191,216],
  21015. tomato: [255,99,71],
  21016. transparent: [0, 0, 0, 0],
  21017. turquoise: [64,224,208],
  21018. violet: [238,130,238],
  21019. wheat: [245,222,179],
  21020. whitesmoke: [245,245,245],
  21021. yellowgreen: [154,205,50]
  21022. });
  21023. }
  21024. if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21025. dojo._hasResource["dijit._PaletteMixin"] = true;
  21026. dojo.provide("dijit._PaletteMixin");
  21027. dojo.declare("dijit._PaletteMixin",
  21028. [dijit._CssStateMixin],
  21029. {
  21030. // summary:
  21031. // A keyboard accessible palette, for picking a color/emoticon/etc.
  21032. // description:
  21033. // A mixin for a grid showing various entities, so the user can pick a certain entity.
  21034. // defaultTimeout: Number
  21035. // Number of milliseconds before a held key or button becomes typematic
  21036. defaultTimeout: 500,
  21037. // timeoutChangeRate: Number
  21038. // Fraction of time used to change the typematic timer between events
  21039. // 1.0 means that each typematic event fires at defaultTimeout intervals
  21040. // < 1.0 means that each typematic event fires at an increasing faster rate
  21041. timeoutChangeRate: 0.90,
  21042. // value: String
  21043. // Currently selected color/emoticon/etc.
  21044. value: null,
  21045. // _selectedCell: [private] Integer
  21046. // Index of the currently selected cell. Initially, none selected
  21047. _selectedCell: -1,
  21048. /*=====
  21049. // _currentFocus: [private] DomNode
  21050. // The currently focused cell (if the palette itself has focus), or otherwise
  21051. // the cell to be focused when the palette itself gets focus.
  21052. // Different from value, which represents the selected (i.e. clicked) cell.
  21053. _currentFocus: null,
  21054. =====*/
  21055. /*=====
  21056. // _xDim: [protected] Integer
  21057. // This is the number of cells horizontally across.
  21058. _xDim: null,
  21059. =====*/
  21060. /*=====
  21061. // _yDim: [protected] Integer
  21062. // This is the number of cells vertically down.
  21063. _yDim: null,
  21064. =====*/
  21065. // tabIndex: String
  21066. // Widget tab index.
  21067. tabIndex: "0",
  21068. // cellClass: [protected] String
  21069. // CSS class applied to each cell in the palette
  21070. cellClass: "dijitPaletteCell",
  21071. // dyeClass: [protected] String
  21072. // Name of javascript class for Object created for each cell of the palette.
  21073. // dyeClass should implements dijit.Dye interface
  21074. dyeClass: '',
  21075. _preparePalette: function(choices, titles, dyeClassObj) {
  21076. // summary:
  21077. // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
  21078. // for each cell
  21079. // choices: String[][]
  21080. // id's for each cell of the palette, used to create Dye JS object for each cell
  21081. // titles: String[]
  21082. // Localized tooltip for each cell
  21083. // dyeClassObj: Constructor?
  21084. // If specified, use this constructor rather than this.dyeClass
  21085. this._cells = [];
  21086. var url = this._blankGif;
  21087. dyeClassObj = dyeClassObj || dojo.getObject(this.dyeClass);
  21088. for(var row=0; row < choices.length; row++){
  21089. var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
  21090. for(var col=0; col < choices[row].length; col++){
  21091. var value = choices[row][col];
  21092. if(value){
  21093. var cellObject = new dyeClassObj(value, row, col);
  21094. var cellNode = dojo.create("td", {
  21095. "class": this.cellClass,
  21096. tabIndex: "-1",
  21097. title: titles[value],
  21098. role: "presentation"
  21099. });
  21100. // prepare cell inner structure
  21101. cellObject.fillCell(cellNode, url);
  21102. this.connect(cellNode, "ondijitclick", "_onCellClick");
  21103. this._trackMouseState(cellNode, this.cellClass);
  21104. dojo.place(cellNode, rowNode);
  21105. cellNode.index = this._cells.length;
  21106. // save cell info into _cells
  21107. this._cells.push({node:cellNode, dye:cellObject});
  21108. }
  21109. }
  21110. }
  21111. this._xDim = choices[0].length;
  21112. this._yDim = choices.length;
  21113. // Now set all events
  21114. // The palette itself is navigated to with the tab key on the keyboard
  21115. // Keyboard navigation within the Palette is with the arrow keys
  21116. // Spacebar selects the cell.
  21117. // For the up key the index is changed by negative the x dimension.
  21118. var keyIncrementMap = {
  21119. UP_ARROW: -this._xDim,
  21120. // The down key the index is increase by the x dimension.
  21121. DOWN_ARROW: this._xDim,
  21122. // Right and left move the index by 1.
  21123. RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
  21124. LEFT_ARROW: this.isLeftToRight() ? -1 : 1
  21125. };
  21126. for(var key in keyIncrementMap){
  21127. this._connects.push(
  21128. dijit.typematic.addKeyListener(
  21129. this.domNode,
  21130. {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
  21131. this,
  21132. function(){
  21133. var increment = keyIncrementMap[key];
  21134. return function(count){ this._navigateByKey(increment, count); };
  21135. }(),
  21136. this.timeoutChangeRate,
  21137. this.defaultTimeout
  21138. )
  21139. );
  21140. }
  21141. },
  21142. postCreate: function(){
  21143. this.inherited(arguments);
  21144. // Set initial navigable node.
  21145. this._setCurrent(this._cells[0].node);
  21146. },
  21147. focus: function(){
  21148. // summary:
  21149. // Focus this widget. Puts focus on the most recently focused cell.
  21150. // The cell already has tabIndex set, just need to set CSS and focus it
  21151. dijit.focus(this._currentFocus);
  21152. },
  21153. _onCellClick: function(/*Event*/ evt){
  21154. // summary:
  21155. // Handler for click, enter key & space key. Selects the cell.
  21156. // evt:
  21157. // The event.
  21158. // tags:
  21159. // private
  21160. var target = evt.currentTarget,
  21161. value = this._getDye(target).getValue();
  21162. // First focus the clicked cell, and then send onChange() notification.
  21163. // onChange() (via _setValueAttr) must be after the focus call, because
  21164. // it may trigger a refocus to somewhere else (like the Editor content area), and that
  21165. // second focus should win.
  21166. // Use setTimeout because IE doesn't like changing focus inside of an event handler.
  21167. this._setCurrent(target);
  21168. setTimeout(dojo.hitch(this, function(){
  21169. dijit.focus(target);
  21170. this._setValueAttr(value, true);
  21171. }));
  21172. // workaround bug where hover class is not removed on popup because the popup is
  21173. // closed and then there's no onblur event on the cell
  21174. dojo.removeClass(target, "dijitPaletteCellHover");
  21175. dojo.stopEvent(evt);
  21176. },
  21177. _setCurrent: function(/*DomNode*/ node){
  21178. // summary:
  21179. // Sets which node is the focused cell.
  21180. // description:
  21181. // At any point in time there's exactly one
  21182. // cell with tabIndex != -1. If focus is inside the palette then
  21183. // focus is on that cell.
  21184. //
  21185. // After calling this method, arrow key handlers and mouse click handlers
  21186. // should focus the cell in a setTimeout().
  21187. // tags:
  21188. // protected
  21189. if("_currentFocus" in this){
  21190. // Remove tabIndex on old cell
  21191. dojo.attr(this._currentFocus, "tabIndex", "-1");
  21192. }
  21193. // Set tabIndex of new cell
  21194. this._currentFocus = node;
  21195. if(node){
  21196. dojo.attr(node, "tabIndex", this.tabIndex);
  21197. }
  21198. },
  21199. _setValueAttr: function(value, priorityChange){
  21200. // summary:
  21201. // This selects a cell. It triggers the onChange event.
  21202. // value: String value of the cell to select
  21203. // tags:
  21204. // protected
  21205. // priorityChange:
  21206. // Optional parameter used to tell the select whether or not to fire
  21207. // onChange event.
  21208. // clear old selected cell
  21209. if(this._selectedCell >= 0){
  21210. dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
  21211. }
  21212. this._selectedCell = -1;
  21213. // search for cell matching specified value
  21214. if(value){
  21215. for(var i = 0; i < this._cells.length; i++){
  21216. if(value == this._cells[i].dye.getValue()){
  21217. this._selectedCell = i;
  21218. dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
  21219. break;
  21220. }
  21221. }
  21222. }
  21223. // record new value, or null if no matching cell
  21224. this._set("value", this._selectedCell >= 0 ? value : null);
  21225. if(priorityChange || priorityChange === undefined){
  21226. this.onChange(value);
  21227. }
  21228. },
  21229. onChange: function(value){
  21230. // summary:
  21231. // Callback when a cell is selected.
  21232. // value: String
  21233. // Value corresponding to cell.
  21234. },
  21235. _navigateByKey: function(increment, typeCount){
  21236. // summary:
  21237. // This is the callback for typematic.
  21238. // It changes the focus and the highlighed cell.
  21239. // increment:
  21240. // How much the key is navigated.
  21241. // typeCount:
  21242. // How many times typematic has fired.
  21243. // tags:
  21244. // private
  21245. // typecount == -1 means the key is released.
  21246. if(typeCount == -1){ return; }
  21247. var newFocusIndex = this._currentFocus.index + increment;
  21248. if(newFocusIndex < this._cells.length && newFocusIndex > -1){
  21249. var focusNode = this._cells[newFocusIndex].node;
  21250. this._setCurrent(focusNode);
  21251. // Actually focus the node, for the benefit of screen readers.
  21252. // Use setTimeout because IE doesn't like changing focus inside of an event handler
  21253. setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
  21254. }
  21255. },
  21256. _getDye: function(/*DomNode*/ cell){
  21257. // summary:
  21258. // Get JS object for given cell DOMNode
  21259. return this._cells[cell.index].dye;
  21260. }
  21261. });
  21262. /*=====
  21263. dojo.declare("dijit.Dye",
  21264. null,
  21265. {
  21266. // summary:
  21267. // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
  21268. constructor: function(alias, row, col){
  21269. // summary:
  21270. // Initialize according to value or alias like "white"
  21271. // alias: String
  21272. },
  21273. getValue: function(){
  21274. // summary:
  21275. // Return "value" of cell; meaning of "value" varies by subclass.
  21276. // description:
  21277. // For example color hex value, emoticon ascii value etc, entity hex value.
  21278. },
  21279. fillCell: function(cell, blankGif){
  21280. // summary:
  21281. // Add cell DOMNode inner structure
  21282. // cell: DomNode
  21283. // The surrounding cell
  21284. // blankGif: String
  21285. // URL for blank cell image
  21286. }
  21287. }
  21288. );
  21289. =====*/
  21290. }
  21291. if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21292. dojo._hasResource["dijit.ColorPalette"] = true;
  21293. dojo.provide("dijit.ColorPalette");
  21294. dojo.declare("dijit.ColorPalette",
  21295. [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
  21296. {
  21297. // summary:
  21298. // A keyboard accessible color-picking widget
  21299. // description:
  21300. // Grid showing various colors, so the user can pick a certain color.
  21301. // Can be used standalone, or as a popup.
  21302. //
  21303. // example:
  21304. // | <div dojoType="dijit.ColorPalette"></div>
  21305. //
  21306. // example:
  21307. // | var picker = new dijit.ColorPalette({ },srcNode);
  21308. // | picker.startup();
  21309. // palette: [const] String
  21310. // Size of grid, either "7x10" or "3x4".
  21311. palette: "7x10",
  21312. // _palettes: [protected] Map
  21313. // This represents the value of the colors.
  21314. // The first level is a hashmap of the different palettes available.
  21315. // The next two dimensions represent the columns and rows of colors.
  21316. _palettes: {
  21317. "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
  21318. ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
  21319. ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
  21320. ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
  21321. ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
  21322. ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
  21323. ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
  21324. "3x4": [["white", "lime", "green", "blue"],
  21325. ["silver", "yellow", "fuchsia", "navy"],
  21326. ["gray", "red", "purple", "black"]]
  21327. },
  21328. // templateString: String
  21329. // The template of this widget.
  21330. 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"),
  21331. baseClass: "dijitColorPalette",
  21332. buildRendering: function(){
  21333. // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
  21334. // <img> nodes
  21335. this.inherited(arguments);
  21336. // Creates <img> nodes in each cell of the template.
  21337. // Pass in "customized" dijit._Color constructor for specified palette and high-contrast vs. normal mode
  21338. this._preparePalette(
  21339. this._palettes[this.palette],
  21340. dojo.i18n.getLocalization("dojo", "colors", this.lang),
  21341. dojo.declare(dijit._Color, {
  21342. hc: dojo.hasClass(dojo.body(), "dijit_a11y"),
  21343. palette: this.palette
  21344. })
  21345. );
  21346. }
  21347. });
  21348. dojo.declare("dijit._Color", dojo.Color, {
  21349. // summary:
  21350. // Object associated with each cell in a ColorPalette palette.
  21351. // Implements dijit.Dye.
  21352. // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
  21353. // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
  21354. // for showing the color.
  21355. template:
  21356. "<span class='dijitInline dijitPaletteImg'>" +
  21357. "<img src='${blankGif}' alt='${alt}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
  21358. "</span>",
  21359. // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
  21360. // but scrolled and clipped to show the correct color only
  21361. hcTemplate:
  21362. "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
  21363. "<img src='${image}' alt='${alt}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
  21364. "</span>",
  21365. // _imagePaths: [protected] Map
  21366. // This is stores the path to the palette images used for high-contrast mode display
  21367. _imagePaths: {
  21368. "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
  21369. "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png")
  21370. },
  21371. constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
  21372. this._alias = alias;
  21373. this._row = row;
  21374. this._col = col;
  21375. this.setColor(dojo.Color.named[alias]);
  21376. },
  21377. getValue: function(){
  21378. // summary:
  21379. // Note that although dijit._Color is initialized with a value like "white" getValue() always
  21380. // returns a hex value
  21381. return this.toHex();
  21382. },
  21383. fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
  21384. var html = dojo.string.substitute(this.hc ? this.hcTemplate : this.template, {
  21385. // substitution variables for normal mode
  21386. color: this.toHex(),
  21387. blankGif: blankGif,
  21388. alt: this._alias,
  21389. // variables used for high contrast mode
  21390. image: this._imagePaths[this.palette].toString(),
  21391. left: this._col * -20 - 5,
  21392. top: this._row * -20 - 5,
  21393. size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
  21394. });
  21395. dojo.place(html, cell);
  21396. }
  21397. });
  21398. }
  21399. if(!dojo._hasResource["dijit._editor.plugins.TextColor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21400. dojo._hasResource["dijit._editor.plugins.TextColor"] = true;
  21401. dojo.provide("dijit._editor.plugins.TextColor");
  21402. dojo.declare("dijit._editor.plugins.TextColor", dijit._editor._Plugin, {
  21403. // summary:
  21404. // This plugin provides dropdown color pickers for setting text color and background color
  21405. //
  21406. // description:
  21407. // The commands provided by this plugin are:
  21408. // * foreColor - sets the text color
  21409. // * hiliteColor - sets the background color
  21410. // Override _Plugin.buttonClass to use DropDownButton (with ColorPalette) to control this plugin
  21411. buttonClass: dijit.form.DropDownButton,
  21412. // useDefaultCommand: Boolean
  21413. // False as we do not use the default editor command/click behavior.
  21414. useDefaultCommand: false,
  21415. _initButton: function(){
  21416. this.inherited(arguments);
  21417. // Setup to lazy load ColorPalette first time the button is clicked
  21418. var self = this;
  21419. this.button.loadDropDown = function(callback){
  21420. this.dropDown = new dijit.ColorPalette({
  21421. value: self.value,
  21422. onChange: function(color){
  21423. self.editor.execCommand(self.command, color);
  21424. }
  21425. });
  21426. callback();
  21427. };
  21428. },
  21429. updateState: function(){
  21430. // summary:
  21431. // Overrides _Plugin.updateState(). This updates the ColorPalette
  21432. // to show the color of the currently selected text.
  21433. // tags:
  21434. // protected
  21435. var _e = this.editor;
  21436. var _c = this.command;
  21437. if(!_e || !_e.isLoaded || !_c.length){
  21438. return;
  21439. }
  21440. if(this.button){
  21441. var disabled = this.get("disabled");
  21442. this.button.set("disabled", disabled);
  21443. if(disabled){ return; }
  21444. var value;
  21445. try{
  21446. value = _e.queryCommandValue(_c)|| "";
  21447. }catch(e){
  21448. //Firefox may throw error above if the editor is just loaded, ignore it
  21449. value = "";
  21450. }
  21451. }
  21452. if(value == ""){
  21453. value = "#000000";
  21454. }
  21455. if(value == "transparent"){
  21456. value = "#ffffff";
  21457. }
  21458. if(typeof value == "string"){
  21459. //if RGB value, convert to hex value
  21460. if(value.indexOf("rgb")> -1){
  21461. value = dojo.colorFromRgb(value).toHex();
  21462. }
  21463. }else{ //it's an integer(IE returns an MS access #)
  21464. value =((value & 0x0000ff)<< 16)|(value & 0x00ff00)|((value & 0xff0000)>>> 16);
  21465. value = value.toString(16);
  21466. value = "#000000".slice(0, 7 - value.length)+ value;
  21467. }
  21468. var dropDown = this.button.dropDown;
  21469. if(dropDown && value !== dropDown.get('value')){
  21470. dropDown.set('value', value, false);
  21471. }
  21472. }
  21473. });
  21474. // Register this plugin.
  21475. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin", null, function(o){
  21476. if(o.plugin){
  21477. return;
  21478. }
  21479. switch(o.args.name){
  21480. case "foreColor":
  21481. case "hiliteColor":
  21482. o.plugin = new dijit._editor.plugins.TextColor({
  21483. command: o.args.name
  21484. });
  21485. }
  21486. });
  21487. }
  21488. if(!dojo._hasResource["dijit.tree._dndContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21489. dojo._hasResource["dijit.tree._dndContainer"] = true;
  21490. dojo.provide("dijit.tree._dndContainer");
  21491. dojo.getObject("tree", true, dojo);
  21492. dijit.tree._compareNodes = function(n1, n2){
  21493. if(n1 === n2){
  21494. return 0;
  21495. }
  21496. if('sourceIndex' in document.documentElement){ //IE
  21497. //TODO: does not yet work if n1 and/or n2 is a text node
  21498. return n1.sourceIndex - n2.sourceIndex;
  21499. }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
  21500. return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
  21501. }else if(document.createRange){ //Webkit
  21502. var r1 = doc.createRange();
  21503. r1.setStartBefore(n1);
  21504. var r2 = doc.createRange();
  21505. r2.setStartBefore(n2);
  21506. return r1.compareBoundaryPoints(r1.END_TO_END, r2);
  21507. }else{
  21508. throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
  21509. }
  21510. };
  21511. dojo.declare("dijit.tree._dndContainer",
  21512. null,
  21513. {
  21514. // summary:
  21515. // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
  21516. // It's modeled after `dojo.dnd.Container`.
  21517. // tags:
  21518. // protected
  21519. /*=====
  21520. // current: DomNode
  21521. // The currently hovered TreeNode.rowNode (which is the DOM node
  21522. // associated w/a given node in the tree, excluding it's descendants)
  21523. current: null,
  21524. =====*/
  21525. constructor: function(tree, params){
  21526. // summary:
  21527. // A constructor of the Container
  21528. // tree: Node
  21529. // Node or node's id to build the container on
  21530. // params: dijit.tree.__SourceArgs
  21531. // A dict of parameters, which gets mixed into the object
  21532. // tags:
  21533. // private
  21534. this.tree = tree;
  21535. this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
  21536. dojo.mixin(this, params);
  21537. // class-specific variables
  21538. this.map = {};
  21539. this.current = null; // current TreeNode's DOM node
  21540. // states
  21541. this.containerState = "";
  21542. dojo.addClass(this.node, "dojoDndContainer");
  21543. // set up events
  21544. this.events = [
  21545. // container level events
  21546. dojo.connect(this.node, "onmouseenter", this, "onOverEvent"),
  21547. dojo.connect(this.node, "onmouseleave", this, "onOutEvent"),
  21548. // switching between TreeNodes
  21549. dojo.connect(this.tree, "_onNodeMouseEnter", this, "onMouseOver"),
  21550. dojo.connect(this.tree, "_onNodeMouseLeave", this, "onMouseOut"),
  21551. // cancel text selection and text dragging
  21552. dojo.connect(this.node, "ondragstart", dojo, "stopEvent"),
  21553. dojo.connect(this.node, "onselectstart", dojo, "stopEvent")
  21554. ];
  21555. },
  21556. getItem: function(/*String*/ key){
  21557. // summary:
  21558. // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
  21559. // Called by dojo.dnd.Source.checkAcceptance().
  21560. // tags:
  21561. // protected
  21562. var widget = this.selection[key],
  21563. ret = {
  21564. data: widget,
  21565. type: ["treeNode"]
  21566. };
  21567. return ret; // dojo.dnd.Item
  21568. },
  21569. destroy: function(){
  21570. // summary:
  21571. // Prepares this object to be garbage-collected
  21572. dojo.forEach(this.events, dojo.disconnect);
  21573. // this.clearItems();
  21574. this.node = this.parent = null;
  21575. },
  21576. // mouse events
  21577. onMouseOver: function(/*TreeNode*/ widget, /*Event*/ evt){
  21578. // summary:
  21579. // Called when mouse is moved over a TreeNode
  21580. // tags:
  21581. // protected
  21582. this.current = widget;
  21583. },
  21584. onMouseOut: function(/*TreeNode*/ widget, /*Event*/ evt){
  21585. // summary:
  21586. // Called when mouse is moved away from a TreeNode
  21587. // tags:
  21588. // protected
  21589. this.current = null;
  21590. },
  21591. _changeState: function(type, newState){
  21592. // summary:
  21593. // Changes a named state to new state value
  21594. // type: String
  21595. // A name of the state to change
  21596. // newState: String
  21597. // new state
  21598. var prefix = "dojoDnd" + type;
  21599. var state = type.toLowerCase() + "State";
  21600. //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  21601. dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
  21602. this[state] = newState;
  21603. },
  21604. _addItemClass: function(node, type){
  21605. // summary:
  21606. // Adds a class with prefix "dojoDndItem"
  21607. // node: Node
  21608. // A node
  21609. // type: String
  21610. // A variable suffix for a class name
  21611. dojo.addClass(node, "dojoDndItem" + type);
  21612. },
  21613. _removeItemClass: function(node, type){
  21614. // summary:
  21615. // Removes a class with prefix "dojoDndItem"
  21616. // node: Node
  21617. // A node
  21618. // type: String
  21619. // A variable suffix for a class name
  21620. dojo.removeClass(node, "dojoDndItem" + type);
  21621. },
  21622. onOverEvent: function(){
  21623. // summary:
  21624. // This function is called once, when mouse is over our container
  21625. // tags:
  21626. // protected
  21627. this._changeState("Container", "Over");
  21628. },
  21629. onOutEvent: function(){
  21630. // summary:
  21631. // This function is called once, when mouse is out of our container
  21632. // tags:
  21633. // protected
  21634. this._changeState("Container", "");
  21635. }
  21636. });
  21637. }
  21638. if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21639. dojo._hasResource["dijit.tree._dndSelector"] = true;
  21640. dojo.provide("dijit.tree._dndSelector");
  21641. dojo.declare("dijit.tree._dndSelector",
  21642. dijit.tree._dndContainer,
  21643. {
  21644. // summary:
  21645. // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
  21646. // It's based on `dojo.dnd.Selector`.
  21647. // tags:
  21648. // protected
  21649. /*=====
  21650. // selection: Hash<String, DomNode>
  21651. // (id, DomNode) map for every TreeNode that's currently selected.
  21652. // The DOMNode is the TreeNode.rowNode.
  21653. selection: {},
  21654. =====*/
  21655. constructor: function(tree, params){
  21656. // summary:
  21657. // Initialization
  21658. // tags:
  21659. // private
  21660. this.selection={};
  21661. this.anchor = null;
  21662. dijit.setWaiState(this.tree.domNode, "multiselect", !this.singular);
  21663. this.events.push(
  21664. dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"),
  21665. dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"),
  21666. dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove")
  21667. );
  21668. },
  21669. // singular: Boolean
  21670. // Allows selection of only one element, if true.
  21671. // Tree hasn't been tested in singular=true mode, unclear if it works.
  21672. singular: false,
  21673. // methods
  21674. getSelectedTreeNodes: function(){
  21675. // summary:
  21676. // Returns a list of selected node(s).
  21677. // Used by dndSource on the start of a drag.
  21678. // tags:
  21679. // protected
  21680. var nodes=[], sel = this.selection;
  21681. for(var i in sel){
  21682. nodes.push(sel[i]);
  21683. }
  21684. return nodes;
  21685. },
  21686. selectNone: function(){
  21687. // summary:
  21688. // Unselects all items
  21689. // tags:
  21690. // private
  21691. this.setSelection([]);
  21692. return this; // self
  21693. },
  21694. destroy: function(){
  21695. // summary:
  21696. // Prepares the object to be garbage-collected
  21697. this.inherited(arguments);
  21698. this.selection = this.anchor = null;
  21699. },
  21700. addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
  21701. // summary
  21702. // add node to current selection
  21703. // node: Node
  21704. // node to add
  21705. // isAnchor: Boolean
  21706. // Whether the node should become anchor.
  21707. this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
  21708. if(isAnchor){ this.anchor = node; }
  21709. return node;
  21710. },
  21711. removeTreeNode: function(/*dijit._TreeNode*/node){
  21712. // summary
  21713. // remove node from current selection
  21714. // node: Node
  21715. // node to remove
  21716. this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]))
  21717. return node;
  21718. },
  21719. isTreeNodeSelected: function(/*dijit._TreeNode*/node){
  21720. // summary
  21721. // return true if node is currently selected
  21722. // node: Node
  21723. // the node to check whether it's in the current selection
  21724. return node.id && !!this.selection[node.id];
  21725. },
  21726. setSelection: function(/*dijit._treeNode[]*/ newSelection){
  21727. // summary
  21728. // set the list of selected nodes to be exactly newSelection. All changes to the
  21729. // selection should be passed through this function, which ensures that derived
  21730. // attributes are kept up to date. Anchor will be deleted if it has been removed
  21731. // from the selection, but no new anchor will be added by this function.
  21732. // newSelection: Node[]
  21733. // list of tree nodes to make selected
  21734. var oldSelection = this.getSelectedTreeNodes();
  21735. dojo.forEach(this._setDifference(oldSelection, newSelection), dojo.hitch(this, function(node){
  21736. node.setSelected(false);
  21737. if(this.anchor == node){
  21738. delete this.anchor;
  21739. }
  21740. delete this.selection[node.id];
  21741. }));
  21742. dojo.forEach(this._setDifference(newSelection, oldSelection), dojo.hitch(this, function(node){
  21743. node.setSelected(true);
  21744. this.selection[node.id] = node;
  21745. }));
  21746. this._updateSelectionProperties();
  21747. },
  21748. _setDifference: function(xs,ys){
  21749. // summary
  21750. // Returns a copy of xs which lacks any objects
  21751. // occurring in ys. Checks for membership by
  21752. // modifying and then reading the object, so it will
  21753. // not properly handle sets of numbers or strings.
  21754. dojo.forEach(ys, function(y){ y.__exclude__ = true; });
  21755. var ret = dojo.filter(xs, function(x){ return !x.__exclude__; });
  21756. // clean up after ourselves.
  21757. dojo.forEach(ys, function(y){ delete y['__exclude__'] });
  21758. return ret;
  21759. },
  21760. _updateSelectionProperties: function() {
  21761. // summary
  21762. // Update the following tree properties from the current selection:
  21763. // path[s], selectedItem[s], selectedNode[s]
  21764. var selected = this.getSelectedTreeNodes();
  21765. var paths = [], nodes = [];
  21766. dojo.forEach(selected, function(node) {
  21767. nodes.push(node);
  21768. paths.push(node.getTreePath());
  21769. });
  21770. var items = dojo.map(nodes,function(node) { return node.item; });
  21771. this.tree._set("paths", paths);
  21772. this.tree._set("path", paths[0] || []);
  21773. this.tree._set("selectedNodes", nodes);
  21774. this.tree._set("selectedNode", nodes[0] || null);
  21775. this.tree._set("selectedItems", items);
  21776. this.tree._set("selectedItem", items[0] || null);
  21777. },
  21778. // mouse events
  21779. onMouseDown: function(e){
  21780. // summary:
  21781. // Event processor for onmousedown
  21782. // e: Event
  21783. // mouse event
  21784. // tags:
  21785. // protected
  21786. // ignore click on expando node
  21787. if(!this.current || this.tree.isExpandoNode( e.target, this.current)){ return; }
  21788. if(e.button == dojo.mouseButtons.RIGHT){ return; } // ignore right-click
  21789. dojo.stopEvent(e);
  21790. var treeNode = this.current,
  21791. copy = dojo.isCopyKey(e), id = treeNode.id;
  21792. // if shift key is not pressed, and the node is already in the selection,
  21793. // delay deselection until onmouseup so in the case of DND, deselection
  21794. // will be canceled by onmousemove.
  21795. if(!this.singular && !e.shiftKey && this.selection[id]){
  21796. this._doDeselect = true;
  21797. return;
  21798. }else{
  21799. this._doDeselect = false;
  21800. }
  21801. this.userSelect(treeNode, copy, e.shiftKey);
  21802. },
  21803. onMouseUp: function(e){
  21804. // summary:
  21805. // Event processor for onmouseup
  21806. // e: Event
  21807. // mouse event
  21808. // tags:
  21809. // protected
  21810. // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
  21811. // a already selected item (to deselect the item), or click on a not-yet selected item
  21812. // (which should remove all current selection, and add the clicked item). This can not
  21813. // be done in onMouseDown, because the user may start a drag after mousedown. By moving
  21814. // the deselection logic here, the user can drags an already selected item.
  21815. if(!this._doDeselect){ return; }
  21816. this._doDeselect = false;
  21817. this.userSelect(this.current, dojo.isCopyKey( e ), e.shiftKey);
  21818. },
  21819. onMouseMove: function(e){
  21820. // summary
  21821. // event processor for onmousemove
  21822. // e: Event
  21823. // mouse event
  21824. this._doDeselect = false;
  21825. },
  21826. userSelect: function(node, multi, range){
  21827. // summary:
  21828. // Add or remove the given node from selection, responding
  21829. // to a user action such as a click or keypress.
  21830. // multi: Boolean
  21831. // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
  21832. // range: Boolean
  21833. // Indicates whether this is meant to be a ranged action (e.g. shift-click)
  21834. // tags:
  21835. // protected
  21836. if(this.singular){
  21837. if(this.anchor == node && multi){
  21838. this.selectNone();
  21839. }else{
  21840. this.setSelection([node]);
  21841. this.anchor = node;
  21842. }
  21843. }else{
  21844. if(range && this.anchor){
  21845. var cr = dijit.tree._compareNodes(this.anchor.rowNode, node.rowNode),
  21846. begin, end, anchor = this.anchor;
  21847. if(cr < 0){ //current is after anchor
  21848. begin = anchor;
  21849. end = node;
  21850. }else{ //current is before anchor
  21851. begin = node;
  21852. end = anchor;
  21853. }
  21854. nodes = [];
  21855. //add everything betweeen begin and end inclusively
  21856. while(begin != end) {
  21857. nodes.push(begin)
  21858. begin = this.tree._getNextNode(begin);
  21859. }
  21860. nodes.push(end)
  21861. this.setSelection(nodes);
  21862. }else{
  21863. if( this.selection[ node.id ] && multi ) {
  21864. this.removeTreeNode( node );
  21865. } else if(multi) {
  21866. this.addTreeNode(node, true);
  21867. } else {
  21868. this.setSelection([node]);
  21869. this.anchor = node;
  21870. }
  21871. }
  21872. }
  21873. },
  21874. forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
  21875. // summary:
  21876. // Iterates over selected items;
  21877. // see `dojo.dnd.Container.forInItems()` for details
  21878. o = o || dojo.global;
  21879. for(var id in this.selection){
  21880. // console.log("selected item id: " + id);
  21881. f.call(o, this.getItem(id), id, this);
  21882. }
  21883. }
  21884. });
  21885. }
  21886. if(!dojo._hasResource["dijit.tree.dndSource"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  21887. dojo._hasResource["dijit.tree.dndSource"] = true;
  21888. dojo.provide("dijit.tree.dndSource");
  21889. /*=====
  21890. dijit.tree.__SourceArgs = function(){
  21891. // summary:
  21892. // A dict of parameters for Tree source configuration.
  21893. // isSource: Boolean?
  21894. // Can be used as a DnD source. Defaults to true.
  21895. // accept: String[]
  21896. // List of accepted types (text strings) for a target; defaults to
  21897. // ["text", "treeNode"]
  21898. // copyOnly: Boolean?
  21899. // Copy items, if true, use a state of Ctrl key otherwise,
  21900. // dragThreshold: Number
  21901. // The move delay in pixels before detecting a drag; 0 by default
  21902. // betweenThreshold: Integer
  21903. // Distance from upper/lower edge of node to allow drop to reorder nodes
  21904. this.isSource = isSource;
  21905. this.accept = accept;
  21906. this.autoSync = autoSync;
  21907. this.copyOnly = copyOnly;
  21908. this.dragThreshold = dragThreshold;
  21909. this.betweenThreshold = betweenThreshold;
  21910. }
  21911. =====*/
  21912. dojo.declare("dijit.tree.dndSource", dijit.tree._dndSelector, {
  21913. // summary:
  21914. // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
  21915. // isSource: [private] Boolean
  21916. // Can be used as a DnD source.
  21917. isSource: true,
  21918. // accept: String[]
  21919. // List of accepted types (text strings) for the Tree; defaults to
  21920. // ["text"]
  21921. accept: ["text", "treeNode"],
  21922. // copyOnly: [private] Boolean
  21923. // Copy items, if true, use a state of Ctrl key otherwise
  21924. copyOnly: false,
  21925. // dragThreshold: Number
  21926. // The move delay in pixels before detecting a drag; 5 by default
  21927. dragThreshold: 5,
  21928. // betweenThreshold: Integer
  21929. // Distance from upper/lower edge of node to allow drop to reorder nodes
  21930. betweenThreshold: 0,
  21931. constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
  21932. // summary:
  21933. // a constructor of the Tree DnD Source
  21934. // tags:
  21935. // private
  21936. if(!params){ params = {}; }
  21937. dojo.mixin(this, params);
  21938. this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
  21939. var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
  21940. this.accept = null;
  21941. if(type.length){
  21942. this.accept = {};
  21943. for(var i = 0; i < type.length; ++i){
  21944. this.accept[type[i]] = 1;
  21945. }
  21946. }
  21947. // class-specific variables
  21948. this.isDragging = false;
  21949. this.mouseDown = false;
  21950. this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode
  21951. this.targetBox = null; // coordinates of this.targetAnchor
  21952. this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
  21953. this._lastX = 0;
  21954. this._lastY = 0;
  21955. // states
  21956. this.sourceState = "";
  21957. if(this.isSource){
  21958. dojo.addClass(this.node, "dojoDndSource");
  21959. }
  21960. this.targetState = "";
  21961. if(this.accept){
  21962. dojo.addClass(this.node, "dojoDndTarget");
  21963. }
  21964. // set up events
  21965. this.topics = [
  21966. dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
  21967. dojo.subscribe("/dnd/start", this, "onDndStart"),
  21968. dojo.subscribe("/dnd/drop", this, "onDndDrop"),
  21969. dojo.subscribe("/dnd/cancel", this, "onDndCancel")
  21970. ];
  21971. },
  21972. // methods
  21973. checkAcceptance: function(source, nodes){
  21974. // summary:
  21975. // Checks if the target can accept nodes from this source
  21976. // source: dijit.tree.dndSource
  21977. // The source which provides items
  21978. // nodes: DOMNode[]
  21979. // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
  21980. // source is a dijit.Tree.
  21981. // tags:
  21982. // extension
  21983. return true; // Boolean
  21984. },
  21985. copyState: function(keyPressed){
  21986. // summary:
  21987. // Returns true, if we need to copy items, false to move.
  21988. // It is separated to be overwritten dynamically, if needed.
  21989. // keyPressed: Boolean
  21990. // The "copy" control key was pressed
  21991. // tags:
  21992. // protected
  21993. return this.copyOnly || keyPressed; // Boolean
  21994. },
  21995. destroy: function(){
  21996. // summary:
  21997. // Prepares the object to be garbage-collected.
  21998. this.inherited("destroy",arguments);
  21999. dojo.forEach(this.topics, dojo.unsubscribe);
  22000. this.targetAnchor = null;
  22001. },
  22002. _onDragMouse: function(e){
  22003. // summary:
  22004. // Helper method for processing onmousemove/onmouseover events while drag is in progress.
  22005. // Keeps track of current drop target.
  22006. var m = dojo.dnd.manager(),
  22007. oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over
  22008. newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over
  22009. oldDropPosition = this.dropPosition; // the previous drop position (over/before/after)
  22010. // calculate if user is indicating to drop the dragged node before, after, or over
  22011. // (i.e., to become a child of) the target node
  22012. var newDropPosition = "Over";
  22013. if(newTarget && this.betweenThreshold > 0){
  22014. // If mouse is over a new TreeNode, then get new TreeNode's position and size
  22015. if(!this.targetBox || oldTarget != newTarget){
  22016. this.targetBox = dojo.position(newTarget.rowNode, true);
  22017. }
  22018. if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
  22019. newDropPosition = "Before";
  22020. }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
  22021. newDropPosition = "After";
  22022. }
  22023. }
  22024. if(newTarget != oldTarget || newDropPosition != oldDropPosition){
  22025. if(oldTarget){
  22026. this._removeItemClass(oldTarget.rowNode, oldDropPosition);
  22027. }
  22028. if(newTarget){
  22029. this._addItemClass(newTarget.rowNode, newDropPosition);
  22030. }
  22031. // Check if it's ok to drop the dragged node on/before/after the target node.
  22032. if(!newTarget){
  22033. m.canDrop(false);
  22034. }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
  22035. // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
  22036. m.canDrop(false);
  22037. }else if(m.source == this && (newTarget.id in this.selection)){
  22038. // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
  22039. m.canDrop(false);
  22040. }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
  22041. && !this._isParentChildDrop(m.source, newTarget.rowNode)){
  22042. m.canDrop(true);
  22043. }else{
  22044. m.canDrop(false);
  22045. }
  22046. this.targetAnchor = newTarget;
  22047. this.dropPosition = newDropPosition;
  22048. }
  22049. },
  22050. onMouseMove: function(e){
  22051. // summary:
  22052. // Called for any onmousemove events over the Tree
  22053. // e: Event
  22054. // onmousemouse event
  22055. // tags:
  22056. // private
  22057. if(this.isDragging && this.targetState == "Disabled"){ return; }
  22058. this.inherited(arguments);
  22059. var m = dojo.dnd.manager();
  22060. if(this.isDragging){
  22061. this._onDragMouse(e);
  22062. }else{
  22063. if(this.mouseDown && this.isSource &&
  22064. (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
  22065. var nodes = this.getSelectedTreeNodes();
  22066. if(nodes.length){
  22067. if(nodes.length > 1){
  22068. //filter out all selected items which has one of their ancestor selected as well
  22069. var seen = this.selection, i = 0, r = [], n, p;
  22070. nextitem: while((n = nodes[i++])){
  22071. for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
  22072. if(seen[p.id]){ //parent is already selected, skip this node
  22073. continue nextitem;
  22074. }
  22075. }
  22076. //this node does not have any ancestors selected, add it
  22077. r.push(n);
  22078. }
  22079. nodes = r;
  22080. }
  22081. nodes = dojo.map(nodes, function(n){return n.domNode});
  22082. m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
  22083. }
  22084. }
  22085. }
  22086. },
  22087. onMouseDown: function(e){
  22088. // summary:
  22089. // Event processor for onmousedown
  22090. // e: Event
  22091. // onmousedown event
  22092. // tags:
  22093. // private
  22094. this.mouseDown = true;
  22095. this.mouseButton = e.button;
  22096. this._lastX = e.pageX;
  22097. this._lastY = e.pageY;
  22098. this.inherited(arguments);
  22099. },
  22100. onMouseUp: function(e){
  22101. // summary:
  22102. // Event processor for onmouseup
  22103. // e: Event
  22104. // onmouseup event
  22105. // tags:
  22106. // private
  22107. if(this.mouseDown){
  22108. this.mouseDown = false;
  22109. this.inherited(arguments);
  22110. }
  22111. },
  22112. onMouseOut: function(){
  22113. // summary:
  22114. // Event processor for when mouse is moved away from a TreeNode
  22115. // tags:
  22116. // private
  22117. this.inherited(arguments);
  22118. this._unmarkTargetAnchor();
  22119. },
  22120. checkItemAcceptance: function(target, source, position){
  22121. // summary:
  22122. // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
  22123. // description:
  22124. // In the base case, this is called to check if target can become a child of source.
  22125. // When betweenThreshold is set, position="before" or "after" means that we
  22126. // are asking if the source node can be dropped before/after the target node.
  22127. // target: DOMNode
  22128. // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
  22129. // Use dijit.getEnclosingWidget(target) to get the TreeNode.
  22130. // source: dijit.tree.dndSource
  22131. // The (set of) nodes we are dropping
  22132. // position: String
  22133. // "over", "before", or "after"
  22134. // tags:
  22135. // extension
  22136. return true;
  22137. },
  22138. // topic event processors
  22139. onDndSourceOver: function(source){
  22140. // summary:
  22141. // Topic event processor for /dnd/source/over, called when detected a current source.
  22142. // source: Object
  22143. // The dijit.tree.dndSource / dojo.dnd.Source which has the mouse over it
  22144. // tags:
  22145. // private
  22146. if(this != source){
  22147. this.mouseDown = false;
  22148. this._unmarkTargetAnchor();
  22149. }else if(this.isDragging){
  22150. var m = dojo.dnd.manager();
  22151. m.canDrop(false);
  22152. }
  22153. },
  22154. onDndStart: function(source, nodes, copy){
  22155. // summary:
  22156. // Topic event processor for /dnd/start, called to initiate the DnD operation
  22157. // source: Object
  22158. // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
  22159. // nodes: DomNode[]
  22160. // The list of transferred items, dndTreeNode nodes if dragging from a Tree
  22161. // copy: Boolean
  22162. // Copy items, if true, move items otherwise
  22163. // tags:
  22164. // private
  22165. if(this.isSource){
  22166. this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
  22167. }
  22168. var accepted = this.checkAcceptance(source, nodes);
  22169. this._changeState("Target", accepted ? "" : "Disabled");
  22170. if(this == source){
  22171. dojo.dnd.manager().overSource(this);
  22172. }
  22173. this.isDragging = true;
  22174. },
  22175. itemCreator: function(/*DomNode[]*/ nodes, target, /*dojo.dnd.Source*/ source){
  22176. // summary:
  22177. // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
  22178. // dropped onto the tree. Developer must override this method to enable
  22179. // dropping from external sources onto this Tree, unless the Tree.model's items
  22180. // happen to look like {id: 123, name: "Apple" } with no other attributes.
  22181. // description:
  22182. // For each node in nodes[], which came from source, create a hash of name/value
  22183. // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
  22184. // returns: Object[]
  22185. // Array of name/value hashes for each new item to be added to the Tree, like:
  22186. // | [
  22187. // | { id: 123, label: "apple", foo: "bar" },
  22188. // | { id: 456, label: "pear", zaz: "bam" }
  22189. // | ]
  22190. // tags:
  22191. // extension
  22192. // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
  22193. // make signature itemCreator(sourceItem, node, target) (or similar).
  22194. return dojo.map(nodes, function(node){
  22195. return {
  22196. "id": node.id,
  22197. "name": node.textContent || node.innerText || ""
  22198. };
  22199. }); // Object[]
  22200. },
  22201. onDndDrop: function(source, nodes, copy){
  22202. // summary:
  22203. // Topic event processor for /dnd/drop, called to finish the DnD operation.
  22204. // description:
  22205. // Updates data store items according to where node was dragged from and dropped
  22206. // to. The tree will then respond to those data store updates and redraw itself.
  22207. // source: Object
  22208. // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
  22209. // nodes: DomNode[]
  22210. // The list of transferred items, dndTreeNode nodes if dragging from a Tree
  22211. // copy: Boolean
  22212. // Copy items, if true, move items otherwise
  22213. // tags:
  22214. // protected
  22215. if(this.containerState == "Over"){
  22216. var tree = this.tree,
  22217. model = tree.model,
  22218. target = this.targetAnchor,
  22219. requeryRoot = false; // set to true iff top level items change
  22220. this.isDragging = false;
  22221. // Compute the new parent item
  22222. var targetWidget = target;
  22223. var newParentItem;
  22224. var insertIndex;
  22225. newParentItem = (targetWidget && targetWidget.item) || tree.item;
  22226. if(this.dropPosition == "Before" || this.dropPosition == "After"){
  22227. // TODO: if there is no parent item then disallow the drop.
  22228. // Actually this should be checked during onMouseMove too, to make the drag icon red.
  22229. newParentItem = (targetWidget.getParent() && targetWidget.getParent().item) || tree.item;
  22230. // Compute the insert index for reordering
  22231. insertIndex = targetWidget.getIndexInParent();
  22232. if(this.dropPosition == "After"){
  22233. insertIndex = targetWidget.getIndexInParent() + 1;
  22234. }
  22235. }else{
  22236. newParentItem = (targetWidget && targetWidget.item) || tree.item;
  22237. }
  22238. // If necessary, use this variable to hold array of hashes to pass to model.newItem()
  22239. // (one entry in the array for each dragged node).
  22240. var newItemsParams;
  22241. dojo.forEach(nodes, function(node, idx){
  22242. // dojo.dnd.Item representing the thing being dropped.
  22243. // Don't confuse the use of item here (meaning a DnD item) with the
  22244. // uses below where item means dojo.data item.
  22245. var sourceItem = source.getItem(node.id);
  22246. // Information that's available if the source is another Tree
  22247. // (possibly but not necessarily this tree, possibly but not
  22248. // necessarily the same model as this Tree)
  22249. if(dojo.indexOf(sourceItem.type, "treeNode") != -1){
  22250. var childTreeNode = sourceItem.data,
  22251. childItem = childTreeNode.item,
  22252. oldParentItem = childTreeNode.getParent().item;
  22253. }
  22254. if(source == this){
  22255. // This is a node from my own tree, and we are moving it, not copying.
  22256. // Remove item from old parent's children attribute.
  22257. // TODO: dijit.tree.dndSelector should implement deleteSelectedNodes()
  22258. // and this code should go there.
  22259. if(typeof insertIndex == "number"){
  22260. if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
  22261. insertIndex -= 1;
  22262. }
  22263. }
  22264. model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
  22265. }else if(model.isItem(childItem)){
  22266. // Item from same model
  22267. // (maybe we should only do this branch if the source is a tree?)
  22268. model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
  22269. }else{
  22270. // Get the hash to pass to model.newItem(). A single call to
  22271. // itemCreator() returns an array of hashes, one for each drag source node.
  22272. if(!newItemsParams){
  22273. newItemsParams = this.itemCreator(nodes, target.rowNode, source);
  22274. }
  22275. // Create new item in the tree, based on the drag source.
  22276. model.newItem(newItemsParams[idx], newParentItem, insertIndex);
  22277. }
  22278. }, this);
  22279. // Expand the target node (if it's currently collapsed) so the user can see
  22280. // where their node was dropped. In particular since that node is still selected.
  22281. this.tree._expandNode(targetWidget);
  22282. }
  22283. this.onDndCancel();
  22284. },
  22285. onDndCancel: function(){
  22286. // summary:
  22287. // Topic event processor for /dnd/cancel, called to cancel the DnD operation
  22288. // tags:
  22289. // private
  22290. this._unmarkTargetAnchor();
  22291. this.isDragging = false;
  22292. this.mouseDown = false;
  22293. delete this.mouseButton;
  22294. this._changeState("Source", "");
  22295. this._changeState("Target", "");
  22296. },
  22297. // When focus moves in/out of the entire Tree
  22298. onOverEvent: function(){
  22299. // summary:
  22300. // This method is called when mouse is moved over our container (like onmouseenter)
  22301. // tags:
  22302. // private
  22303. this.inherited(arguments);
  22304. dojo.dnd.manager().overSource(this);
  22305. },
  22306. onOutEvent: function(){
  22307. // summary:
  22308. // This method is called when mouse is moved out of our container (like onmouseleave)
  22309. // tags:
  22310. // private
  22311. this._unmarkTargetAnchor();
  22312. var m = dojo.dnd.manager();
  22313. if(this.isDragging){
  22314. m.canDrop(false);
  22315. }
  22316. m.outSource(this);
  22317. this.inherited(arguments);
  22318. },
  22319. _isParentChildDrop: function(source, targetRow){
  22320. // summary:
  22321. // Checks whether the dragged items are parent rows in the tree which are being
  22322. // dragged into their own children.
  22323. //
  22324. // source:
  22325. // The DragSource object.
  22326. //
  22327. // targetRow:
  22328. // The tree row onto which the dragged nodes are being dropped.
  22329. //
  22330. // tags:
  22331. // private
  22332. // If the dragged object is not coming from the tree this widget belongs to,
  22333. // it cannot be invalid.
  22334. if(!source.tree || source.tree != this.tree){
  22335. return false;
  22336. }
  22337. var root = source.tree.domNode;
  22338. var ids = source.selection;
  22339. var node = targetRow.parentNode;
  22340. // Iterate up the DOM hierarchy from the target drop row,
  22341. // checking of any of the dragged nodes have the same ID.
  22342. while(node != root && !ids[node.id]){
  22343. node = node.parentNode;
  22344. }
  22345. return node.id && ids[node.id];
  22346. },
  22347. _unmarkTargetAnchor: function(){
  22348. // summary:
  22349. // Removes hover class of the current target anchor
  22350. // tags:
  22351. // private
  22352. if(!this.targetAnchor){ return; }
  22353. this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
  22354. this.targetAnchor = null;
  22355. this.targetBox = null;
  22356. this.dropPosition = null;
  22357. },
  22358. _markDndStatus: function(copy){
  22359. // summary:
  22360. // Changes source's state based on "copy" status
  22361. this._changeState("Source", copy ? "Copied" : "Moved");
  22362. }
  22363. });
  22364. }
  22365. if(!dojo._hasResource["dijit._tree.dndSource"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22366. dojo._hasResource["dijit._tree.dndSource"] = true;
  22367. dojo.provide("dijit._tree.dndSource");
  22368. // TODO: remove this file in 2.0
  22369. dojo.deprecated("dijit._tree.dndSource has been moved to dijit.tree.dndSource, use that instead", "", "2.0");
  22370. dijit._tree.dndSource = dijit.tree.dndSource;
  22371. }
  22372. if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22373. dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
  22374. dojo.provide("dojo.dnd.TimedMoveable");
  22375. /*=====
  22376. dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
  22377. // timeout: Number
  22378. // delay move by this number of ms,
  22379. // accumulating position changes during the timeout
  22380. timeout: 0
  22381. });
  22382. =====*/
  22383. (function(){
  22384. // precalculate long expressions
  22385. var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
  22386. dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
  22387. // summary:
  22388. // A specialized version of Moveable to support an FPS throttling.
  22389. // This class puts an upper restriction on FPS, which may reduce
  22390. // the CPU load. The additional parameter "timeout" regulates
  22391. // the delay before actually moving the moveable object.
  22392. // object attributes (for markup)
  22393. timeout: 40, // in ms, 40ms corresponds to 25 fps
  22394. constructor: function(node, params){
  22395. // summary:
  22396. // an object that makes a node moveable with a timer
  22397. // node: Node||String
  22398. // a node (or node's id) to be moved
  22399. // params: dojo.dnd.__TimedMoveableArgs
  22400. // object with additional parameters.
  22401. // sanitize parameters
  22402. if(!params){ params = {}; }
  22403. if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
  22404. this.timeout = params.timeout;
  22405. }
  22406. },
  22407. // markup methods
  22408. markupFactory: function(params, node){
  22409. return new dojo.dnd.TimedMoveable(node, params);
  22410. },
  22411. onMoveStop: function(/* dojo.dnd.Mover */ mover){
  22412. if(mover._timer){
  22413. // stop timer
  22414. clearTimeout(mover._timer)
  22415. // reflect the last received position
  22416. oldOnMove.call(this, mover, mover._leftTop)
  22417. }
  22418. dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
  22419. },
  22420. onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
  22421. mover._leftTop = leftTop;
  22422. if(!mover._timer){
  22423. var _t = this; // to avoid using dojo.hitch()
  22424. mover._timer = setTimeout(function(){
  22425. // we don't have any pending requests
  22426. mover._timer = null;
  22427. // reflect the last received position
  22428. oldOnMove.call(_t, mover, mover._leftTop);
  22429. }, this.timeout);
  22430. }
  22431. }
  22432. });
  22433. })();
  22434. }
  22435. if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22436. dojo._hasResource["dijit.DialogUnderlay"] = true;
  22437. dojo.provide("dijit.DialogUnderlay");
  22438. dojo.declare(
  22439. "dijit.DialogUnderlay",
  22440. [dijit._Widget, dijit._Templated],
  22441. {
  22442. // summary:
  22443. // The component that blocks the screen behind a `dijit.Dialog`
  22444. //
  22445. // description:
  22446. // A component used to block input behind a `dijit.Dialog`. Only a single
  22447. // instance of this widget is created by `dijit.Dialog`, and saved as
  22448. // a reference to be shared between all Dialogs as `dijit._underlay`
  22449. //
  22450. // The underlay itself can be styled based on and id:
  22451. // | #myDialog_underlay { background-color:red; }
  22452. //
  22453. // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
  22454. // suffixed with _underlay.
  22455. // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
  22456. // Inner div has opacity specified in CSS file.
  22457. templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>",
  22458. // Parameters on creation or updatable later
  22459. // dialogId: String
  22460. // Id of the dialog.... DialogUnderlay's id is based on this id
  22461. dialogId: "",
  22462. // class: String
  22463. // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
  22464. "class": "",
  22465. attributeMap: { id: "domNode" },
  22466. _setDialogIdAttr: function(id){
  22467. dojo.attr(this.node, "id", id + "_underlay");
  22468. this._set("dialogId", id);
  22469. },
  22470. _setClassAttr: function(clazz){
  22471. this.node.className = "dijitDialogUnderlay " + clazz;
  22472. this._set("class", clazz);
  22473. },
  22474. postCreate: function(){
  22475. // summary:
  22476. // Append the underlay to the body
  22477. dojo.body().appendChild(this.domNode);
  22478. },
  22479. layout: function(){
  22480. // summary:
  22481. // Sets the background to the size of the viewport
  22482. //
  22483. // description:
  22484. // Sets the background to the size of the viewport (rather than the size
  22485. // of the document) since we need to cover the whole browser window, even
  22486. // if the document is only a few lines long.
  22487. // tags:
  22488. // private
  22489. var is = this.node.style,
  22490. os = this.domNode.style;
  22491. // hide the background temporarily, so that the background itself isn't
  22492. // causing scrollbars to appear (might happen when user shrinks browser
  22493. // window and then we are called to resize)
  22494. os.display = "none";
  22495. // then resize and show
  22496. var viewport = dojo.window.getBox();
  22497. os.top = viewport.t + "px";
  22498. os.left = viewport.l + "px";
  22499. is.width = viewport.w + "px";
  22500. is.height = viewport.h + "px";
  22501. os.display = "block";
  22502. },
  22503. show: function(){
  22504. // summary:
  22505. // Show the dialog underlay
  22506. this.domNode.style.display = "block";
  22507. this.layout();
  22508. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  22509. },
  22510. hide: function(){
  22511. // summary:
  22512. // Hides the dialog underlay
  22513. this.bgIframe.destroy();
  22514. delete this.bgIframe;
  22515. this.domNode.style.display = "none";
  22516. }
  22517. }
  22518. );
  22519. }
  22520. if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  22521. dojo._hasResource["dijit.Dialog"] = true;
  22522. dojo.provide("dijit.Dialog");
  22523. // dijit/TooltipDialog required for back-compat. TODO: remove in 2.0
  22524. /*=====
  22525. dijit._underlay = function(kwArgs){
  22526. // summary:
  22527. // A shared instance of a `dijit.DialogUnderlay`
  22528. //
  22529. // description:
  22530. // A shared instance of a `dijit.DialogUnderlay` created and
  22531. // used by `dijit.Dialog`, though never created until some Dialog
  22532. // or subclass thereof is shown.
  22533. };
  22534. =====*/
  22535. dojo.declare(
  22536. "dijit._DialogBase",
  22537. [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
  22538. {
  22539. // summary:
  22540. // A modal dialog Widget
  22541. //
  22542. // description:
  22543. // Pops up a modal dialog window, blocking access to the screen
  22544. // and also graying out the screen Dialog is extended from
  22545. // ContentPane so it supports all the same parameters (href, etc.)
  22546. //
  22547. // example:
  22548. // | <div dojoType="dijit.Dialog" href="test.html"></div>
  22549. //
  22550. // example:
  22551. // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
  22552. // | dojo.body().appendChild(foo.domNode);
  22553. // | foo.startup();
  22554. 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"),
  22555. baseClass: "dijitDialog",
  22556. cssStateNodes: {
  22557. closeButtonNode: "dijitDialogCloseIcon"
  22558. },
  22559. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  22560. title: [
  22561. { node: "titleNode", type: "innerHTML" },
  22562. { node: "titleBar", type: "attribute" }
  22563. ],
  22564. "aria-describedby":""
  22565. }),
  22566. // open: [readonly] Boolean
  22567. // True if Dialog is currently displayed on screen.
  22568. open: false,
  22569. // duration: Integer
  22570. // The time in milliseconds it takes the dialog to fade in and out
  22571. duration: dijit.defaultDuration,
  22572. // refocus: Boolean
  22573. // A Toggle to modify the default focus behavior of a Dialog, which
  22574. // is to re-focus the element which had focus before being opened.
  22575. // False will disable refocusing. Default: true
  22576. refocus: true,
  22577. // autofocus: Boolean
  22578. // A Toggle to modify the default focus behavior of a Dialog, which
  22579. // is to focus on the first dialog element after opening the dialog.
  22580. // False will disable autofocusing. Default: true
  22581. autofocus: true,
  22582. // _firstFocusItem: [private readonly] DomNode
  22583. // The pointer to the first focusable node in the dialog.
  22584. // Set by `dijit._DialogMixin._getFocusItems`.
  22585. _firstFocusItem: null,
  22586. // _lastFocusItem: [private readonly] DomNode
  22587. // The pointer to which node has focus prior to our dialog.
  22588. // Set by `dijit._DialogMixin._getFocusItems`.
  22589. _lastFocusItem: null,
  22590. // doLayout: [protected] Boolean
  22591. // Don't change this parameter from the default value.
  22592. // This ContentPane parameter doesn't make sense for Dialog, since Dialog
  22593. // is never a child of a layout container, nor can you specify the size of
  22594. // Dialog in order to control the size of an inner widget.
  22595. doLayout: false,
  22596. // draggable: Boolean
  22597. // Toggles the moveable aspect of the Dialog. If true, Dialog
  22598. // can be dragged by it's title. If false it will remain centered
  22599. // in the viewport.
  22600. draggable: true,
  22601. //aria-describedby: String
  22602. // Allows the user to add an aria-describedby attribute onto the dialog. The value should
  22603. // be the id of the container element of text that describes the dialog purpose (usually
  22604. // the first text in the dialog).
  22605. // <div dojoType="dijit.Dialog" aria-describedby="intro" .....>
  22606. // <div id="intro">Introductory text</div>
  22607. // <div>rest of dialog contents</div>
  22608. // </div>
  22609. "aria-describedby":"",
  22610. postMixInProperties: function(){
  22611. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  22612. dojo.mixin(this, _nlsResources);
  22613. this.inherited(arguments);
  22614. },
  22615. postCreate: function(){
  22616. dojo.style(this.domNode, {
  22617. display: "none",
  22618. position:"absolute"
  22619. });
  22620. dojo.body().appendChild(this.domNode);
  22621. this.inherited(arguments);
  22622. this.connect(this, "onExecute", "hide");
  22623. this.connect(this, "onCancel", "hide");
  22624. this._modalconnects = [];
  22625. },
  22626. onLoad: function(){
  22627. // summary:
  22628. // Called when data has been loaded from an href.
  22629. // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
  22630. // but should *not* be overridden.
  22631. // tags:
  22632. // callback
  22633. // when href is specified we need to reposition the dialog after the data is loaded
  22634. // and find the focusable elements
  22635. this._position();
  22636. if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
  22637. this._getFocusItems(this.domNode);
  22638. dijit.focus(this._firstFocusItem);
  22639. }
  22640. this.inherited(arguments);
  22641. },
  22642. _endDrag: function(){
  22643. // summary:
  22644. // Called after dragging the Dialog. Saves the position of the dialog in the viewport
  22645. // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
  22646. // tags:
  22647. // private
  22648. var nodePosition = dojo.position(this.domNode),
  22649. viewport = dojo.window.getBox();
  22650. nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
  22651. nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
  22652. this._relativePosition = nodePosition;
  22653. this._position();
  22654. },
  22655. _setup: function(){
  22656. // summary:
  22657. // Stuff we need to do before showing the Dialog for the first
  22658. // time (but we defer it until right beforehand, for
  22659. // performance reasons).
  22660. // tags:
  22661. // private
  22662. var node = this.domNode;
  22663. if(this.titleBar && this.draggable){
  22664. this._moveable = (dojo.isIE == 6) ?
  22665. new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) : // prevent overload, see #5285
  22666. new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
  22667. this.connect(this._moveable, "onMoveStop", "_endDrag");
  22668. }else{
  22669. dojo.addClass(node,"dijitDialogFixed");
  22670. }
  22671. this.underlayAttrs = {
  22672. dialogId: this.id,
  22673. "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
  22674. };
  22675. },
  22676. _size: function(){
  22677. // summary:
  22678. // If necessary, shrink dialog contents so dialog fits in viewport
  22679. // tags:
  22680. // private
  22681. this._checkIfSingleChild();
  22682. // If we resized the dialog contents earlier, reset them back to original size, so
  22683. // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
  22684. // Need to do this before the dojo.marginBox(this.domNode) call below.
  22685. if(this._singleChild){
  22686. if(this._singleChildOriginalStyle){
  22687. this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
  22688. }
  22689. delete this._singleChildOriginalStyle;
  22690. }else{
  22691. dojo.style(this.containerNode, {
  22692. width:"auto",
  22693. height:"auto"
  22694. });
  22695. }
  22696. var mb = dojo._getMarginSize(this.domNode);
  22697. var viewport = dojo.window.getBox();
  22698. if(mb.w >= viewport.w || mb.h >= viewport.h){
  22699. // Reduce size of dialog contents so that dialog fits in viewport
  22700. var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
  22701. h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
  22702. if(this._singleChild && this._singleChild.resize){
  22703. this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
  22704. this._singleChild.resize({w: w, h: h});
  22705. }else{
  22706. dojo.style(this.containerNode, {
  22707. width: w + "px",
  22708. height: h + "px",
  22709. overflow: "auto",
  22710. position: "relative" // workaround IE bug moving scrollbar or dragging dialog
  22711. });
  22712. }
  22713. }else{
  22714. if(this._singleChild && this._singleChild.resize){
  22715. this._singleChild.resize();
  22716. }
  22717. }
  22718. },
  22719. _position: function(){
  22720. // summary:
  22721. // Position modal dialog in the viewport. If no relative offset
  22722. // in the viewport has been determined (by dragging, for instance),
  22723. // center the node. Otherwise, use the Dialog's stored relative offset,
  22724. // and position the node to top: left: values based on the viewport.
  22725. // tags:
  22726. // private
  22727. if(!dojo.hasClass(dojo.body(), "dojoMove")){ // don't do anything if called during auto-scroll
  22728. var node = this.domNode,
  22729. viewport = dojo.window.getBox(),
  22730. p = this._relativePosition,
  22731. bb = p ? null : dojo._getBorderBox(node),
  22732. l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
  22733. t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
  22734. ;
  22735. dojo.style(node,{
  22736. left: l + "px",
  22737. top: t + "px"
  22738. });
  22739. }
  22740. },
  22741. _onKey: function(/*Event*/ evt){
  22742. // summary:
  22743. // Handles the keyboard events for accessibility reasons
  22744. // tags:
  22745. // private
  22746. if(evt.charOrCode){
  22747. var dk = dojo.keys;
  22748. var node = evt.target;
  22749. if(evt.charOrCode === dk.TAB){
  22750. this._getFocusItems(this.domNode);
  22751. }
  22752. var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
  22753. // see if we are shift-tabbing from first focusable item on dialog
  22754. if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
  22755. if(!singleFocusItem){
  22756. dijit.focus(this._lastFocusItem); // send focus to last item in dialog
  22757. }
  22758. dojo.stopEvent(evt);
  22759. }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
  22760. if(!singleFocusItem){
  22761. dijit.focus(this._firstFocusItem); // send focus to first item in dialog
  22762. }
  22763. dojo.stopEvent(evt);
  22764. }else{
  22765. // see if the key is for the dialog
  22766. while(node){
  22767. if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
  22768. if(evt.charOrCode == dk.ESCAPE){
  22769. this.onCancel();
  22770. }else{
  22771. return; // just let it go
  22772. }
  22773. }
  22774. node = node.parentNode;
  22775. }
  22776. // this key is for the disabled document window
  22777. if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
  22778. dojo.stopEvent(evt);
  22779. // opera won't tab to a div
  22780. }else if(!dojo.isOpera){
  22781. try{
  22782. this._firstFocusItem.focus();
  22783. }catch(e){ /*squelch*/ }
  22784. }
  22785. }
  22786. }
  22787. },
  22788. show: function(){
  22789. // summary:
  22790. // Display the dialog
  22791. // returns: dojo.Deferred
  22792. // Deferred object that resolves when the display animation is complete
  22793. if(this.open){ return; }
  22794. if(!this._started){
  22795. this.startup();
  22796. }
  22797. // first time we show the dialog, there's some initialization stuff to do
  22798. if(!this._alreadyInitialized){
  22799. this._setup();
  22800. this._alreadyInitialized=true;
  22801. }
  22802. if(this._fadeOutDeferred){
  22803. // There's a hide() operation in progress, so cancel it, but still call DialogLevelManager.hide()
  22804. // as though the hide() completed, in preparation for the DialogLevelManager.show() call below.
  22805. this._fadeOutDeferred.cancel();
  22806. dijit._DialogLevelManager.hide(this);
  22807. }
  22808. this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
  22809. this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
  22810. // IE gives spurious resize events and can actually get stuck
  22811. // in an infinite loop if we don't ignore them
  22812. var viewport = dojo.window.getBox();
  22813. if(!this._oldViewport ||
  22814. viewport.h != this._oldViewport.h ||
  22815. viewport.w != this._oldViewport.w){
  22816. this.layout();
  22817. this._oldViewport = viewport;
  22818. }
  22819. }));
  22820. this._modalconnects.push(dojo.connect(this.domNode, "onkeypress", this, "_onKey"));
  22821. dojo.style(this.domNode, {
  22822. opacity:0,
  22823. display:""
  22824. });
  22825. this._set("open", true);
  22826. this._onShow(); // lazy load trigger
  22827. this._size();
  22828. this._position();
  22829. // fade-in Animation object, setup below
  22830. var fadeIn;
  22831. this._fadeInDeferred = new dojo.Deferred(dojo.hitch(this, function(){
  22832. fadeIn.stop();
  22833. delete this._fadeInDeferred;
  22834. }));
  22835. fadeIn = dojo.fadeIn({
  22836. node: this.domNode,
  22837. duration: this.duration,
  22838. beforeBegin: dojo.hitch(this, function(){
  22839. dijit._DialogLevelManager.show(this, this.underlayAttrs);
  22840. }),
  22841. onEnd: dojo.hitch(this, function(){
  22842. if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
  22843. // find focusable items each time dialog is shown since if dialog contains a widget the
  22844. // first focusable items can change
  22845. this._getFocusItems(this.domNode);
  22846. dijit.focus(this._firstFocusItem);
  22847. }
  22848. this._fadeInDeferred.callback(true);
  22849. delete this._fadeInDeferred;
  22850. })
  22851. }).play();
  22852. return this._fadeInDeferred;
  22853. },
  22854. hide: function(){
  22855. // summary:
  22856. // Hide the dialog
  22857. // returns: dojo.Deferred
  22858. // Deferred object that resolves when the hide animation is complete
  22859. // If we haven't been initialized yet then we aren't showing and we can just return.
  22860. // Likewise if we are already hidden, or are currently fading out.
  22861. if(!this._alreadyInitialized || !this.open){
  22862. return;
  22863. }
  22864. if(this._fadeInDeferred){
  22865. this._fadeInDeferred.cancel();
  22866. }
  22867. // fade-in Animation object, setup below
  22868. var fadeOut;
  22869. this._fadeOutDeferred = new dojo.Deferred(dojo.hitch(this, function(){
  22870. fadeOut.stop();
  22871. delete this._fadeOutDeferred;
  22872. }));
  22873. fadeOut = dojo.fadeOut({
  22874. node: this.domNode,
  22875. duration: this.duration,
  22876. onEnd: dojo.hitch(this, function(){
  22877. this.domNode.style.display = "none";
  22878. dijit._DialogLevelManager.hide(this);
  22879. this.onHide();
  22880. this._fadeOutDeferred.callback(true);
  22881. delete this._fadeOutDeferred;
  22882. })
  22883. }).play();
  22884. if(this._scrollConnected){
  22885. this._scrollConnected = false;
  22886. }
  22887. dojo.forEach(this._modalconnects, dojo.disconnect);
  22888. this._modalconnects = [];
  22889. if(this._relativePosition){
  22890. delete this._relativePosition;
  22891. }
  22892. this._set("open", false);
  22893. return this._fadeOutDeferred;
  22894. },
  22895. layout: function(){
  22896. // summary:
  22897. // Position the Dialog and the underlay
  22898. // tags:
  22899. // private
  22900. if(this.domNode.style.display != "none"){
  22901. if(dijit._underlay){ // avoid race condition during show()
  22902. dijit._underlay.layout();
  22903. }
  22904. this._position();
  22905. }
  22906. },
  22907. destroy: function(){
  22908. if(this._fadeInDeferred){
  22909. this._fadeInDeferred.cancel();
  22910. }
  22911. if(this._fadeOutDeferred){
  22912. this._fadeOutDeferred.cancel();
  22913. }
  22914. if(this._moveable){
  22915. this._moveable.destroy();
  22916. }
  22917. dojo.forEach(this._modalconnects, dojo.disconnect);
  22918. dijit._DialogLevelManager.hide(this);
  22919. this.inherited(arguments);
  22920. }
  22921. }
  22922. );
  22923. dojo.declare(
  22924. "dijit.Dialog",
  22925. [dijit.layout.ContentPane, dijit._DialogBase],
  22926. {}
  22927. );
  22928. dijit._DialogLevelManager = {
  22929. // summary:
  22930. // Controls the various active "levels" on the page, starting with the
  22931. // stuff initially visible on the page (at z-index 0), and then having an entry for
  22932. // each Dialog shown.
  22933. show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
  22934. // summary:
  22935. // Call right before fade-in animation for new dialog.
  22936. // Saves current focus, displays/adjusts underlay for new dialog,
  22937. // and sets the z-index of the dialog itself.
  22938. //
  22939. // New dialog will be displayed on top of all currently displayed dialogs.
  22940. //
  22941. // Caller is responsible for setting focus in new dialog after the fade-in
  22942. // animation completes.
  22943. var ds = dijit._dialogStack;
  22944. // Save current focus
  22945. ds[ds.length-1].focus = dijit.getFocus(dialog);
  22946. // Display the underlay, or if already displayed then adjust for this new dialog
  22947. var underlay = dijit._underlay;
  22948. if(!underlay || underlay._destroyed){
  22949. underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs);
  22950. }else{
  22951. underlay.set(dialog.underlayAttrs);
  22952. }
  22953. // Set z-index a bit above previous dialog
  22954. var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : 950;
  22955. if(ds.length == 1){ // first dialog
  22956. underlay.show();
  22957. }
  22958. dojo.style(dijit._underlay.domNode, 'zIndex', zIndex - 1);
  22959. // Dialog
  22960. dojo.style(dialog.domNode, 'zIndex', zIndex);
  22961. ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
  22962. },
  22963. hide: function(/*dijit._Widget*/ dialog){
  22964. // summary:
  22965. // Called when the specified dialog is hidden/destroyed, after the fade-out
  22966. // animation ends, in order to reset page focus, fix the underlay, etc.
  22967. // If the specified dialog isn't open then does nothing.
  22968. //
  22969. // Caller is responsible for either setting display:none on the dialog domNode,
  22970. // or calling dijit.popup.hide(), or removing it from the page DOM.
  22971. var ds = dijit._dialogStack;
  22972. if(ds[ds.length-1].dialog == dialog){
  22973. // Removing the top (or only) dialog in the stack, return focus
  22974. // to previous dialog
  22975. ds.pop();
  22976. var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
  22977. // Adjust underlay
  22978. if(ds.length == 1){
  22979. // Returning to original page.
  22980. // Hide the underlay, unless the underlay widget has already been destroyed
  22981. // because we are being called during page unload (when all widgets are destroyed)
  22982. if(!dijit._underlay._destroyed){
  22983. dijit._underlay.hide();
  22984. }
  22985. }else{
  22986. // Popping back to previous dialog, adjust underlay
  22987. dojo.style(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
  22988. dijit._underlay.set(pd.underlayAttrs);
  22989. }
  22990. // Adjust focus
  22991. if(dialog.refocus){
  22992. // If we are returning control to a previous dialog but for some reason
  22993. // that dialog didn't have a focused field, set focus to first focusable item.
  22994. // This situation could happen if two dialogs appeared at nearly the same time,
  22995. // since a dialog doesn't set it's focus until the fade-in is finished.
  22996. var focus = pd.focus;
  22997. if(!focus || (pd.dialog && !dojo.isDescendant(focus.node, pd.dialog.domNode))){
  22998. pd.dialog._getFocusItems(pd.dialog.domNode);
  22999. focus = pd.dialog._firstFocusItem;
  23000. }
  23001. try{
  23002. dijit.focus(focus);
  23003. }catch(e){
  23004. /* focus() will fail if user opened the dialog by clicking a non-focusable element */
  23005. }
  23006. }
  23007. }else{
  23008. // Removing a dialog out of order (#9944, #10705).
  23009. // Don't need to mess with underlay or z-index or anything.
  23010. var idx = dojo.indexOf(dojo.map(ds, function(elem){return elem.dialog}), dialog);
  23011. if(idx != -1){
  23012. ds.splice(idx, 1);
  23013. }
  23014. }
  23015. },
  23016. isTop: function(/*dijit._Widget*/ dialog){
  23017. // summary:
  23018. // Returns true if specified Dialog is the top in the task
  23019. var ds = dijit._dialogStack;
  23020. return ds[ds.length-1].dialog == dialog;
  23021. }
  23022. };
  23023. // Stack representing the various active "levels" on the page, starting with the
  23024. // stuff initially visible on the page (at z-index 0), and then having an entry for
  23025. // each Dialog shown.
  23026. // Each element in stack has form {
  23027. // dialog: dialogWidget,
  23028. // focus: returnFromGetFocus(),
  23029. // underlayAttrs: attributes to set on underlay (when this widget is active)
  23030. // }
  23031. dijit._dialogStack = [
  23032. {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
  23033. ];
  23034. }
  23035. if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23036. dojo._hasResource["dijit.dijit"] = true;
  23037. dojo.provide("dijit.dijit");
  23038. /*=====
  23039. dijit.dijit = {
  23040. // summary:
  23041. // A roll-up for common dijit methods
  23042. // description:
  23043. // A rollup file for the build system including the core and common
  23044. // dijit files.
  23045. //
  23046. // example:
  23047. // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script>
  23048. //
  23049. };
  23050. =====*/
  23051. // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
  23052. // And some other stuff that we tend to pull in all the time anyway
  23053. }
  23054. if(!dojo._hasResource["dijit._editor.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23055. dojo._hasResource["dijit._editor.html"] = true;
  23056. dojo.provide("dijit._editor.html");
  23057. var exports = dojo.getObject("_editor", true, dijit);
  23058. var escape = exports.escapeXml=function(/*String*/str, /*Boolean?*/noSingleQuotes){
  23059. // summary:
  23060. // Adds escape sequences for special characters in XML: &<>"'
  23061. // Optionally skips escapes for single quotes
  23062. str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  23063. if(!noSingleQuotes){
  23064. str = str.replace(/'/gm, "&#39;");
  23065. }
  23066. return str; // string
  23067. };
  23068. exports.getNodeHtml = function(/*DomNode*/ node){
  23069. // summary:
  23070. // Return string representing HTML for node and it's children
  23071. var output = [];
  23072. exports.getNodeHtmlHelper(node, output);
  23073. return output.join("");
  23074. };
  23075. exports.getNodeHtmlHelper = function(/*DomNode*/ node, /*String[]*/ output){
  23076. // summary:
  23077. // Pushes array of strings into output[] which represent HTML for node and it's children
  23078. switch(node.nodeType){
  23079. case 1: //element node
  23080. var lName = node.nodeName.toLowerCase();
  23081. if(!lName || lName.charAt(0) == "/"){
  23082. // IE does some strange things with malformed HTML input, like
  23083. // treating a close tag </span> without an open tag <span>, as
  23084. // a new tag with tagName of /span. Corrupts output HTML, remove
  23085. // them. Other browsers don't prefix tags that way, so will
  23086. // never show up.
  23087. return "";
  23088. }
  23089. output.push('<', lName);
  23090. //store the list of attributes and sort it to have the
  23091. //attributes appear in the dictionary order
  23092. var attrarray = [];
  23093. var attr;
  23094. if(dojo.isIE < 9){
  23095. var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false);
  23096. var s = clone.outerHTML;
  23097. s = s.substr(0, s.indexOf('>'))
  23098. .replace(/(['"])[^"']*\1/g, ''); //to make the following regexp safe
  23099. var reg = /(\b\w+)\s?=/g;
  23100. var m, key;
  23101. while((m = reg.exec(s))){
  23102. key = m[1];
  23103. if(key.substr(0,3) != '_dj'){
  23104. if(key == 'src' || key == 'href'){
  23105. if(node.getAttribute('_djrealurl')){
  23106. attrarray.push([key,node.getAttribute('_djrealurl')]);
  23107. continue;
  23108. }
  23109. }
  23110. var val, match;
  23111. switch(key){
  23112. case 'style':
  23113. val = node.style.cssText.toLowerCase();
  23114. break;
  23115. case 'class':
  23116. val = node.className;
  23117. break;
  23118. case 'width':
  23119. if(lName === "img"){
  23120. // This somehow gets lost on IE for IMG tags and the like
  23121. // and we have to find it in outerHTML, known IE oddity.
  23122. match=/width=(\S+)/i.exec(s);
  23123. if(match){
  23124. val = match[1];
  23125. }
  23126. break;
  23127. }
  23128. case 'height':
  23129. if(lName === "img"){
  23130. // This somehow gets lost on IE for IMG tags and the like
  23131. // and we have to find it in outerHTML, known IE oddity.
  23132. match=/height=(\S+)/i.exec(s);
  23133. if(match){
  23134. val = match[1];
  23135. }
  23136. break;
  23137. }
  23138. default:
  23139. val = node.getAttribute(key);
  23140. }
  23141. if(val != null){
  23142. attrarray.push([key, val.toString()]);
  23143. }
  23144. }
  23145. }
  23146. }else{
  23147. var i = 0;
  23148. while((attr = node.attributes[i++])){
  23149. //ignore all attributes starting with _dj which are
  23150. //internal temporary attributes used by the editor
  23151. var n = attr.name;
  23152. if(n.substr(0,3) != '_dj' /*&&
  23153. (attr.specified == undefined || attr.specified)*/){
  23154. var v = attr.value;
  23155. if(n == 'src' || n == 'href'){
  23156. if(node.getAttribute('_djrealurl')){
  23157. v = node.getAttribute('_djrealurl');
  23158. }
  23159. }
  23160. attrarray.push([n,v]);
  23161. }
  23162. }
  23163. }
  23164. attrarray.sort(function(a,b){
  23165. return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1);
  23166. });
  23167. var j = 0;
  23168. while((attr = attrarray[j++])){
  23169. output.push(' ', attr[0], '="',
  23170. (dojo.isString(attr[1]) ? escape(attr[1], true) : attr[1]), '"');
  23171. }
  23172. switch(lName){
  23173. case 'br':
  23174. case 'hr':
  23175. case 'img':
  23176. case 'input':
  23177. case 'base':
  23178. case 'meta':
  23179. case 'area':
  23180. case 'basefont':
  23181. // These should all be singly closed
  23182. output.push(' />');
  23183. break;
  23184. case 'script':
  23185. // Browsers handle script tags differently in how you get content,
  23186. // but innerHTML always seems to work, so insert its content that way
  23187. // Yes, it's bad to allow script tags in the editor code, but some people
  23188. // seem to want to do it, so we need to at least return them right.
  23189. // other plugins/filters can strip them.
  23190. output.push('>', node.innerHTML, '</', lName, '>');
  23191. break;
  23192. default:
  23193. output.push('>');
  23194. if(node.hasChildNodes()){
  23195. exports.getChildrenHtmlHelper(node, output);
  23196. }
  23197. output.push('</', lName, '>');
  23198. }
  23199. break;
  23200. case 4: // cdata
  23201. case 3: // text
  23202. // FIXME:
  23203. output.push(escape(node.nodeValue, true));
  23204. break;
  23205. case 8: //comment
  23206. // FIXME:
  23207. output.push('<!--', escape(node.nodeValue, true), '-->');
  23208. break;
  23209. default:
  23210. output.push("<!-- Element not recognized - Type: ", node.nodeType, " Name: ", node.nodeName, "-->");
  23211. }
  23212. };
  23213. exports.getChildrenHtml = function(/*DomNode*/ node){
  23214. // summary:
  23215. // Returns the html content of a DomNode's children
  23216. var output = [];
  23217. exports.getChildrenHtmlHelper(node, output);
  23218. return output.join("");
  23219. };
  23220. exports.getChildrenHtmlHelper = function(/*DomNode*/ dom, /*String[]*/ output){
  23221. // summary:
  23222. // Pushes the html content of a DomNode's children into out[]
  23223. if(!dom){ return; }
  23224. var nodes = dom["childNodes"] || dom;
  23225. //IE issue.
  23226. //If we have an actual node we can check parent relationships on for IE,
  23227. //We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check
  23228. //And should just process it and hope for the best.
  23229. var checkParent = !dojo.isIE || nodes !== dom;
  23230. var node, i = 0;
  23231. while((node = nodes[i++])){
  23232. //IE is broken. DOMs are supposed to be a tree. But in the case of malformed HTML, IE generates a graph
  23233. //meaning one node ends up with multiple references (multiple parents). This is totally wrong and invalid, but
  23234. //such is what it is. We have to keep track and check for this because otherwise the source output HTML will have dups.
  23235. //No other browser generates a graph. Leave it to IE to break a fundamental DOM rule. So, we check the parent if we can
  23236. //If we can't, nothing more we can do other than walk it.
  23237. if(!checkParent || node.parentNode == dom){
  23238. exports.getNodeHtmlHelper(node, output);
  23239. }
  23240. }
  23241. };
  23242. }
  23243. if(!dojo._hasResource["dijit._editor.RichText"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  23244. dojo._hasResource["dijit._editor.RichText"] = true;
  23245. dojo.provide("dijit._editor.RichText");
  23246. // used to restore content when user leaves this page then comes back
  23247. // but do not try doing dojo.doc.write if we are using xd loading.
  23248. // dojo.doc.write will only work if RichText.js is included in the dojo.js
  23249. // file. If it is included in dojo.js and you want to allow rich text saving
  23250. // for back/forward actions, then set dojo.config.allowXdRichTextSave = true.
  23251. if(!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"]){
  23252. if(dojo._postLoad){
  23253. (function(){
  23254. var savetextarea = dojo.doc.createElement('textarea');
  23255. savetextarea.id = dijit._scopeName + "._editor.RichText.value";
  23256. dojo.style(savetextarea, {
  23257. display:'none',
  23258. position:'absolute',
  23259. top:"-100px",
  23260. height:"3px",
  23261. width:"3px"
  23262. });
  23263. dojo.body().appendChild(savetextarea);
  23264. })();
  23265. }else{
  23266. //dojo.body() is not available before onLoad is fired
  23267. try{
  23268. dojo.doc.write('<textarea id="' + dijit._scopeName + '._editor.RichText.value" ' +
  23269. 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>');
  23270. }catch(e){ }
  23271. }
  23272. }
  23273. dojo.declare("dijit._editor.RichText", [dijit._Widget, dijit._CssStateMixin], {
  23274. constructor: function(params){
  23275. // summary:
  23276. // dijit._editor.RichText is the core of dijit.Editor, which provides basic
  23277. // WYSIWYG editing features.
  23278. //
  23279. // description:
  23280. // dijit._editor.RichText is the core of dijit.Editor, which provides basic
  23281. // WYSIWYG editing features. It also encapsulates the differences
  23282. // of different js engines for various browsers. Do not use this widget
  23283. // with an HTML &lt;TEXTAREA&gt; tag, since the browser unescapes XML escape characters,
  23284. // like &lt;. This can have unexpected behavior and lead to security issues
  23285. // such as scripting attacks.
  23286. //
  23287. // tags:
  23288. // private
  23289. // contentPreFilters: Function(String)[]
  23290. // Pre content filter function register array.
  23291. // these filters will be executed before the actual
  23292. // editing area gets the html content.
  23293. this.contentPreFilters = [];
  23294. // contentPostFilters: Function(String)[]
  23295. // post content filter function register array.
  23296. // These will be used on the resulting html
  23297. // from contentDomPostFilters. The resulting
  23298. // content is the final html (returned by getValue()).
  23299. this.contentPostFilters = [];
  23300. // contentDomPreFilters: Function(DomNode)[]
  23301. // Pre content dom filter function register array.
  23302. // These filters are applied after the result from
  23303. // contentPreFilters are set to the editing area.
  23304. this.contentDomPreFilters = [];
  23305. // contentDomPostFilters: Function(DomNode)[]
  23306. // Post content dom filter function register array.
  23307. // These filters are executed on the editing area dom.
  23308. // The result from these will be passed to contentPostFilters.
  23309. this.contentDomPostFilters = [];
  23310. // editingAreaStyleSheets: dojo._URL[]
  23311. // array to store all the stylesheets applied to the editing area
  23312. this.editingAreaStyleSheets = [];
  23313. // Make a copy of this.events before we start writing into it, otherwise we
  23314. // will modify the prototype which leads to bad things on pages w/multiple editors
  23315. this.events = [].concat(this.events);
  23316. this._keyHandlers = {};
  23317. if(params && dojo.isString(params.value)){
  23318. this.value = params.value;
  23319. }
  23320. this.onLoadDeferred = new dojo.Deferred();
  23321. },
  23322. baseClass: "dijitEditor",
  23323. // inheritWidth: Boolean
  23324. // whether to inherit the parent's width or simply use 100%
  23325. inheritWidth: false,
  23326. // focusOnLoad: [deprecated] Boolean
  23327. // Focus into this widget when the page is loaded
  23328. focusOnLoad: false,
  23329. // name: String?
  23330. // Specifies the name of a (hidden) <textarea> node on the page that's used to save
  23331. // the editor content on page leave. Used to restore editor contents after navigating
  23332. // to a new page and then hitting the back button.
  23333. name: "",
  23334. // styleSheets: [const] String
  23335. // semicolon (";") separated list of css files for the editing area
  23336. styleSheets: "",
  23337. // height: String
  23338. // Set height to fix the editor at a specific height, with scrolling.
  23339. // By default, this is 300px. If you want to have the editor always
  23340. // resizes to accommodate the content, use AlwaysShowToolbar plugin
  23341. // and set height="". If this editor is used within a layout widget,
  23342. // set height="100%".
  23343. height: "300px",
  23344. // minHeight: String
  23345. // The minimum height that the editor should have.
  23346. minHeight: "1em",
  23347. // isClosed: [private] Boolean
  23348. isClosed: true,
  23349. // isLoaded: [private] Boolean
  23350. isLoaded: false,
  23351. // _SEPARATOR: [private] String
  23352. // Used to concat contents from multiple editors into a single string,
  23353. // so they can be saved into a single <textarea> node. See "name" attribute.
  23354. _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
  23355. // _NAME_CONTENT_SEP: [private] String
  23356. // USed to separate name from content. Just a colon isn't safe.
  23357. _NAME_CONTENT_SEP: "@@**%%:%%**@@",
  23358. // onLoadDeferred: [readonly] dojo.Deferred
  23359. // Deferred which is fired when the editor finishes loading.
  23360. // Call myEditor.onLoadDeferred.then(callback) it to be informed
  23361. // when the rich-text area initialization is finalized.
  23362. onLoadDeferred: null,
  23363. // isTabIndent: Boolean
  23364. // Make tab key and shift-tab indent and outdent rather than navigating.
  23365. // Caution: sing this makes web pages inaccessible to users unable to use a mouse.
  23366. isTabIndent: false,
  23367. // disableSpellCheck: [const] Boolean
  23368. // When true, disables the browser's native spell checking, if supported.
  23369. // Works only in Firefox.
  23370. disableSpellCheck: false,
  23371. postCreate: function(){
  23372. if("textarea" == this.domNode.tagName.toLowerCase()){
  23373. console.warn("RichText should not be used with the TEXTAREA tag. See dijit._editor.RichText docs.");
  23374. }
  23375. // Push in the builtin filters now, making them the first executed, but not over-riding anything
  23376. // users passed in. See: #6062
  23377. this.contentPreFilters = [dojo.hitch(this, "_preFixUrlAttributes")].concat(this.contentPreFilters);
  23378. if(dojo.isMoz){
  23379. this.contentPreFilters = [this._normalizeFontStyle].concat(this.contentPreFilters);
  23380. this.contentPostFilters = [this._removeMozBogus].concat(this.contentPostFilters);
  23381. }
  23382. if(dojo.isWebKit){
  23383. // Try to clean up WebKit bogus artifacts. The inserted classes
  23384. // made by WebKit sometimes messes things up.
  23385. this.contentPreFilters = [this._removeWebkitBogus].concat(this.contentPreFilters);
  23386. this.contentPostFilters = [this._removeWebkitBogus].concat(this.contentPostFilters);
  23387. }
  23388. if(dojo.isIE){
  23389. // IE generates <strong> and <em> but we want to normalize to <b> and <i>
  23390. this.contentPostFilters = [this._normalizeFontStyle].concat(this.contentPostFilters);
  23391. }
  23392. this.inherited(arguments);
  23393. dojo.publish(dijit._scopeName + "._editor.RichText::init", [this]);
  23394. this.open();
  23395. this.setupDefaultShortcuts();
  23396. },
  23397. setupDefaultShortcuts: function(){
  23398. // summary:
  23399. // Add some default key handlers
  23400. // description:
  23401. // Overwrite this to setup your own handlers. The default
  23402. // implementation does not use Editor commands, but directly
  23403. // executes the builtin commands within the underlying browser
  23404. // support.
  23405. // tags:
  23406. // protected
  23407. var exec = dojo.hitch(this, function(cmd, arg){
  23408. return function(){
  23409. return !this.execCommand(cmd,arg);
  23410. };
  23411. });
  23412. var ctrlKeyHandlers = {
  23413. b: exec("bold"),
  23414. i: exec("italic"),
  23415. u: exec("underline"),
  23416. a: exec("selectall"),
  23417. s: function(){ this.save(true); },
  23418. m: function(){ this.isTabIndent = !this.isTabIndent; },
  23419. "1": exec("formatblock", "h1"),
  23420. "2": exec("formatblock", "h2"),
  23421. "3": exec("formatblock", "h3"),
  23422. "4": exec("formatblock", "h4"),
  23423. "\\": exec("insertunorderedlist")
  23424. };
  23425. if(!dojo.isIE){
  23426. ctrlKeyHandlers.Z = exec("redo"); //FIXME: undo?
  23427. }
  23428. for(var key in ctrlKeyHandlers){
  23429. this.addKeyHandler(key, true, false, ctrlKeyHandlers[key]);
  23430. }
  23431. },
  23432. // events: [private] String[]
  23433. // events which should be connected to the underlying editing area
  23434. events: ["onKeyPress", "onKeyDown", "onKeyUp"], // onClick handled specially
  23435. // captureEvents: [deprecated] String[]
  23436. // Events which should be connected to the underlying editing
  23437. // area, events in this array will be addListener with
  23438. // capture=true.
  23439. // TODO: looking at the code I don't see any distinction between events and captureEvents,
  23440. // so get rid of this for 2.0 if not sooner
  23441. captureEvents: [],
  23442. _editorCommandsLocalized: false,
  23443. _localizeEditorCommands: function(){
  23444. // summary:
  23445. // When IE is running in a non-English locale, the API actually changes,
  23446. // so that we have to say (for example) danraku instead of p (for paragraph).
  23447. // Handle that here.
  23448. // tags:
  23449. // private
  23450. if(dijit._editor._editorCommandsLocalized){
  23451. // Use the already generate cache of mappings.
  23452. this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames;
  23453. this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames;
  23454. return;
  23455. }
  23456. dijit._editor._editorCommandsLocalized = true;
  23457. dijit._editor._local2NativeFormatNames = {};
  23458. dijit._editor._native2LocalFormatNames = {};
  23459. this._local2NativeFormatNames = dijit._editor._local2NativeFormatNames;
  23460. this._native2LocalFormatNames = dijit._editor._native2LocalFormatNames;
  23461. //in IE, names for blockformat is locale dependent, so we cache the values here
  23462. //put p after div, so if IE returns Normal, we show it as paragraph
  23463. //We can distinguish p and div if IE returns Normal, however, in order to detect that,
  23464. //we have to call this.document.selection.createRange().parentElement() or such, which
  23465. //could slow things down. Leave it as it is for now
  23466. var formats = ['div', 'p', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'address'];
  23467. var localhtml = "", format, i=0;
  23468. while((format=formats[i++])){
  23469. //append a <br> after each element to separate the elements more reliably
  23470. if(format.charAt(1) !== 'l'){
  23471. localhtml += "<"+format+"><span>content</span></"+format+"><br/>";
  23472. }else{
  23473. localhtml += "<"+format+"><li>content</li></"+format+"><br/>";
  23474. }
  23475. }
  23476. // queryCommandValue returns empty if we hide editNode, so move it out of screen temporary
  23477. // Also, IE9 does weird stuff unless we do it inside the editor iframe.
  23478. var style = { position: "absolute", top: "0px", zIndex: 10, opacity: 0.01 };
  23479. var div = dojo.create('div', {style: style, innerHTML: localhtml});
  23480. dojo.body().appendChild(div);
  23481. // IE9 has a timing issue with doing this right after setting
  23482. // the inner HTML, so put a delay in.
  23483. var inject = dojo.hitch(this, function(){
  23484. var node = div.firstChild;
  23485. while(node){
  23486. try{
  23487. dijit._editor.selection.selectElement(node.firstChild);
  23488. var nativename = node.tagName.toLowerCase();
  23489. this._local2NativeFormatNames[nativename] = document.queryCommandValue("formatblock");
  23490. this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename;
  23491. node = node.nextSibling.nextSibling;
  23492. //console.log("Mapped: ", nativename, " to: ", this._local2NativeFormatNames[nativename]);
  23493. }catch(e) { /*Sqelch the occasional IE9 error */ }
  23494. }
  23495. div.parentNode.removeChild(div);
  23496. div.innerHTML = "";
  23497. });
  23498. setTimeout(inject, 0);
  23499. },
  23500. open: function(/*DomNode?*/ element){
  23501. // summary:
  23502. // Transforms the node referenced in this.domNode into a rich text editing
  23503. // node.
  23504. // description:
  23505. // Sets up the editing area asynchronously. This will result in
  23506. // the creation and replacement with an iframe.
  23507. // tags:
  23508. // private
  23509. if(!this.onLoadDeferred || this.onLoadDeferred.fired >= 0){
  23510. this.onLoadDeferred = new dojo.Deferred();
  23511. }
  23512. if(!this.isClosed){ this.close(); }
  23513. dojo.publish(dijit._scopeName + "._editor.RichText::open", [ this ]);
  23514. if(arguments.length == 1 && element.nodeName){ // else unchanged
  23515. this.domNode = element;
  23516. }
  23517. var dn = this.domNode;
  23518. // "html" will hold the innerHTML of the srcNodeRef and will be used to
  23519. // initialize the editor.
  23520. var html;
  23521. if(dojo.isString(this.value)){
  23522. // Allow setting the editor content programmatically instead of
  23523. // relying on the initial content being contained within the target
  23524. // domNode.
  23525. html = this.value;
  23526. delete this.value;
  23527. dn.innerHTML = "";
  23528. }else if(dn.nodeName && dn.nodeName.toLowerCase() == "textarea"){
  23529. // if we were created from a textarea, then we need to create a
  23530. // new editing harness node.
  23531. var ta = (this.textarea = dn);
  23532. this.name = ta.name;
  23533. html = ta.value;
  23534. dn = this.domNode = dojo.doc.createElement("div");
  23535. dn.setAttribute('widgetId', this.id);
  23536. ta.removeAttribute('widgetId');
  23537. dn.cssText = ta.cssText;
  23538. dn.className += " " + ta.className;
  23539. dojo.place(dn, ta, "before");
  23540. var tmpFunc = dojo.hitch(this, function(){
  23541. //some browsers refuse to submit display=none textarea, so
  23542. //move the textarea off screen instead
  23543. dojo.style(ta, {
  23544. display: "block",
  23545. position: "absolute",
  23546. top: "-1000px"
  23547. });
  23548. if(dojo.isIE){ //nasty IE bug: abnormal formatting if overflow is not hidden
  23549. var s = ta.style;
  23550. this.__overflow = s.overflow;
  23551. s.overflow = "hidden";
  23552. }
  23553. });
  23554. if(dojo.isIE){
  23555. setTimeout(tmpFunc, 10);
  23556. }else{
  23557. tmpFunc();
  23558. }
  23559. if(ta.form){
  23560. var resetValue = ta.value;
  23561. this.reset = function(){
  23562. var current = this.getValue();
  23563. if(current != resetValue){
  23564. this.replaceValue(resetValue);
  23565. }
  23566. };
  23567. dojo.connect(ta.form, "onsubmit", this, function(){
  23568. // Copy value to the <textarea> so it gets submitted along with form.
  23569. // FIXME: should we be calling close() here instead?
  23570. dojo.attr(ta, 'disabled', this.disabled); // don't submit the value if disabled
  23571. ta.value = this.getValue();
  23572. });
  23573. }
  23574. }else{
  23575. html = dijit._editor.getChildrenHtml(dn);
  23576. dn.innerHTML = "";
  23577. }
  23578. var content = dojo.contentBox(dn);
  23579. this._oldHeight = content.h;
  23580. this._oldWidth = content.w;
  23581. this.value = html;
  23582. // If we're a list item we have to put in a blank line to force the
  23583. // bullet to nicely align at the top of text
  23584. if(dn.nodeName && dn.nodeName == "LI"){
  23585. dn.innerHTML = " <br>";
  23586. }
  23587. // Construct the editor div structure.
  23588. this.header = dn.ownerDocument.createElement("div");
  23589. dn.appendChild(this.header);
  23590. this.editingArea = dn.ownerDocument.createElement("div");
  23591. dn.appendChild(this.editingArea);
  23592. this.footer = dn.ownerDocument.createElement("div");
  23593. dn.appendChild(this.footer);
  23594. if(!this.name){
  23595. this.name = this.id + "_AUTOGEN";
  23596. }
  23597. // User has pressed back/forward button so we lost the text in the editor, but it's saved
  23598. // in a hidden <textarea> (which contains the data for all the editors on this page),
  23599. // so get editor value from there
  23600. if(this.name !== "" && (!dojo.config["useXDomain"] || dojo.config["allowXdRichTextSave"])){
  23601. var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value");
  23602. if(saveTextarea && saveTextarea.value !== ""){
  23603. var datas = saveTextarea.value.split(this._SEPARATOR), i=0, dat;
  23604. while((dat=datas[i++])){
  23605. var data = dat.split(this._NAME_CONTENT_SEP);
  23606. if(data[0] == this.name){
  23607. html = data[1];
  23608. datas = datas.splice(i, 1);
  23609. saveTextarea.value = datas.join(this._SEPARATOR);
  23610. break;
  23611. }
  23612. }
  23613. }
  23614. if(!dijit._editor._globalSaveHandler){
  23615. dijit._editor._globalSaveHandler = {};
  23616. dojo.addOnUnload(function() {
  23617. var id;
  23618. for(id in dijit._editor._globalSaveHandler){
  23619. var f = dijit._editor._globalSaveHandler[id];
  23620. if(dojo.isFunction(f)){
  23621. f();
  23622. }
  23623. }
  23624. });
  23625. }
  23626. dijit._editor._globalSaveHandler[this.id] = dojo.hitch(this, "_saveContent");
  23627. }
  23628. this.isClosed = false;
  23629. var ifr = (this.editorObject = this.iframe = dojo.doc.createElement('iframe'));
  23630. ifr.id = this.id+"_iframe";
  23631. this._iframeSrc = this._getIframeDocTxt();
  23632. ifr.style.border = "none";
  23633. ifr.style.width = "100%";
  23634. if(this._layoutMode){
  23635. // iframe should be 100% height, thus getting it's height from surrounding
  23636. // <div> (which has the correct height set by Editor)
  23637. ifr.style.height = "100%";
  23638. }else{
  23639. if(dojo.isIE >= 7){
  23640. if(this.height){
  23641. ifr.style.height = this.height;
  23642. }
  23643. if(this.minHeight){
  23644. ifr.style.minHeight = this.minHeight;
  23645. }
  23646. }else{
  23647. ifr.style.height = this.height ? this.height : this.minHeight;
  23648. }
  23649. }
  23650. ifr.frameBorder = 0;
  23651. ifr._loadFunc = dojo.hitch( this, function(win){
  23652. this.window = win;
  23653. this.document = this.window.document;
  23654. if(dojo.isIE){
  23655. this._localizeEditorCommands();
  23656. }
  23657. // Do final setup and set initial contents of editor
  23658. this.onLoad(html);
  23659. });
  23660. // Set the iframe's initial (blank) content.
  23661. var s = 'javascript:parent.' + dijit._scopeName + '.byId("'+this.id+'")._iframeSrc';
  23662. ifr.setAttribute('src', s);
  23663. this.editingArea.appendChild(ifr);
  23664. if(dojo.isSafari <= 4){
  23665. var src = ifr.getAttribute("src");
  23666. if(!src || src.indexOf("javascript") == -1){
  23667. // Safari 4 and earlier sometimes act oddly
  23668. // So we have to set it again.
  23669. setTimeout(function(){ifr.setAttribute('src', s);},0);
  23670. }
  23671. }
  23672. // TODO: this is a guess at the default line-height, kinda works
  23673. if(dn.nodeName == "LI"){
  23674. dn.lastChild.style.marginTop = "-1.2em";
  23675. }
  23676. dojo.addClass(this.domNode, this.baseClass);
  23677. },
  23678. //static cache variables shared among all instance of this class
  23679. _local2NativeFormatNames: {},
  23680. _native2LocalFormatNames: {},
  23681. _getIframeDocTxt: function(){
  23682. // summary:
  23683. // Generates the boilerplate text of the document inside the iframe (ie, <html><head>...</head><body/></html>).
  23684. // Editor content (if not blank) should be added afterwards.
  23685. // tags:
  23686. // private
  23687. var _cs = dojo.getComputedStyle(this.domNode);
  23688. // The contents inside of <body>. The real contents are set later via a call to setValue().
  23689. var html = "";
  23690. var setBodyId = true;
  23691. if(dojo.isIE || dojo.isWebKit || (!this.height && !dojo.isMoz)){
  23692. // In auto-expand mode, need a wrapper div for AlwaysShowToolbar plugin to correctly
  23693. // expand/contract the editor as the content changes.
  23694. html = "<div id='dijitEditorBody'></div>";
  23695. setBodyId = false;
  23696. }else if(dojo.isMoz){
  23697. // workaround bug where can't select then delete text (until user types something
  23698. // into the editor)... and/or issue where typing doesn't erase selected text
  23699. this._cursorToStart = true;
  23700. html = "&nbsp;";
  23701. }
  23702. var font = [ _cs.fontWeight, _cs.fontSize, _cs.fontFamily ].join(" ");
  23703. // line height is tricky - applying a units value will mess things up.
  23704. // if we can't get a non-units value, bail out.
  23705. var lineHeight = _cs.lineHeight;
  23706. if(lineHeight.indexOf("px") >= 0){
  23707. lineHeight = parseFloat(lineHeight)/parseFloat(_cs.fontSize);
  23708. // console.debug(lineHeight);
  23709. }else if(lineHeight.indexOf("em")>=0){
  23710. lineHeight = parseFloat(lineHeight);
  23711. }else{
  23712. // If we can't get a non-units value, just default
  23713. // it to the CSS spec default of 'normal'. Seems to
  23714. // work better, esp on IE, than '1.0'
  23715. lineHeight = "normal";
  23716. }
  23717. var userStyle = "";
  23718. var self = this;
  23719. this.style.replace(/(^|;)\s*(line-|font-?)[^;]+/ig, function(match){
  23720. match = match.replace(/^;/ig,"") + ';';
  23721. var s = match.split(":")[0];
  23722. if(s){
  23723. s = dojo.trim(s);
  23724. s = s.toLowerCase();
  23725. var i;
  23726. var sC = "";
  23727. for(i = 0; i < s.length; i++){
  23728. var c = s.charAt(i);
  23729. switch(c){
  23730. case "-":
  23731. i++;
  23732. c = s.charAt(i).toUpperCase();
  23733. default:
  23734. sC += c;
  23735. }
  23736. }
  23737. dojo.style(self.domNode, sC, "");
  23738. }
  23739. userStyle += match + ';';
  23740. });
  23741. // need to find any associated label element and update iframe document title
  23742. var label=dojo.query('label[for="'+this.id+'"]');
  23743. return [
  23744. this.isLeftToRight() ? "<html>\n<head>\n" : "<html dir='rtl'>\n<head>\n",
  23745. (dojo.isMoz && label.length ? "<title>" + label[0].innerHTML + "</title>\n" : ""),
  23746. "<meta http-equiv='Content-Type' content='text/html'>\n",
  23747. "<style>\n",
  23748. "\tbody,html {\n",
  23749. "\t\tbackground:transparent;\n",
  23750. "\t\tpadding: 1px 0 0 0;\n",
  23751. "\t\tmargin: -1px 0 0 0;\n", // remove extraneous vertical scrollbar on safari and firefox
  23752. // Set the html/body sizing. Webkit always needs this, other browsers
  23753. // only set it when height is defined (not auto-expanding), otherwise
  23754. // scrollers do not appear.
  23755. ((dojo.isWebKit)?"\t\twidth: 100%;\n":""),
  23756. ((dojo.isWebKit)?"\t\theight: 100%;\n":""),
  23757. "\t}\n",
  23758. // TODO: left positioning will cause contents to disappear out of view
  23759. // if it gets too wide for the visible area
  23760. "\tbody{\n",
  23761. "\t\ttop:0px;\n",
  23762. "\t\tleft:0px;\n",
  23763. "\t\tright:0px;\n",
  23764. "\t\tfont:", font, ";\n",
  23765. ((this.height||dojo.isOpera) ? "" : "\t\tposition: fixed;\n"),
  23766. // FIXME: IE 6 won't understand min-height?
  23767. "\t\tmin-height:", this.minHeight, ";\n",
  23768. "\t\tline-height:", lineHeight,";\n",
  23769. "\t}\n",
  23770. "\tp{ margin: 1em 0; }\n",
  23771. // Determine how scrollers should be applied. In autoexpand mode (height = "") no scrollers on y at all.
  23772. // But in fixed height mode we want both x/y scrollers. Also, if it's using wrapping div and in auto-expand
  23773. // (Mainly IE) we need to kill the y scroller on body and html.
  23774. (!setBodyId && !this.height ? "\tbody,html {overflow-y: hidden;}\n" : ""),
  23775. "\t#dijitEditorBody{overflow-x: auto; overflow-y:" + (this.height ? "auto;" : "hidden;") + " outline: 0px;}\n",
  23776. "\tli > ul:-moz-first-node, li > ol:-moz-first-node{ padding-top: 1.2em; }\n",
  23777. // Can't set min-height in IE9, it puts layout on li, which puts move/resize handles.
  23778. (!dojo.isIE ? "\tli{ min-height:1.2em; }\n" : ""),
  23779. "</style>\n",
  23780. this._applyEditingAreaStyleSheets(),"\n",
  23781. "</head>\n<body ",
  23782. (setBodyId?"id='dijitEditorBody' ":""),
  23783. "onload='frameElement._loadFunc(window,document)' style='"+userStyle+"'>", html, "</body>\n</html>"
  23784. ].join(""); // String
  23785. },
  23786. _applyEditingAreaStyleSheets: function(){
  23787. // summary:
  23788. // apply the specified css files in styleSheets
  23789. // tags:
  23790. // private
  23791. var files = [];
  23792. if(this.styleSheets){
  23793. files = this.styleSheets.split(';');
  23794. this.styleSheets = '';
  23795. }
  23796. //empty this.editingAreaStyleSheets here, as it will be filled in addStyleSheet
  23797. files = files.concat(this.editingAreaStyleSheets);
  23798. this.editingAreaStyleSheets = [];
  23799. var text='', i=0, url;
  23800. while((url=files[i++])){
  23801. var abstring = (new dojo._Url(dojo.global.location, url)).toString();
  23802. this.editingAreaStyleSheets.push(abstring);
  23803. text += '<link rel="stylesheet" type="text/css" href="'+abstring+'"/>';
  23804. }
  23805. return text;
  23806. },
  23807. addStyleSheet: function(/*dojo._Url*/ uri){
  23808. // summary:
  23809. // add an external stylesheet for the editing area
  23810. // uri:
  23811. // A dojo.uri.Uri pointing to the url of the external css file
  23812. var url=uri.toString();
  23813. //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
  23814. if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
  23815. url = (new dojo._Url(dojo.global.location, url)).toString();
  23816. }
  23817. if(dojo.indexOf(this.editingAreaStyleSheets, url) > -1){
  23818. // console.debug("dijit._editor.RichText.addStyleSheet: Style sheet "+url+" is already applied");
  23819. return;
  23820. }
  23821. this.editingAreaStyleSheets.push(url);
  23822. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  23823. if(this.document.createStyleSheet){ //IE
  23824. this.document.createStyleSheet(url);
  23825. }else{ //other browser
  23826. var head = this.document.getElementsByTagName("head")[0];
  23827. var stylesheet = this.document.createElement("link");
  23828. stylesheet.rel="stylesheet";
  23829. stylesheet.type="text/css";
  23830. stylesheet.href=url;
  23831. head.appendChild(stylesheet);
  23832. }
  23833. }));
  23834. },
  23835. removeStyleSheet: function(/*dojo._Url*/ uri){
  23836. // summary:
  23837. // remove an external stylesheet for the editing area
  23838. var url=uri.toString();
  23839. //if uri is relative, then convert it to absolute so that it can be resolved correctly in iframe
  23840. if(url.charAt(0) == '.' || (url.charAt(0) != '/' && !uri.host)){
  23841. url = (new dojo._Url(dojo.global.location, url)).toString();
  23842. }
  23843. var index = dojo.indexOf(this.editingAreaStyleSheets, url);
  23844. if(index == -1){
  23845. // console.debug("dijit._editor.RichText.removeStyleSheet: Style sheet "+url+" has not been applied");
  23846. return;
  23847. }
  23848. delete this.editingAreaStyleSheets[index];
  23849. dojo.withGlobal(this.window,'query', dojo, ['link:[href="'+url+'"]']).orphan();
  23850. },
  23851. // disabled: Boolean
  23852. // The editor is disabled; the text cannot be changed.
  23853. disabled: false,
  23854. _mozSettingProps: {'styleWithCSS':false},
  23855. _setDisabledAttr: function(/*Boolean*/ value){
  23856. value = !!value;
  23857. this._set("disabled", value);
  23858. if(!this.isLoaded){ return; } // this method requires init to be complete
  23859. if(dojo.isIE || dojo.isWebKit || dojo.isOpera){
  23860. var preventIEfocus = dojo.isIE && (this.isLoaded || !this.focusOnLoad);
  23861. if(preventIEfocus){ this.editNode.unselectable = "on"; }
  23862. this.editNode.contentEditable = !value;
  23863. if(preventIEfocus){
  23864. var _this = this;
  23865. setTimeout(function(){ _this.editNode.unselectable = "off"; }, 0);
  23866. }
  23867. }else{ //moz
  23868. try{
  23869. this.document.designMode=(value?'off':'on');
  23870. }catch(e){ return; } // ! _disabledOK
  23871. if(!value && this._mozSettingProps){
  23872. var ps = this._mozSettingProps;
  23873. for(var n in ps){
  23874. if(ps.hasOwnProperty(n)){
  23875. try{
  23876. this.document.execCommand(n,false,ps[n]);
  23877. }catch(e2){}
  23878. }
  23879. }
  23880. }
  23881. // this.document.execCommand('contentReadOnly', false, value);
  23882. // if(value){
  23883. // this.blur(); //to remove the blinking caret
  23884. // }
  23885. }
  23886. this._disabledOK = true;
  23887. },
  23888. /* Event handlers
  23889. *****************/
  23890. onLoad: function(/*String*/ html){
  23891. // summary:
  23892. // Handler after the iframe finishes loading.
  23893. // html: String
  23894. // Editor contents should be set to this value
  23895. // tags:
  23896. // protected
  23897. // TODO: rename this to _onLoad, make empty public onLoad() method, deprecate/make protected onLoadDeferred handler?
  23898. if(!this.window.__registeredWindow){
  23899. this.window.__registeredWindow = true;
  23900. this._iframeRegHandle = dijit.registerIframe(this.iframe);
  23901. }
  23902. if(!dojo.isIE && !dojo.isWebKit && (this.height || dojo.isMoz)){
  23903. this.editNode=this.document.body;
  23904. }else{
  23905. // there's a wrapper div around the content, see _getIframeDocTxt().
  23906. this.editNode=this.document.body.firstChild;
  23907. var _this = this;
  23908. if(dojo.isIE){ // #4996 IE wants to focus the BODY tag
  23909. this.tabStop = dojo.create('div', { tabIndex: -1 }, this.editingArea);
  23910. this.iframe.onfocus = function(){ _this.editNode.setActive(); };
  23911. }
  23912. }
  23913. this.focusNode = this.editNode; // for InlineEditBox
  23914. var events = this.events.concat(this.captureEvents);
  23915. var ap = this.iframe ? this.document : this.editNode;
  23916. dojo.forEach(events, function(item){
  23917. this.connect(ap, item.toLowerCase(), item);
  23918. }, this);
  23919. this.connect(ap, "onmouseup", "onClick"); // mouseup in the margin does not generate an onclick event
  23920. if(dojo.isIE){ // IE contentEditable
  23921. this.connect(this.document, "onmousedown", "_onIEMouseDown"); // #4996 fix focus
  23922. // give the node Layout on IE
  23923. // TODO: this may no longer be needed, since we've reverted IE to using an iframe,
  23924. // not contentEditable. Removing it would also probably remove the need for creating
  23925. // the extra <div> in _getIframeDocTxt()
  23926. this.editNode.style.zoom = 1.0;
  23927. }else{
  23928. this.connect(this.document, "onmousedown", function(){
  23929. // Clear the moveToStart focus, as mouse
  23930. // down will set cursor point. Required to properly
  23931. // work with selection/position driven plugins and clicks in
  23932. // the window. refs: #10678
  23933. delete this._cursorToStart;
  23934. });
  23935. }
  23936. if(dojo.isWebKit){
  23937. //WebKit sometimes doesn't fire right on selections, so the toolbar
  23938. //doesn't update right. Therefore, help it out a bit with an additional
  23939. //listener. A mouse up will typically indicate a display change, so fire this
  23940. //and get the toolbar to adapt. Reference: #9532
  23941. this._webkitListener = this.connect(this.document, "onmouseup", "onDisplayChanged");
  23942. this.connect(this.document, "onmousedown", function(e){
  23943. var t = e.target;
  23944. if(t && (t === this.document.body || t === this.document)){
  23945. // Since WebKit uses the inner DIV, we need to check and set position.
  23946. // See: #12024 as to why the change was made.
  23947. setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
  23948. }
  23949. });
  23950. }
  23951. if(dojo.isIE){
  23952. // Try to make sure 'hidden' elements aren't visible in edit mode (like browsers other than IE
  23953. // do). See #9103
  23954. try{
  23955. this.document.execCommand('RespectVisibilityInDesign', true, null);
  23956. }catch(e){/* squelch */}
  23957. }
  23958. this.isLoaded = true;
  23959. this.set('disabled', this.disabled); // initialize content to editable (or not)
  23960. // Note that setValue() call will only work after isLoaded is set to true (above)
  23961. // Set up a function to allow delaying the setValue until a callback is fired
  23962. // This ensures extensions like dijit.Editor have a way to hold the value set
  23963. // until plugins load (and do things like register filters).
  23964. var setContent = dojo.hitch(this, function(){
  23965. this.setValue(html);
  23966. if(this.onLoadDeferred){
  23967. this.onLoadDeferred.callback(true);
  23968. }
  23969. this.onDisplayChanged();
  23970. if(this.focusOnLoad){
  23971. // after the document loads, then set focus after updateInterval expires so that
  23972. // onNormalizedDisplayChanged has run to avoid input caret issues
  23973. dojo.addOnLoad(dojo.hitch(this, function(){ setTimeout(dojo.hitch(this, "focus"), this.updateInterval); }));
  23974. }
  23975. // Save off the initial content now
  23976. this.value = this.getValue(true);
  23977. });
  23978. if(this.setValueDeferred){
  23979. this.setValueDeferred.addCallback(setContent);
  23980. }else{
  23981. setContent();
  23982. }
  23983. },
  23984. onKeyDown: function(/* Event */ e){
  23985. // summary:
  23986. // Handler for onkeydown event
  23987. // tags:
  23988. // protected
  23989. // we need this event at the moment to get the events from control keys
  23990. // such as the backspace. It might be possible to add this to Dojo, so that
  23991. // keyPress events can be emulated by the keyDown and keyUp detection.
  23992. if(e.keyCode === dojo.keys.TAB && this.isTabIndent ){
  23993. dojo.stopEvent(e); //prevent tab from moving focus out of editor
  23994. // FIXME: this is a poor-man's indent/outdent. It would be
  23995. // better if it added 4 "&nbsp;" chars in an undoable way.
  23996. // Unfortunately pasteHTML does not prove to be undoable
  23997. if(this.queryCommandEnabled((e.shiftKey ? "outdent" : "indent"))){
  23998. this.execCommand((e.shiftKey ? "outdent" : "indent"));
  23999. }
  24000. }
  24001. if(dojo.isIE){
  24002. if(e.keyCode == dojo.keys.TAB && !this.isTabIndent){
  24003. if(e.shiftKey && !e.ctrlKey && !e.altKey){
  24004. // focus the BODY so the browser will tab away from it instead
  24005. this.iframe.focus();
  24006. }else if(!e.shiftKey && !e.ctrlKey && !e.altKey){
  24007. // focus the BODY so the browser will tab away from it instead
  24008. this.tabStop.focus();
  24009. }
  24010. }else if(e.keyCode === dojo.keys.BACKSPACE && this.document.selection.type === "Control"){
  24011. // IE has a bug where if a non-text object is selected in the editor,
  24012. // hitting backspace would act as if the browser's back button was
  24013. // clicked instead of deleting the object. see #1069
  24014. dojo.stopEvent(e);
  24015. this.execCommand("delete");
  24016. }else if((65 <= e.keyCode && e.keyCode <= 90) ||
  24017. (e.keyCode>=37 && e.keyCode<=40) // FIXME: get this from connect() instead!
  24018. ){ //arrow keys
  24019. e.charCode = e.keyCode;
  24020. this.onKeyPress(e);
  24021. }
  24022. }
  24023. return true;
  24024. },
  24025. onKeyUp: function(e){
  24026. // summary:
  24027. // Handler for onkeyup event
  24028. // tags:
  24029. // callback
  24030. return;
  24031. },
  24032. setDisabled: function(/*Boolean*/ disabled){
  24033. // summary:
  24034. // Deprecated, use set('disabled', ...) instead.
  24035. // tags:
  24036. // deprecated
  24037. dojo.deprecated('dijit.Editor::setDisabled is deprecated','use dijit.Editor::attr("disabled",boolean) instead', 2.0);
  24038. this.set('disabled',disabled);
  24039. },
  24040. _setValueAttr: function(/*String*/ value){
  24041. // summary:
  24042. // Registers that attr("value", foo) should call setValue(foo)
  24043. this.setValue(value);
  24044. },
  24045. _setDisableSpellCheckAttr: function(/*Boolean*/ disabled){
  24046. if(this.document){
  24047. dojo.attr(this.document.body, "spellcheck", !disabled);
  24048. }else{
  24049. // try again after the editor is finished loading
  24050. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  24051. dojo.attr(this.document.body, "spellcheck", !disabled);
  24052. }));
  24053. }
  24054. this._set("disableSpellCheck", disabled);
  24055. },
  24056. onKeyPress: function(e){
  24057. // summary:
  24058. // Handle the various key events
  24059. // tags:
  24060. // protected
  24061. var c = (e.keyChar && e.keyChar.toLowerCase()) || e.keyCode,
  24062. handlers = this._keyHandlers[c],
  24063. args = arguments;
  24064. if(handlers && !e.altKey){
  24065. dojo.some(handlers, function(h){
  24066. // treat meta- same as ctrl-, for benefit of mac users
  24067. if(!(h.shift ^ e.shiftKey) && !(h.ctrl ^ (e.ctrlKey||e.metaKey))){
  24068. if(!h.handler.apply(this, args)){
  24069. e.preventDefault();
  24070. }
  24071. return true;
  24072. }
  24073. }, this);
  24074. }
  24075. // function call after the character has been inserted
  24076. if(!this._onKeyHitch){
  24077. this._onKeyHitch = dojo.hitch(this, "onKeyPressed");
  24078. }
  24079. setTimeout(this._onKeyHitch, 1);
  24080. return true;
  24081. },
  24082. addKeyHandler: function(/*String*/ key, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Function*/ handler){
  24083. // summary:
  24084. // Add a handler for a keyboard shortcut
  24085. // description:
  24086. // The key argument should be in lowercase if it is a letter character
  24087. // tags:
  24088. // protected
  24089. if(!dojo.isArray(this._keyHandlers[key])){
  24090. this._keyHandlers[key] = [];
  24091. }
  24092. //TODO: would be nice to make this a hash instead of an array for quick lookups
  24093. this._keyHandlers[key].push({
  24094. shift: shift || false,
  24095. ctrl: ctrl || false,
  24096. handler: handler
  24097. });
  24098. },
  24099. onKeyPressed: function(){
  24100. // summary:
  24101. // Handler for after the user has pressed a key, and the display has been updated.
  24102. // (Runs on a timer so that it runs after the display is updated)
  24103. // tags:
  24104. // private
  24105. this.onDisplayChanged(/*e*/); // can't pass in e
  24106. },
  24107. onClick: function(/*Event*/ e){
  24108. // summary:
  24109. // Handler for when the user clicks.
  24110. // tags:
  24111. // private
  24112. // console.info('onClick',this._tryDesignModeOn);
  24113. this.onDisplayChanged(e);
  24114. },
  24115. _onIEMouseDown: function(/*Event*/ e){
  24116. // summary:
  24117. // IE only to prevent 2 clicks to focus
  24118. // tags:
  24119. // protected
  24120. if(!this._focused && !this.disabled){
  24121. this.focus();
  24122. }
  24123. },
  24124. _onBlur: function(e){
  24125. // summary:
  24126. // Called from focus manager when focus has moved away from this editor
  24127. // tags:
  24128. // protected
  24129. // console.info('_onBlur')
  24130. this.inherited(arguments);
  24131. var newValue = this.getValue(true);
  24132. if(newValue != this.value){
  24133. this.onChange(newValue);
  24134. }
  24135. this._set("value", newValue);
  24136. },
  24137. _onFocus: function(/*Event*/ e){
  24138. // summary:
  24139. // Called from focus manager when focus has moved into this editor
  24140. // tags:
  24141. // protected
  24142. // console.info('_onFocus')
  24143. if(!this.disabled){
  24144. if(!this._disabledOK){
  24145. this.set('disabled', false);
  24146. }
  24147. this.inherited(arguments);
  24148. }
  24149. },
  24150. // TODO: remove in 2.0
  24151. blur: function(){
  24152. // summary:
  24153. // Remove focus from this instance.
  24154. // tags:
  24155. // deprecated
  24156. if(!dojo.isIE && this.window.document.documentElement && this.window.document.documentElement.focus){
  24157. this.window.document.documentElement.focus();
  24158. }else if(dojo.doc.body.focus){
  24159. dojo.doc.body.focus();
  24160. }
  24161. },
  24162. focus: function(){
  24163. // summary:
  24164. // Move focus to this editor
  24165. if(!this.isLoaded){
  24166. this.focusOnLoad = true;
  24167. return;
  24168. }
  24169. if(this._cursorToStart){
  24170. delete this._cursorToStart;
  24171. if(this.editNode.childNodes){
  24172. this.placeCursorAtStart(); // this calls focus() so return
  24173. return;
  24174. }
  24175. }
  24176. if(!dojo.isIE){
  24177. dijit.focus(this.iframe);
  24178. }else if(this.editNode && this.editNode.focus){
  24179. // editNode may be hidden in display:none div, lets just punt in this case
  24180. //this.editNode.focus(); -> causes IE to scroll always (strict and quirks mode) to the top the Iframe
  24181. // if we fire the event manually and let the browser handle the focusing, the latest
  24182. // cursor position is focused like in FF
  24183. this.iframe.fireEvent('onfocus', document.createEventObject()); // createEventObject only in IE
  24184. // }else{
  24185. // TODO: should we throw here?
  24186. // console.debug("Have no idea how to focus into the editor!");
  24187. }
  24188. },
  24189. // _lastUpdate: 0,
  24190. updateInterval: 200,
  24191. _updateTimer: null,
  24192. onDisplayChanged: function(/*Event*/ e){
  24193. // summary:
  24194. // This event will be fired everytime the display context
  24195. // changes and the result needs to be reflected in the UI.
  24196. // description:
  24197. // If you don't want to have update too often,
  24198. // onNormalizedDisplayChanged should be used instead
  24199. // tags:
  24200. // private
  24201. // var _t=new Date();
  24202. if(this._updateTimer){
  24203. clearTimeout(this._updateTimer);
  24204. }
  24205. if(!this._updateHandler){
  24206. this._updateHandler = dojo.hitch(this,"onNormalizedDisplayChanged");
  24207. }
  24208. this._updateTimer = setTimeout(this._updateHandler, this.updateInterval);
  24209. // Technically this should trigger a call to watch("value", ...) registered handlers,
  24210. // but getValue() is too slow to call on every keystroke so we don't.
  24211. },
  24212. onNormalizedDisplayChanged: function(){
  24213. // summary:
  24214. // This event is fired every updateInterval ms or more
  24215. // description:
  24216. // If something needs to happen immediately after a
  24217. // user change, please use onDisplayChanged instead.
  24218. // tags:
  24219. // private
  24220. delete this._updateTimer;
  24221. },
  24222. onChange: function(newContent){
  24223. // summary:
  24224. // This is fired if and only if the editor loses focus and
  24225. // the content is changed.
  24226. },
  24227. _normalizeCommand: function(/*String*/ cmd, /*Anything?*/argument){
  24228. // summary:
  24229. // Used as the advice function by dojo.connect to map our
  24230. // normalized set of commands to those supported by the target
  24231. // browser.
  24232. // tags:
  24233. // private
  24234. var command = cmd.toLowerCase();
  24235. if(command == "formatblock"){
  24236. if(dojo.isSafari && argument === undefined){ command = "heading"; }
  24237. }else if(command == "hilitecolor" && !dojo.isMoz){
  24238. command = "backcolor";
  24239. }
  24240. return command;
  24241. },
  24242. _qcaCache: {},
  24243. queryCommandAvailable: function(/*String*/ command){
  24244. // summary:
  24245. // Tests whether a command is supported by the host. Clients
  24246. // SHOULD check whether a command is supported before attempting
  24247. // to use it, behaviour for unsupported commands is undefined.
  24248. // command:
  24249. // The command to test for
  24250. // tags:
  24251. // private
  24252. // memoizing version. See _queryCommandAvailable for computing version
  24253. var ca = this._qcaCache[command];
  24254. if(ca !== undefined){ return ca; }
  24255. return (this._qcaCache[command] = this._queryCommandAvailable(command));
  24256. },
  24257. _queryCommandAvailable: function(/*String*/ command){
  24258. // summary:
  24259. // See queryCommandAvailable().
  24260. // tags:
  24261. // private
  24262. var ie = 1;
  24263. var mozilla = 1 << 1;
  24264. var webkit = 1 << 2;
  24265. var opera = 1 << 3;
  24266. function isSupportedBy(browsers){
  24267. return {
  24268. ie: Boolean(browsers & ie),
  24269. mozilla: Boolean(browsers & mozilla),
  24270. webkit: Boolean(browsers & webkit),
  24271. opera: Boolean(browsers & opera)
  24272. };
  24273. }
  24274. var supportedBy = null;
  24275. switch(command.toLowerCase()){
  24276. case "bold": case "italic": case "underline":
  24277. case "subscript": case "superscript":
  24278. case "fontname": case "fontsize":
  24279. case "forecolor": case "hilitecolor":
  24280. case "justifycenter": case "justifyfull": case "justifyleft":
  24281. case "justifyright": case "delete": case "selectall": case "toggledir":
  24282. supportedBy = isSupportedBy(mozilla | ie | webkit | opera);
  24283. break;
  24284. case "createlink": case "unlink": case "removeformat":
  24285. case "inserthorizontalrule": case "insertimage":
  24286. case "insertorderedlist": case "insertunorderedlist":
  24287. case "indent": case "outdent": case "formatblock":
  24288. case "inserthtml": case "undo": case "redo": case "strikethrough": case "tabindent":
  24289. supportedBy = isSupportedBy(mozilla | ie | opera | webkit);
  24290. break;
  24291. case "blockdirltr": case "blockdirrtl":
  24292. case "dirltr": case "dirrtl":
  24293. case "inlinedirltr": case "inlinedirrtl":
  24294. supportedBy = isSupportedBy(ie);
  24295. break;
  24296. case "cut": case "copy": case "paste":
  24297. supportedBy = isSupportedBy( ie | mozilla | webkit);
  24298. break;
  24299. case "inserttable":
  24300. supportedBy = isSupportedBy(mozilla | ie);
  24301. break;
  24302. case "insertcell": case "insertcol": case "insertrow":
  24303. case "deletecells": case "deletecols": case "deleterows":
  24304. case "mergecells": case "splitcell":
  24305. supportedBy = isSupportedBy(ie | mozilla);
  24306. break;
  24307. default: return false;
  24308. }
  24309. return (dojo.isIE && supportedBy.ie) ||
  24310. (dojo.isMoz && supportedBy.mozilla) ||
  24311. (dojo.isWebKit && supportedBy.webkit) ||
  24312. (dojo.isOpera && supportedBy.opera); // Boolean return true if the command is supported, false otherwise
  24313. },
  24314. execCommand: function(/*String*/ command, argument){
  24315. // summary:
  24316. // Executes a command in the Rich Text area
  24317. // command:
  24318. // The command to execute
  24319. // argument:
  24320. // An optional argument to the command
  24321. // tags:
  24322. // protected
  24323. var returnValue;
  24324. //focus() is required for IE to work
  24325. //In addition, focus() makes sure after the execution of
  24326. //the command, the editor receives the focus as expected
  24327. this.focus();
  24328. command = this._normalizeCommand(command, argument);
  24329. if(argument !== undefined){
  24330. if(command == "heading"){
  24331. throw new Error("unimplemented");
  24332. }else if((command == "formatblock") && dojo.isIE){
  24333. argument = '<'+argument+'>';
  24334. }
  24335. }
  24336. //Check to see if we have any over-rides for commands, they will be functions on this
  24337. //widget of the form _commandImpl. If we don't, fall through to the basic native
  24338. //exec command of the browser.
  24339. var implFunc = "_" + command + "Impl";
  24340. if(this[implFunc]){
  24341. returnValue = this[implFunc](argument);
  24342. }else{
  24343. argument = arguments.length > 1 ? argument : null;
  24344. if(argument || command!="createlink"){
  24345. returnValue = this.document.execCommand(command, false, argument);
  24346. }
  24347. }
  24348. this.onDisplayChanged();
  24349. return returnValue;
  24350. },
  24351. queryCommandEnabled: function(/*String*/ command){
  24352. // summary:
  24353. // Check whether a command is enabled or not.
  24354. // tags:
  24355. // protected
  24356. if(this.disabled || !this._disabledOK){ return false; }
  24357. command = this._normalizeCommand(command);
  24358. if(dojo.isMoz || dojo.isWebKit){
  24359. if(command == "unlink"){ // mozilla returns true always
  24360. // console.debug(this._sCall("hasAncestorElement", ['a']));
  24361. return this._sCall("hasAncestorElement", ["a"]);
  24362. }else if(command == "inserttable"){
  24363. return true;
  24364. }
  24365. }
  24366. //see #4109
  24367. if(dojo.isWebKit){
  24368. if(command == "cut" || command == "copy") {
  24369. // WebKit deems clipboard activity as a security threat and natively would return false
  24370. var sel = this.window.getSelection();
  24371. if(sel){ sel = sel.toString(); }
  24372. return !!sel;
  24373. }else if(command == "paste"){
  24374. return true;
  24375. }
  24376. }
  24377. var elem = dojo.isIE ? this.document.selection.createRange() : this.document;
  24378. try{
  24379. return elem.queryCommandEnabled(command);
  24380. }catch(e){
  24381. //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
  24382. return false;
  24383. }
  24384. },
  24385. queryCommandState: function(command){
  24386. // summary:
  24387. // Check the state of a given command and returns true or false.
  24388. // tags:
  24389. // protected
  24390. if(this.disabled || !this._disabledOK){ return false; }
  24391. command = this._normalizeCommand(command);
  24392. try{
  24393. return this.document.queryCommandState(command);
  24394. }catch(e){
  24395. //Squelch, occurs if editor is hidden on FF 3 (and maybe others.)
  24396. return false;
  24397. }
  24398. },
  24399. queryCommandValue: function(command){
  24400. // summary:
  24401. // Check the value of a given command. This matters most for
  24402. // custom selections and complex values like font value setting.
  24403. // tags:
  24404. // protected
  24405. if(this.disabled || !this._disabledOK){ return false; }
  24406. var r;
  24407. command = this._normalizeCommand(command);
  24408. if(dojo.isIE && command == "formatblock"){
  24409. r = this._native2LocalFormatNames[this.document.queryCommandValue(command)];
  24410. }else if(dojo.isMoz && command === "hilitecolor"){
  24411. var oldValue;
  24412. try{
  24413. oldValue = this.document.queryCommandValue("styleWithCSS");
  24414. }catch(e){
  24415. oldValue = false;
  24416. }
  24417. this.document.execCommand("styleWithCSS", false, true);
  24418. r = this.document.queryCommandValue(command);
  24419. this.document.execCommand("styleWithCSS", false, oldValue);
  24420. }else{
  24421. r = this.document.queryCommandValue(command);
  24422. }
  24423. return r;
  24424. },
  24425. // Misc.
  24426. _sCall: function(name, args){
  24427. // summary:
  24428. // Run the named method of dijit._editor.selection over the
  24429. // current editor instance's window, with the passed args.
  24430. // tags:
  24431. // private
  24432. return dojo.withGlobal(this.window, name, dijit._editor.selection, args);
  24433. },
  24434. // FIXME: this is a TON of code duplication. Why?
  24435. placeCursorAtStart: function(){
  24436. // summary:
  24437. // Place the cursor at the start of the editing area.
  24438. // tags:
  24439. // private
  24440. this.focus();
  24441. //see comments in placeCursorAtEnd
  24442. var isvalid=false;
  24443. if(dojo.isMoz){
  24444. // TODO: Is this branch even necessary?
  24445. var first=this.editNode.firstChild;
  24446. while(first){
  24447. if(first.nodeType == 3){
  24448. if(first.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
  24449. isvalid=true;
  24450. this._sCall("selectElement", [ first ]);
  24451. break;
  24452. }
  24453. }else if(first.nodeType == 1){
  24454. isvalid=true;
  24455. var tg = first.tagName ? first.tagName.toLowerCase() : "";
  24456. // Collapse before childless tags.
  24457. if(/br|input|img|base|meta|area|basefont|hr|link/.test(tg)){
  24458. this._sCall("selectElement", [ first ]);
  24459. }else{
  24460. // Collapse inside tags with children.
  24461. this._sCall("selectElementChildren", [ first ]);
  24462. }
  24463. break;
  24464. }
  24465. first = first.nextSibling;
  24466. }
  24467. }else{
  24468. isvalid=true;
  24469. this._sCall("selectElementChildren", [ this.editNode ]);
  24470. }
  24471. if(isvalid){
  24472. this._sCall("collapse", [ true ]);
  24473. }
  24474. },
  24475. placeCursorAtEnd: function(){
  24476. // summary:
  24477. // Place the cursor at the end of the editing area.
  24478. // tags:
  24479. // private
  24480. this.focus();
  24481. //In mozilla, if last child is not a text node, we have to use
  24482. // selectElementChildren on this.editNode.lastChild otherwise the
  24483. // cursor would be placed at the end of the closing tag of
  24484. //this.editNode.lastChild
  24485. var isvalid=false;
  24486. if(dojo.isMoz){
  24487. var last=this.editNode.lastChild;
  24488. while(last){
  24489. if(last.nodeType == 3){
  24490. if(last.nodeValue.replace(/^\s+|\s+$/g, "").length>0){
  24491. isvalid=true;
  24492. this._sCall("selectElement", [ last ]);
  24493. break;
  24494. }
  24495. }else if(last.nodeType == 1){
  24496. isvalid=true;
  24497. if(last.lastChild){
  24498. this._sCall("selectElement", [ last.lastChild ]);
  24499. }else{
  24500. this._sCall("selectElement", [ last ]);
  24501. }
  24502. break;
  24503. }
  24504. last = last.previousSibling;
  24505. }
  24506. }else{
  24507. isvalid=true;
  24508. this._sCall("selectElementChildren", [ this.editNode ]);
  24509. }
  24510. if(isvalid){
  24511. this._sCall("collapse", [ false ]);
  24512. }
  24513. },
  24514. getValue: function(/*Boolean?*/ nonDestructive){
  24515. // summary:
  24516. // Return the current content of the editing area (post filters
  24517. // are applied). Users should call get('value') instead.
  24518. // nonDestructive:
  24519. // defaults to false. Should the post-filtering be run over a copy
  24520. // of the live DOM? Most users should pass "true" here unless they
  24521. // *really* know that none of the installed filters are going to
  24522. // mess up the editing session.
  24523. // tags:
  24524. // private
  24525. if(this.textarea){
  24526. if(this.isClosed || !this.isLoaded){
  24527. return this.textarea.value;
  24528. }
  24529. }
  24530. return this._postFilterContent(null, nonDestructive);
  24531. },
  24532. _getValueAttr: function(){
  24533. // summary:
  24534. // Hook to make attr("value") work
  24535. return this.getValue(true);
  24536. },
  24537. setValue: function(/*String*/ html){
  24538. // summary:
  24539. // This function sets the content. No undo history is preserved.
  24540. // Users should use set('value', ...) instead.
  24541. // tags:
  24542. // deprecated
  24543. // TODO: remove this and getValue() for 2.0, and move code to _setValueAttr()
  24544. if(!this.isLoaded){
  24545. // try again after the editor is finished loading
  24546. this.onLoadDeferred.addCallback(dojo.hitch(this, function(){
  24547. this.setValue(html);
  24548. }));
  24549. return;
  24550. }
  24551. this._cursorToStart = true;
  24552. if(this.textarea && (this.isClosed || !this.isLoaded)){
  24553. this.textarea.value=html;
  24554. }else{
  24555. html = this._preFilterContent(html);
  24556. var node = this.isClosed ? this.domNode : this.editNode;
  24557. if(html && dojo.isMoz && html.toLowerCase() == "<p></p>"){
  24558. html = "<p>&nbsp;</p>";
  24559. }
  24560. // Use &nbsp; to avoid webkit problems where editor is disabled until the user clicks it
  24561. if(!html && dojo.isWebKit){
  24562. html = "&nbsp;";
  24563. }
  24564. node.innerHTML = html;
  24565. this._preDomFilterContent(node);
  24566. }
  24567. this.onDisplayChanged();
  24568. this._set("value", this.getValue(true));
  24569. },
  24570. replaceValue: function(/*String*/ html){
  24571. // summary:
  24572. // This function set the content while trying to maintain the undo stack
  24573. // (now only works fine with Moz, this is identical to setValue in all
  24574. // other browsers)
  24575. // tags:
  24576. // protected
  24577. if(this.isClosed){
  24578. this.setValue(html);
  24579. }else if(this.window && this.window.getSelection && !dojo.isMoz){ // Safari
  24580. // look ma! it's a totally f'd browser!
  24581. this.setValue(html);
  24582. }else if(this.window && this.window.getSelection){ // Moz
  24583. html = this._preFilterContent(html);
  24584. this.execCommand("selectall");
  24585. if(!html){
  24586. this._cursorToStart = true;
  24587. html = "&nbsp;";
  24588. }
  24589. this.execCommand("inserthtml", html);
  24590. this._preDomFilterContent(this.editNode);
  24591. }else if(this.document && this.document.selection){//IE
  24592. //In IE, when the first element is not a text node, say
  24593. //an <a> tag, when replacing the content of the editing
  24594. //area, the <a> tag will be around all the content
  24595. //so for now, use setValue for IE too
  24596. this.setValue(html);
  24597. }
  24598. this._set("value", this.getValue(true));
  24599. },
  24600. _preFilterContent: function(/*String*/ html){
  24601. // summary:
  24602. // Filter the input before setting the content of the editing
  24603. // area. DOM pre-filtering may happen after this
  24604. // string-based filtering takes place but as of 1.2, this is not
  24605. // guaranteed for operations such as the inserthtml command.
  24606. // tags:
  24607. // private
  24608. var ec = html;
  24609. dojo.forEach(this.contentPreFilters, function(ef){ if(ef){ ec = ef(ec); } });
  24610. return ec;
  24611. },
  24612. _preDomFilterContent: function(/*DomNode*/ dom){
  24613. // summary:
  24614. // filter the input's live DOM. All filter operations should be
  24615. // considered to be "live" and operating on the DOM that the user
  24616. // will be interacting with in their editing session.
  24617. // tags:
  24618. // private
  24619. dom = dom || this.editNode;
  24620. dojo.forEach(this.contentDomPreFilters, function(ef){
  24621. if(ef && dojo.isFunction(ef)){
  24622. ef(dom);
  24623. }
  24624. }, this);
  24625. },
  24626. _postFilterContent: function(
  24627. /*DomNode|DomNode[]|String?*/ dom,
  24628. /*Boolean?*/ nonDestructive){
  24629. // summary:
  24630. // filter the output after getting the content of the editing area
  24631. //
  24632. // description:
  24633. // post-filtering allows plug-ins and users to specify any number
  24634. // of transforms over the editor's content, enabling many common
  24635. // use-cases such as transforming absolute to relative URLs (and
  24636. // vice-versa), ensuring conformance with a particular DTD, etc.
  24637. // The filters are registered in the contentDomPostFilters and
  24638. // contentPostFilters arrays. Each item in the
  24639. // contentDomPostFilters array is a function which takes a DOM
  24640. // Node or array of nodes as its only argument and returns the
  24641. // same. It is then passed down the chain for further filtering.
  24642. // The contentPostFilters array behaves the same way, except each
  24643. // member operates on strings. Together, the DOM and string-based
  24644. // filtering allow the full range of post-processing that should
  24645. // be necessaray to enable even the most agressive of post-editing
  24646. // conversions to take place.
  24647. //
  24648. // If nonDestructive is set to "true", the nodes are cloned before
  24649. // filtering proceeds to avoid potentially destructive transforms
  24650. // to the content which may still needed to be edited further.
  24651. // Once DOM filtering has taken place, the serialized version of
  24652. // the DOM which is passed is run through each of the
  24653. // contentPostFilters functions.
  24654. //
  24655. // dom:
  24656. // a node, set of nodes, which to filter using each of the current
  24657. // members of the contentDomPostFilters and contentPostFilters arrays.
  24658. //
  24659. // nonDestructive:
  24660. // defaults to "false". If true, ensures that filtering happens on
  24661. // a clone of the passed-in content and not the actual node
  24662. // itself.
  24663. //
  24664. // tags:
  24665. // private
  24666. var ec;
  24667. if(!dojo.isString(dom)){
  24668. dom = dom || this.editNode;
  24669. if(this.contentDomPostFilters.length){
  24670. if(nonDestructive){
  24671. dom = dojo.clone(dom);
  24672. }
  24673. dojo.forEach(this.contentDomPostFilters, function(ef){
  24674. dom = ef(dom);
  24675. });
  24676. }
  24677. ec = dijit._editor.getChildrenHtml(dom);
  24678. }else{
  24679. ec = dom;
  24680. }
  24681. if(!dojo.trim(ec.replace(/^\xA0\xA0*/, '').replace(/\xA0\xA0*$/, '')).length){
  24682. ec = "";
  24683. }
  24684. // if(dojo.isIE){
  24685. // //removing appended <P>&nbsp;</P> for IE
  24686. // ec = ec.replace(/(?:<p>&nbsp;</p>[\n\r]*)+$/i,"");
  24687. // }
  24688. dojo.forEach(this.contentPostFilters, function(ef){
  24689. ec = ef(ec);
  24690. });
  24691. return ec;
  24692. },
  24693. _saveContent: function(/*Event*/ e){
  24694. // summary:
  24695. // Saves the content in an onunload event if the editor has not been closed
  24696. // tags:
  24697. // private
  24698. var saveTextarea = dojo.byId(dijit._scopeName + "._editor.RichText.value");
  24699. if(saveTextarea.value){
  24700. saveTextarea.value += this._SEPARATOR;
  24701. }
  24702. saveTextarea.value += this.name + this._NAME_CONTENT_SEP + this.getValue(true);
  24703. },
  24704. escapeXml: function(/*String*/ str, /*Boolean*/ noSingleQuotes){
  24705. // summary:
  24706. // Adds escape sequences for special characters in XML.
  24707. // Optionally skips escapes for single quotes
  24708. // tags:
  24709. // private
  24710. str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  24711. if(!noSingleQuotes){
  24712. str = str.replace(/'/gm, "&#39;");
  24713. }
  24714. return str; // string
  24715. },
  24716. getNodeHtml: function(/* DomNode */ node){
  24717. // summary:
  24718. // Deprecated. Use dijit._editor._getNodeHtml() instead.
  24719. // tags:
  24720. // deprecated
  24721. dojo.deprecated('dijit.Editor::getNodeHtml is deprecated','use dijit._editor.getNodeHtml instead', 2);
  24722. return dijit._editor.getNodeHtml(node); // String
  24723. },
  24724. getNodeChildrenHtml: function(/* DomNode */ dom){
  24725. // summary:
  24726. // Deprecated. Use dijit._editor.getChildrenHtml() instead.
  24727. // tags:
  24728. // deprecated
  24729. dojo.deprecated('dijit.Editor::getNodeChildrenHtml is deprecated','use dijit._editor.getChildrenHtml instead', 2);
  24730. return dijit._editor.getChildrenHtml(dom);
  24731. },
  24732. close: function(/*Boolean?*/ save){
  24733. // summary:
  24734. // Kills the editor and optionally writes back the modified contents to the
  24735. // element from which it originated.
  24736. // save:
  24737. // Whether or not to save the changes. If false, the changes are discarded.
  24738. // tags:
  24739. // private
  24740. if(this.isClosed){ return; }
  24741. if(!arguments.length){ save = true; }
  24742. if(save){
  24743. this._set("value", this.getValue(true));
  24744. }
  24745. // line height is squashed for iframes
  24746. // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; }
  24747. if(this.interval){ clearInterval(this.interval); }
  24748. if(this._webkitListener){
  24749. //Cleaup of WebKit fix: #9532
  24750. this.disconnect(this._webkitListener);
  24751. delete this._webkitListener;
  24752. }
  24753. // Guard against memory leaks on IE (see #9268)
  24754. if(dojo.isIE){
  24755. this.iframe.onfocus = null;
  24756. }
  24757. this.iframe._loadFunc = null;
  24758. if(this._iframeRegHandle){
  24759. dijit.unregisterIframe(this._iframeRegHandle);
  24760. delete this._iframeRegHandle;
  24761. }
  24762. if(this.textarea){
  24763. var s = this.textarea.style;
  24764. s.position = "";
  24765. s.left = s.top = "";
  24766. if(dojo.isIE){
  24767. s.overflow = this.__overflow;
  24768. this.__overflow = null;
  24769. }
  24770. this.textarea.value = this.value;
  24771. dojo.destroy(this.domNode);
  24772. this.domNode = this.textarea;
  24773. }else{
  24774. // Note that this destroys the iframe
  24775. this.domNode.innerHTML = this.value;
  24776. }
  24777. delete this.iframe;
  24778. dojo.removeClass(this.domNode, this.baseClass);
  24779. this.isClosed = true;
  24780. this.isLoaded = false;
  24781. delete this.editNode;
  24782. delete this.focusNode;
  24783. if(this.window && this.window._frameElement){
  24784. this.window._frameElement = null;
  24785. }
  24786. this.window = null;
  24787. this.document = null;
  24788. this.editingArea = null;
  24789. this.editorObject = null;
  24790. },
  24791. destroy: function(){
  24792. if(!this.isClosed){ this.close(false); }
  24793. this.inherited(arguments);
  24794. if(dijit._editor._globalSaveHandler){
  24795. delete dijit._editor._globalSaveHandler[this.id];
  24796. }
  24797. },
  24798. _removeMozBogus: function(/* String */ html){
  24799. // summary:
  24800. // Post filter to remove unwanted HTML attributes generated by mozilla
  24801. // tags:
  24802. // private
  24803. return html.replace(/\stype="_moz"/gi, '').replace(/\s_moz_dirty=""/gi, '').replace(/_moz_resizing="(true|false)"/gi,''); // String
  24804. },
  24805. _removeWebkitBogus: function(/* String */ html){
  24806. // summary:
  24807. // Post filter to remove unwanted HTML attributes generated by webkit
  24808. // tags:
  24809. // private
  24810. html = html.replace(/\sclass="webkit-block-placeholder"/gi, '');
  24811. html = html.replace(/\sclass="apple-style-span"/gi, '');
  24812. // For some reason copy/paste sometime adds extra meta tags for charset on
  24813. // webkit (chrome) on mac.They need to be removed. See: #12007"
  24814. html = html.replace(/<meta charset=\"utf-8\" \/>/gi, '');
  24815. return html; // String
  24816. },
  24817. _normalizeFontStyle: function(/* String */ html){
  24818. // summary:
  24819. // Convert 'strong' and 'em' to 'b' and 'i'.
  24820. // description:
  24821. // Moz can not handle strong/em tags correctly, so to help
  24822. // mozilla and also to normalize output, convert them to 'b' and 'i'.
  24823. //
  24824. // Note the IE generates 'strong' and 'em' rather than 'b' and 'i'
  24825. // tags:
  24826. // private
  24827. return html.replace(/<(\/)?strong([ \>])/gi, '<$1b$2')
  24828. .replace(/<(\/)?em([ \>])/gi, '<$1i$2' ); // String
  24829. },
  24830. _preFixUrlAttributes: function(/* String */ html){
  24831. // summary:
  24832. // Pre-filter to do fixing to href attributes on <a> and <img> tags
  24833. // tags:
  24834. // private
  24835. return html.replace(/(?:(<a(?=\s).*?\shref=)("|')(.*?)\2)|(?:(<a\s.*?href=)([^"'][^ >]+))/gi,
  24836. '$1$4$2$3$5$2 _djrealurl=$2$3$5$2')
  24837. .replace(/(?:(<img(?=\s).*?\ssrc=)("|')(.*?)\2)|(?:(<img\s.*?src=)([^"'][^ >]+))/gi,
  24838. '$1$4$2$3$5$2 _djrealurl=$2$3$5$2'); // String
  24839. },
  24840. /*****************************************************************************
  24841. The following functions implement HTML manipulation commands for various
  24842. browser/contentEditable implementations. The goal of them is to enforce
  24843. standard behaviors of them.
  24844. ******************************************************************************/
  24845. _inserthorizontalruleImpl: function(argument){
  24846. // summary:
  24847. // This function implements the insertion of HTML 'HR' tags.
  24848. // into a point on the page. IE doesn't to it right, so
  24849. // we have to use an alternate form
  24850. // argument:
  24851. // arguments to the exec command, if any.
  24852. // tags:
  24853. // protected
  24854. if(dojo.isIE){
  24855. return this._inserthtmlImpl("<hr>");
  24856. }
  24857. return this.document.execCommand("inserthorizontalrule", false, argument);
  24858. },
  24859. _unlinkImpl: function(argument){
  24860. // summary:
  24861. // This function implements the unlink of an 'a' tag.
  24862. // argument:
  24863. // arguments to the exec command, if any.
  24864. // tags:
  24865. // protected
  24866. if((this.queryCommandEnabled("unlink")) && (dojo.isMoz || dojo.isWebKit)){
  24867. var a = this._sCall("getAncestorElement", [ "a" ]);
  24868. this._sCall("selectElement", [ a ]);
  24869. return this.document.execCommand("unlink", false, null);
  24870. }
  24871. return this.document.execCommand("unlink", false, argument);
  24872. },
  24873. _hilitecolorImpl: function(argument){
  24874. // summary:
  24875. // This function implements the hilitecolor command
  24876. // argument:
  24877. // arguments to the exec command, if any.
  24878. // tags:
  24879. // protected
  24880. var returnValue;
  24881. if(dojo.isMoz){
  24882. // mozilla doesn't support hilitecolor properly when useCSS is
  24883. // set to false (bugzilla #279330)
  24884. this.document.execCommand("styleWithCSS", false, true);
  24885. returnValue = this.document.execCommand("hilitecolor", false, argument);
  24886. this.document.execCommand("styleWithCSS", false, false);
  24887. }else{
  24888. returnValue = this.document.execCommand("hilitecolor", false, argument);
  24889. }
  24890. return returnValue;
  24891. },
  24892. _backcolorImpl: function(argument){
  24893. // summary:
  24894. // This function implements the backcolor command
  24895. // argument:
  24896. // arguments to the exec command, if any.
  24897. // tags:
  24898. // protected
  24899. if(dojo.isIE){
  24900. // Tested under IE 6 XP2, no problem here, comment out
  24901. // IE weirdly collapses ranges when we exec these commands, so prevent it
  24902. // var tr = this.document.selection.createRange();
  24903. argument = argument ? argument : null;
  24904. }
  24905. return this.document.execCommand("backcolor", false, argument);
  24906. },
  24907. _forecolorImpl: function(argument){
  24908. // summary:
  24909. // This function implements the forecolor command
  24910. // argument:
  24911. // arguments to the exec command, if any.
  24912. // tags:
  24913. // protected
  24914. if(dojo.isIE){
  24915. // Tested under IE 6 XP2, no problem here, comment out
  24916. // IE weirdly collapses ranges when we exec these commands, so prevent it
  24917. // var tr = this.document.selection.createRange();
  24918. argument = argument? argument : null;
  24919. }
  24920. return this.document.execCommand("forecolor", false, argument);
  24921. },
  24922. _inserthtmlImpl: function(argument){
  24923. // summary:
  24924. // This function implements the insertion of HTML content into
  24925. // a point on the page.
  24926. // argument:
  24927. // The content to insert, if any.
  24928. // tags:
  24929. // protected
  24930. argument = this._preFilterContent(argument);
  24931. var rv = true;
  24932. if(dojo.isIE){
  24933. var insertRange = this.document.selection.createRange();
  24934. if(this.document.selection.type.toUpperCase() == 'CONTROL'){
  24935. var n=insertRange.item(0);
  24936. while(insertRange.length){
  24937. insertRange.remove(insertRange.item(0));
  24938. }
  24939. n.outerHTML=argument;
  24940. }else{
  24941. insertRange.pasteHTML(argument);
  24942. }
  24943. insertRange.select();
  24944. //insertRange.collapse(true);
  24945. }else if(dojo.isMoz && !argument.length){
  24946. //mozilla can not inserthtml an empty html to delete current selection
  24947. //so we delete the selection instead in this case
  24948. this._sCall("remove"); // FIXME
  24949. }else{
  24950. rv = this.document.execCommand("inserthtml", false, argument);
  24951. }
  24952. return rv;
  24953. },
  24954. _boldImpl: function(argument){
  24955. // summary:
  24956. // This function implements an over-ride of the bold command.
  24957. // argument:
  24958. // Not used, operates by selection.
  24959. // tags:
  24960. // protected
  24961. if(dojo.isIE){
  24962. this._adaptIESelection()
  24963. }
  24964. return this.document.execCommand("bold", false, argument);
  24965. },
  24966. _italicImpl: function(argument){
  24967. // summary:
  24968. // This function implements an over-ride of the italic command.
  24969. // argument:
  24970. // Not used, operates by selection.
  24971. // tags:
  24972. // protected
  24973. if(dojo.isIE){
  24974. this._adaptIESelection()
  24975. }
  24976. return this.document.execCommand("italic", false, argument);
  24977. },
  24978. _underlineImpl: function(argument){
  24979. // summary:
  24980. // This function implements an over-ride of the underline command.
  24981. // argument:
  24982. // Not used, operates by selection.
  24983. // tags:
  24984. // protected
  24985. if(dojo.isIE){
  24986. this._adaptIESelection()
  24987. }
  24988. return this.document.execCommand("underline", false, argument);
  24989. },
  24990. _strikethroughImpl: function(argument){
  24991. // summary:
  24992. // This function implements an over-ride of the strikethrough command.
  24993. // argument:
  24994. // Not used, operates by selection.
  24995. // tags:
  24996. // protected
  24997. if(dojo.isIE){
  24998. this._adaptIESelection()
  24999. }
  25000. return this.document.execCommand("strikethrough", false, argument);
  25001. },
  25002. getHeaderHeight: function(){
  25003. // summary:
  25004. // A function for obtaining the height of the header node
  25005. return this._getNodeChildrenHeight(this.header); // Number
  25006. },
  25007. getFooterHeight: function(){
  25008. // summary:
  25009. // A function for obtaining the height of the footer node
  25010. return this._getNodeChildrenHeight(this.footer); // Number
  25011. },
  25012. _getNodeChildrenHeight: function(node){
  25013. // summary:
  25014. // An internal function for computing the cumulative height of all child nodes of 'node'
  25015. // node:
  25016. // The node to process the children of;
  25017. var h = 0;
  25018. if(node && node.childNodes){
  25019. // IE didn't compute it right when position was obtained on the node directly is some cases,
  25020. // so we have to walk over all the children manually.
  25021. var i;
  25022. for(i = 0; i < node.childNodes.length; i++){
  25023. var size = dojo.position(node.childNodes[i]);
  25024. h += size.h;
  25025. }
  25026. }
  25027. return h; // Number
  25028. },
  25029. _isNodeEmpty: function(node, startOffset){
  25030. // summary:
  25031. // Function to test if a node is devoid of real content.
  25032. // node:
  25033. // The node to check.
  25034. // tags:
  25035. // private.
  25036. if(node.nodeType == 1/*element*/){
  25037. if(node.childNodes.length > 0){
  25038. return this._isNodeEmpty(node.childNodes[0], startOffset);
  25039. }
  25040. return true;
  25041. }else if(node.nodeType == 3/*text*/){
  25042. return (node.nodeValue.substring(startOffset) == "");
  25043. }
  25044. return false;
  25045. },
  25046. _removeStartingRangeFromRange: function(node, range){
  25047. // summary:
  25048. // Function to adjust selection range by removing the current
  25049. // start node.
  25050. // node:
  25051. // The node to remove from the starting range.
  25052. // range:
  25053. // The range to adapt.
  25054. // tags:
  25055. // private
  25056. if(node.nextSibling){
  25057. range.setStart(node.nextSibling,0);
  25058. }else{
  25059. var parent = node.parentNode;
  25060. while(parent && parent.nextSibling == null){
  25061. //move up the tree until we find a parent that has another node, that node will be the next node
  25062. parent = parent.parentNode;
  25063. }
  25064. if(parent){
  25065. range.setStart(parent.nextSibling,0);
  25066. }
  25067. }
  25068. return range;
  25069. },
  25070. _adaptIESelection: function(){
  25071. // summary:
  25072. // Function to adapt the IE range by removing leading 'newlines'
  25073. // Needed to fix issue with bold/italics/underline not working if
  25074. // range included leading 'newlines'.
  25075. // In IE, if a user starts a selection at the very end of a line,
  25076. // then the native browser commands will fail to execute correctly.
  25077. // To work around the issue, we can remove all empty nodes from
  25078. // the start of the range selection.
  25079. var selection = dijit.range.getSelection(this.window);
  25080. if(selection && selection.rangeCount && !selection.isCollapsed){
  25081. var range = selection.getRangeAt(0);
  25082. var firstNode = range.startContainer;
  25083. var startOffset = range.startOffset;
  25084. while(firstNode.nodeType == 3/*text*/ && startOffset >= firstNode.length && firstNode.nextSibling){
  25085. //traverse the text nodes until we get to the one that is actually highlighted
  25086. startOffset = startOffset - firstNode.length;
  25087. firstNode = firstNode.nextSibling;
  25088. }
  25089. //Remove the starting ranges until the range does not start with an empty node.
  25090. var lastNode=null;
  25091. while(this._isNodeEmpty(firstNode, startOffset) && firstNode != lastNode){
  25092. lastNode =firstNode; //this will break the loop in case we can't find the next sibling
  25093. range = this._removeStartingRangeFromRange(firstNode, range); //move the start container to the next node in the range
  25094. firstNode = range.startContainer;
  25095. startOffset = 0; //start at the beginning of the new starting range
  25096. }
  25097. selection.removeAllRanges();// this will work as long as users cannot select multiple ranges. I have not been able to do that in the editor.
  25098. selection.addRange(range);
  25099. }
  25100. }
  25101. });
  25102. }
  25103. if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25104. dojo._hasResource["dijit.ToolbarSeparator"] = true;
  25105. dojo.provide("dijit.ToolbarSeparator");
  25106. dojo.declare("dijit.ToolbarSeparator",
  25107. [ dijit._Widget, dijit._Templated ],
  25108. {
  25109. // summary:
  25110. // A spacer between two `dijit.Toolbar` items
  25111. templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
  25112. buildRendering: function(){
  25113. this.inherited(arguments);
  25114. dojo.setSelectable(this.domNode, false);
  25115. },
  25116. isFocusable: function(){
  25117. // summary:
  25118. // This widget isn't focusable, so pass along that fact.
  25119. // tags:
  25120. // protected
  25121. return false;
  25122. }
  25123. });
  25124. }
  25125. if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25126. dojo._hasResource["dijit.Toolbar"] = true;
  25127. dojo.provide("dijit.Toolbar");
  25128. // Note: require of ToolbarSeparator is for back-compat, remove for 2.0
  25129. dojo.declare("dijit.Toolbar",
  25130. [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
  25131. {
  25132. // summary:
  25133. // A Toolbar widget, used to hold things like `dijit.Editor` buttons
  25134. templateString:
  25135. '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
  25136. // '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
  25137. // '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
  25138. // '</table>' +
  25139. '</div>',
  25140. baseClass: "dijitToolbar",
  25141. postCreate: function(){
  25142. this.inherited(arguments);
  25143. this.connectKeyNavHandlers(
  25144. this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
  25145. this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
  25146. );
  25147. },
  25148. startup: function(){
  25149. if(this._started){ return; }
  25150. this.startupKeyNavChildren();
  25151. this.inherited(arguments);
  25152. }
  25153. }
  25154. );
  25155. }
  25156. if(!dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25157. dojo._hasResource["dijit._editor.plugins.EnterKeyHandling"] = true;
  25158. dojo.provide("dijit._editor.plugins.EnterKeyHandling");
  25159. dojo.declare("dijit._editor.plugins.EnterKeyHandling", dijit._editor._Plugin, {
  25160. // summary:
  25161. // This plugin tries to make all browsers behave consistently with regard to
  25162. // how ENTER behaves in the editor window. It traps the ENTER key and alters
  25163. // the way DOM is constructed in certain cases to try to commonize the generated
  25164. // DOM and behaviors across browsers.
  25165. //
  25166. // description:
  25167. // This plugin has three modes:
  25168. //
  25169. // * blockModeForEnter=BR
  25170. // * blockModeForEnter=DIV
  25171. // * blockModeForEnter=P
  25172. //
  25173. // In blockModeForEnter=P, the ENTER key starts a new
  25174. // paragraph, and shift-ENTER starts a new line in the current paragraph.
  25175. // For example, the input:
  25176. //
  25177. // | first paragraph <shift-ENTER>
  25178. // | second line of first paragraph <ENTER>
  25179. // | second paragraph
  25180. //
  25181. // will generate:
  25182. //
  25183. // | <p>
  25184. // | first paragraph
  25185. // | <br/>
  25186. // | second line of first paragraph
  25187. // | </p>
  25188. // | <p>
  25189. // | second paragraph
  25190. // | </p>
  25191. //
  25192. // In BR and DIV mode, the ENTER key conceptually goes to a new line in the
  25193. // current paragraph, and users conceptually create a new paragraph by pressing ENTER twice.
  25194. // For example, if the user enters text into an editor like this:
  25195. //
  25196. // | one <ENTER>
  25197. // | two <ENTER>
  25198. // | three <ENTER>
  25199. // | <ENTER>
  25200. // | four <ENTER>
  25201. // | five <ENTER>
  25202. // | six <ENTER>
  25203. //
  25204. // It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates:
  25205. //
  25206. // BR:
  25207. // | one<br/>
  25208. // | two<br/>
  25209. // | three<br/>
  25210. // | <br/>
  25211. // | four<br/>
  25212. // | five<br/>
  25213. // | six<br/>
  25214. //
  25215. // DIV:
  25216. // | <div>one</div>
  25217. // | <div>two</div>
  25218. // | <div>three</div>
  25219. // | <div>&nbsp;</div>
  25220. // | <div>four</div>
  25221. // | <div>five</div>
  25222. // | <div>six</div>
  25223. // blockNodeForEnter: String
  25224. // This property decides the behavior of Enter key. It can be either P,
  25225. // DIV, BR, or empty (which means disable this feature). Anything else
  25226. // will trigger errors. The default is 'BR'
  25227. //
  25228. // See class description for more details.
  25229. blockNodeForEnter: 'BR',
  25230. constructor: function(args){
  25231. if(args){
  25232. if("blockNodeForEnter" in args){
  25233. args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase();
  25234. }
  25235. dojo.mixin(this,args);
  25236. }
  25237. },
  25238. setEditor: function(editor){
  25239. // Overrides _Plugin.setEditor().
  25240. if(this.editor === editor) { return; }
  25241. this.editor = editor;
  25242. if(this.blockNodeForEnter == 'BR'){
  25243. // While Moz has a mode tht mostly works, it's still a little different,
  25244. // So, try to just have a common mode and be consistent. Which means
  25245. // we need to enable customUndo, if not already enabled.
  25246. this.editor.customUndo = true;
  25247. editor.onLoadDeferred.addCallback(dojo.hitch(this,function(d){
  25248. this.connect(editor.document, "onkeypress", function(e){
  25249. if(e.charOrCode == dojo.keys.ENTER){
  25250. // Just do it manually. The handleEnterKey has a shift mode that
  25251. // Always acts like <br>, so just use it.
  25252. var ne = dojo.mixin({},e);
  25253. ne.shiftKey = true;
  25254. if(!this.handleEnterKey(ne)){
  25255. dojo.stopEvent(e);
  25256. }
  25257. }
  25258. });
  25259. if(dojo.isIE >= 9){
  25260. this.connect(editor.document.body, "onpaste", function(e){
  25261. setTimeout(dojo.hitch(this, function(){
  25262. // Use the old range/selection code to kick IE 9 into updating
  25263. // its range by moving it back, then forward, one 'character'.
  25264. var r = this.editor.document.selection.createRange();
  25265. r.move('character',-1);
  25266. r.select();
  25267. r.move('character',1);
  25268. r.select();
  25269. }),0);
  25270. });
  25271. }
  25272. return d;
  25273. }));
  25274. }else if(this.blockNodeForEnter){
  25275. // add enter key handler
  25276. // FIXME: need to port to the new event code!!
  25277. var h = dojo.hitch(this,this.handleEnterKey);
  25278. editor.addKeyHandler(13, 0, 0, h); //enter
  25279. editor.addKeyHandler(13, 0, 1, h); //shift+enter
  25280. this.connect(this.editor,'onKeyPressed','onKeyPressed');
  25281. }
  25282. },
  25283. onKeyPressed: function(e){
  25284. // summary:
  25285. // Handler for keypress events.
  25286. // tags:
  25287. // private
  25288. if(this._checkListLater){
  25289. if(dojo.withGlobal(this.editor.window, 'isCollapsed', dijit)){
  25290. var liparent=dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, ['LI']);
  25291. if(!liparent){
  25292. // circulate the undo detection code by calling RichText::execCommand directly
  25293. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
  25294. // set the innerHTML of the new block node
  25295. var block = dojo.withGlobal(this.editor.window, 'getAncestorElement', dijit._editor.selection, [this.blockNodeForEnter]);
  25296. if(block){
  25297. block.innerHTML=this.bogusHtmlContent;
  25298. if(dojo.isIE <= 9){
  25299. // move to the start by moving backwards one char
  25300. var r = this.editor.document.selection.createRange();
  25301. r.move('character',-1);
  25302. r.select();
  25303. }
  25304. }else{
  25305. console.error('onKeyPressed: Cannot find the new block node'); // FIXME
  25306. }
  25307. }else{
  25308. if(dojo.isMoz){
  25309. if(liparent.parentNode.parentNode.nodeName == 'LI'){
  25310. liparent=liparent.parentNode.parentNode;
  25311. }
  25312. }
  25313. var fc=liparent.firstChild;
  25314. if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){
  25315. liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'),fc);
  25316. var newrange = dijit.range.create(this.editor.window);
  25317. newrange.setStart(liparent.firstChild,0);
  25318. var selection = dijit.range.getSelection(this.editor.window, true);
  25319. selection.removeAllRanges();
  25320. selection.addRange(newrange);
  25321. }
  25322. }
  25323. }
  25324. this._checkListLater = false;
  25325. }
  25326. if(this._pressedEnterInBlock){
  25327. // the new created is the original current P, so we have previousSibling below
  25328. if(this._pressedEnterInBlock.previousSibling){
  25329. this.removeTrailingBr(this._pressedEnterInBlock.previousSibling);
  25330. }
  25331. delete this._pressedEnterInBlock;
  25332. }
  25333. },
  25334. // bogusHtmlContent: [private] String
  25335. // HTML to stick into a new empty block
  25336. bogusHtmlContent: '&nbsp;',
  25337. // blockNodes: [private] Regex
  25338. // Regex for testing if a given tag is a block level (display:block) tag
  25339. blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/,
  25340. handleEnterKey: function(e){
  25341. // summary:
  25342. // Handler for enter key events when blockModeForEnter is DIV or P.
  25343. // description:
  25344. // Manually handle enter key event to make the behavior consistent across
  25345. // all supported browsers. See class description for details.
  25346. // tags:
  25347. // private
  25348. var selection, range, newrange, startNode, endNode, brNode, doc=this.editor.document,br,rs,txt;
  25349. if(e.shiftKey){ // shift+enter always generates <br>
  25350. var parent = dojo.withGlobal(this.editor.window, "getParentElement", dijit._editor.selection);
  25351. var header = dijit.range.getAncestor(parent,this.blockNodes);
  25352. if(header){
  25353. if(header.tagName == 'LI'){
  25354. return true; // let browser handle
  25355. }
  25356. selection = dijit.range.getSelection(this.editor.window);
  25357. range = selection.getRangeAt(0);
  25358. if(!range.collapsed){
  25359. range.deleteContents();
  25360. selection = dijit.range.getSelection(this.editor.window);
  25361. range = selection.getRangeAt(0);
  25362. }
  25363. if(dijit.range.atBeginningOfContainer(header, range.startContainer, range.startOffset)){
  25364. br=doc.createElement('br');
  25365. newrange = dijit.range.create(this.editor.window);
  25366. header.insertBefore(br,header.firstChild);
  25367. newrange.setStartBefore(br.nextSibling);
  25368. selection.removeAllRanges();
  25369. selection.addRange(newrange);
  25370. }else if(dijit.range.atEndOfContainer(header, range.startContainer, range.startOffset)){
  25371. newrange = dijit.range.create(this.editor.window);
  25372. br=doc.createElement('br');
  25373. header.appendChild(br);
  25374. header.appendChild(doc.createTextNode('\xA0'));
  25375. newrange.setStart(header.lastChild,0);
  25376. selection.removeAllRanges();
  25377. selection.addRange(newrange);
  25378. }else{
  25379. rs = range.startContainer;
  25380. if(rs && rs.nodeType == 3){
  25381. // Text node, we have to split it.
  25382. txt = rs.nodeValue;
  25383. dojo.withGlobal(this.editor.window, function(){
  25384. startNode = doc.createTextNode(txt.substring(0, range.startOffset));
  25385. endNode = doc.createTextNode(txt.substring(range.startOffset));
  25386. brNode = doc.createElement("br");
  25387. if(endNode.nodeValue == "" && dojo.isWebKit){
  25388. endNode = doc.createTextNode('\xA0')
  25389. }
  25390. dojo.place(startNode, rs, "after");
  25391. dojo.place(brNode, startNode, "after");
  25392. dojo.place(endNode, brNode, "after");
  25393. dojo.destroy(rs);
  25394. newrange = dijit.range.create(dojo.gobal);
  25395. newrange.setStart(endNode,0);
  25396. selection.removeAllRanges();
  25397. selection.addRange(newrange);
  25398. });
  25399. return false;
  25400. }
  25401. return true; // let browser handle
  25402. }
  25403. }else{
  25404. selection = dijit.range.getSelection(this.editor.window);
  25405. if(selection.rangeCount){
  25406. range = selection.getRangeAt(0);
  25407. if(range && range.startContainer){
  25408. if(!range.collapsed){
  25409. range.deleteContents();
  25410. selection = dijit.range.getSelection(this.editor.window);
  25411. range = selection.getRangeAt(0);
  25412. }
  25413. rs = range.startContainer;
  25414. if(rs && rs.nodeType == 3){
  25415. // Text node, we have to split it.
  25416. dojo.withGlobal(this.editor.window, dojo.hitch(this, function(){
  25417. var endEmpty = false;
  25418. var offset = range.startOffset;
  25419. if(rs.length < offset){
  25420. //We are not splitting the right node, try to locate the correct one
  25421. ret = this._adjustNodeAndOffset(rs, offset);
  25422. rs = ret.node;
  25423. offset = ret.offset;
  25424. }
  25425. txt = rs.nodeValue;
  25426. startNode = doc.createTextNode(txt.substring(0, offset));
  25427. endNode = doc.createTextNode(txt.substring(offset));
  25428. brNode = doc.createElement("br");
  25429. if(!endNode.length){
  25430. endNode = doc.createTextNode('\xA0');
  25431. endEmpty = true;
  25432. }
  25433. if(startNode.length){
  25434. dojo.place(startNode, rs, "after");
  25435. }else{
  25436. startNode = rs;
  25437. }
  25438. dojo.place(brNode, startNode, "after");
  25439. dojo.place(endNode, brNode, "after");
  25440. dojo.destroy(rs);
  25441. newrange = dijit.range.create(dojo.gobal);
  25442. newrange.setStart(endNode,0);
  25443. newrange.setEnd(endNode, endNode.length);
  25444. selection.removeAllRanges();
  25445. selection.addRange(newrange);
  25446. if(endEmpty && !dojo.isWebKit){
  25447. dijit._editor.selection.remove();
  25448. }else{
  25449. dijit._editor.selection.collapse(true);
  25450. }
  25451. }));
  25452. }else{
  25453. var targetNode;
  25454. if(range.startOffset >= 0){
  25455. targetNode = rs.childNodes[range.startOffset];
  25456. }
  25457. dojo.withGlobal(this.editor.window, dojo.hitch(this, function(){
  25458. var brNode = doc.createElement("br");
  25459. var endNode = doc.createTextNode('\xA0');
  25460. if(!targetNode){
  25461. rs.appendChild(brNode);
  25462. rs.appendChild(endNode);
  25463. }else{
  25464. dojo.place(brNode, targetNode, "before");
  25465. dojo.place(endNode, brNode, "after");
  25466. }
  25467. newrange = dijit.range.create(dojo.global);
  25468. newrange.setStart(endNode,0);
  25469. newrange.setEnd(endNode, endNode.length);
  25470. selection.removeAllRanges();
  25471. selection.addRange(newrange);
  25472. dijit._editor.selection.collapse(true);
  25473. }));
  25474. }
  25475. }
  25476. }else{
  25477. // don't change this: do not call this.execCommand, as that may have other logic in subclass
  25478. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>');
  25479. }
  25480. }
  25481. return false;
  25482. }
  25483. var _letBrowserHandle = true;
  25484. // first remove selection
  25485. selection = dijit.range.getSelection(this.editor.window);
  25486. range = selection.getRangeAt(0);
  25487. if(!range.collapsed){
  25488. range.deleteContents();
  25489. selection = dijit.range.getSelection(this.editor.window);
  25490. range = selection.getRangeAt(0);
  25491. }
  25492. var block = dijit.range.getBlockAncestor(range.endContainer, null, this.editor.editNode);
  25493. var blockNode = block.blockNode;
  25494. // if this is under a LI or the parent of the blockNode is LI, just let browser to handle it
  25495. if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){
  25496. if(dojo.isMoz){
  25497. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  25498. this._pressedEnterInBlock = blockNode;
  25499. }
  25500. // if this li only contains spaces, set the content to empty so the browser will outdent this item
  25501. if(/^(\s|&nbsp;|\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s|&nbsp;|\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){
  25502. // empty LI node
  25503. blockNode.innerHTML = '';
  25504. if(dojo.isWebKit){ // WebKit tosses the range when innerHTML is reset
  25505. newrange = dijit.range.create(this.editor.window);
  25506. newrange.setStart(blockNode, 0);
  25507. selection.removeAllRanges();
  25508. selection.addRange(newrange);
  25509. }
  25510. this._checkListLater = false; // nothing to check since the browser handles outdent
  25511. }
  25512. return true;
  25513. }
  25514. // text node directly under body, let's wrap them in a node
  25515. if(!block.blockNode || block.blockNode===this.editor.editNode){
  25516. try{
  25517. dijit._editor.RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter);
  25518. }catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ }
  25519. // get the newly created block node
  25520. // FIXME
  25521. block = {blockNode:dojo.withGlobal(this.editor.window, "getAncestorElement", dijit._editor.selection, [this.blockNodeForEnter]),
  25522. blockContainer: this.editor.editNode};
  25523. if(block.blockNode){
  25524. if(block.blockNode != this.editor.editNode &&
  25525. (!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){
  25526. this.removeTrailingBr(block.blockNode);
  25527. return false;
  25528. }
  25529. }else{ // we shouldn't be here if formatblock worked
  25530. block.blockNode = this.editor.editNode;
  25531. }
  25532. selection = dijit.range.getSelection(this.editor.window);
  25533. range = selection.getRangeAt(0);
  25534. }
  25535. var newblock = doc.createElement(this.blockNodeForEnter);
  25536. newblock.innerHTML=this.bogusHtmlContent;
  25537. this.removeTrailingBr(block.blockNode);
  25538. var endOffset = range.endOffset;
  25539. var node = range.endContainer;
  25540. if(node.length < endOffset){
  25541. //We are not checking the right node, try to locate the correct one
  25542. var ret = this._adjustNodeAndOffset(node, endOffset);
  25543. node = ret.node;
  25544. endOffset = ret.offset;
  25545. }
  25546. if(dijit.range.atEndOfContainer(block.blockNode, node, endOffset)){
  25547. if(block.blockNode === block.blockContainer){
  25548. block.blockNode.appendChild(newblock);
  25549. }else{
  25550. dojo.place(newblock, block.blockNode, "after");
  25551. }
  25552. _letBrowserHandle = false;
  25553. // lets move caret to the newly created block
  25554. newrange = dijit.range.create(this.editor.window);
  25555. newrange.setStart(newblock, 0);
  25556. selection.removeAllRanges();
  25557. selection.addRange(newrange);
  25558. if(this.editor.height){
  25559. dojo.window.scrollIntoView(newblock);
  25560. }
  25561. }else if(dijit.range.atBeginningOfContainer(block.blockNode,
  25562. range.startContainer, range.startOffset)){
  25563. dojo.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before");
  25564. if(newblock.nextSibling && this.editor.height){
  25565. // position input caret - mostly WebKit needs this
  25566. newrange = dijit.range.create(this.editor.window);
  25567. newrange.setStart(newblock.nextSibling, 0);
  25568. selection.removeAllRanges();
  25569. selection.addRange(newrange);
  25570. // browser does not scroll the caret position into view, do it manually
  25571. dojo.window.scrollIntoView(newblock.nextSibling);
  25572. }
  25573. _letBrowserHandle = false;
  25574. }else{ //press enter in the middle of P/DIV/Whatever/
  25575. if(block.blockNode === block.blockContainer){
  25576. block.blockNode.appendChild(newblock);
  25577. }else{
  25578. dojo.place(newblock, block.blockNode, "after");
  25579. }
  25580. _letBrowserHandle = false;
  25581. // Clone any block level styles.
  25582. if(block.blockNode.style){
  25583. if(newblock.style){
  25584. if(block.blockNode.style.cssText){
  25585. newblock.style.cssText = block.blockNode.style.cssText;
  25586. }
  25587. }
  25588. }
  25589. // Okay, we probably have to split.
  25590. rs = range.startContainer;
  25591. var firstNodeMoved;
  25592. if(rs && rs.nodeType == 3){
  25593. // Text node, we have to split it.
  25594. var nodeToMove, tNode;
  25595. endOffset = range.endOffset;
  25596. if(rs.length < endOffset){
  25597. //We are not splitting the right node, try to locate the correct one
  25598. ret = this._adjustNodeAndOffset(rs, endOffset);
  25599. rs = ret.node;
  25600. endOffset = ret.offset;
  25601. }
  25602. txt = rs.nodeValue;
  25603. startNode = doc.createTextNode(txt.substring(0, endOffset));
  25604. endNode = doc.createTextNode(txt.substring(endOffset, txt.length));
  25605. // Place the split, then remove original nodes.
  25606. dojo.place(startNode, rs, "before");
  25607. dojo.place(endNode, rs, "after");
  25608. dojo.destroy(rs);
  25609. // Okay, we split the text. Now we need to see if we're
  25610. // parented to the block element we're splitting and if
  25611. // not, we have to split all the way up. Ugh.
  25612. var parentC = startNode.parentNode;
  25613. while(parentC !== block.blockNode){
  25614. var tg = parentC.tagName;
  25615. var newTg = doc.createElement(tg);
  25616. // Clone over any 'style' data.
  25617. if(parentC.style){
  25618. if(newTg.style){
  25619. if(parentC.style.cssText){
  25620. newTg.style.cssText = parentC.style.cssText;
  25621. }
  25622. }
  25623. }
  25624. // If font also need to clone over any font data.
  25625. if(parentC.tagName === "FONT"){
  25626. if(parentC.color){
  25627. newTg.color = parentC.color;
  25628. }
  25629. if(parentC.face){
  25630. newTg.face = parentC.face;
  25631. }
  25632. if(parentC.size){ // this check was necessary on IE
  25633. newTg.size = parentC.size;
  25634. }
  25635. }
  25636. nodeToMove = endNode;
  25637. while(nodeToMove){
  25638. tNode = nodeToMove.nextSibling;
  25639. newTg.appendChild(nodeToMove);
  25640. nodeToMove = tNode;
  25641. }
  25642. dojo.place(newTg, parentC, "after");
  25643. startNode = parentC;
  25644. endNode = newTg;
  25645. parentC = parentC.parentNode;
  25646. }
  25647. // Lastly, move the split out tags to the new block.
  25648. // as they should now be split properly.
  25649. nodeToMove = endNode;
  25650. if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){
  25651. // Non-blank text and non-text nodes need to clear out that blank space
  25652. // before moving the contents.
  25653. newblock.innerHTML = "";
  25654. }
  25655. firstNodeMoved = nodeToMove;
  25656. while(nodeToMove){
  25657. tNode = nodeToMove.nextSibling;
  25658. newblock.appendChild(nodeToMove);
  25659. nodeToMove = tNode;
  25660. }
  25661. }
  25662. //lets move caret to the newly created block
  25663. newrange = dijit.range.create(this.editor.window);
  25664. var nodeForCursor;
  25665. var innerMostFirstNodeMoved = firstNodeMoved;
  25666. if(this.blockNodeForEnter !== 'BR'){
  25667. while(innerMostFirstNodeMoved){
  25668. nodeForCursor = innerMostFirstNodeMoved;
  25669. tNode = innerMostFirstNodeMoved.firstChild;
  25670. innerMostFirstNodeMoved = tNode;
  25671. }
  25672. if(nodeForCursor && nodeForCursor.parentNode){
  25673. newblock = nodeForCursor.parentNode;
  25674. newrange.setStart(newblock, 0);
  25675. selection.removeAllRanges();
  25676. selection.addRange(newrange);
  25677. if(this.editor.height){
  25678. dijit.scrollIntoView(newblock);
  25679. }
  25680. if(dojo.isMoz){
  25681. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  25682. this._pressedEnterInBlock = block.blockNode;
  25683. }
  25684. }else{
  25685. _letBrowserHandle = true;
  25686. }
  25687. }else{
  25688. newrange.setStart(newblock, 0);
  25689. selection.removeAllRanges();
  25690. selection.addRange(newrange);
  25691. if(this.editor.height){
  25692. dijit.scrollIntoView(newblock);
  25693. }
  25694. if(dojo.isMoz){
  25695. // press enter in middle of P may leave a trailing <br/>, let's remove it later
  25696. this._pressedEnterInBlock = block.blockNode;
  25697. }
  25698. }
  25699. }
  25700. return _letBrowserHandle;
  25701. },
  25702. _adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){
  25703. // summary:
  25704. // 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
  25705. // the next text sibling until it locates the text node in which the offset refers to
  25706. // node:
  25707. // The node to check.
  25708. // offset:
  25709. // The position to find within the text node
  25710. // tags:
  25711. // private.
  25712. while(node.length < offset && node.nextSibling && node.nextSibling.nodeType==3){
  25713. //Adjust the offset and node in the case of multiple text nodes in a row
  25714. offset = offset - node.length;
  25715. node = node.nextSibling;
  25716. }
  25717. var ret = {"node": node, "offset": offset};
  25718. return ret;
  25719. },
  25720. removeTrailingBr: function(container){
  25721. // summary:
  25722. // If last child of container is a <br>, then remove it.
  25723. // tags:
  25724. // private
  25725. var para = /P|DIV|LI/i.test(container.tagName) ?
  25726. container : dijit._editor.selection.getParentOfType(container,['P','DIV','LI']);
  25727. if(!para){ return; }
  25728. if(para.lastChild){
  25729. if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) ||
  25730. para.lastChild.tagName=='BR'){
  25731. dojo.destroy(para.lastChild);
  25732. }
  25733. }
  25734. if(!para.childNodes.length){
  25735. para.innerHTML=this.bogusHtmlContent;
  25736. }
  25737. }
  25738. });
  25739. }
  25740. if(!dojo._hasResource["dijit.Editor"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  25741. dojo._hasResource["dijit.Editor"] = true;
  25742. dojo.provide("dijit.Editor");
  25743. dojo.declare(
  25744. "dijit.Editor",
  25745. dijit._editor.RichText,
  25746. {
  25747. // summary:
  25748. // A rich text Editing widget
  25749. //
  25750. // description:
  25751. // This widget provides basic WYSIWYG editing features, based on the browser's
  25752. // underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`).
  25753. // A plugin model is available to extend the editor's capabilities as well as the
  25754. // the options available in the toolbar. Content generation may vary across
  25755. // browsers, and clipboard operations may have different results, to name
  25756. // a few limitations. Note: this widget should not be used with the HTML
  25757. // &lt;TEXTAREA&gt; tag -- see dijit._editor.RichText for details.
  25758. // plugins: [const] Object[]
  25759. // A list of plugin names (as strings) or instances (as objects)
  25760. // for this widget.
  25761. //
  25762. // When declared in markup, it might look like:
  25763. // | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]"
  25764. plugins: null,
  25765. // extraPlugins: [const] Object[]
  25766. // A list of extra plugin names which will be appended to plugins array
  25767. extraPlugins: null,
  25768. constructor: function(){
  25769. // summary:
  25770. // Runs on widget initialization to setup arrays etc.
  25771. // tags:
  25772. // private
  25773. if(!dojo.isArray(this.plugins)){
  25774. this.plugins=["undo","redo","|","cut","copy","paste","|","bold","italic","underline","strikethrough","|",
  25775. "insertOrderedList","insertUnorderedList","indent","outdent","|","justifyLeft","justifyRight","justifyCenter","justifyFull",
  25776. "dijit._editor.plugins.EnterKeyHandling" /*, "createLink"*/];
  25777. }
  25778. this._plugins=[];
  25779. this._editInterval = this.editActionInterval * 1000;
  25780. //IE will always lose focus when other element gets focus, while for FF and safari,
  25781. //when no iframe is used, focus will be lost whenever another element gets focus.
  25782. //For IE, we can connect to onBeforeDeactivate, which will be called right before
  25783. //the focus is lost, so we can obtain the selected range. For other browsers,
  25784. //no equivelent of onBeforeDeactivate, so we need to do two things to make sure
  25785. //selection is properly saved before focus is lost: 1) when user clicks another
  25786. //element in the page, in which case we listen to mousedown on the entire page and
  25787. //see whether user clicks out of a focus editor, if so, save selection (focus will
  25788. //only lost after onmousedown event is fired, so we can obtain correct caret pos.)
  25789. //2) when user tabs away from the editor, which is handled in onKeyDown below.
  25790. if(dojo.isIE){
  25791. this.events.push("onBeforeDeactivate");
  25792. this.events.push("onBeforeActivate");
  25793. }
  25794. },
  25795. postMixInProperties: function() {
  25796. // summary:
  25797. // Extension to make sure a deferred is in place before certain functions
  25798. // execute, like making sure all the plugins are properly inserted.
  25799. // Set up a deferred so that the value isn't applied to the editor
  25800. // until all the plugins load, needed to avoid timing condition
  25801. // reported in #10537.
  25802. this.setValueDeferred = new dojo.Deferred();
  25803. this.inherited(arguments);
  25804. },
  25805. postCreate: function(){
  25806. //for custom undo/redo, if enabled.
  25807. this._steps=this._steps.slice(0);
  25808. this._undoedSteps=this._undoedSteps.slice(0);
  25809. if(dojo.isArray(this.extraPlugins)){
  25810. this.plugins=this.plugins.concat(this.extraPlugins);
  25811. }
  25812. this.inherited(arguments);
  25813. this.commands = dojo.i18n.getLocalization("dijit._editor", "commands", this.lang);
  25814. if(!this.toolbar){
  25815. // if we haven't been assigned a toolbar, create one
  25816. this.toolbar = new dijit.Toolbar({
  25817. dir: this.dir,
  25818. lang: this.lang
  25819. });
  25820. this.header.appendChild(this.toolbar.domNode);
  25821. }
  25822. dojo.forEach(this.plugins, this.addPlugin, this);
  25823. // Okay, denote the value can now be set.
  25824. this.setValueDeferred.callback(true);
  25825. dojo.addClass(this.iframe.parentNode, "dijitEditorIFrameContainer");
  25826. dojo.addClass(this.iframe, "dijitEditorIFrame");
  25827. dojo.attr(this.iframe, "allowTransparency", true);
  25828. if(dojo.isWebKit){
  25829. // Disable selecting the entire editor by inadvertant double-clicks.
  25830. // on buttons, title bar, etc. Otherwise clicking too fast on
  25831. // a button such as undo/redo selects the entire editor.
  25832. dojo.style(this.domNode, "KhtmlUserSelect", "none");
  25833. }
  25834. this.toolbar.startup();
  25835. this.onNormalizedDisplayChanged(); //update toolbar button status
  25836. },
  25837. destroy: function(){
  25838. dojo.forEach(this._plugins, function(p){
  25839. if(p && p.destroy){
  25840. p.destroy();
  25841. }
  25842. });
  25843. this._plugins=[];
  25844. this.toolbar.destroyRecursive();
  25845. delete this.toolbar;
  25846. this.inherited(arguments);
  25847. },
  25848. addPlugin: function(/*String||Object*/plugin, /*Integer?*/index){
  25849. // summary:
  25850. // takes a plugin name as a string or a plugin instance and
  25851. // adds it to the toolbar and associates it with this editor
  25852. // instance. The resulting plugin is added to the Editor's
  25853. // plugins array. If index is passed, it's placed in the plugins
  25854. // array at that index. No big magic, but a nice helper for
  25855. // passing in plugin names via markup.
  25856. //
  25857. // plugin: String, args object or plugin instance
  25858. //
  25859. // args:
  25860. // This object will be passed to the plugin constructor
  25861. //
  25862. // index: Integer
  25863. // Used when creating an instance from
  25864. // something already in this.plugins. Ensures that the new
  25865. // instance is assigned to this.plugins at that index.
  25866. var args=dojo.isString(plugin)?{name:plugin}:plugin;
  25867. if(!args.setEditor){
  25868. var o={"args":args,"plugin":null,"editor":this};
  25869. dojo.publish(dijit._scopeName + ".Editor.getPlugin",[o]);
  25870. if(!o.plugin){
  25871. var pc = dojo.getObject(args.name);
  25872. if(pc){
  25873. o.plugin=new pc(args);
  25874. }
  25875. }
  25876. if(!o.plugin){
  25877. console.warn('Cannot find plugin',plugin);
  25878. return;
  25879. }
  25880. plugin=o.plugin;
  25881. }
  25882. if(arguments.length > 1){
  25883. this._plugins[index] = plugin;
  25884. }else{
  25885. this._plugins.push(plugin);
  25886. }
  25887. plugin.setEditor(this);
  25888. if(dojo.isFunction(plugin.setToolbar)){
  25889. plugin.setToolbar(this.toolbar);
  25890. }
  25891. },
  25892. //the following 3 functions are required to make the editor play nice under a layout widget, see #4070
  25893. startup: function(){
  25894. // summary:
  25895. // Exists to make Editor work as a child of a layout widget.
  25896. // Developers don't need to call this method.
  25897. // tags:
  25898. // protected
  25899. //console.log('startup',arguments);
  25900. },
  25901. resize: function(size){
  25902. // summary:
  25903. // Resize the editor to the specified size, see `dijit.layout._LayoutWidget.resize`
  25904. if(size){
  25905. // we've been given a height/width for the entire editor (toolbar + contents), calls layout()
  25906. // to split the allocated size between the toolbar and the contents
  25907. dijit.layout._LayoutWidget.prototype.resize.apply(this, arguments);
  25908. }
  25909. /*
  25910. else{
  25911. // do nothing, the editor is already laid out correctly. The user has probably specified
  25912. // the height parameter, which was used to set a size on the iframe
  25913. }
  25914. */
  25915. },
  25916. layout: function(){
  25917. // summary:
  25918. // Called from `dijit.layout._LayoutWidget.resize`. This shouldn't be called directly
  25919. // tags:
  25920. // protected
  25921. // Converts the iframe (or rather the <div> surrounding it) to take all the available space
  25922. // except what's needed for the header (toolbars) and footer (breadcrumbs, etc).
  25923. // A class was added to the iframe container and some themes style it, so we have to
  25924. // calc off the added margins and padding too. See tracker: #10662
  25925. var areaHeight = (this._contentBox.h -
  25926. (this.getHeaderHeight() + this.getFooterHeight() +
  25927. dojo._getPadBorderExtents(this.iframe.parentNode).h +
  25928. dojo._getMarginExtents(this.iframe.parentNode).h));
  25929. this.editingArea.style.height = areaHeight + "px";
  25930. if(this.iframe){
  25931. this.iframe.style.height="100%";
  25932. }
  25933. this._layoutMode = true;
  25934. },
  25935. _onIEMouseDown: function(/*Event*/ e){
  25936. // summary:
  25937. // IE only to prevent 2 clicks to focus
  25938. // tags:
  25939. // private
  25940. var outsideClientArea;
  25941. // IE 8's componentFromPoint is broken, which is a shame since it
  25942. // was smaller code, but oh well. We have to do this brute force
  25943. // to detect if the click was scroller or not.
  25944. var b = this.document.body;
  25945. var clientWidth = b.clientWidth;
  25946. var clientHeight = b.clientHeight;
  25947. var clientLeft = b.clientLeft;
  25948. var offsetWidth = b.offsetWidth;
  25949. var offsetHeight = b.offsetHeight;
  25950. var offsetLeft = b.offsetLeft;
  25951. //Check for vertical scroller click.
  25952. bodyDir = b.dir ? b.dir.toLowerCase() : "";
  25953. if(bodyDir != "rtl"){
  25954. if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){
  25955. // Check the click was between width and offset width, if so, scroller
  25956. outsideClientArea = true;
  25957. }
  25958. }else{
  25959. // RTL mode, we have to go by the left offsets.
  25960. if(e.x < clientLeft && e.x > offsetLeft){
  25961. // Check the click was between width and offset width, if so, scroller
  25962. outsideClientArea = true;
  25963. }
  25964. }
  25965. if(!outsideClientArea){
  25966. // Okay, might be horiz scroller, check that.
  25967. if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){
  25968. // Horizontal scroller.
  25969. outsideClientArea = true;
  25970. }
  25971. }
  25972. if(!outsideClientArea){
  25973. delete this._cursorToStart; // Remove the force to cursor to start position.
  25974. delete this._savedSelection; // new mouse position overrides old selection
  25975. if(e.target.tagName == "BODY"){
  25976. setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
  25977. }
  25978. this.inherited(arguments);
  25979. }
  25980. },
  25981. onBeforeActivate: function(e){
  25982. this._restoreSelection();
  25983. },
  25984. onBeforeDeactivate: function(e){
  25985. // summary:
  25986. // Called on IE right before focus is lost. Saves the selected range.
  25987. // tags:
  25988. // private
  25989. if(this.customUndo){
  25990. this.endEditing(true);
  25991. }
  25992. //in IE, the selection will be lost when other elements get focus,
  25993. //let's save focus before the editor is deactivated
  25994. if(e.target.tagName != "BODY"){
  25995. this._saveSelection();
  25996. }
  25997. //console.log('onBeforeDeactivate',this);
  25998. },
  25999. /* beginning of custom undo/redo support */
  26000. // customUndo: Boolean
  26001. // Whether we shall use custom undo/redo support instead of the native
  26002. // browser support. By default, we now use custom undo. It works better
  26003. // than native browser support and provides a consistent behavior across
  26004. // browsers with a minimal performance hit. We already had the hit on
  26005. // the slowest browser, IE, anyway.
  26006. customUndo: true,
  26007. // editActionInterval: Integer
  26008. // When using customUndo, not every keystroke will be saved as a step.
  26009. // Instead typing (including delete) will be grouped together: after
  26010. // a user stops typing for editActionInterval seconds, a step will be
  26011. // saved; if a user resume typing within editActionInterval seconds,
  26012. // the timeout will be restarted. By default, editActionInterval is 3
  26013. // seconds.
  26014. editActionInterval: 3,
  26015. beginEditing: function(cmd){
  26016. // summary:
  26017. // Called to note that the user has started typing alphanumeric characters, if it's not already noted.
  26018. // Deals with saving undo; see editActionInterval parameter.
  26019. // tags:
  26020. // private
  26021. if(!this._inEditing){
  26022. this._inEditing=true;
  26023. this._beginEditing(cmd);
  26024. }
  26025. if(this.editActionInterval>0){
  26026. if(this._editTimer){
  26027. clearTimeout(this._editTimer);
  26028. }
  26029. this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
  26030. }
  26031. },
  26032. // TODO: declaring these in the prototype is meaningless, just create in the constructor/postCreate
  26033. _steps:[],
  26034. _undoedSteps:[],
  26035. execCommand: function(cmd){
  26036. // summary:
  26037. // Main handler for executing any commands to the editor, like paste, bold, etc.
  26038. // Called by plugins, but not meant to be called by end users.
  26039. // tags:
  26040. // protected
  26041. if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
  26042. return this[cmd]();
  26043. }else{
  26044. if(this.customUndo){
  26045. this.endEditing();
  26046. this._beginEditing();
  26047. }
  26048. var r;
  26049. var isClipboard = /copy|cut|paste/.test(cmd);
  26050. try{
  26051. r = this.inherited(arguments);
  26052. if(dojo.isWebKit && isClipboard && !r){ //see #4598: webkit does not guarantee clipboard support from js
  26053. throw { code: 1011 }; // throw an object like Mozilla's error
  26054. }
  26055. }catch(e){
  26056. //TODO: when else might we get an exception? Do we need the Mozilla test below?
  26057. if(e.code == 1011 /* Mozilla: service denied */ && isClipboard){
  26058. // Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
  26059. var sub = dojo.string.substitute,
  26060. accel = {cut:'X', copy:'C', paste:'V'};
  26061. alert(sub(this.commands.systemShortcut,
  26062. [this.commands[cmd], sub(this.commands[dojo.isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
  26063. }
  26064. r = false;
  26065. }
  26066. if(this.customUndo){
  26067. this._endEditing();
  26068. }
  26069. return r;
  26070. }
  26071. },
  26072. queryCommandEnabled: function(cmd){
  26073. // summary:
  26074. // Returns true if specified editor command is enabled.
  26075. // Used by the plugins to know when to highlight/not highlight buttons.
  26076. // tags:
  26077. // protected
  26078. if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
  26079. return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0);
  26080. }else{
  26081. return this.inherited(arguments);
  26082. }
  26083. },
  26084. _moveToBookmark: function(b){
  26085. // summary:
  26086. // Selects the text specified in bookmark b
  26087. // tags:
  26088. // private
  26089. var bookmark = b.mark;
  26090. var mark = b.mark;
  26091. var col = b.isCollapsed;
  26092. var r, sNode, eNode, sel;
  26093. if(mark){
  26094. if(dojo.isIE < 9){
  26095. if(dojo.isArray(mark)){
  26096. //IE CONTROL, have to use the native bookmark.
  26097. bookmark = [];
  26098. dojo.forEach(mark,function(n){
  26099. bookmark.push(dijit.range.getNode(n,this.editNode));
  26100. },this);
  26101. dojo.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]);
  26102. }else{
  26103. if(mark.startContainer && mark.endContainer){
  26104. // Use the pseudo WC3 range API. This works better for positions
  26105. // than the IE native bookmark code.
  26106. sel = dijit.range.getSelection(this.window);
  26107. if(sel && sel.removeAllRanges){
  26108. sel.removeAllRanges();
  26109. r = dijit.range.create(this.window);
  26110. sNode = dijit.range.getNode(mark.startContainer,this.editNode);
  26111. eNode = dijit.range.getNode(mark.endContainer,this.editNode);
  26112. if(sNode && eNode){
  26113. // Okay, we believe we found the position, so add it into the selection
  26114. // There are cases where it may not be found, particularly in undo/redo, when
  26115. // IE changes the underlying DOM on us (wraps text in a <p> tag or similar.
  26116. // So, in those cases, don't bother restoring selection.
  26117. r.setStart(sNode,mark.startOffset);
  26118. r.setEnd(eNode,mark.endOffset);
  26119. sel.addRange(r);
  26120. }
  26121. }
  26122. }
  26123. }
  26124. }else{//w3c range
  26125. sel = dijit.range.getSelection(this.window);
  26126. if(sel && sel.removeAllRanges){
  26127. sel.removeAllRanges();
  26128. r = dijit.range.create(this.window);
  26129. sNode = dijit.range.getNode(mark.startContainer,this.editNode);
  26130. eNode = dijit.range.getNode(mark.endContainer,this.editNode);
  26131. if(sNode && eNode){
  26132. // Okay, we believe we found the position, so add it into the selection
  26133. // There are cases where it may not be found, particularly in undo/redo, when
  26134. // formatting as been done and so on, so don't restore selection then.
  26135. r.setStart(sNode,mark.startOffset);
  26136. r.setEnd(eNode,mark.endOffset);
  26137. sel.addRange(r);
  26138. }
  26139. }
  26140. }
  26141. }
  26142. },
  26143. _changeToStep: function(from, to){
  26144. // summary:
  26145. // Reverts editor to "to" setting, from the undo stack.
  26146. // tags:
  26147. // private
  26148. this.setValue(to.text);
  26149. var b=to.bookmark;
  26150. if(!b){ return; }
  26151. this._moveToBookmark(b);
  26152. },
  26153. undo: function(){
  26154. // summary:
  26155. // Handler for editor undo (ex: ctrl-z) operation
  26156. // tags:
  26157. // private
  26158. //console.log('undo');
  26159. var ret = false;
  26160. if(!this._undoRedoActive){
  26161. this._undoRedoActive = true;
  26162. this.endEditing(true);
  26163. var s=this._steps.pop();
  26164. if(s && this._steps.length>0){
  26165. this.focus();
  26166. this._changeToStep(s,this._steps[this._steps.length-1]);
  26167. this._undoedSteps.push(s);
  26168. this.onDisplayChanged();
  26169. delete this._undoRedoActive;
  26170. ret = true;
  26171. }
  26172. delete this._undoRedoActive;
  26173. }
  26174. return ret;
  26175. },
  26176. redo: function(){
  26177. // summary:
  26178. // Handler for editor redo (ex: ctrl-y) operation
  26179. // tags:
  26180. // private
  26181. //console.log('redo');
  26182. var ret = false;
  26183. if(!this._undoRedoActive){
  26184. this._undoRedoActive = true;
  26185. this.endEditing(true);
  26186. var s=this._undoedSteps.pop();
  26187. if(s && this._steps.length>0){
  26188. this.focus();
  26189. this._changeToStep(this._steps[this._steps.length-1],s);
  26190. this._steps.push(s);
  26191. this.onDisplayChanged();
  26192. ret = true;
  26193. }
  26194. delete this._undoRedoActive;
  26195. }
  26196. return ret;
  26197. },
  26198. endEditing: function(ignore_caret){
  26199. // summary:
  26200. // Called to note that the user has stopped typing alphanumeric characters, if it's not already noted.
  26201. // Deals with saving undo; see editActionInterval parameter.
  26202. // tags:
  26203. // private
  26204. if(this._editTimer){
  26205. clearTimeout(this._editTimer);
  26206. }
  26207. if(this._inEditing){
  26208. this._endEditing(ignore_caret);
  26209. this._inEditing=false;
  26210. }
  26211. },
  26212. _getBookmark: function(){
  26213. // summary:
  26214. // Get the currently selected text
  26215. // tags:
  26216. // protected
  26217. var b=dojo.withGlobal(this.window,dijit.getBookmark);
  26218. var tmp=[];
  26219. if(b && b.mark){
  26220. var mark = b.mark;
  26221. if(dojo.isIE < 9){
  26222. // Try to use the pseudo range API on IE for better accuracy.
  26223. var sel = dijit.range.getSelection(this.window);
  26224. if(!dojo.isArray(mark)){
  26225. if(sel){
  26226. var range;
  26227. if(sel.rangeCount){
  26228. range = sel.getRangeAt(0);
  26229. }
  26230. if(range){
  26231. b.mark = range.cloneRange();
  26232. }else{
  26233. b.mark = dojo.withGlobal(this.window,dijit.getBookmark);
  26234. }
  26235. }
  26236. }else{
  26237. // Control ranges (img, table, etc), handle differently.
  26238. dojo.forEach(b.mark,function(n){
  26239. tmp.push(dijit.range.getIndex(n,this.editNode).o);
  26240. },this);
  26241. b.mark = tmp;
  26242. }
  26243. }
  26244. try{
  26245. if(b.mark && b.mark.startContainer){
  26246. tmp=dijit.range.getIndex(b.mark.startContainer,this.editNode).o;
  26247. b.mark={startContainer:tmp,
  26248. startOffset:b.mark.startOffset,
  26249. endContainer:b.mark.endContainer===b.mark.startContainer?tmp:dijit.range.getIndex(b.mark.endContainer,this.editNode).o,
  26250. endOffset:b.mark.endOffset};
  26251. }
  26252. }catch(e){
  26253. b.mark = null;
  26254. }
  26255. }
  26256. return b;
  26257. },
  26258. _beginEditing: function(cmd){
  26259. // summary:
  26260. // Called when the user starts typing alphanumeric characters.
  26261. // Deals with saving undo; see editActionInterval parameter.
  26262. // tags:
  26263. // private
  26264. if(this._steps.length === 0){
  26265. // You want to use the editor content without post filtering
  26266. // to make sure selection restores right for the 'initial' state.
  26267. // and undo is called. So not using this.value, as it was 'processed'
  26268. // and the line-up for selections may have been altered.
  26269. this._steps.push({'text':dijit._editor.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()});
  26270. }
  26271. },
  26272. _endEditing: function(ignore_caret){
  26273. // summary:
  26274. // Called when the user stops typing alphanumeric characters.
  26275. // Deals with saving undo; see editActionInterval parameter.
  26276. // tags:
  26277. // private
  26278. // Avoid filtering to make sure selections restore.
  26279. var v = dijit._editor.getChildrenHtml(this.editNode);
  26280. this._undoedSteps=[];//clear undoed steps
  26281. this._steps.push({text: v, bookmark: this._getBookmark()});
  26282. },
  26283. onKeyDown: function(e){
  26284. // summary:
  26285. // Handler for onkeydown event.
  26286. // tags:
  26287. // private
  26288. //We need to save selection if the user TAB away from this editor
  26289. //no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate
  26290. if(!dojo.isIE && !this.iframe && e.keyCode == dojo.keys.TAB && !this.tabIndent){
  26291. this._saveSelection();
  26292. }
  26293. if(!this.customUndo){
  26294. this.inherited(arguments);
  26295. return;
  26296. }
  26297. var k = e.keyCode, ks = dojo.keys;
  26298. if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
  26299. if(k == 90 || k == 122){ //z
  26300. dojo.stopEvent(e);
  26301. this.undo();
  26302. return;
  26303. }else if(k == 89 || k == 121){ //y
  26304. dojo.stopEvent(e);
  26305. this.redo();
  26306. return;
  26307. }
  26308. }
  26309. this.inherited(arguments);
  26310. switch(k){
  26311. case ks.ENTER:
  26312. case ks.BACKSPACE:
  26313. case ks.DELETE:
  26314. this.beginEditing();
  26315. break;
  26316. case 88: //x
  26317. case 86: //v
  26318. if(e.ctrlKey && !e.altKey && !e.metaKey){
  26319. this.endEditing();//end current typing step if any
  26320. if(e.keyCode == 88){
  26321. this.beginEditing('cut');
  26322. //use timeout to trigger after the cut is complete
  26323. setTimeout(dojo.hitch(this, this.endEditing), 1);
  26324. }else{
  26325. this.beginEditing('paste');
  26326. //use timeout to trigger after the paste is complete
  26327. setTimeout(dojo.hitch(this, this.endEditing), 1);
  26328. }
  26329. break;
  26330. }
  26331. //pass through
  26332. default:
  26333. if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode<dojo.keys.F1 || e.keyCode>dojo.keys.F15)){
  26334. this.beginEditing();
  26335. break;
  26336. }
  26337. //pass through
  26338. case ks.ALT:
  26339. this.endEditing();
  26340. break;
  26341. case ks.UP_ARROW:
  26342. case ks.DOWN_ARROW:
  26343. case ks.LEFT_ARROW:
  26344. case ks.RIGHT_ARROW:
  26345. case ks.HOME:
  26346. case ks.END:
  26347. case ks.PAGE_UP:
  26348. case ks.PAGE_DOWN:
  26349. this.endEditing(true);
  26350. break;
  26351. //maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
  26352. case ks.CTRL:
  26353. case ks.SHIFT:
  26354. case ks.TAB:
  26355. break;
  26356. }
  26357. },
  26358. _onBlur: function(){
  26359. // summary:
  26360. // Called from focus manager when focus has moved away from this editor
  26361. // tags:
  26362. // protected
  26363. //this._saveSelection();
  26364. this.inherited(arguments);
  26365. this.endEditing(true);
  26366. },
  26367. _saveSelection: function(){
  26368. // summary:
  26369. // Save the currently selected text in _savedSelection attribute
  26370. // tags:
  26371. // private
  26372. try{
  26373. this._savedSelection=this._getBookmark();
  26374. }catch(e){ /* Squelch any errors that occur if selection save occurs due to being hidden simultaniously. */}
  26375. },
  26376. _restoreSelection: function(){
  26377. // summary:
  26378. // Re-select the text specified in _savedSelection attribute;
  26379. // see _saveSelection().
  26380. // tags:
  26381. // private
  26382. if(this._savedSelection){
  26383. // Clear off cursor to start, we're deliberately going to a selection.
  26384. delete this._cursorToStart;
  26385. // only restore the selection if the current range is collapsed
  26386. // if not collapsed, then it means the editor does not lose
  26387. // selection and there is no need to restore it
  26388. if(dojo.withGlobal(this.window,'isCollapsed',dijit)){
  26389. this._moveToBookmark(this._savedSelection);
  26390. }
  26391. delete this._savedSelection;
  26392. }
  26393. },
  26394. onClick: function(){
  26395. // summary:
  26396. // Handler for when editor is clicked
  26397. // tags:
  26398. // protected
  26399. this.endEditing(true);
  26400. this.inherited(arguments);
  26401. },
  26402. replaceValue: function(/*String*/ html){
  26403. // summary:
  26404. // over-ride of replaceValue to support custom undo and stack maintainence.
  26405. // tags:
  26406. // protected
  26407. if(!this.customUndo){
  26408. this.inherited(arguments);
  26409. }else{
  26410. if(this.isClosed){
  26411. this.setValue(html);
  26412. }else{
  26413. this.beginEditing();
  26414. if(!html){
  26415. html = "&nbsp;"
  26416. }
  26417. this.setValue(html);
  26418. this.endEditing();
  26419. }
  26420. }
  26421. },
  26422. _setDisabledAttr: function(/*Boolean*/ value){
  26423. var disableFunc = dojo.hitch(this, function(){
  26424. if((!this.disabled && value) || (!this._buttonEnabledPlugins && value)){
  26425. // Disable editor: disable all enabled buttons and remember that list
  26426. dojo.forEach(this._plugins, function(p){
  26427. p.set("disabled", true);
  26428. });
  26429. }else if(this.disabled && !value){
  26430. // Restore plugins to being active.
  26431. dojo.forEach(this._plugins, function(p){
  26432. p.set("disabled", false);
  26433. });
  26434. }
  26435. });
  26436. this.setValueDeferred.addCallback(disableFunc);
  26437. this.inherited(arguments);
  26438. },
  26439. _setStateClass: function(){
  26440. try{
  26441. this.inherited(arguments);
  26442. // Let theme set the editor's text color based on editor enabled/disabled state.
  26443. // We need to jump through hoops because the main document (where the theme CSS is)
  26444. // is separate from the iframe's document.
  26445. if(this.document && this.document.body){
  26446. dojo.style(this.document.body, "color", dojo.style(this.iframe, "color"));
  26447. }
  26448. }catch(e){ /* Squelch any errors caused by focus change if hidden during a state change */}
  26449. }
  26450. }
  26451. );
  26452. // Register the "default plugins", ie, the built-in editor commands
  26453. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  26454. if(o.plugin){ return; }
  26455. var args = o.args, p;
  26456. var _p = dijit._editor._Plugin;
  26457. var name = args.name;
  26458. switch(name){
  26459. case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
  26460. case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
  26461. case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
  26462. case "selectAll": case "removeFormat": case "unlink":
  26463. case "insertHorizontalRule":
  26464. p = new _p({ command: name });
  26465. break;
  26466. case "bold": case "italic": case "underline": case "strikethrough":
  26467. case "subscript": case "superscript":
  26468. p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
  26469. break;
  26470. case "|":
  26471. p = new _p({ button: new dijit.ToolbarSeparator(), setEditor: function(editor) {this.editor = editor;} });
  26472. }
  26473. // console.log('name',name,p);
  26474. o.plugin=p;
  26475. });
  26476. }
  26477. if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  26478. dojo._hasResource["dijit.form.ToggleButton"] = true;
  26479. dojo.provide("dijit.form.ToggleButton");
  26480. }
  26481. if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  26482. dojo._hasResource["dijit.form.CheckBox"] = true;
  26483. dojo.provide("dijit.form.CheckBox");
  26484. dojo.declare(
  26485. "dijit.form.CheckBox",
  26486. dijit.form.ToggleButton,
  26487. {
  26488. // summary:
  26489. // Same as an HTML checkbox, but with fancy styling.
  26490. //
  26491. // description:
  26492. // User interacts with real html inputs.
  26493. // On onclick (which occurs by mouse click, space-bar, or
  26494. // using the arrow keys to switch the selected radio button),
  26495. // we update the state of the checkbox/radio.
  26496. //
  26497. // There are two modes:
  26498. // 1. High contrast mode
  26499. // 2. Normal mode
  26500. //
  26501. // In case 1, the regular html inputs are shown and used by the user.
  26502. // In case 2, the regular html inputs are invisible but still used by
  26503. // the user. They are turned quasi-invisible and overlay the background-image.
  26504. 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"),
  26505. baseClass: "dijitCheckBox",
  26506. // type: [private] String
  26507. // type attribute on <input> node.
  26508. // Overrides `dijit.form.Button.type`. Users should not change this value.
  26509. type: "checkbox",
  26510. // value: String
  26511. // As an initialization parameter, equivalent to value field on normal checkbox
  26512. // (if checked, the value is passed as the value when form is submitted).
  26513. //
  26514. // However, get('value') will return either the string or false depending on
  26515. // whether or not the checkbox is checked.
  26516. //
  26517. // set('value', string) will check the checkbox and change the value to the
  26518. // specified string
  26519. //
  26520. // set('value', boolean) will change the checked state.
  26521. value: "on",
  26522. // readOnly: Boolean
  26523. // Should this widget respond to user input?
  26524. // In markup, this is specified as "readOnly".
  26525. // Similar to disabled except readOnly form values are submitted.
  26526. readOnly: false,
  26527. // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
  26528. // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
  26529. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  26530. readOnly: "focusNode"
  26531. }),
  26532. _setReadOnlyAttr: function(/*Boolean*/ value){
  26533. this._set("readOnly", value);
  26534. dojo.attr(this.focusNode, 'readOnly', value);
  26535. dijit.setWaiState(this.focusNode, "readonly", value);
  26536. },
  26537. _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
  26538. // summary:
  26539. // Handler for value= attribute to constructor, and also calls to
  26540. // set('value', val).
  26541. // description:
  26542. // During initialization, just saves as attribute to the <input type=checkbox>.
  26543. //
  26544. // After initialization,
  26545. // when passed a boolean, controls whether or not the CheckBox is checked.
  26546. // If passed a string, changes the value attribute of the CheckBox (the one
  26547. // specified as "value" when the CheckBox was constructed (ex: <input
  26548. // dojoType="dijit.CheckBox" value="chicken">)
  26549. if(typeof newValue == "string"){
  26550. this._set("value", newValue);
  26551. dojo.attr(this.focusNode, 'value', newValue);
  26552. newValue = true;
  26553. }
  26554. if(this._created){
  26555. this.set('checked', newValue, priorityChange);
  26556. }
  26557. },
  26558. _getValueAttr: function(){
  26559. // summary:
  26560. // Hook so get('value') works.
  26561. // description:
  26562. // If the CheckBox is checked, returns the value attribute.
  26563. // Otherwise returns false.
  26564. return (this.checked ? this.value : false);
  26565. },
  26566. // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
  26567. // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
  26568. _setLabelAttr: undefined,
  26569. postMixInProperties: function(){
  26570. if(this.value == ""){
  26571. this.value = "on";
  26572. }
  26573. // Need to set initial checked state as part of template, so that form submit works.
  26574. // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
  26575. // to <body>, see #8666
  26576. this.checkedAttrSetting = this.checked ? "checked" : "";
  26577. this.inherited(arguments);
  26578. },
  26579. _fillContent: function(/*DomNode*/ source){
  26580. // Override Button::_fillContent() since it doesn't make sense for CheckBox,
  26581. // since CheckBox doesn't even have a container
  26582. },
  26583. reset: function(){
  26584. // Override ToggleButton.reset()
  26585. this._hasBeenBlurred = false;
  26586. this.set('checked', this.params.checked || false);
  26587. // Handle unlikely event that the <input type=checkbox> value attribute has changed
  26588. this._set("value", this.params.value || "on");
  26589. dojo.attr(this.focusNode, 'value', this.value);
  26590. },
  26591. _onFocus: function(){
  26592. if(this.id){
  26593. dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
  26594. }
  26595. this.inherited(arguments);
  26596. },
  26597. _onBlur: function(){
  26598. if(this.id){
  26599. dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
  26600. }
  26601. this.inherited(arguments);
  26602. },
  26603. _onClick: function(/*Event*/ e){
  26604. // summary:
  26605. // Internal function to handle click actions - need to check
  26606. // readOnly, since button no longer does that check.
  26607. if(this.readOnly){
  26608. dojo.stopEvent(e);
  26609. return false;
  26610. }
  26611. return this.inherited(arguments);
  26612. }
  26613. }
  26614. );
  26615. dojo.declare(
  26616. "dijit.form.RadioButton",
  26617. dijit.form.CheckBox,
  26618. {
  26619. // summary:
  26620. // Same as an HTML radio, but with fancy styling.
  26621. type: "radio",
  26622. baseClass: "dijitRadio",
  26623. _setCheckedAttr: function(/*Boolean*/ value){
  26624. // If I am being checked then have to deselect currently checked radio button
  26625. this.inherited(arguments);
  26626. if(!this._created){ return; }
  26627. if(value){
  26628. var _this = this;
  26629. // search for radio buttons with the same name that need to be unchecked
  26630. dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
  26631. function(inputNode){
  26632. if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
  26633. var widget = dijit.getEnclosingWidget(inputNode);
  26634. if(widget && widget.checked){
  26635. widget.set('checked', false);
  26636. }
  26637. }
  26638. }
  26639. );
  26640. }
  26641. },
  26642. _clicked: function(/*Event*/ e){
  26643. if(!this.checked){
  26644. this.set('checked', true);
  26645. }
  26646. }
  26647. }
  26648. );
  26649. }
  26650. if(!dojo._hasResource["dijit.Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  26651. dojo._hasResource["dijit.Calendar"] = true;
  26652. dojo.provide("dijit.Calendar");
  26653. dojo.declare(
  26654. "dijit.Calendar",
  26655. [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
  26656. {
  26657. // summary:
  26658. // A simple GUI for choosing a date in the context of a monthly calendar.
  26659. //
  26660. // description:
  26661. // A simple GUI for choosing a date in the context of a monthly calendar.
  26662. // This widget can't be used in a form because it doesn't serialize the date to an
  26663. // `<input>` field. For a form element, use dijit.form.DateTextBox instead.
  26664. //
  26665. // Note that the parser takes all dates attributes passed in the
  26666. // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
  26667. // so that they are serializable and locale-independent.
  26668. //
  26669. // example:
  26670. // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode"));
  26671. //
  26672. // example:
  26673. // | <div dojoType="dijit.Calendar"></div>
  26674. 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"),
  26675. widgetsInTemplate: true,
  26676. // value: Date
  26677. // The currently selected Date, initially set to invalid date to indicate no selection.
  26678. value: new Date(""),
  26679. // TODO: for 2.0 make this a string (ISO format) rather than a Date
  26680. // datePackage: String
  26681. // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines
  26682. // at dojo.date by default.
  26683. datePackage: "dojo.date",
  26684. // dayWidth: String
  26685. // How to represent the days of the week in the calendar header. See dojo.date.locale
  26686. dayWidth: "narrow",
  26687. // tabIndex: Integer
  26688. // Order fields are traversed when user hits the tab key
  26689. tabIndex: "0",
  26690. // currentFocus: Date
  26691. // Date object containing the currently focused date, or the date which would be focused
  26692. // if the calendar itself was focused. Also indicates which year and month to display,
  26693. // i.e. the current "page" the calendar is on.
  26694. currentFocus: new Date(),
  26695. baseClass:"dijitCalendar",
  26696. // Set node classes for various mouse events, see dijit._CssStateMixin for more details
  26697. cssStateNodes: {
  26698. "decrementMonth": "dijitCalendarArrow",
  26699. "incrementMonth": "dijitCalendarArrow",
  26700. "previousYearLabelNode": "dijitCalendarPreviousYear",
  26701. "nextYearLabelNode": "dijitCalendarNextYear"
  26702. },
  26703. _isValidDate: function(/*Date*/ value){
  26704. // summary:
  26705. // Runs various tests on the value, checking that it's a valid date, rather
  26706. // than blank or NaN.
  26707. // tags:
  26708. // private
  26709. return value && !isNaN(value) && typeof value == "object" &&
  26710. value.toString() != this.constructor.prototype.value.toString();
  26711. },
  26712. setValue: function(/*Date*/ value){
  26713. // summary:
  26714. // Deprecated. Use set('value', ...) instead.
  26715. // tags:
  26716. // deprecated
  26717. dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
  26718. this.set('value', value);
  26719. },
  26720. _getValueAttr: function(){
  26721. // summary:
  26722. // Support get('value')
  26723. // this.value is set to 1AM, but return midnight, local time for back-compat
  26724. var value = new this.dateClassObj(this.value);
  26725. value.setHours(0, 0, 0, 0);
  26726. // If daylight savings pushes midnight to the previous date, fix the Date
  26727. // object to point at 1am so it will represent the correct day. See #9366
  26728. if(value.getDate() < this.value.getDate()){
  26729. value = this.dateFuncObj.add(value, "hour", 1);
  26730. }
  26731. return value;
  26732. },
  26733. _setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){
  26734. // summary:
  26735. // Support set("value", ...)
  26736. // description:
  26737. // Set the current date and update the UI. If the date is disabled, the value will
  26738. // not change, but the display will change to the corresponding month.
  26739. // value:
  26740. // Either a Date or the number of seconds since 1970.
  26741. // tags:
  26742. // protected
  26743. if(value){
  26744. // convert from Number to Date, or make copy of Date object so that setHours() call below
  26745. // doesn't affect original value
  26746. value = new this.dateClassObj(value);
  26747. }
  26748. if(this._isValidDate(value)){
  26749. if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){
  26750. value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
  26751. if(!this.isDisabledDate(value, this.lang)){
  26752. this._set("value", value);
  26753. // Set focus cell to the new value. Arguably this should only happen when there isn't a current
  26754. // focus point. This will also repopulate the grid, showing the new selected value (and possibly
  26755. // new month/year).
  26756. this.set("currentFocus", value);
  26757. if(priorityChange || typeof priorityChange == "undefined"){
  26758. this.onChange(this.get('value'));
  26759. this.onValueSelected(this.get('value')); // remove in 2.0
  26760. }
  26761. }
  26762. }
  26763. }else{
  26764. // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus
  26765. this._set("value", null);
  26766. this.set("currentFocus", this.currentFocus);
  26767. }
  26768. },
  26769. _setText: function(node, text){
  26770. // summary:
  26771. // This just sets the content of node to the specified text.
  26772. // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434.
  26773. // tags:
  26774. // private
  26775. while(node.firstChild){
  26776. node.removeChild(node.firstChild);
  26777. }
  26778. node.appendChild(dojo.doc.createTextNode(text));
  26779. },
  26780. _populateGrid: function(){
  26781. // summary:
  26782. // Fills in the calendar grid with each day (1-31)
  26783. // tags:
  26784. // private
  26785. var month = new this.dateClassObj(this.currentFocus);
  26786. month.setDate(1);
  26787. var firstDay = month.getDay(),
  26788. daysInMonth = this.dateFuncObj.getDaysInMonth(month),
  26789. daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)),
  26790. today = new this.dateClassObj(),
  26791. dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
  26792. if(dayOffset > firstDay){ dayOffset -= 7; }
  26793. // Iterate through dates in the calendar and fill in date numbers and style info
  26794. dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
  26795. i += dayOffset;
  26796. var date = new this.dateClassObj(month),
  26797. number, clazz = "dijitCalendar", adj = 0;
  26798. if(i < firstDay){
  26799. number = daysInPreviousMonth - firstDay + i + 1;
  26800. adj = -1;
  26801. clazz += "Previous";
  26802. }else if(i >= (firstDay + daysInMonth)){
  26803. number = i - firstDay - daysInMonth + 1;
  26804. adj = 1;
  26805. clazz += "Next";
  26806. }else{
  26807. number = i - firstDay + 1;
  26808. clazz += "Current";
  26809. }
  26810. if(adj){
  26811. date = this.dateFuncObj.add(date, "month", adj);
  26812. }
  26813. date.setDate(number);
  26814. if(!this.dateFuncObj.compare(date, today, "date")){
  26815. clazz = "dijitCalendarCurrentDate " + clazz;
  26816. }
  26817. if(this._isSelectedDate(date, this.lang)){
  26818. clazz = "dijitCalendarSelectedDate " + clazz;
  26819. }
  26820. if(this.isDisabledDate(date, this.lang)){
  26821. clazz = "dijitCalendarDisabledDate " + clazz;
  26822. }
  26823. var clazz2 = this.getClassForDate(date, this.lang);
  26824. if(clazz2){
  26825. clazz = clazz2 + " " + clazz;
  26826. }
  26827. template.className = clazz + "Month dijitCalendarDateTemplate";
  26828. template.dijitDateValue = date.valueOf(); // original code
  26829. dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it
  26830. var label = dojo.query(".dijitCalendarDateLabel", template)[0],
  26831. text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate();
  26832. this._setText(label, text);
  26833. }, this);
  26834. // Repopulate month drop down list based on current year.
  26835. // Need to do this to hide leap months in Hebrew calendar.
  26836. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
  26837. this.monthDropDownButton.dropDown.set("months", monthNames);
  26838. // Set name of current month and also fill in spacer element with all the month names
  26839. // (invisible) so that the maximum width will affect layout. But not on IE6 because then
  26840. // the center <TH> overlaps the right <TH> (due to a browser bug).
  26841. this.monthDropDownButton.containerNode.innerHTML =
  26842. (dojo.isIE == 6 ? "" : "<div class='dijitSpacer'>" + this.monthDropDownButton.dropDown.domNode.innerHTML + "</div>") +
  26843. "<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" + monthNames[month.getMonth()] + "</div>";
  26844. // Fill in localized prev/current/next years
  26845. var y = month.getFullYear() - 1;
  26846. var d = new this.dateClassObj();
  26847. dojo.forEach(["previous", "current", "next"], function(name){
  26848. d.setFullYear(y++);
  26849. this._setText(this[name+"YearLabelNode"],
  26850. this.dateLocaleModule.format(d, {selector:'year', locale:this.lang}));
  26851. }, this);
  26852. },
  26853. goToToday: function(){
  26854. // summary:
  26855. // Sets calendar's value to today's date
  26856. this.set('value', new this.dateClassObj());
  26857. },
  26858. constructor: function(/*Object*/args){
  26859. var dateClass = (args.datePackage && (args.datePackage != "dojo.date"))? args.datePackage + ".Date" : "Date";
  26860. this.dateClassObj = dojo.getObject(dateClass, false);
  26861. this.datePackage = args.datePackage || this.datePackage;
  26862. this.dateFuncObj = dojo.getObject(this.datePackage, false);
  26863. this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
  26864. },
  26865. postMixInProperties: function(){
  26866. // Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead.
  26867. // TODO: remove this for 2.0 (thanks to #11511)
  26868. if(isNaN(this.value)){ delete this.value; }
  26869. this.inherited(arguments);
  26870. },
  26871. buildRendering: function(){
  26872. this.inherited(arguments);
  26873. dojo.setSelectable(this.domNode, false);
  26874. var cloneClass = dojo.hitch(this, function(clazz, n){
  26875. var template = dojo.query(clazz, this.domNode)[0];
  26876. for(var i=0; i<n; i++){
  26877. template.parentNode.appendChild(template.cloneNode(true));
  26878. }
  26879. });
  26880. // clone the day label and calendar day templates 6 times to make 7 columns
  26881. cloneClass(".dijitCalendarDayLabelTemplate", 6);
  26882. cloneClass(".dijitCalendarDateTemplate", 6);
  26883. // now make 6 week rows
  26884. cloneClass(".dijitCalendarWeekTemplate", 5);
  26885. // insert localized day names in the header
  26886. var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang);
  26887. var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
  26888. dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
  26889. this._setText(label, dayNames[(i + dayOffset) % 7]);
  26890. }, this);
  26891. var dateObj = new this.dateClassObj(this.currentFocus);
  26892. this.monthDropDownButton.dropDown = new dijit.Calendar._MonthDropDown({
  26893. id: this.id + "_mdd",
  26894. onChange: dojo.hitch(this, "_onMonthSelect")
  26895. });
  26896. this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
  26897. // Set up repeating mouse behavior for increment/decrement of months/years
  26898. var _this = this;
  26899. var typematic = function(nodeProp, dateProp, adj){
  26900. _this._connects.push(
  26901. dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
  26902. if(count >= 0){ _this._adjustDisplay(dateProp, adj); }
  26903. }, 0.8, 500)
  26904. );
  26905. };
  26906. typematic("incrementMonth", "month", 1);
  26907. typematic("decrementMonth", "month", -1);
  26908. typematic("nextYearLabelNode", "year", 1);
  26909. typematic("previousYearLabelNode", "year", -1);
  26910. },
  26911. _adjustDisplay: function(/*String*/ part, /*int*/ amount){
  26912. // summary:
  26913. // Moves calendar forwards or backwards by months or years
  26914. // part:
  26915. // "month" or "year"
  26916. // amount:
  26917. // Number of months or years
  26918. // tags:
  26919. // private
  26920. this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount));
  26921. },
  26922. _setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){
  26923. // summary:
  26924. // If the calendar currently has focus, then focuses specified date,
  26925. // changing the currently displayed month/year if necessary.
  26926. // If the calendar doesn't have focus, updates currently
  26927. // displayed month/year, and sets the cell that will get focus.
  26928. // forceFocus:
  26929. // If true, will focus() the cell even if calendar itself doesn't have focus
  26930. var oldFocus = this.currentFocus,
  26931. oldCell = oldFocus ? dojo.query("[dijitDateValue=" + oldFocus.valueOf() + "]", this.domNode)[0] : null;
  26932. // round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
  26933. date = new this.dateClassObj(date);
  26934. date.setHours(1, 0, 0, 0);
  26935. this._set("currentFocus", date);
  26936. // TODO: only re-populate grid when month/year has changed
  26937. this._populateGrid();
  26938. // set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
  26939. var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0];
  26940. newCell.setAttribute("tabIndex", this.tabIndex);
  26941. if(this._focused || forceFocus){
  26942. newCell.focus();
  26943. }
  26944. // set tabIndex=-1 on old focusable cell
  26945. if(oldCell && oldCell != newCell){
  26946. if(dojo.isWebKit){ // see #11064 about webkit bug
  26947. oldCell.setAttribute("tabIndex", "-1");
  26948. }else{
  26949. oldCell.removeAttribute("tabIndex");
  26950. }
  26951. }
  26952. },
  26953. focus: function(){
  26954. // summary:
  26955. // Focus the calendar by focusing one of the calendar cells
  26956. this._setCurrentFocusAttr(this.currentFocus, true);
  26957. },
  26958. _onMonthSelect: function(/*Number*/ newMonth){
  26959. // summary:
  26960. // Handler for when user selects a month from the drop down list
  26961. // tags:
  26962. // protected
  26963. // move to selected month, bounding by the number of days in the month
  26964. // (ex: dec 31 --> jan 28, not jan 31)
  26965. this.currentFocus = this.dateFuncObj.add(this.currentFocus, "month",
  26966. newMonth - this.currentFocus.getMonth());
  26967. this._populateGrid();
  26968. },
  26969. _onDayClick: function(/*Event*/ evt){
  26970. // summary:
  26971. // Handler for day clicks, selects the date if appropriate
  26972. // tags:
  26973. // protected
  26974. dojo.stopEvent(evt);
  26975. for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode);
  26976. if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){
  26977. this.set('value', node.dijitDateValue);
  26978. }
  26979. },
  26980. _onDayMouseOver: function(/*Event*/ evt){
  26981. // summary:
  26982. // Handler for mouse over events on days, sets hovered style
  26983. // tags:
  26984. // protected
  26985. // event can occur on <td> or the <span> inside the td,
  26986. // set node to the <td>.
  26987. var node =
  26988. dojo.hasClass(evt.target, "dijitCalendarDateLabel") ?
  26989. evt.target.parentNode :
  26990. evt.target;
  26991. if(node && (node.dijitDateValue || node == this.previousYearLabelNode || node == this.nextYearLabelNode) ){
  26992. dojo.addClass(node, "dijitCalendarHoveredDate");
  26993. this._currentNode = node;
  26994. }
  26995. },
  26996. _onDayMouseOut: function(/*Event*/ evt){
  26997. // summary:
  26998. // Handler for mouse out events on days, clears hovered style
  26999. // tags:
  27000. // protected
  27001. if(!this._currentNode){ return; }
  27002. // if mouse out occurs moving from <td> to <span> inside <td>, ignore it
  27003. if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; }
  27004. var cls = "dijitCalendarHoveredDate";
  27005. if(dojo.hasClass(this._currentNode, "dijitCalendarActiveDate")) {
  27006. cls += " dijitCalendarActiveDate";
  27007. }
  27008. dojo.removeClass(this._currentNode, cls);
  27009. this._currentNode = null;
  27010. },
  27011. _onDayMouseDown: function(/*Event*/ evt){
  27012. var node = evt.target.parentNode;
  27013. if(node && node.dijitDateValue){
  27014. dojo.addClass(node, "dijitCalendarActiveDate");
  27015. this._currentNode = node;
  27016. }
  27017. },
  27018. _onDayMouseUp: function(/*Event*/ evt){
  27019. var node = evt.target.parentNode;
  27020. if(node && node.dijitDateValue){
  27021. dojo.removeClass(node, "dijitCalendarActiveDate");
  27022. }
  27023. },
  27024. //TODO: use typematic
  27025. handleKey: function(/*Event*/ evt){
  27026. // summary:
  27027. // Provides keyboard navigation of calendar.
  27028. // description:
  27029. // Called from _onKeyPress() to handle keypress on a stand alone Calendar,
  27030. // and also from `dijit.form._DateTimeTextBox` to pass a keypress event
  27031. // from the `dijit.form.DateTextBox` to be handled in this widget
  27032. // returns:
  27033. // False if the key was recognized as a navigation key,
  27034. // to indicate that the event was handled by Calendar and shouldn't be propogated
  27035. // tags:
  27036. // protected
  27037. var dk = dojo.keys,
  27038. increment = -1,
  27039. interval,
  27040. newValue = this.currentFocus;
  27041. switch(evt.keyCode){
  27042. case dk.RIGHT_ARROW:
  27043. increment = 1;
  27044. //fallthrough...
  27045. case dk.LEFT_ARROW:
  27046. interval = "day";
  27047. if(!this.isLeftToRight()){ increment *= -1; }
  27048. break;
  27049. case dk.DOWN_ARROW:
  27050. increment = 1;
  27051. //fallthrough...
  27052. case dk.UP_ARROW:
  27053. interval = "week";
  27054. break;
  27055. case dk.PAGE_DOWN:
  27056. increment = 1;
  27057. //fallthrough...
  27058. case dk.PAGE_UP:
  27059. interval = evt.ctrlKey || evt.altKey ? "year" : "month";
  27060. break;
  27061. case dk.END:
  27062. // go to the next month
  27063. newValue = this.dateFuncObj.add(newValue, "month", 1);
  27064. // subtract a day from the result when we're done
  27065. interval = "day";
  27066. //fallthrough...
  27067. case dk.HOME:
  27068. newValue = new this.dateClassObj(newValue);
  27069. newValue.setDate(1);
  27070. break;
  27071. case dk.ENTER:
  27072. case dk.SPACE:
  27073. this.set("value", this.currentFocus);
  27074. break;
  27075. default:
  27076. return true;
  27077. }
  27078. if(interval){
  27079. newValue = this.dateFuncObj.add(newValue, interval, increment);
  27080. }
  27081. this._setCurrentFocusAttr(newValue);
  27082. return false;
  27083. },
  27084. _onKeyPress: function(/*Event*/ evt){
  27085. // summary:
  27086. // For handling keypress events on a stand alone calendar
  27087. if(!this.handleKey(evt)){
  27088. dojo.stopEvent(evt);
  27089. }
  27090. },
  27091. onValueSelected: function(/*Date*/ date){
  27092. // summary:
  27093. // Notification that a date cell was selected. It may be the same as the previous value.
  27094. // description:
  27095. // Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
  27096. // to get notification when the user has clicked a date. Now onExecute() (above) is used.
  27097. // tags:
  27098. // protected
  27099. },
  27100. onChange: function(/*Date*/ date){
  27101. // summary:
  27102. // Called only when the selected date has changed
  27103. },
  27104. _isSelectedDate: function(/*Date*/ dateObject, /*String?*/ locale){
  27105. // summary:
  27106. // Extension point so developers can subclass Calendar to
  27107. // support multiple (concurrently) selected dates
  27108. // tags:
  27109. // protected extension
  27110. return this._isValidDate(this.value) && !this.dateFuncObj.compare(dateObject, this.value, "date")
  27111. },
  27112. isDisabledDate: function(/*Date*/ dateObject, /*String?*/ locale){
  27113. // summary:
  27114. // May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
  27115. // tags:
  27116. // extension
  27117. /*=====
  27118. return false; // Boolean
  27119. =====*/
  27120. },
  27121. getClassForDate: function(/*Date*/ dateObject, /*String?*/ locale){
  27122. // summary:
  27123. // May be overridden to return CSS classes to associate with the date entry for the given dateObject,
  27124. // for example to indicate a holiday in specified locale.
  27125. // tags:
  27126. // extension
  27127. /*=====
  27128. return ""; // String
  27129. =====*/
  27130. }
  27131. }
  27132. );
  27133. dojo.declare("dijit.Calendar._MonthDropDown", [dijit._Widget, dijit._Templated], {
  27134. // summary:
  27135. // The month drop down
  27136. // months: String[]
  27137. // List of names of months, possibly w/some undefined entries for Hebrew leap months
  27138. // (ex: ["January", "February", undefined, "April", ...])
  27139. months: [],
  27140. templateString: "<div class='dijitCalendarMonthMenu dijitMenu' " +
  27141. "dojoAttachEvent='onclick:_onClick,onmouseover:_onMenuHover,onmouseout:_onMenuHover'></div>",
  27142. _setMonthsAttr: function(/*String[]*/ months){
  27143. this.domNode.innerHTML = dojo.map(months, function(month, idx){
  27144. return month ? "<div class='dijitCalendarMonthLabel' month='" + idx +"'>" + month + "</div>" : "";
  27145. }).join("");
  27146. },
  27147. _onClick: function(/*Event*/ evt){
  27148. this.onChange(dojo.attr(evt.target, "month"));
  27149. },
  27150. onChange: function(/*Number*/ month){
  27151. // summary:
  27152. // Callback when month is selected from drop down
  27153. },
  27154. _onMenuHover: function(evt){
  27155. dojo.toggleClass(evt.target, "dijitCalendarMonthLabelHover", evt.type == "mouseover");
  27156. }
  27157. });
  27158. }
  27159. if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27160. dojo._hasResource["dijit.form._DateTimeTextBox"] = true;
  27161. dojo.provide("dijit.form._DateTimeTextBox");
  27162. new Date("X"); // workaround for #11279, new Date("") == NaN
  27163. /*=====
  27164. dojo.declare(
  27165. "dijit.form._DateTimeTextBox.__Constraints",
  27166. [dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions], {
  27167. // summary:
  27168. // Specifies both the rules on valid/invalid values (first/last date/time allowed),
  27169. // and also formatting options for how the date/time is displayed.
  27170. // example:
  27171. // To restrict to dates within 2004, displayed in a long format like "December 25, 2005":
  27172. // | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'}
  27173. });
  27174. =====*/
  27175. dojo.declare(
  27176. "dijit.form._DateTimeTextBox",
  27177. [ dijit.form.RangeBoundTextBox, dijit._HasDropDown ],
  27178. {
  27179. // summary:
  27180. // Base class for validating, serializable, range-bound date or time text box.
  27181. 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"),
  27182. // hasDownArrow: [const] Boolean
  27183. // Set this textbox to display a down arrow button, to open the drop down list.
  27184. hasDownArrow: true,
  27185. // openOnClick: [const] Boolean
  27186. // Set to true to open drop down upon clicking anywhere on the textbox.
  27187. openOnClick: true,
  27188. /*=====
  27189. // constraints: dijit.form._DateTimeTextBox.__Constraints
  27190. // Despite the name, this parameter specifies both constraints on the input
  27191. // (including starting/ending dates/times allowed) as well as
  27192. // formatting options like whether the date is displayed in long (ex: December 25, 2005)
  27193. // or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details.
  27194. constraints: {},
  27195. ======*/
  27196. // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
  27197. // than a straight regexp to deal with locale (plus formatting options too?)
  27198. regExpGen: dojo.date.locale.regexp,
  27199. // datePackage: String
  27200. // JavaScript namespace to find calendar routines. Uses Gregorian calendar routines
  27201. // at dojo.date, by default.
  27202. datePackage: "dojo.date",
  27203. // Override _FormWidget.compare() to work for dates/times
  27204. compare: function(/*Date*/ val1, /*Date*/ val2){
  27205. var isInvalid1 = this._isInvalidDate(val1);
  27206. var isInvalid2 = this._isInvalidDate(val2);
  27207. return isInvalid1 ? (isInvalid2 ? 0 : -1) : (isInvalid2 ? 1 : dojo.date.compare(val1, val2, this._selector));
  27208. },
  27209. // flag to _HasDropDown to make drop down Calendar width == <input> width
  27210. forceWidth: true,
  27211. format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
  27212. // summary:
  27213. // Formats the value as a Date, according to specified locale (second argument)
  27214. // tags:
  27215. // protected
  27216. if(!value){ return ''; }
  27217. return this.dateLocaleModule.format(value, constraints);
  27218. },
  27219. "parse": function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
  27220. // summary:
  27221. // Parses as string as a Date, according to constraints
  27222. // tags:
  27223. // protected
  27224. return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date
  27225. },
  27226. // Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format.
  27227. serialize: function(/*anything*/ val, /*Object?*/ options){
  27228. if(val.toGregorian){
  27229. val = val.toGregorian();
  27230. }
  27231. return dojo.date.stamp.toISOString(val, options);
  27232. },
  27233. // dropDownDefaultValue: Date
  27234. // The default value to focus in the popupClass widget when the textbox value is empty.
  27235. dropDownDefaultValue : new Date(),
  27236. // value: Date
  27237. // The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate.
  27238. // When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
  27239. value: new Date(""), // value.toString()="NaN"
  27240. _blankValue: null, // used by filter() when the textbox is blank
  27241. // popupClass: [protected extension] String
  27242. // Name of the popup widget class used to select a date/time.
  27243. // Subclasses should specify this.
  27244. popupClass: "", // default is no popup = text only
  27245. // _selector: [protected extension] String
  27246. // Specifies constraints.selector passed to dojo.date functions, should be either
  27247. // "date" or "time".
  27248. // Subclass must specify this.
  27249. _selector: "",
  27250. constructor: function(/*Object*/ args){
  27251. var dateClass = args.datePackage ? args.datePackage + ".Date" : "Date";
  27252. this.dateClassObj = dojo.getObject(dateClass, false);
  27253. this.value = new this.dateClassObj("");
  27254. this.datePackage = args.datePackage || this.datePackage;
  27255. this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
  27256. this.regExpGen = this.dateLocaleModule.regexp;
  27257. this._invalidDate = dijit.form._DateTimeTextBox.prototype.value.toString();
  27258. },
  27259. buildRendering: function(){
  27260. this.inherited(arguments);
  27261. if(!this.hasDownArrow){
  27262. this._buttonNode.style.display = "none";
  27263. }
  27264. // If openOnClick is true, we basically just want to treat the whole widget as the
  27265. // button. We need to do that also if the actual drop down button will be hidden,
  27266. // so that there's a mouse method for opening the drop down.
  27267. if(this.openOnClick || !this.hasDownArrow){
  27268. this._buttonNode = this.domNode;
  27269. this.baseClass += " dijitComboBoxOpenOnClick";
  27270. }
  27271. },
  27272. _setConstraintsAttr: function(/*Object*/ constraints){
  27273. constraints.selector = this._selector;
  27274. constraints.fullYear = true; // see #5465 - always format with 4-digit years
  27275. var fromISO = dojo.date.stamp.fromISOString;
  27276. if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
  27277. if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
  27278. this.inherited(arguments);
  27279. },
  27280. _isInvalidDate: function(/*Date*/ value){
  27281. // summary:
  27282. // Runs various tests on the value, checking for invalid conditions
  27283. // tags:
  27284. // private
  27285. return !value || isNaN(value) || typeof value != "object" || value.toString() == this._invalidDate;
  27286. },
  27287. _setValueAttr: function(/*Date|String*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  27288. // summary:
  27289. // Sets the date on this textbox. Note: value can be a JavaScript Date literal or a string to be parsed.
  27290. if(value !== undefined){
  27291. if(typeof value == "string"){
  27292. value = dojo.date.stamp.fromISOString(value);
  27293. }
  27294. if(this._isInvalidDate(value)){
  27295. value = null;
  27296. }
  27297. if(value instanceof Date && !(this.dateClassObj instanceof Date)){
  27298. value = new this.dateClassObj(value);
  27299. }
  27300. }
  27301. this.inherited(arguments);
  27302. if(this.value instanceof Date){
  27303. this.filterString = "";
  27304. }
  27305. if(this.dropDown){
  27306. this.dropDown.set('value', value, false);
  27307. }
  27308. },
  27309. _set: function(attr, value){
  27310. // Avoid spurious watch() notifications when value is changed to new Date object w/the same value
  27311. if(attr == "value" && this.value instanceof Date && this.compare(value, this.value) == 0){
  27312. return;
  27313. }
  27314. this.inherited(arguments);
  27315. },
  27316. _setDropDownDefaultValueAttr: function(/*Date*/ val){
  27317. if(this._isInvalidDate(val)){
  27318. // convert null setting into today's date, since there needs to be *some* default at all times.
  27319. val = new this.dateClassObj();
  27320. }
  27321. this.dropDownDefaultValue = val;
  27322. },
  27323. openDropDown: function(/*Function*/ callback){
  27324. // rebuild drop down every time, so that constraints get copied (#6002)
  27325. if(this.dropDown){
  27326. this.dropDown.destroy();
  27327. }
  27328. var PopupProto = dojo.getObject(this.popupClass, false),
  27329. textBox = this,
  27330. value = this.get("value");
  27331. this.dropDown = new PopupProto({
  27332. onChange: function(value){
  27333. // this will cause InlineEditBox and other handlers to do stuff so make sure it's last
  27334. textBox.set('value', value, true);
  27335. },
  27336. id: this.id + "_popup",
  27337. dir: textBox.dir,
  27338. lang: textBox.lang,
  27339. value: value,
  27340. currentFocus: !this._isInvalidDate(value) ? value : this.dropDownDefaultValue,
  27341. constraints: textBox.constraints,
  27342. filterString: textBox.filterString, // for TimeTextBox, to filter times shown
  27343. datePackage: textBox.datePackage,
  27344. isDisabledDate: function(/*Date*/ date){
  27345. // summary:
  27346. // disables dates outside of the min/max of the _DateTimeTextBox
  27347. return !textBox.rangeCheck(date, textBox.constraints);
  27348. }
  27349. });
  27350. this.inherited(arguments);
  27351. },
  27352. _getDisplayedValueAttr: function(){
  27353. return this.textbox.value;
  27354. },
  27355. _setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
  27356. this._setValueAttr(this.parse(value, this.constraints), priorityChange, value);
  27357. }
  27358. }
  27359. );
  27360. }
  27361. if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27362. dojo._hasResource["dijit.form.DateTextBox"] = true;
  27363. dojo.provide("dijit.form.DateTextBox");
  27364. dojo.declare(
  27365. "dijit.form.DateTextBox",
  27366. dijit.form._DateTimeTextBox,
  27367. {
  27368. // summary:
  27369. // A validating, serializable, range-bound date text box with a drop down calendar
  27370. //
  27371. // Example:
  27372. // | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)})
  27373. //
  27374. // Example:
  27375. // | <input dojotype='dijit.form.DateTextBox' value='2009-01-20'>
  27376. baseClass: "dijitTextBox dijitComboBox dijitDateTextBox",
  27377. popupClass: "dijit.Calendar",
  27378. _selector: "date",
  27379. // value: Date
  27380. // The value of this widget as a JavaScript Date object, with only year/month/day specified.
  27381. // If specified in markup, use the format specified in `dojo.date.stamp.fromISOString`.
  27382. // set("value", ...) accepts either a Date object or a string.
  27383. value: new Date("") // value.toString()="NaN"
  27384. }
  27385. );
  27386. }
  27387. if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27388. dojo._hasResource["dijit.form.Form"] = true;
  27389. dojo.provide("dijit.form.Form");
  27390. dojo.declare(
  27391. "dijit.form.Form",
  27392. [dijit._Widget, dijit._Templated, dijit.form._FormMixin, dijit.layout._ContentPaneResizeMixin],
  27393. {
  27394. // summary:
  27395. // Widget corresponding to HTML form tag, for validation and serialization
  27396. //
  27397. // example:
  27398. // | <form dojoType="dijit.form.Form" id="myForm">
  27399. // | Name: <input type="text" name="name" />
  27400. // | </form>
  27401. // | myObj = {name: "John Doe"};
  27402. // | dijit.byId('myForm').set('value', myObj);
  27403. // |
  27404. // | myObj=dijit.byId('myForm').get('value');
  27405. // HTML <FORM> attributes
  27406. // name: String?
  27407. // Name of form for scripting.
  27408. name: "",
  27409. // action: String?
  27410. // Server-side form handler.
  27411. action: "",
  27412. // method: String?
  27413. // HTTP method used to submit the form, either "GET" or "POST".
  27414. method: "",
  27415. // encType: String?
  27416. // Encoding type for the form, ex: application/x-www-form-urlencoded.
  27417. encType: "",
  27418. // accept-charset: String?
  27419. // List of supported charsets.
  27420. "accept-charset": "",
  27421. // accept: String?
  27422. // List of MIME types for file upload.
  27423. accept: "",
  27424. // target: String?
  27425. // Target frame for the document to be opened in.
  27426. target: "",
  27427. templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
  27428. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  27429. action: "",
  27430. method: "",
  27431. encType: "",
  27432. "accept-charset": "",
  27433. accept: "",
  27434. target: ""
  27435. }),
  27436. postMixInProperties: function(){
  27437. // Setup name=foo string to be referenced from the template (but only if a name has been specified)
  27438. // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
  27439. this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
  27440. this.inherited(arguments);
  27441. },
  27442. execute: function(/*Object*/ formContents){
  27443. // summary:
  27444. // Deprecated: use submit()
  27445. // tags:
  27446. // deprecated
  27447. },
  27448. onExecute: function(){
  27449. // summary:
  27450. // Deprecated: use onSubmit()
  27451. // tags:
  27452. // deprecated
  27453. },
  27454. _setEncTypeAttr: function(/*String*/ value){
  27455. this.encType = value;
  27456. dojo.attr(this.domNode, "encType", value);
  27457. if(dojo.isIE){ this.domNode.encoding = value; }
  27458. },
  27459. postCreate: function(){
  27460. // IE tries to hide encType
  27461. // TODO: remove in 2.0, no longer necessary with data-dojo-params
  27462. if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
  27463. var item = this.srcNodeRef.attributes.getNamedItem('encType');
  27464. if(item && !item.specified && (typeof item.value == "string")){
  27465. this.set('encType', item.value);
  27466. }
  27467. }
  27468. this.inherited(arguments);
  27469. },
  27470. reset: function(/*Event?*/ e){
  27471. // summary:
  27472. // restores all widget values back to their init values,
  27473. // calls onReset() which can cancel the reset by returning false
  27474. // create fake event so we can know if preventDefault() is called
  27475. var faux = {
  27476. returnValue: true, // the IE way
  27477. preventDefault: function(){ // not IE
  27478. this.returnValue = false;
  27479. },
  27480. stopPropagation: function(){},
  27481. currentTarget: e ? e.target : this.domNode,
  27482. target: e ? e.target : this.domNode
  27483. };
  27484. // if return value is not exactly false, and haven't called preventDefault(), then reset
  27485. if(!(this.onReset(faux) === false) && faux.returnValue){
  27486. this.inherited(arguments, []);
  27487. }
  27488. },
  27489. onReset: function(/*Event?*/ e){
  27490. // summary:
  27491. // Callback when user resets the form. This method is intended
  27492. // to be over-ridden. When the `reset` method is called
  27493. // programmatically, the return value from `onReset` is used
  27494. // to compute whether or not resetting should proceed
  27495. // tags:
  27496. // callback
  27497. return true; // Boolean
  27498. },
  27499. _onReset: function(e){
  27500. this.reset(e);
  27501. dojo.stopEvent(e);
  27502. return false;
  27503. },
  27504. _onSubmit: function(e){
  27505. var fp = dijit.form.Form.prototype;
  27506. // TODO: remove this if statement beginning with 2.0
  27507. if(this.execute != fp.execute || this.onExecute != fp.onExecute){
  27508. dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
  27509. this.onExecute();
  27510. this.execute(this.getValues());
  27511. }
  27512. if(this.onSubmit(e) === false){ // only exactly false stops submit
  27513. dojo.stopEvent(e);
  27514. }
  27515. },
  27516. onSubmit: function(/*Event?*/ e){
  27517. // summary:
  27518. // Callback when user submits the form.
  27519. // description:
  27520. // This method is intended to be over-ridden, but by default it checks and
  27521. // returns the validity of form elements. When the `submit`
  27522. // method is called programmatically, the return value from
  27523. // `onSubmit` is used to compute whether or not submission
  27524. // should proceed
  27525. // tags:
  27526. // extension
  27527. return this.isValid(); // Boolean
  27528. },
  27529. submit: function(){
  27530. // summary:
  27531. // programmatically submit form if and only if the `onSubmit` returns true
  27532. if(!(this.onSubmit() === false)){
  27533. this.containerNode.submit();
  27534. }
  27535. }
  27536. }
  27537. );
  27538. }
  27539. if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27540. dojo._hasResource["dijit.form.NumberTextBox"] = true;
  27541. dojo.provide("dijit.form.NumberTextBox");
  27542. /*=====
  27543. dojo.declare(
  27544. "dijit.form.NumberTextBox.__Constraints",
  27545. [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
  27546. // summary:
  27547. // Specifies both the rules on valid/invalid values (minimum, maximum,
  27548. // number of required decimal places), and also formatting options for
  27549. // displaying the value when the field is not focused.
  27550. // example:
  27551. // Minimum/maximum:
  27552. // To specify a field between 0 and 120:
  27553. // | {min:0,max:120}
  27554. // To specify a field that must be an integer:
  27555. // | {fractional:false}
  27556. // To specify a field where 0 to 3 decimal places are allowed on input:
  27557. // | {places:'0,3'}
  27558. });
  27559. =====*/
  27560. dojo.declare("dijit.form.NumberTextBoxMixin",
  27561. null,
  27562. {
  27563. // summary:
  27564. // A mixin for all number textboxes
  27565. // tags:
  27566. // protected
  27567. // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
  27568. // than a straight regexp to deal with locale (plus formatting options too?)
  27569. regExpGen: dojo.number.regexp,
  27570. /*=====
  27571. // constraints: dijit.form.NumberTextBox.__Constraints
  27572. // Despite the name, this parameter specifies both constraints on the input
  27573. // (including minimum/maximum allowed values) as well as
  27574. // formatting options like places (the number of digits to display after
  27575. // the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
  27576. constraints: {},
  27577. ======*/
  27578. // value: Number
  27579. // The value of this NumberTextBox as a Javascript Number (i.e., not a String).
  27580. // If the displayed value is blank, the value is NaN, and if the user types in
  27581. // an gibberish value (like "hello world"), the value is undefined
  27582. // (i.e. get('value') returns undefined).
  27583. //
  27584. // Symmetrically, set('value', NaN) will clear the displayed value,
  27585. // whereas set('value', undefined) will have no effect.
  27586. value: NaN,
  27587. // editOptions: [protected] Object
  27588. // Properties to mix into constraints when the value is being edited.
  27589. // This is here because we edit the number in the format "12345", which is
  27590. // different than the display value (ex: "12,345")
  27591. editOptions: { pattern: '#.######' },
  27592. /*=====
  27593. _formatter: function(value, options){
  27594. // summary:
  27595. // _formatter() is called by format(). It's the base routine for formatting a number,
  27596. // as a string, for example converting 12345 into "12,345".
  27597. // value: Number
  27598. // The number to be converted into a string.
  27599. // options: dojo.number.__FormatOptions?
  27600. // Formatting options
  27601. // tags:
  27602. // protected extension
  27603. return "12345"; // String
  27604. },
  27605. =====*/
  27606. _formatter: dojo.number.format,
  27607. _setConstraintsAttr: function(/*Object*/ constraints){
  27608. var places = typeof constraints.places == "number"? constraints.places : 0;
  27609. if(places){ places++; } // decimal rounding errors take away another digit of precision
  27610. if(typeof constraints.max != "number"){
  27611. constraints.max = 9 * Math.pow(10, 15-places);
  27612. }
  27613. if(typeof constraints.min != "number"){
  27614. constraints.min = -9 * Math.pow(10, 15-places);
  27615. }
  27616. this.inherited(arguments, [ constraints ]);
  27617. if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
  27618. this.set('value', this.value);
  27619. }
  27620. },
  27621. _onFocus: function(){
  27622. if(this.disabled){ return; }
  27623. var val = this.get('value');
  27624. if(typeof val == "number" && !isNaN(val)){
  27625. var formattedValue = this.format(val, this.constraints);
  27626. if(formattedValue !== undefined){
  27627. this.textbox.value = formattedValue;
  27628. }
  27629. }
  27630. this.inherited(arguments);
  27631. },
  27632. format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
  27633. // summary:
  27634. // Formats the value as a Number, according to constraints.
  27635. // tags:
  27636. // protected
  27637. var formattedValue = String(value);
  27638. if(typeof value != "number"){ return formattedValue; }
  27639. if(isNaN(value)){ return ""; }
  27640. // check for exponential notation that dojo.number.format chokes on
  27641. if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
  27642. return formattedValue;
  27643. }
  27644. if(this.editOptions && this._focused){
  27645. constraints = dojo.mixin({}, constraints, this.editOptions);
  27646. }
  27647. return this._formatter(value, constraints);
  27648. },
  27649. /*=====
  27650. _parser: function(value, constraints){
  27651. // summary:
  27652. // Parses the string value as a Number, according to constraints.
  27653. // value: String
  27654. // String representing a number
  27655. // constraints: dojo.number.__ParseOptions
  27656. // Formatting options
  27657. // tags:
  27658. // protected
  27659. return 123.45; // Number
  27660. },
  27661. =====*/
  27662. _parser: dojo.number.parse,
  27663. parse: function(/*String*/ value, /*dojo.number.__FormatOptions*/ constraints){
  27664. // summary:
  27665. // Replacable function to convert a formatted string to a number value
  27666. // tags:
  27667. // protected extension
  27668. var v = this._parser(value, dojo.mixin({}, constraints, (this.editOptions && this._focused) ? this.editOptions : {}));
  27669. if(this.editOptions && this._focused && isNaN(v)){
  27670. v = this._parser(value, constraints); // parse w/o editOptions: not technically needed but is nice for the user
  27671. }
  27672. return v;
  27673. },
  27674. _getDisplayedValueAttr: function(){
  27675. var v = this.inherited(arguments);
  27676. return isNaN(v) ? this.textbox.value : v;
  27677. },
  27678. filter: function(/*Number*/ value){
  27679. // summary:
  27680. // This is called with both the display value (string), and the actual value (a number).
  27681. // When called with the actual value it does corrections so that '' etc. are represented as NaN.
  27682. // Otherwise it dispatches to the superclass's filter() method.
  27683. //
  27684. // See `dijit.form.TextBox.filter` for more details.
  27685. return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // set('value', null||''||undefined) should fire onChange(NaN)
  27686. },
  27687. serialize: function(/*Number*/ value, /*Object?*/ options){
  27688. // summary:
  27689. // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
  27690. // tags:
  27691. // protected
  27692. return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
  27693. },
  27694. _setBlurValue: function(){
  27695. var val = dojo.hitch(dojo.mixin({}, this, { _focused: true }), "get")('value'); // parse with editOptions
  27696. this._setValueAttr(val, true);
  27697. },
  27698. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
  27699. // summary:
  27700. // Hook so set('value', ...) works.
  27701. if(value !== undefined && formattedValue === undefined){
  27702. formattedValue = String(value);
  27703. if(typeof value == "number"){
  27704. if(isNaN(value)){ formattedValue = '' }
  27705. // check for exponential notation that dojo.number.format chokes on
  27706. else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
  27707. formattedValue = undefined; // lets format comnpute a real string value
  27708. }
  27709. }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
  27710. formattedValue = '';
  27711. value = NaN;
  27712. }else{ // non-numeric values
  27713. value = undefined;
  27714. }
  27715. }
  27716. this.inherited(arguments, [value, priorityChange, formattedValue]);
  27717. },
  27718. _getValueAttr: function(){
  27719. // summary:
  27720. // Hook so get('value') works.
  27721. // Returns Number, NaN for '', or undefined for unparsable text
  27722. var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
  27723. // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
  27724. // returns NaN; this if() branch converts the return value to undefined.
  27725. // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
  27726. // A blank displayed value is still returned as NaN.
  27727. if(isNaN(v) && this.textbox.value !== ''){
  27728. 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?)
  27729. var n = Number(this.textbox.value);
  27730. return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
  27731. }else{
  27732. return undefined; // gibberish
  27733. }
  27734. }else{
  27735. return v; // Number or NaN for ''
  27736. }
  27737. },
  27738. isValid: function(/*Boolean*/ isFocused){
  27739. // Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
  27740. // it may not be formatted according to the regExp vaidation rules
  27741. if(!this._focused || this._isEmpty(this.textbox.value)){
  27742. return this.inherited(arguments);
  27743. }else{
  27744. var v = this.get('value');
  27745. if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
  27746. if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
  27747. return true; // valid exponential number in range
  27748. }else{
  27749. return this.inherited(arguments);
  27750. }
  27751. }else{
  27752. return false;
  27753. }
  27754. }
  27755. }
  27756. }
  27757. );
  27758. dojo.declare("dijit.form.NumberTextBox",
  27759. [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
  27760. {
  27761. // summary:
  27762. // A TextBox for entering numbers, with formatting and range checking
  27763. // description:
  27764. // NumberTextBox is a textbox for entering and displaying numbers, supporting
  27765. // the following main features:
  27766. //
  27767. // 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
  27768. // a number rather than a random string)
  27769. // 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
  27770. // depending on locale).
  27771. // 3. Separate modes for editing the value and displaying it, specifically that
  27772. // the thousands separator character (typically comma) disappears when editing
  27773. // but reappears after the field is blurred.
  27774. // 4. Formatting and constraints regarding the number of places (digits after the decimal point)
  27775. // allowed on input, and number of places displayed when blurred (see `constraints` parameter).
  27776. baseClass: "dijitTextBox dijitNumberTextBox"
  27777. }
  27778. );
  27779. }
  27780. if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27781. dojo._hasResource["dijit.form.RadioButton"] = true;
  27782. dojo.provide("dijit.form.RadioButton");
  27783. // TODO: for 2.0, move the RadioButton code into this file
  27784. }
  27785. if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  27786. dojo._hasResource["dijit.form.HorizontalSlider"] = true;
  27787. dojo.provide("dijit.form.HorizontalSlider");
  27788. dojo.declare(
  27789. "dijit.form.HorizontalSlider",
  27790. [dijit.form._FormValueWidget, dijit._Container],
  27791. {
  27792. // summary:
  27793. // A form widget that allows one to select a value with a horizontally draggable handle
  27794. 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"),
  27795. // Overrides FormValueWidget.value to indicate numeric value
  27796. value: 0,
  27797. // showButtons: [const] Boolean
  27798. // Show increment/decrement buttons at the ends of the slider?
  27799. showButtons: true,
  27800. // minimum:: [const] Integer
  27801. // The minimum value the slider can be set to.
  27802. minimum: 0,
  27803. // maximum: [const] Integer
  27804. // The maximum value the slider can be set to.
  27805. maximum: 100,
  27806. // discreteValues: Integer
  27807. // If specified, indicates that the slider handle has only 'discreteValues' possible positions,
  27808. // and that after dragging the handle, it will snap to the nearest possible position.
  27809. // Thus, the slider has only 'discreteValues' possible values.
  27810. //
  27811. // For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
  27812. // three possible positions, representing values 10, 20, or 30.
  27813. //
  27814. // If discreteValues is not specified or if it's value is higher than the number of pixels
  27815. // in the slider bar, then the slider handle can be moved freely, and the slider's value will be
  27816. // computed/reported based on pixel position (in this case it will likely be fractional,
  27817. // such as 123.456789).
  27818. discreteValues: Infinity,
  27819. // pageIncrement: Integer
  27820. // If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
  27821. // that the slider handle is moved via pageup/pagedown keys.
  27822. // If discreteValues is not specified, it indicates the number of pixels.
  27823. pageIncrement: 2,
  27824. // clickSelect: Boolean
  27825. // If clicking the slider bar changes the value or not
  27826. clickSelect: true,
  27827. // slideDuration: Number
  27828. // The time in ms to take to animate the slider handle from 0% to 100%,
  27829. // when clicking the slider bar to make the handle move.
  27830. slideDuration: dijit.defaultDuration,
  27831. // Flag to _Templated (TODO: why is this here? I see no widgets in the template.)
  27832. widgetsInTemplate: true,
  27833. attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
  27834. id: ""
  27835. }),
  27836. baseClass: "dijitSlider",
  27837. // Apply CSS classes to up/down arrows and handle per mouse state
  27838. cssStateNodes: {
  27839. incrementButton: "dijitSliderIncrementButton",
  27840. decrementButton: "dijitSliderDecrementButton",
  27841. focusNode: "dijitSliderThumb"
  27842. },
  27843. _mousePixelCoord: "pageX",
  27844. _pixelCount: "w",
  27845. _startingPixelCoord: "x",
  27846. _startingPixelCount: "l",
  27847. _handleOffsetCoord: "left",
  27848. _progressPixelSize: "width",
  27849. _onKeyUp: function(/*Event*/ e){
  27850. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  27851. this._setValueAttr(this.value, true);
  27852. },
  27853. _onKeyPress: function(/*Event*/ e){
  27854. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
  27855. switch(e.charOrCode){
  27856. case dojo.keys.HOME:
  27857. this._setValueAttr(this.minimum, false);
  27858. break;
  27859. case dojo.keys.END:
  27860. this._setValueAttr(this.maximum, false);
  27861. break;
  27862. // this._descending === false: if ascending vertical (min on top)
  27863. // (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
  27864. case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
  27865. case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
  27866. case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
  27867. this.increment(e);
  27868. break;
  27869. case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
  27870. case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
  27871. case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
  27872. this.decrement(e);
  27873. break;
  27874. default:
  27875. return;
  27876. }
  27877. dojo.stopEvent(e);
  27878. },
  27879. _onHandleClick: function(e){
  27880. if(this.disabled || this.readOnly){ return; }
  27881. if(!dojo.isIE){
  27882. // make sure you get focus when dragging the handle
  27883. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  27884. dijit.focus(this.sliderHandle);
  27885. }
  27886. dojo.stopEvent(e);
  27887. },
  27888. _isReversed: function(){
  27889. // summary:
  27890. // Returns true if direction is from right to left
  27891. // tags:
  27892. // protected extension
  27893. return !this.isLeftToRight();
  27894. },
  27895. _onBarClick: function(e){
  27896. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  27897. dijit.focus(this.sliderHandle);
  27898. dojo.stopEvent(e);
  27899. var abspos = dojo.position(this.sliderBarContainer, true);
  27900. var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
  27901. this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
  27902. this._movable.onMouseDown(e);
  27903. },
  27904. _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){
  27905. if(this.disabled || this.readOnly){ return; }
  27906. pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
  27907. var count = this.discreteValues;
  27908. if(count <= 1 || count == Infinity){ count = maxPixels; }
  27909. count--;
  27910. var pixelsPerValue = maxPixels / count;
  27911. var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
  27912. this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
  27913. },
  27914. _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
  27915. // summary:
  27916. // Hook so set('value', value) works.
  27917. this._set("value", value);
  27918. this.valueNode.value = value;
  27919. dijit.setWaiState(this.focusNode, "valuenow", value);
  27920. this.inherited(arguments);
  27921. var percent = (value - this.minimum) / (this.maximum - this.minimum);
  27922. var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
  27923. var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
  27924. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  27925. this._inProgressAnim.stop(true);
  27926. }
  27927. if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
  27928. // animate the slider
  27929. var _this = this;
  27930. var props = {};
  27931. var start = parseFloat(progressBar.style[this._progressPixelSize]);
  27932. var duration = this.slideDuration * (percent-start/100);
  27933. if(duration == 0){ return; }
  27934. if(duration < 0){ duration = 0 - duration; }
  27935. props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
  27936. this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration,
  27937. onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; },
  27938. onEnd: function(){ delete _this._inProgressAnim; },
  27939. properties: props
  27940. })
  27941. this._inProgressAnim.play();
  27942. }else{
  27943. progressBar.style[this._progressPixelSize] = (percent*100) + "%";
  27944. remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
  27945. }
  27946. },
  27947. _bumpValue: function(signedChange, /*Boolean?*/ priorityChange){
  27948. if(this.disabled || this.readOnly){ return; }
  27949. var s = dojo.getComputedStyle(this.sliderBarContainer);
  27950. var c = dojo._getContentBox(this.sliderBarContainer, s);
  27951. var count = this.discreteValues;
  27952. if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
  27953. count--;
  27954. var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
  27955. if(value < 0){ value = 0; }
  27956. if(value > count){ value = count; }
  27957. value = value * (this.maximum - this.minimum) / count + this.minimum;
  27958. this._setValueAttr(value, priorityChange);
  27959. },
  27960. _onClkBumper: function(val){
  27961. if(this.disabled || this.readOnly || !this.clickSelect){ return; }
  27962. this._setValueAttr(val, true);
  27963. },
  27964. _onClkIncBumper: function(){
  27965. this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
  27966. },
  27967. _onClkDecBumper: function(){
  27968. this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
  27969. },
  27970. decrement: function(/*Event*/ e){
  27971. // summary:
  27972. // Decrement slider
  27973. // tags:
  27974. // private
  27975. this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1);
  27976. },
  27977. increment: function(/*Event*/ e){
  27978. // summary:
  27979. // Increment slider
  27980. // tags:
  27981. // private
  27982. this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1);
  27983. },
  27984. _mouseWheeled: function(/*Event*/ evt){
  27985. // summary:
  27986. // Event handler for mousewheel where supported
  27987. dojo.stopEvent(evt);
  27988. var janky = !dojo.isMozilla;
  27989. var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
  27990. this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
  27991. },
  27992. startup: function(){
  27993. if(this._started){ return; }
  27994. dojo.forEach(this.getChildren(), function(child){
  27995. if(this[child.container] != this.containerNode){
  27996. this[child.container].appendChild(child.domNode);
  27997. }
  27998. }, this);
  27999. this.inherited(arguments);
  28000. },
  28001. _typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
  28002. if(count == -1){
  28003. this._setValueAttr(this.value, true);
  28004. }else{
  28005. this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
  28006. }
  28007. },
  28008. buildRendering: function(){
  28009. this.inherited(arguments);
  28010. if(this.showButtons){
  28011. this.incrementButton.style.display="";
  28012. this.decrementButton.style.display="";
  28013. }
  28014. // find any associated label element and add to slider focusnode.
  28015. var label = dojo.query('label[for="'+this.id+'"]');
  28016. if(label.length){
  28017. label[0].id = (this.id+"_label");
  28018. dijit.setWaiState(this.focusNode, "labelledby", label[0].id);
  28019. }
  28020. dijit.setWaiState(this.focusNode, "valuemin", this.minimum);
  28021. dijit.setWaiState(this.focusNode, "valuemax", this.maximum);
  28022. },
  28023. postCreate: function(){
  28024. this.inherited(arguments);
  28025. if(this.showButtons){
  28026. this._connects.push(dijit.typematic.addMouseListener(
  28027. this.decrementButton, this, "_typematicCallback", 25, 500));
  28028. this._connects.push(dijit.typematic.addMouseListener(
  28029. this.incrementButton, this, "_typematicCallback", 25, 500));
  28030. }
  28031. this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
  28032. // define a custom constructor for a SliderMover that points back to me
  28033. var mover = dojo.declare(dijit.form._SliderMover, {
  28034. widget: this
  28035. });
  28036. this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
  28037. this._layoutHackIE7();
  28038. },
  28039. destroy: function(){
  28040. this._movable.destroy();
  28041. if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
  28042. this._inProgressAnim.stop(true);
  28043. }
  28044. this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
  28045. this.inherited(arguments);
  28046. }
  28047. });
  28048. dojo.declare("dijit.form._SliderMover",
  28049. dojo.dnd.Mover,
  28050. {
  28051. onMouseMove: function(e){
  28052. var widget = this.widget;
  28053. var abspos = widget._abspos;
  28054. if(!abspos){
  28055. abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true);
  28056. widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
  28057. widget._isReversed_ = widget._isReversed();
  28058. }
  28059. var coordEvent = e.touches ? e.touches[0] : e, // if multitouch take first touch for coords
  28060. pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
  28061. widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
  28062. },
  28063. destroy: function(e){
  28064. dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
  28065. var widget = this.widget;
  28066. widget._abspos = null;
  28067. widget._setValueAttr(widget.value, true);
  28068. }
  28069. });
  28070. }
  28071. if(!dojo._hasResource["dijit.form.VerticalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28072. dojo._hasResource["dijit.form.VerticalSlider"] = true;
  28073. dojo.provide("dijit.form.VerticalSlider");
  28074. dojo.declare(
  28075. "dijit.form.VerticalSlider",
  28076. dijit.form.HorizontalSlider,
  28077. {
  28078. // summary:
  28079. // A form widget that allows one to select a value with a vertically draggable handle
  28080. 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"),
  28081. _mousePixelCoord: "pageY",
  28082. _pixelCount: "h",
  28083. _startingPixelCoord: "y",
  28084. _startingPixelCount: "t",
  28085. _handleOffsetCoord: "top",
  28086. _progressPixelSize: "height",
  28087. // _descending: Boolean
  28088. // Specifies if the slider values go from high-on-top (true), or low-on-top (false)
  28089. // TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
  28090. _descending: true,
  28091. _isReversed: function(){
  28092. // summary:
  28093. // Overrides HorizontalSlider._isReversed.
  28094. // Indicates if values are high on top (with low numbers on the bottom).
  28095. return this._descending;
  28096. }
  28097. });
  28098. }
  28099. if(!dojo._hasResource["dijit.form.HorizontalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28100. dojo._hasResource["dijit.form.HorizontalRule"] = true;
  28101. dojo.provide("dijit.form.HorizontalRule");
  28102. dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
  28103. {
  28104. // summary:
  28105. // Hash marks for `dijit.form.HorizontalSlider`
  28106. templateString: '<div class="dijitRuleContainer dijitRuleContainerH"></div>',
  28107. // count: Integer
  28108. // Number of hash marks to generate
  28109. count: 3,
  28110. // container: String
  28111. // For HorizontalSlider, this is either "topDecoration" or "bottomDecoration",
  28112. // and indicates whether this rule goes above or below the slider.
  28113. container: "containerNode",
  28114. // ruleStyle: String
  28115. // CSS style to apply to individual hash marks
  28116. ruleStyle: "",
  28117. _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkH" style="left:',
  28118. _positionSuffix: '%;',
  28119. _suffix: '"></div>',
  28120. _genHTML: function(pos, ndx){
  28121. return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
  28122. },
  28123. // _isHorizontal: [protected extension] Boolean
  28124. // VerticalRule will override this...
  28125. _isHorizontal: true,
  28126. buildRendering: function(){
  28127. this.inherited(arguments);
  28128. var innerHTML;
  28129. if(this.count == 1){
  28130. innerHTML = this._genHTML(50, 0);
  28131. }else{
  28132. var i;
  28133. var interval = 100 / (this.count-1);
  28134. if(!this._isHorizontal || this.isLeftToRight()){
  28135. innerHTML = this._genHTML(0, 0);
  28136. for(i=1; i < this.count-1; i++){
  28137. innerHTML += this._genHTML(interval*i, i);
  28138. }
  28139. innerHTML += this._genHTML(100, this.count-1);
  28140. }else{
  28141. innerHTML = this._genHTML(100, 0);
  28142. for(i=1; i < this.count-1; i++){
  28143. innerHTML += this._genHTML(100-interval*i, i);
  28144. }
  28145. innerHTML += this._genHTML(0, this.count-1);
  28146. }
  28147. }
  28148. this.domNode.innerHTML = innerHTML;
  28149. }
  28150. });
  28151. }
  28152. if(!dojo._hasResource["dijit.form.VerticalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28153. dojo._hasResource["dijit.form.VerticalRule"] = true;
  28154. dojo.provide("dijit.form.VerticalRule");
  28155. dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
  28156. {
  28157. // summary:
  28158. // Hash marks for the `dijit.form.VerticalSlider`
  28159. templateString: '<div class="dijitRuleContainer dijitRuleContainerV"></div>',
  28160. _positionPrefix: '<div class="dijitRuleMark dijitRuleMarkV" style="top:',
  28161. /*=====
  28162. // container: String
  28163. // This is either "leftDecoration" or "rightDecoration",
  28164. // to indicate whether this rule goes to the left or to the right of the slider.
  28165. // Note that on RTL system, "leftDecoration" would actually go to the right, and vice-versa.
  28166. container: "",
  28167. =====*/
  28168. // Overrides HorizontalRule._isHorizontal
  28169. _isHorizontal: false
  28170. });
  28171. }
  28172. if(!dojo._hasResource["dijit.form.HorizontalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28173. dojo._hasResource["dijit.form.HorizontalRuleLabels"] = true;
  28174. dojo.provide("dijit.form.HorizontalRuleLabels");
  28175. dojo.declare("dijit.form.HorizontalRuleLabels", dijit.form.HorizontalRule,
  28176. {
  28177. // summary:
  28178. // Labels for `dijit.form.HorizontalSlider`
  28179. templateString: '<div class="dijitRuleContainer dijitRuleContainerH dijitRuleLabelsContainer dijitRuleLabelsContainerH"></div>',
  28180. // labelStyle: String
  28181. // CSS style to apply to individual text labels
  28182. labelStyle: "",
  28183. // labels: String[]?
  28184. // Array of text labels to render - evenly spaced from left-to-right or bottom-to-top.
  28185. // Alternately, minimum and maximum can be specified, to get numeric labels.
  28186. labels: [],
  28187. // numericMargin: Integer
  28188. // Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
  28189. numericMargin: 0,
  28190. // numericMinimum: Integer
  28191. // Leftmost label value for generated numeric labels when labels[] are not specified
  28192. minimum: 0,
  28193. // numericMaximum: Integer
  28194. // Rightmost label value for generated numeric labels when labels[] are not specified
  28195. maximum: 1,
  28196. // constraints: Object
  28197. // pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
  28198. constraints: {pattern:"#%"},
  28199. _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerH" style="left:',
  28200. _labelPrefix: '"><div class="dijitRuleLabel dijitRuleLabelH">',
  28201. _suffix: '</div></div>',
  28202. _calcPosition: function(pos){
  28203. // summary:
  28204. // Returns the value to be used in HTML for the label as part of the left: attribute
  28205. // tags:
  28206. // protected extension
  28207. return pos;
  28208. },
  28209. _genHTML: function(pos, ndx){
  28210. return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
  28211. },
  28212. getLabels: function(){
  28213. // summary:
  28214. // Overridable function to return array of labels to use for this slider.
  28215. // Can specify a getLabels() method instead of a labels[] array, or min/max attributes.
  28216. // tags:
  28217. // protected extension
  28218. // if the labels array was not specified directly, then see if <li> children were
  28219. var labels = this.labels;
  28220. if(!labels.length){
  28221. // for markup creation, labels are specified as child elements
  28222. labels = dojo.query("> li", this.srcNodeRef).map(function(node){
  28223. return String(node.innerHTML);
  28224. });
  28225. }
  28226. this.srcNodeRef.innerHTML = '';
  28227. // if the labels were not specified directly and not as <li> children, then calculate numeric labels
  28228. if(!labels.length && this.count > 1){
  28229. var start = this.minimum;
  28230. var inc = (this.maximum - start) / (this.count-1);
  28231. for(var i=0; i < this.count; i++){
  28232. labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints));
  28233. start += inc;
  28234. }
  28235. }
  28236. return labels;
  28237. },
  28238. postMixInProperties: function(){
  28239. this.inherited(arguments);
  28240. this.labels = this.getLabels();
  28241. this.count = this.labels.length;
  28242. }
  28243. });
  28244. }
  28245. if(!dojo._hasResource["dijit.form.VerticalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28246. dojo._hasResource["dijit.form.VerticalRuleLabels"] = true;
  28247. dojo.provide("dijit.form.VerticalRuleLabels");
  28248. dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
  28249. {
  28250. // summary:
  28251. // Labels for the `dijit.form.VerticalSlider`
  28252. templateString: '<div class="dijitRuleContainer dijitRuleContainerV dijitRuleLabelsContainer dijitRuleLabelsContainerV"></div>',
  28253. _positionPrefix: '<div class="dijitRuleLabelContainer dijitRuleLabelContainerV" style="top:',
  28254. _labelPrefix: '"><span class="dijitRuleLabel dijitRuleLabelV">',
  28255. _calcPosition: function(pos){
  28256. // Overrides HorizontalRuleLabel._calcPosition()
  28257. return 100-pos;
  28258. },
  28259. // needed to prevent labels from being reversed in RTL mode
  28260. _isHorizontal: false
  28261. });
  28262. }
  28263. if(!dojo._hasResource["dijit.form.Slider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28264. dojo._hasResource["dijit.form.Slider"] = true;
  28265. dojo.provide("dijit.form.Slider");
  28266. dojo.deprecated("Call require() for HorizontalSlider / VerticalRule, explicitly rather than 'dijit.form.Slider' itself", "", "2.0");
  28267. // For back-compat, remove for 2.0
  28268. }
  28269. if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28270. dojo._hasResource["dijit.form.SimpleTextarea"] = true;
  28271. dojo.provide("dijit.form.SimpleTextarea");
  28272. dojo.declare("dijit.form.SimpleTextarea",
  28273. dijit.form.TextBox,
  28274. {
  28275. // summary:
  28276. // A simple textarea that degrades, and responds to
  28277. // minimal LayoutContainer usage, and works with dijit.form.Form.
  28278. // Doesn't automatically size according to input, like Textarea.
  28279. //
  28280. // example:
  28281. // | <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
  28282. //
  28283. // example:
  28284. // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
  28285. baseClass: "dijitTextBox dijitTextArea",
  28286. attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
  28287. rows:"textbox", cols: "textbox"
  28288. }),
  28289. // rows: Number
  28290. // The number of rows of text.
  28291. rows: "3",
  28292. // rows: Number
  28293. // The number of characters per line.
  28294. cols: "20",
  28295. templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
  28296. postMixInProperties: function(){
  28297. // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
  28298. // TODO: parser will handle this in 2.0
  28299. if(!this.value && this.srcNodeRef){
  28300. this.value = this.srcNodeRef.value;
  28301. }
  28302. this.inherited(arguments);
  28303. },
  28304. buildRendering: function(){
  28305. this.inherited(arguments);
  28306. if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
  28307. dojo.addClass(this.textbox, "dijitTextAreaCols");
  28308. }
  28309. },
  28310. filter: function(/*String*/ value){
  28311. // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
  28312. // as \r\n instead of just \n
  28313. if(value){
  28314. value = value.replace(/\r/g,"");
  28315. }
  28316. return this.inherited(arguments);
  28317. },
  28318. _previousValue: "",
  28319. _onInput: function(/*Event?*/ e){
  28320. // Override TextBox._onInput() to enforce maxLength restriction
  28321. if(this.maxLength){
  28322. var maxLength = parseInt(this.maxLength);
  28323. var value = this.textbox.value.replace(/\r/g,'');
  28324. var overflow = value.length - maxLength;
  28325. if(overflow > 0){
  28326. if(e){ dojo.stopEvent(e); }
  28327. var textarea = this.textbox;
  28328. if(textarea.selectionStart){
  28329. var pos = textarea.selectionStart;
  28330. var cr = 0;
  28331. if(dojo.isOpera){
  28332. cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
  28333. }
  28334. this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
  28335. textarea.setSelectionRange(pos-overflow, pos-overflow);
  28336. }else if(dojo.doc.selection){ //IE
  28337. textarea.focus();
  28338. var range = dojo.doc.selection.createRange();
  28339. // delete overflow characters
  28340. range.moveStart("character", -overflow);
  28341. range.text = '';
  28342. // show cursor
  28343. range.select();
  28344. }
  28345. }
  28346. this._previousValue = this.textbox.value;
  28347. }
  28348. this.inherited(arguments);
  28349. }
  28350. });
  28351. }
  28352. if(!dojo._hasResource["dijit.form._ExpandingTextAreaMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28353. dojo._hasResource["dijit.form._ExpandingTextAreaMixin"] = true;
  28354. dojo.provide("dijit.form._ExpandingTextAreaMixin");
  28355. // module:
  28356. // dijit/form/_ExpandingTextAreaMixin
  28357. // summary:
  28358. // Mixin for textarea widgets to add auto-expanding capability
  28359. // feature detection
  28360. var needsHelpShrinking;
  28361. dojo.declare("dijit.form._ExpandingTextAreaMixin", null, {
  28362. // summary:
  28363. // Mixin for textarea widgets to add auto-expanding capability
  28364. _setValueAttr: function(){
  28365. this.inherited(arguments);
  28366. this.resize();
  28367. },
  28368. postCreate: function(){
  28369. this.inherited(arguments);
  28370. var textarea = this.textbox;
  28371. if(needsHelpShrinking == undefined){
  28372. 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");
  28373. needsHelpShrinking = te.scrollHeight >= te.clientHeight;
  28374. dojo.body().removeChild(te);
  28375. }
  28376. this.connect(textarea, "onresize", "_resizeLater");
  28377. this.connect(textarea, "onfocus", "_resizeLater");
  28378. textarea.style.overflowY = "hidden";
  28379. },
  28380. startup: function(){
  28381. this.inherited(arguments);
  28382. this._resizeLater();
  28383. },
  28384. _onInput: function(e){
  28385. this.inherited(arguments);
  28386. this.resize();
  28387. },
  28388. _estimateHeight: function(){
  28389. // summary:
  28390. // Approximate the height when the textarea is invisible with the number of lines in the text.
  28391. // Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
  28392. // In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
  28393. //
  28394. var textarea = this.textbox;
  28395. // #rows = #newlines+1
  28396. textarea.rows = (textarea.value.match(/\n/g) || []).length + 1;
  28397. },
  28398. _resizeLater: function(){
  28399. this.defer("resize");
  28400. },
  28401. resize: function(){
  28402. // summary:
  28403. // Resizes the textarea vertically (should be called after a style/value change)
  28404. var textarea = this.textbox;
  28405. function textareaScrollHeight(){
  28406. var empty = false;
  28407. if(textarea.value === ''){
  28408. textarea.value = ' ';
  28409. empty = true;
  28410. }
  28411. var sh = textarea.scrollHeight;
  28412. if(empty){ textarea.value = ''; }
  28413. return sh;
  28414. }
  28415. if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
  28416. if(this.busyResizing){ return; }
  28417. this.busyResizing = true;
  28418. if(textareaScrollHeight() || textarea.offsetHeight){
  28419. var newH = textareaScrollHeight() + Math.max(textarea.offsetHeight - textarea.clientHeight, 0);
  28420. var newHpx = newH + "px";
  28421. if(newHpx != textarea.style.height){
  28422. textarea.style.height = newHpx;
  28423. textarea.rows = 1; // rows can act like a minHeight if not cleared
  28424. }
  28425. if(needsHelpShrinking){
  28426. var origScrollHeight = textareaScrollHeight(),
  28427. newScrollHeight = origScrollHeight,
  28428. origMinHeight = textarea.style.minHeight,
  28429. decrement = 4, // not too fast, not too slow
  28430. thisScrollHeight,
  28431. origScrollTop = textarea.scrollTop;
  28432. textarea.style.minHeight = newHpx; // maintain current height
  28433. textarea.style.height = "auto"; // allow scrollHeight to change
  28434. while(newH > 0){
  28435. textarea.style.minHeight = Math.max(newH - decrement, 4) + "px";
  28436. thisScrollHeight = textareaScrollHeight();
  28437. var change = newScrollHeight - thisScrollHeight;
  28438. newH -= change;
  28439. if(change < decrement){
  28440. break; // scrollHeight didn't shrink
  28441. }
  28442. newScrollHeight = thisScrollHeight;
  28443. decrement <<= 1;
  28444. }
  28445. textarea.style.height = newH + "px";
  28446. textarea.style.minHeight = origMinHeight;
  28447. textarea.scrollTop = origScrollTop;
  28448. }
  28449. textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden";
  28450. if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
  28451. }else{
  28452. // hidden content of unknown size
  28453. this._estimateHeight();
  28454. }
  28455. this.busyResizing = false;
  28456. }
  28457. });
  28458. }
  28459. if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28460. dojo._hasResource["dijit.form.Textarea"] = true;
  28461. dojo.provide("dijit.form.Textarea");
  28462. /*=====
  28463. var _ExpandingTextAreaMixin = dijit.form._ExpandingTextAreaMixin;
  28464. var SimpleTextarea = dijit.form.SimpleTextarea;
  28465. =====*/
  28466. // module:
  28467. // dijit/form/Textarea
  28468. // summary:
  28469. // A textarea widget that adjusts it's height according to the amount of data.
  28470. dojo.declare("dijit.form.Textarea", [dijit.form.SimpleTextarea, dijit.form._ExpandingTextAreaMixin], {
  28471. // summary:
  28472. // A textarea widget that adjusts it's height according to the amount of data.
  28473. //
  28474. // description:
  28475. // A textarea that dynamically expands/contracts (changing it's height) as
  28476. // the user types, to display all the text without requiring a scroll bar.
  28477. //
  28478. // Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
  28479. // Rows is not supported since this widget adjusts the height.
  28480. //
  28481. // example:
  28482. // | <textarea data-dojo-type="dijit.form.TextArea">...</textarea>
  28483. // TODO: for 2.0, rename this to ExpandingTextArea, and rename SimpleTextarea to TextArea
  28484. baseClass: "dijitTextBox dijitTextArea dijitExpandingTextArea",
  28485. // Override SimpleTextArea.cols to default to width:100%, for backward compatibility
  28486. cols: "",
  28487. buildRendering: function(){
  28488. this.inherited(arguments);
  28489. // tweak textarea style to reduce browser differences
  28490. dojo.style(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' });
  28491. }
  28492. });
  28493. }
  28494. if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28495. dojo._hasResource["dijit.layout.BorderContainer"] = true;
  28496. dojo.provide("dijit.layout.BorderContainer");
  28497. dojo.declare(
  28498. "dijit.layout.BorderContainer",
  28499. dijit.layout._LayoutWidget,
  28500. {
  28501. // summary:
  28502. // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
  28503. //
  28504. // description:
  28505. // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
  28506. // that contains a child widget marked region="center" and optionally children widgets marked
  28507. // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
  28508. // Children along the edges will be laid out according to width or height dimensions and may
  28509. // include optional splitters (splitter="true") to make them resizable by the user. The remaining
  28510. // space is designated for the center region.
  28511. //
  28512. // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
  28513. // and height for the top and bottom, respectively. No dimensions should be specified on the center;
  28514. // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
  28515. // "left" and "right" except that they will be reversed in right-to-left environments.
  28516. //
  28517. // For complex layouts, multiple children can be specified for a single region. In this case, the
  28518. // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
  28519. // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
  28520. // instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes.
  28521. // example:
  28522. // | <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
  28523. // | style="width: 400px; height: 300px;">
  28524. // | <div dojoType="dijit.layout.ContentPane" region="top">header text</div>
  28525. // | <div dojoType="dijit.layout.ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
  28526. // | <div dojoType="dijit.layout.ContentPane" region="center">client area</div>
  28527. // | </div>
  28528. // design: String
  28529. // Which design is used for the layout:
  28530. // - "headline" (default) where the top and bottom extend
  28531. // the full width of the container
  28532. // - "sidebar" where the left and right sides extend from top to bottom.
  28533. design: "headline",
  28534. // gutters: [const] Boolean
  28535. // Give each pane a border and margin.
  28536. // Margin determined by domNode.paddingLeft.
  28537. // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
  28538. gutters: true,
  28539. // liveSplitters: [const] Boolean
  28540. // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
  28541. liveSplitters: true,
  28542. // persist: Boolean
  28543. // Save splitter positions in a cookie.
  28544. persist: false,
  28545. baseClass: "dijitBorderContainer",
  28546. // _splitterClass: String
  28547. // Optional hook to override the default Splitter widget used by BorderContainer
  28548. _splitterClass: "dijit.layout._Splitter",
  28549. postMixInProperties: function(){
  28550. // change class name to indicate that BorderContainer is being used purely for
  28551. // layout (like LayoutContainer) rather than for pretty formatting.
  28552. if(!this.gutters){
  28553. this.baseClass += "NoGutter";
  28554. }
  28555. this.inherited(arguments);
  28556. },
  28557. startup: function(){
  28558. if(this._started){ return; }
  28559. dojo.forEach(this.getChildren(), this._setupChild, this);
  28560. this.inherited(arguments);
  28561. },
  28562. _setupChild: function(/*dijit._Widget*/ child){
  28563. // Override _LayoutWidget._setupChild().
  28564. var region = child.region;
  28565. if(region){
  28566. this.inherited(arguments);
  28567. dojo.addClass(child.domNode, this.baseClass+"Pane");
  28568. var ltr = this.isLeftToRight();
  28569. if(region == "leading"){ region = ltr ? "left" : "right"; }
  28570. if(region == "trailing"){ region = ltr ? "right" : "left"; }
  28571. // Create draggable splitter for resizing pane,
  28572. // or alternately if splitter=false but BorderContainer.gutters=true then
  28573. // insert dummy div just for spacing
  28574. if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
  28575. var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
  28576. var splitter = new _Splitter({
  28577. id: child.id + "_splitter",
  28578. container: this,
  28579. child: child,
  28580. region: region,
  28581. live: this.liveSplitters
  28582. });
  28583. splitter.isSplitter = true;
  28584. child._splitterWidget = splitter;
  28585. dojo.place(splitter.domNode, child.domNode, "after");
  28586. // Splitters aren't added as Contained children, so we need to call startup explicitly
  28587. splitter.startup();
  28588. }
  28589. child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
  28590. }
  28591. },
  28592. layout: function(){
  28593. // Implement _LayoutWidget.layout() virtual method.
  28594. this._layoutChildren();
  28595. },
  28596. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  28597. // Override _LayoutWidget.addChild().
  28598. this.inherited(arguments);
  28599. if(this._started){
  28600. this.layout(); //OPT
  28601. }
  28602. },
  28603. removeChild: function(/*dijit._Widget*/ child){
  28604. // Override _LayoutWidget.removeChild().
  28605. var region = child.region;
  28606. var splitter = child._splitterWidget
  28607. if(splitter){
  28608. splitter.destroy();
  28609. delete child._splitterWidget;
  28610. }
  28611. this.inherited(arguments);
  28612. if(this._started){
  28613. this._layoutChildren();
  28614. }
  28615. // Clean up whatever style changes we made to the child pane.
  28616. // Unclear how height and width should be handled.
  28617. dojo.removeClass(child.domNode, this.baseClass+"Pane");
  28618. dojo.style(child.domNode, {
  28619. top: "auto",
  28620. bottom: "auto",
  28621. left: "auto",
  28622. right: "auto",
  28623. position: "static"
  28624. });
  28625. dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
  28626. },
  28627. getChildren: function(){
  28628. // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
  28629. return dojo.filter(this.inherited(arguments), function(widget){
  28630. return !widget.isSplitter;
  28631. });
  28632. },
  28633. // TODO: remove in 2.0
  28634. getSplitter: function(/*String*/region){
  28635. // summary:
  28636. // Returns the widget responsible for rendering the splitter associated with region
  28637. // tags:
  28638. // deprecated
  28639. return dojo.filter(this.getChildren(), function(child){
  28640. return child.region == region;
  28641. })[0]._splitterWidget;
  28642. },
  28643. resize: function(newSize, currentSize){
  28644. // Overrides _LayoutWidget.resize().
  28645. // resetting potential padding to 0px to provide support for 100% width/height + padding
  28646. // TODO: this hack doesn't respect the box model and is a temporary fix
  28647. if(!this.cs || !this.pe){
  28648. var node = this.domNode;
  28649. this.cs = dojo.getComputedStyle(node);
  28650. this.pe = dojo._getPadExtents(node, this.cs);
  28651. this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
  28652. this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
  28653. dojo.style(node, "padding", "0px");
  28654. }
  28655. this.inherited(arguments);
  28656. },
  28657. _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
  28658. // summary:
  28659. // This is the main routine for setting size/position of each child.
  28660. // description:
  28661. // With no arguments, measures the height of top/bottom panes, the width
  28662. // of left/right panes, and then sizes all panes accordingly.
  28663. //
  28664. // With changedRegion specified (as "left", "top", "bottom", or "right"),
  28665. // it changes that region's width/height to changedRegionSize and
  28666. // then resizes other regions that were affected.
  28667. // changedChildId:
  28668. // Id of the child which should be resized because splitter was dragged.
  28669. // changedChildSize:
  28670. // The new width/height (in pixels) to make specified child
  28671. if(!this._borderBox || !this._borderBox.h){
  28672. // We are currently hidden, or we haven't been sized by our parent yet.
  28673. // Abort. Someone will resize us later.
  28674. return;
  28675. }
  28676. // Generate list of wrappers of my children in the order that I want layoutChildren()
  28677. // to process them (i.e. from the outside to the inside)
  28678. var wrappers = dojo.map(this.getChildren(), function(child, idx){
  28679. return {
  28680. pane: child,
  28681. weight: [
  28682. child.region == "center" ? Infinity : 0,
  28683. child.layoutPriority,
  28684. (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
  28685. idx
  28686. ]
  28687. };
  28688. }, this);
  28689. wrappers.sort(function(a, b){
  28690. var aw = a.weight, bw = b.weight;
  28691. for(var i=0; i<aw.length; i++){
  28692. if(aw[i] != bw[i]){
  28693. return aw[i] - bw[i];
  28694. }
  28695. }
  28696. return 0;
  28697. });
  28698. // Make new list, combining the externally specified children with splitters and gutters
  28699. var childrenAndSplitters = [];
  28700. dojo.forEach(wrappers, function(wrapper){
  28701. var pane = wrapper.pane;
  28702. childrenAndSplitters.push(pane);
  28703. if(pane._splitterWidget){
  28704. childrenAndSplitters.push(pane._splitterWidget);
  28705. }
  28706. });
  28707. // Compute the box in which to lay out my children
  28708. var dim = {
  28709. l: this.pe.l,
  28710. t: this.pe.t,
  28711. w: this._borderBox.w - this.pe.w,
  28712. h: this._borderBox.h - this.pe.h
  28713. };
  28714. // Layout the children, possibly changing size due to a splitter drag
  28715. dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters,
  28716. changedChildId, changedChildSize);
  28717. },
  28718. destroyRecursive: function(){
  28719. // Destroy splitters first, while getChildren() still works
  28720. dojo.forEach(this.getChildren(), function(child){
  28721. var splitter = child._splitterWidget;
  28722. if(splitter){
  28723. splitter.destroy();
  28724. }
  28725. delete child._splitterWidget;
  28726. });
  28727. // Then destroy the real children, and myself
  28728. this.inherited(arguments);
  28729. }
  28730. });
  28731. // This argument can be specified for the children of a BorderContainer.
  28732. // Since any widget can be specified as a LayoutContainer child, mix it
  28733. // into the base widget class. (This is a hack, but it's effective.)
  28734. dojo.extend(dijit._Widget, {
  28735. // region: [const] String
  28736. // Parameter for children of `dijit.layout.BorderContainer`.
  28737. // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
  28738. // See the `dijit.layout.BorderContainer` description for details.
  28739. region: '',
  28740. // layoutPriority: [const] Number
  28741. // Parameter for children of `dijit.layout.BorderContainer`.
  28742. // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
  28743. // between children with a lower layoutPriority.
  28744. layoutPriority: 0,
  28745. // splitter: [const] Boolean
  28746. // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
  28747. // If true, enables user to resize the widget by putting a draggable splitter between
  28748. // this widget and the region=center widget.
  28749. splitter: false,
  28750. // minSize: [const] Number
  28751. // Parameter for children of `dijit.layout.BorderContainer`.
  28752. // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
  28753. minSize: 0,
  28754. // maxSize: [const] Number
  28755. // Parameter for children of `dijit.layout.BorderContainer`.
  28756. // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
  28757. maxSize: Infinity
  28758. });
  28759. dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
  28760. {
  28761. // summary:
  28762. // A draggable spacer between two items in a `dijit.layout.BorderContainer`.
  28763. // description:
  28764. // This is instantiated by `dijit.layout.BorderContainer`. Users should not
  28765. // create it directly.
  28766. // tags:
  28767. // private
  28768. /*=====
  28769. // container: [const] dijit.layout.BorderContainer
  28770. // Pointer to the parent BorderContainer
  28771. container: null,
  28772. // child: [const] dijit.layout._LayoutWidget
  28773. // Pointer to the pane associated with this splitter
  28774. child: null,
  28775. // region: [const] String
  28776. // Region of pane associated with this splitter.
  28777. // "top", "bottom", "left", "right".
  28778. region: null,
  28779. =====*/
  28780. // live: [const] Boolean
  28781. // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
  28782. // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
  28783. live: true,
  28784. templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
  28785. postMixInProperties: function(){
  28786. this.inherited(arguments);
  28787. this.horizontal = /top|bottom/.test(this.region);
  28788. this._factor = /top|left/.test(this.region) ? 1 : -1;
  28789. this._cookieName = this.container.id + "_" + this.region;
  28790. },
  28791. buildRendering: function(){
  28792. this.inherited(arguments);
  28793. dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
  28794. if(this.container.persist){
  28795. // restore old size
  28796. var persistSize = dojo.cookie(this._cookieName);
  28797. if(persistSize){
  28798. this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
  28799. }
  28800. }
  28801. },
  28802. _computeMaxSize: function(){
  28803. // summary:
  28804. // Return the maximum size that my corresponding pane can be set to
  28805. var dim = this.horizontal ? 'h' : 'w',
  28806. childSize = dojo.marginBox(this.child.domNode)[dim],
  28807. center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
  28808. spaceAvailable = dojo.marginBox(center.domNode)[dim]; // can expand until center is crushed to 0
  28809. return Math.min(this.child.maxSize, childSize + spaceAvailable);
  28810. },
  28811. _startDrag: function(e){
  28812. if(!this.cover){
  28813. this.cover = dojo.doc.createElement('div');
  28814. dojo.addClass(this.cover, "dijitSplitterCover");
  28815. dojo.place(this.cover, this.child.domNode, "after");
  28816. }
  28817. dojo.addClass(this.cover, "dijitSplitterCoverActive");
  28818. // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
  28819. if(this.fake){ dojo.destroy(this.fake); }
  28820. if(!(this._resize = this.live)){ //TODO: disable live for IE6?
  28821. // create fake splitter to display at old position while we drag
  28822. (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
  28823. dojo.addClass(this.domNode, "dijitSplitterShadow");
  28824. dojo.place(this.fake, this.domNode, "after");
  28825. }
  28826. dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
  28827. if(this.fake){
  28828. dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
  28829. }
  28830. //Performance: load data info local vars for onmousevent function closure
  28831. var factor = this._factor,
  28832. isHorizontal = this.horizontal,
  28833. axis = isHorizontal ? "pageY" : "pageX",
  28834. pageStart = e[axis],
  28835. splitterStyle = this.domNode.style,
  28836. dim = isHorizontal ? 'h' : 'w',
  28837. childStart = dojo.marginBox(this.child.domNode)[dim],
  28838. max = this._computeMaxSize(),
  28839. min = this.child.minSize || 20,
  28840. region = this.region,
  28841. splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
  28842. splitterStart = parseInt(splitterStyle[splitterAttr], 10),
  28843. resize = this._resize,
  28844. layoutFunc = dojo.hitch(this.container, "_layoutChildren", this.child.id),
  28845. de = dojo.doc;
  28846. this._handlers = (this._handlers || []).concat([
  28847. dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
  28848. var delta = e[axis] - pageStart,
  28849. childSize = factor * delta + childStart,
  28850. boundChildSize = Math.max(Math.min(childSize, max), min);
  28851. if(resize || forceResize){
  28852. layoutFunc(boundChildSize);
  28853. }
  28854. // TODO: setting style directly (usually) sets content box size, need to set margin box size
  28855. splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
  28856. }),
  28857. dojo.connect(de, "ondragstart", dojo.stopEvent),
  28858. dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
  28859. dojo.connect(de, "onmouseup", this, "_stopDrag")
  28860. ]);
  28861. dojo.stopEvent(e);
  28862. },
  28863. _onMouse: function(e){
  28864. var o = (e.type == "mouseover" || e.type == "mouseenter");
  28865. dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
  28866. dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
  28867. },
  28868. _stopDrag: function(e){
  28869. try{
  28870. if(this.cover){
  28871. dojo.removeClass(this.cover, "dijitSplitterCoverActive");
  28872. }
  28873. if(this.fake){ dojo.destroy(this.fake); }
  28874. dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter"
  28875. + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
  28876. this._drag(e); //TODO: redundant with onmousemove?
  28877. this._drag(e, true);
  28878. }finally{
  28879. this._cleanupHandlers();
  28880. delete this._drag;
  28881. }
  28882. if(this.container.persist){
  28883. dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
  28884. }
  28885. },
  28886. _cleanupHandlers: function(){
  28887. dojo.forEach(this._handlers, dojo.disconnect);
  28888. delete this._handlers;
  28889. },
  28890. _onKeyPress: function(/*Event*/ e){
  28891. // should we apply typematic to this?
  28892. this._resize = true;
  28893. var horizontal = this.horizontal;
  28894. var tick = 1;
  28895. var dk = dojo.keys;
  28896. switch(e.charOrCode){
  28897. case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
  28898. tick *= -1;
  28899. // break;
  28900. case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
  28901. break;
  28902. default:
  28903. // this.inherited(arguments);
  28904. return;
  28905. }
  28906. var childSize = dojo._getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
  28907. this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
  28908. dojo.stopEvent(e);
  28909. },
  28910. destroy: function(){
  28911. this._cleanupHandlers();
  28912. delete this.child;
  28913. delete this.container;
  28914. delete this.cover;
  28915. delete this.fake;
  28916. this.inherited(arguments);
  28917. }
  28918. });
  28919. dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
  28920. {
  28921. // summary:
  28922. // Just a spacer div to separate side pane from center pane.
  28923. // Basically a trick to lookup the gutter/splitter width from the theme.
  28924. // description:
  28925. // Instantiated by `dijit.layout.BorderContainer`. Users should not
  28926. // create directly.
  28927. // tags:
  28928. // private
  28929. templateString: '<div class="dijitGutter" role="presentation"></div>',
  28930. postMixInProperties: function(){
  28931. this.inherited(arguments);
  28932. this.horizontal = /top|bottom/.test(this.region);
  28933. },
  28934. buildRendering: function(){
  28935. this.inherited(arguments);
  28936. dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
  28937. }
  28938. });
  28939. }
  28940. if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  28941. dojo._hasResource["dijit.layout.StackController"] = true;
  28942. dojo.provide("dijit.layout.StackController");
  28943. dojo.declare(
  28944. "dijit.layout.StackController",
  28945. [dijit._Widget, dijit._Templated, dijit._Container],
  28946. {
  28947. // summary:
  28948. // Set of buttons to select a page in a page list.
  28949. // description:
  28950. // Monitors the specified StackContainer, and whenever a page is
  28951. // added, deleted, or selected, updates itself accordingly.
  28952. templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
  28953. // containerId: [const] String
  28954. // The id of the page container that I point to
  28955. containerId: "",
  28956. // buttonWidget: [const] String
  28957. // The name of the button widget to create to correspond to each page
  28958. buttonWidget: "dijit.layout._StackButton",
  28959. constructor: function(){
  28960. this.pane2button = {}; // mapping from pane id to buttons
  28961. this.pane2connects = {}; // mapping from pane id to this.connect() handles
  28962. this.pane2watches = {}; // mapping from pane id to watch() handles
  28963. },
  28964. buildRendering: function(){
  28965. this.inherited(arguments);
  28966. dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above.
  28967. },
  28968. postCreate: function(){
  28969. this.inherited(arguments);
  28970. // Listen to notifications from StackContainer
  28971. this.subscribe(this.containerId+"-startup", "onStartup");
  28972. this.subscribe(this.containerId+"-addChild", "onAddChild");
  28973. this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
  28974. this.subscribe(this.containerId+"-selectChild", "onSelectChild");
  28975. this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
  28976. },
  28977. onStartup: function(/*Object*/ info){
  28978. // summary:
  28979. // Called after StackContainer has finished initializing
  28980. // tags:
  28981. // private
  28982. dojo.forEach(info.children, this.onAddChild, this);
  28983. if(info.selected){
  28984. // Show button corresponding to selected pane (unless selected
  28985. // is null because there are no panes)
  28986. this.onSelectChild(info.selected);
  28987. }
  28988. },
  28989. destroy: function(){
  28990. for(var pane in this.pane2button){
  28991. this.onRemoveChild(dijit.byId(pane));
  28992. }
  28993. this.inherited(arguments);
  28994. },
  28995. onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
  28996. // summary:
  28997. // Called whenever a page is added to the container.
  28998. // Create button corresponding to the page.
  28999. // tags:
  29000. // private
  29001. // create an instance of the button widget
  29002. var cls = dojo.getObject(this.buttonWidget);
  29003. var button = new cls({
  29004. id: this.id + "_" + page.id,
  29005. label: page.title,
  29006. dir: page.dir,
  29007. lang: page.lang,
  29008. showLabel: page.showTitle,
  29009. iconClass: page.iconClass,
  29010. closeButton: page.closable,
  29011. title: page.tooltip
  29012. });
  29013. dijit.setWaiState(button.focusNode,"selected", "false");
  29014. // map from page attribute to corresponding tab button attribute
  29015. var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
  29016. buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
  29017. // watch() so events like page title changes are reflected in tab button
  29018. this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
  29019. return page.watch(pageAttr, function(name, oldVal, newVal){
  29020. button.set(buttonAttrList[idx], newVal);
  29021. });
  29022. });
  29023. // connections so that clicking a tab button selects the corresponding page
  29024. this.pane2connects[page.id] = [
  29025. this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
  29026. this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
  29027. ];
  29028. this.addChild(button, insertIndex);
  29029. this.pane2button[page.id] = button;
  29030. page.controlButton = button; // this value might be overwritten if two tabs point to same container
  29031. if(!this._currentChild){ // put the first child into the tab order
  29032. button.focusNode.setAttribute("tabIndex", "0");
  29033. dijit.setWaiState(button.focusNode, "selected", "true");
  29034. this._currentChild = page;
  29035. }
  29036. // make sure all tabs have the same length
  29037. if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
  29038. this._rectifyRtlTabList();
  29039. }
  29040. },
  29041. onRemoveChild: function(/*dijit._Widget*/ page){
  29042. // summary:
  29043. // Called whenever a page is removed from the container.
  29044. // Remove the button corresponding to the page.
  29045. // tags:
  29046. // private
  29047. if(this._currentChild === page){ this._currentChild = null; }
  29048. // disconnect/unwatch connections/watches related to page being removed
  29049. dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
  29050. delete this.pane2connects[page.id];
  29051. dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
  29052. delete this.pane2watches[page.id];
  29053. var button = this.pane2button[page.id];
  29054. if(button){
  29055. this.removeChild(button);
  29056. delete this.pane2button[page.id];
  29057. button.destroy();
  29058. }
  29059. delete page.controlButton;
  29060. },
  29061. onSelectChild: function(/*dijit._Widget*/ page){
  29062. // summary:
  29063. // Called when a page has been selected in the StackContainer, either by me or by another StackController
  29064. // tags:
  29065. // private
  29066. if(!page){ return; }
  29067. if(this._currentChild){
  29068. var oldButton=this.pane2button[this._currentChild.id];
  29069. oldButton.set('checked', false);
  29070. dijit.setWaiState(oldButton.focusNode, "selected", "false");
  29071. oldButton.focusNode.setAttribute("tabIndex", "-1");
  29072. }
  29073. var newButton=this.pane2button[page.id];
  29074. newButton.set('checked', true);
  29075. dijit.setWaiState(newButton.focusNode, "selected", "true");
  29076. this._currentChild = page;
  29077. newButton.focusNode.setAttribute("tabIndex", "0");
  29078. var container = dijit.byId(this.containerId);
  29079. dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
  29080. },
  29081. onButtonClick: function(/*dijit._Widget*/ page){
  29082. // summary:
  29083. // Called whenever one of my child buttons is pressed in an attempt to select a page
  29084. // tags:
  29085. // private
  29086. var container = dijit.byId(this.containerId);
  29087. container.selectChild(page);
  29088. },
  29089. onCloseButtonClick: function(/*dijit._Widget*/ page){
  29090. // summary:
  29091. // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
  29092. // tags:
  29093. // private
  29094. var container = dijit.byId(this.containerId);
  29095. container.closeChild(page);
  29096. if(this._currentChild){
  29097. var b = this.pane2button[this._currentChild.id];
  29098. if(b){
  29099. dijit.focus(b.focusNode || b.domNode);
  29100. }
  29101. }
  29102. },
  29103. // TODO: this is a bit redundant with forward, back api in StackContainer
  29104. adjacent: function(/*Boolean*/ forward){
  29105. // summary:
  29106. // Helper for onkeypress to find next/previous button
  29107. // tags:
  29108. // private
  29109. if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
  29110. // find currently focused button in children array
  29111. var children = this.getChildren();
  29112. var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
  29113. // pick next button to focus on
  29114. var offset = forward ? 1 : children.length - 1;
  29115. return children[ (current + offset) % children.length ]; // dijit._Widget
  29116. },
  29117. onkeypress: function(/*Event*/ e){
  29118. // summary:
  29119. // Handle keystrokes on the page list, for advancing to next/previous button
  29120. // and closing the current page if the page is closable.
  29121. // tags:
  29122. // private
  29123. if(this.disabled || e.altKey ){ return; }
  29124. var forward = null;
  29125. if(e.ctrlKey || !e._djpage){
  29126. var k = dojo.keys;
  29127. switch(e.charOrCode){
  29128. case k.LEFT_ARROW:
  29129. case k.UP_ARROW:
  29130. if(!e._djpage){ forward = false; }
  29131. break;
  29132. case k.PAGE_UP:
  29133. if(e.ctrlKey){ forward = false; }
  29134. break;
  29135. case k.RIGHT_ARROW:
  29136. case k.DOWN_ARROW:
  29137. if(!e._djpage){ forward = true; }
  29138. break;
  29139. case k.PAGE_DOWN:
  29140. if(e.ctrlKey){ forward = true; }
  29141. break;
  29142. case k.HOME:
  29143. case k.END:
  29144. var children = this.getChildren();
  29145. if(children && children.length){
  29146. children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
  29147. }
  29148. dojo.stopEvent(e);
  29149. break;
  29150. case k.DELETE:
  29151. if(this._currentChild.closable){
  29152. this.onCloseButtonClick(this._currentChild);
  29153. }
  29154. dojo.stopEvent(e);
  29155. break;
  29156. default:
  29157. if(e.ctrlKey){
  29158. if(e.charOrCode === k.TAB){
  29159. this.adjacent(!e.shiftKey).onClick();
  29160. dojo.stopEvent(e);
  29161. }else if(e.charOrCode == "w"){
  29162. if(this._currentChild.closable){
  29163. this.onCloseButtonClick(this._currentChild);
  29164. }
  29165. dojo.stopEvent(e); // avoid browser tab closing.
  29166. }
  29167. }
  29168. }
  29169. // handle next/previous page navigation (left/right arrow, etc.)
  29170. if(forward !== null){
  29171. this.adjacent(forward).onClick();
  29172. dojo.stopEvent(e);
  29173. }
  29174. }
  29175. },
  29176. onContainerKeyPress: function(/*Object*/ info){
  29177. // summary:
  29178. // Called when there was a keypress on the container
  29179. // tags:
  29180. // private
  29181. info.e._djpage = info.page;
  29182. this.onkeypress(info.e);
  29183. }
  29184. });
  29185. dojo.declare("dijit.layout._StackButton",
  29186. dijit.form.ToggleButton,
  29187. {
  29188. // summary:
  29189. // Internal widget used by StackContainer.
  29190. // description:
  29191. // The button-like or tab-like object you click to select or delete a page
  29192. // tags:
  29193. // private
  29194. // Override _FormWidget.tabIndex.
  29195. // StackContainer buttons are not in the tab order by default.
  29196. // Probably we should be calling this.startupKeyNavChildren() instead.
  29197. tabIndex: "-1",
  29198. buildRendering: function(/*Event*/ evt){
  29199. this.inherited(arguments);
  29200. dijit.setWaiRole((this.focusNode || this.domNode), "tab");
  29201. },
  29202. onClick: function(/*Event*/ evt){
  29203. // summary:
  29204. // This is for TabContainer where the tabs are <span> rather than button,
  29205. // so need to set focus explicitly (on some browsers)
  29206. // Note that you shouldn't override this method, but you can connect to it.
  29207. dijit.focus(this.focusNode);
  29208. // ... now let StackController catch the event and tell me what to do
  29209. },
  29210. onClickCloseButton: function(/*Event*/ evt){
  29211. // summary:
  29212. // StackContainer connects to this function; if your widget contains a close button
  29213. // then clicking it should call this function.
  29214. // Note that you shouldn't override this method, but you can connect to it.
  29215. evt.stopPropagation();
  29216. }
  29217. });
  29218. }
  29219. if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  29220. dojo._hasResource["dijit.layout.StackContainer"] = true;
  29221. dojo.provide("dijit.layout.StackContainer");
  29222. dojo.declare(
  29223. "dijit.layout.StackContainer",
  29224. dijit.layout._LayoutWidget,
  29225. {
  29226. // summary:
  29227. // A container that has multiple children, but shows only
  29228. // one child at a time
  29229. //
  29230. // description:
  29231. // A container for widgets (ContentPanes, for example) That displays
  29232. // only one Widget at a time.
  29233. //
  29234. // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
  29235. //
  29236. // Can be base class for container, Wizard, Show, etc.
  29237. // doLayout: Boolean
  29238. // If true, change the size of my currently displayed child to match my size
  29239. doLayout: true,
  29240. // persist: Boolean
  29241. // Remembers the selected child across sessions
  29242. persist: false,
  29243. baseClass: "dijitStackContainer",
  29244. /*=====
  29245. // selectedChildWidget: [readonly] dijit._Widget
  29246. // References the currently selected child widget, if any.
  29247. // Adjust selected child with selectChild() method.
  29248. selectedChildWidget: null,
  29249. =====*/
  29250. buildRendering: function(){
  29251. this.inherited(arguments);
  29252. dojo.addClass(this.domNode, "dijitLayoutContainer");
  29253. dijit.setWaiRole(this.containerNode, "tabpanel");
  29254. },
  29255. postCreate: function(){
  29256. this.inherited(arguments);
  29257. this.connect(this.domNode, "onkeypress", this._onKeyPress);
  29258. },
  29259. startup: function(){
  29260. if(this._started){ return; }
  29261. var children = this.getChildren();
  29262. // Setup each page panel to be initially hidden
  29263. dojo.forEach(children, this._setupChild, this);
  29264. // Figure out which child to initially display, defaulting to first one
  29265. if(this.persist){
  29266. this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
  29267. }else{
  29268. dojo.some(children, function(child){
  29269. if(child.selected){
  29270. this.selectedChildWidget = child;
  29271. }
  29272. return child.selected;
  29273. }, this);
  29274. }
  29275. var selected = this.selectedChildWidget;
  29276. if(!selected && children[0]){
  29277. selected = this.selectedChildWidget = children[0];
  29278. selected.selected = true;
  29279. }
  29280. // Publish information about myself so any StackControllers can initialize.
  29281. // This needs to happen before this.inherited(arguments) so that for
  29282. // TabContainer, this._contentBox doesn't include the space for the tab labels.
  29283. dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
  29284. // Startup each child widget, and do initial layout like setting this._contentBox,
  29285. // then calls this.resize() which does the initial sizing on the selected child.
  29286. this.inherited(arguments);
  29287. },
  29288. resize: function(){
  29289. // Resize is called when we are first made visible (it's called from startup()
  29290. // if we are initially visible). If this is the first time we've been made
  29291. // visible then show our first child.
  29292. if(!this._hasBeenShown){
  29293. this._hasBeenShown = true;
  29294. var selected = this.selectedChildWidget;
  29295. if(selected){
  29296. this._showChild(selected);
  29297. }
  29298. }
  29299. this.inherited(arguments);
  29300. },
  29301. _setupChild: function(/*dijit._Widget*/ child){
  29302. // Overrides _LayoutWidget._setupChild()
  29303. this.inherited(arguments);
  29304. dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
  29305. // remove the title attribute so it doesn't show up when i hover
  29306. // over a node
  29307. child.domNode.title = "";
  29308. },
  29309. addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
  29310. // Overrides _Container.addChild() to do layout and publish events
  29311. this.inherited(arguments);
  29312. if(this._started){
  29313. dojo.publish(this.id+"-addChild", [child, insertIndex]);
  29314. // in case the tab titles have overflowed from one line to two lines
  29315. // (or, if this if first child, from zero lines to one line)
  29316. // TODO: w/ScrollingTabController this is no longer necessary, although
  29317. // ScrollTabController.resize() does need to get called to show/hide
  29318. // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
  29319. this.layout();
  29320. // if this is the first child, then select it
  29321. if(!this.selectedChildWidget){
  29322. this.selectChild(child);
  29323. }
  29324. }
  29325. },
  29326. removeChild: function(/*dijit._Widget*/ page){
  29327. // Overrides _Container.removeChild() to do layout and publish events
  29328. this.inherited(arguments);
  29329. if(this._started){
  29330. // this will notify any tablists to remove a button; do this first because it may affect sizing
  29331. dojo.publish(this.id + "-removeChild", [page]);
  29332. }
  29333. // If we are being destroyed than don't run the code below (to select another page), because we are deleting
  29334. // every page one by one
  29335. if(this._beingDestroyed){ return; }
  29336. // Select new page to display, also updating TabController to show the respective tab.
  29337. // Do this before layout call because it can affect the height of the TabController.
  29338. if(this.selectedChildWidget === page){
  29339. this.selectedChildWidget = undefined;
  29340. if(this._started){
  29341. var children = this.getChildren();
  29342. if(children.length){
  29343. this.selectChild(children[0]);
  29344. }
  29345. }
  29346. }
  29347. if(this._started){
  29348. // In case the tab titles now take up one line instead of two lines
  29349. // (note though that ScrollingTabController never overflows to multiple lines),
  29350. // or the height has changed slightly because of addition/removal of tab which close icon
  29351. this.layout();
  29352. }
  29353. },
  29354. selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
  29355. // summary:
  29356. // Show the given widget (which must be one of my children)
  29357. // page:
  29358. // Reference to child widget or id of child widget
  29359. page = dijit.byId(page);
  29360. if(this.selectedChildWidget != page){
  29361. // Deselect old page and select new one
  29362. var d = this._transition(page, this.selectedChildWidget, animate);
  29363. this._set("selectedChildWidget", page);
  29364. dojo.publish(this.id+"-selectChild", [page]);
  29365. if(this.persist){
  29366. dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
  29367. }
  29368. }
  29369. return d; // If child has an href, promise that fires when the child's href finishes loading
  29370. },
  29371. _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
  29372. // summary:
  29373. // Hide the old widget and display the new widget.
  29374. // Subclasses should override this.
  29375. // tags:
  29376. // protected extension
  29377. if(oldWidget){
  29378. this._hideChild(oldWidget);
  29379. }
  29380. var d = this._showChild(newWidget);
  29381. // Size the new widget, in case this is the first time it's being shown,
  29382. // or I have been resized since the last time it was shown.
  29383. // Note that page must be visible for resizing to work.
  29384. if(newWidget.resize){
  29385. if(this.doLayout){
  29386. newWidget.resize(this._containerContentBox || this._contentBox);
  29387. }else{
  29388. // the child should pick it's own size but we still need to call resize()
  29389. // (with no arguments) to let the widget lay itself out
  29390. newWidget.resize();
  29391. }
  29392. }
  29393. return d; // If child has an href, promise that fires when the child's href finishes loading
  29394. },
  29395. _adjacent: function(/*Boolean*/ forward){
  29396. // summary:
  29397. // Gets the next/previous child widget in this container from the current selection.
  29398. var children = this.getChildren();
  29399. var index = dojo.indexOf(children, this.selectedChildWidget);
  29400. index += forward ? 1 : children.length - 1;
  29401. return children[ index % children.length ]; // dijit._Widget
  29402. },
  29403. forward: function(){
  29404. // summary:
  29405. // Advance to next page.
  29406. return this.selectChild(this._adjacent(true), true);
  29407. },
  29408. back: function(){
  29409. // summary:
  29410. // Go back to previous page.
  29411. return this.selectChild(this._adjacent(false), true);
  29412. },
  29413. _onKeyPress: function(e){
  29414. dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
  29415. },
  29416. layout: function(){
  29417. // Implement _LayoutWidget.layout() virtual method.
  29418. if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
  29419. this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
  29420. }
  29421. },
  29422. _showChild: function(/*dijit._Widget*/ page){
  29423. // summary:
  29424. // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
  29425. // it can do any updates it needs regarding loading href's etc.
  29426. // returns:
  29427. // Promise that fires when page has finished showing, or true if there's no href
  29428. var children = this.getChildren();
  29429. page.isFirstChild = (page == children[0]);
  29430. page.isLastChild = (page == children[children.length-1]);
  29431. page._set("selected", true);
  29432. dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
  29433. return page._onShow() || true;
  29434. },
  29435. _hideChild: function(/*dijit._Widget*/ page){
  29436. // summary:
  29437. // Hide the specified child by changing it's CSS, and call _onHide() so
  29438. // it's notified.
  29439. page._set("selected", false);
  29440. dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
  29441. page.onHide();
  29442. },
  29443. closeChild: function(/*dijit._Widget*/ page){
  29444. // summary:
  29445. // Callback when user clicks the [X] to remove a page.
  29446. // If onClose() returns true then remove and destroy the child.
  29447. // tags:
  29448. // private
  29449. var remove = page.onClose(this, page);
  29450. if(remove){
  29451. this.removeChild(page);
  29452. // makes sure we can clean up executeScripts in ContentPane onUnLoad
  29453. page.destroyRecursive();
  29454. }
  29455. },
  29456. destroyDescendants: function(/*Boolean*/ preserveDom){
  29457. dojo.forEach(this.getChildren(), function(child){
  29458. this.removeChild(child);
  29459. child.destroyRecursive(preserveDom);
  29460. }, this);
  29461. }
  29462. });
  29463. // For back-compat, remove for 2.0
  29464. // These arguments can be specified for the children of a StackContainer.
  29465. // Since any widget can be specified as a StackContainer child, mix them
  29466. // into the base widget class. (This is a hack, but it's effective.)
  29467. dojo.extend(dijit._Widget, {
  29468. // selected: Boolean
  29469. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  29470. // Specifies that this widget should be the initially displayed pane.
  29471. // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
  29472. selected: false,
  29473. // closable: Boolean
  29474. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  29475. // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
  29476. closable: false,
  29477. // iconClass: String
  29478. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  29479. // CSS Class specifying icon to use in label associated with this pane.
  29480. iconClass: "",
  29481. // showTitle: Boolean
  29482. // Parameter for children of `dijit.layout.StackContainer` or subclasses.
  29483. // When true, display title of this widget as tab label etc., rather than just using
  29484. // icon specified in iconClass
  29485. showTitle: true
  29486. });
  29487. }
  29488. if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  29489. dojo._hasResource["dijit.layout._TabContainerBase"] = true;
  29490. dojo.provide("dijit.layout._TabContainerBase");
  29491. dojo.declare("dijit.layout._TabContainerBase",
  29492. [dijit.layout.StackContainer, dijit._Templated],
  29493. {
  29494. // summary:
  29495. // Abstract base class for TabContainer. Must define _makeController() to instantiate
  29496. // and return the widget that displays the tab labels
  29497. // description:
  29498. // A TabContainer is a container that has multiple panes, but shows only
  29499. // one pane at a time. There are a set of tabs corresponding to each pane,
  29500. // where each tab has the name (aka title) of the pane, and optionally a close button.
  29501. // tabPosition: String
  29502. // Defines where tabs go relative to tab content.
  29503. // "top", "bottom", "left-h", "right-h"
  29504. tabPosition: "top",
  29505. baseClass: "dijitTabContainer",
  29506. // tabStrip: [const] Boolean
  29507. // Defines whether the tablist gets an extra class for layouting, putting a border/shading
  29508. // around the set of tabs. Not supported by claro theme.
  29509. tabStrip: false,
  29510. // nested: [const] Boolean
  29511. // If true, use styling for a TabContainer nested inside another TabContainer.
  29512. // For tundra etc., makes tabs look like links, and hides the outer
  29513. // border since the outer TabContainer already has a border.
  29514. nested: false,
  29515. 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"),
  29516. postMixInProperties: function(){
  29517. // set class name according to tab position, ex: dijitTabContainerTop
  29518. this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
  29519. this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
  29520. this.inherited(arguments);
  29521. },
  29522. buildRendering: function(){
  29523. this.inherited(arguments);
  29524. // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
  29525. this.tablist = this._makeController(this.tablistNode);
  29526. if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
  29527. if(this.nested){
  29528. /* workaround IE's lack of support for "a > b" selectors by
  29529. * tagging each node in the template.
  29530. */
  29531. dojo.addClass(this.domNode, "dijitTabContainerNested");
  29532. dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
  29533. dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
  29534. dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
  29535. }else{
  29536. dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
  29537. }
  29538. },
  29539. _setupChild: function(/*dijit._Widget*/ tab){
  29540. // Overrides StackContainer._setupChild().
  29541. dojo.addClass(tab.domNode, "dijitTabPane");
  29542. this.inherited(arguments);
  29543. },
  29544. startup: function(){
  29545. if(this._started){ return; }
  29546. // wire up the tablist and its tabs
  29547. this.tablist.startup();
  29548. this.inherited(arguments);
  29549. },
  29550. layout: function(){
  29551. // Overrides StackContainer.layout().
  29552. // Configure the content pane to take up all the space except for where the tabs are
  29553. if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
  29554. var sc = this.selectedChildWidget;
  29555. if(this.doLayout){
  29556. // position and size the titles and the container node
  29557. var titleAlign = this.tabPosition.replace(/-h/, "");
  29558. this.tablist.layoutAlign = titleAlign;
  29559. var children = [this.tablist, {
  29560. domNode: this.tablistSpacer,
  29561. layoutAlign: titleAlign
  29562. }, {
  29563. domNode: this.containerNode,
  29564. layoutAlign: "client"
  29565. }];
  29566. dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
  29567. // Compute size to make each of my children.
  29568. // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
  29569. this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
  29570. if(sc && sc.resize){
  29571. sc.resize(this._containerContentBox);
  29572. }
  29573. }else{
  29574. // just layout the tab controller, so it can position left/right buttons etc.
  29575. if(this.tablist.resize){
  29576. //make the tabs zero width so that they don't interfere with width calc, then reset
  29577. var s = this.tablist.domNode.style;
  29578. s.width="0";
  29579. var width = dojo.contentBox(this.domNode).w;
  29580. s.width="";
  29581. this.tablist.resize({w: width});
  29582. }
  29583. // and call resize() on the selected pane just to tell it that it's been made visible
  29584. if(sc && sc.resize){
  29585. sc.resize();
  29586. }
  29587. }
  29588. },
  29589. destroy: function(){
  29590. if(this.tablist){
  29591. this.tablist.destroy();
  29592. }
  29593. this.inherited(arguments);
  29594. }
  29595. });
  29596. }
  29597. if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  29598. dojo._hasResource["dijit.layout.TabController"] = true;
  29599. dojo.provide("dijit.layout.TabController");
  29600. // Menu is used for an accessible close button, would be nice to have a lighter-weight solution
  29601. dojo.declare("dijit.layout.TabController",
  29602. dijit.layout.StackController,
  29603. {
  29604. // summary:
  29605. // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
  29606. // Used internally by `dijit.layout.TabContainer`.
  29607. // description:
  29608. // Lets the user select the currently shown pane in a TabContainer or StackContainer.
  29609. // TabController also monitors the TabContainer, and whenever a pane is
  29610. // added or deleted updates itself accordingly.
  29611. // tags:
  29612. // private
  29613. templateString: "<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
  29614. // tabPosition: String
  29615. // Defines where tabs go relative to the content.
  29616. // "top", "bottom", "left-h", "right-h"
  29617. tabPosition: "top",
  29618. // buttonWidget: String
  29619. // The name of the tab widget to create to correspond to each page
  29620. buttonWidget: "dijit.layout._TabButton",
  29621. _rectifyRtlTabList: function(){
  29622. // summary:
  29623. // 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
  29624. if(0 >= this.tabPosition.indexOf('-h')){ return; }
  29625. if(!this.pane2button){ return; }
  29626. var maxWidth = 0;
  29627. for(var pane in this.pane2button){
  29628. var ow = this.pane2button[pane].innerDiv.scrollWidth;
  29629. maxWidth = Math.max(maxWidth, ow);
  29630. }
  29631. //unify the length of all the tabs
  29632. for(pane in this.pane2button){
  29633. this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
  29634. }
  29635. }
  29636. });
  29637. dojo.declare("dijit.layout._TabButton",
  29638. dijit.layout._StackButton,
  29639. {
  29640. // summary:
  29641. // A tab (the thing you click to select a pane).
  29642. // description:
  29643. // Contains the title of the pane, and optionally a close-button to destroy the pane.
  29644. // This is an internal widget and should not be instantiated directly.
  29645. // tags:
  29646. // private
  29647. // baseClass: String
  29648. // The CSS class applied to the domNode.
  29649. baseClass: "dijitTab",
  29650. // Apply dijitTabCloseButtonHover when close button is hovered
  29651. cssStateNodes: {
  29652. closeNode: "dijitTabCloseButton"
  29653. },
  29654. 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"),
  29655. // Override _FormWidget.scrollOnFocus.
  29656. // Don't scroll the whole tab container into view when the button is focused.
  29657. scrollOnFocus: false,
  29658. buildRendering: function(){
  29659. this.inherited(arguments);
  29660. dojo.setSelectable(this.containerNode, false);
  29661. },
  29662. startup: function(){
  29663. this.inherited(arguments);
  29664. var n = this.domNode;
  29665. // Required to give IE6 a kick, as it initially hides the
  29666. // tabs until they are focused on.
  29667. setTimeout(function(){
  29668. n.className = n.className;
  29669. }, 1);
  29670. },
  29671. _setCloseButtonAttr: function(/*Boolean*/ disp){
  29672. // summary:
  29673. // Hide/show close button
  29674. this._set("closeButton", disp);
  29675. dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
  29676. this.closeNode.style.display = disp ? "" : "none";
  29677. if(disp){
  29678. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  29679. if(this.closeNode){
  29680. dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
  29681. }
  29682. // add context menu onto title button
  29683. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  29684. this._closeMenu = new dijit.Menu({
  29685. id: this.id+"_Menu",
  29686. dir: this.dir,
  29687. lang: this.lang,
  29688. targetNodeIds: [this.domNode]
  29689. });
  29690. this._closeMenu.addChild(new dijit.MenuItem({
  29691. label: _nlsResources.itemClose,
  29692. dir: this.dir,
  29693. lang: this.lang,
  29694. onClick: dojo.hitch(this, "onClickCloseButton")
  29695. }));
  29696. }else{
  29697. if(this._closeMenu){
  29698. this._closeMenu.destroyRecursive();
  29699. delete this._closeMenu;
  29700. }
  29701. }
  29702. },
  29703. _setLabelAttr: function(/*String*/ content){
  29704. // summary:
  29705. // Hook for set('label', ...) to work.
  29706. // description:
  29707. // takes an HTML string.
  29708. // Inherited ToggleButton implementation will Set the label (text) of the button;
  29709. // Need to set the alt attribute of icon on tab buttons if no label displayed
  29710. this.inherited(arguments);
  29711. if(this.showLabel == false && !this.params.title){
  29712. this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
  29713. }
  29714. },
  29715. destroy: function(){
  29716. if(this._closeMenu){
  29717. this._closeMenu.destroyRecursive();
  29718. delete this._closeMenu;
  29719. }
  29720. this.inherited(arguments);
  29721. }
  29722. });
  29723. }
  29724. if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  29725. dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
  29726. dojo.provide("dijit.layout.ScrollingTabController");
  29727. dojo.declare("dijit.layout.ScrollingTabController",
  29728. dijit.layout.TabController,
  29729. {
  29730. // summary:
  29731. // Set of tabs with left/right arrow keys and a menu to switch between tabs not
  29732. // all fitting on a single row.
  29733. // Works only for horizontal tabs (either above or below the content, not to the left
  29734. // or right).
  29735. // tags:
  29736. // private
  29737. 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"),
  29738. // useMenu: [const] Boolean
  29739. // True if a menu should be used to select tabs when they are too
  29740. // wide to fit the TabContainer, false otherwise.
  29741. useMenu: true,
  29742. // useSlider: [const] Boolean
  29743. // True if a slider should be used to select tabs when they are too
  29744. // wide to fit the TabContainer, false otherwise.
  29745. useSlider: true,
  29746. // tabStripClass: [const] String
  29747. // The css class to apply to the tab strip, if it is visible.
  29748. tabStripClass: "",
  29749. widgetsInTemplate: true,
  29750. // _minScroll: Number
  29751. // The distance in pixels from the edge of the tab strip which,
  29752. // if a scroll animation is less than, forces the scroll to
  29753. // go all the way to the left/right.
  29754. _minScroll: 5,
  29755. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  29756. "class": "containerNode"
  29757. }),
  29758. buildRendering: function(){
  29759. this.inherited(arguments);
  29760. var n = this.domNode;
  29761. this.scrollNode = this.tablistWrapper;
  29762. this._initButtons();
  29763. if(!this.tabStripClass){
  29764. this.tabStripClass = "dijitTabContainer" +
  29765. this.tabPosition.charAt(0).toUpperCase() +
  29766. this.tabPosition.substr(1).replace(/-.*/, "") +
  29767. "None";
  29768. dojo.addClass(n, "tabStrip-disabled")
  29769. }
  29770. dojo.addClass(this.tablistWrapper, this.tabStripClass);
  29771. },
  29772. onStartup: function(){
  29773. this.inherited(arguments);
  29774. // Do not show the TabController until the related
  29775. // StackController has added it's children. This gives
  29776. // a less visually jumpy instantiation.
  29777. dojo.style(this.domNode, "visibility", "visible");
  29778. this._postStartup = true;
  29779. },
  29780. onAddChild: function(page, insertIndex){
  29781. this.inherited(arguments);
  29782. // changes to the tab button label or iconClass will have changed the width of the
  29783. // buttons, so do a resize
  29784. dojo.forEach(["label", "iconClass"], function(attr){
  29785. this.pane2watches[page.id].push(
  29786. this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){
  29787. if(this._postStartup && this._dim){
  29788. this.resize(this._dim);
  29789. }
  29790. }))
  29791. );
  29792. }, this);
  29793. // Increment the width of the wrapper when a tab is added
  29794. // This makes sure that the buttons never wrap.
  29795. // The value 200 is chosen as it should be bigger than most
  29796. // Tab button widths.
  29797. dojo.style(this.containerNode, "width",
  29798. (dojo.style(this.containerNode, "width") + 200) + "px");
  29799. },
  29800. onRemoveChild: function(page, insertIndex){
  29801. // null out _selectedTab because we are about to delete that dom node
  29802. var button = this.pane2button[page.id];
  29803. if(this._selectedTab === button.domNode){
  29804. this._selectedTab = null;
  29805. }
  29806. this.inherited(arguments);
  29807. },
  29808. _initButtons: function(){
  29809. // summary:
  29810. // Creates the buttons used to scroll to view tabs that
  29811. // may not be visible if the TabContainer is too narrow.
  29812. // Make a list of the buttons to display when the tab labels become
  29813. // wider than the TabContainer, and hide the other buttons.
  29814. // Also gets the total width of the displayed buttons.
  29815. this._btnWidth = 0;
  29816. this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
  29817. if((this.useMenu && btn == this._menuBtn.domNode) ||
  29818. (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
  29819. this._btnWidth += dojo._getMarginSize(btn).w;
  29820. return true;
  29821. }else{
  29822. dojo.style(btn, "display", "none");
  29823. return false;
  29824. }
  29825. }, this);
  29826. },
  29827. _getTabsWidth: function(){
  29828. var children = this.getChildren();
  29829. if(children.length){
  29830. var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
  29831. rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
  29832. return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
  29833. }else{
  29834. return 0;
  29835. }
  29836. },
  29837. _enableBtn: function(width){
  29838. // summary:
  29839. // Determines if the tabs are wider than the width of the TabContainer, and
  29840. // thus that we need to display left/right/menu navigation buttons.
  29841. var tabsWidth = this._getTabsWidth();
  29842. width = width || dojo.style(this.scrollNode, "width");
  29843. return tabsWidth > 0 && width < tabsWidth;
  29844. },
  29845. resize: function(dim){
  29846. // summary:
  29847. // Hides or displays the buttons used to scroll the tab list and launch the menu
  29848. // that selects tabs.
  29849. if(this.domNode.offsetWidth == 0){
  29850. return;
  29851. }
  29852. // Save the dimensions to be used when a child is renamed.
  29853. this._dim = dim;
  29854. // Set my height to be my natural height (tall enough for one row of tab labels),
  29855. // and my content-box width based on margin-box width specified in dim parameter.
  29856. // But first reset scrollNode.height in case it was set by layoutChildren() call
  29857. // in a previous run of this method.
  29858. this.scrollNode.style.height = "auto";
  29859. this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
  29860. this._contentBox.h = this.scrollNode.offsetHeight;
  29861. dojo.contentBox(this.domNode, this._contentBox);
  29862. // Show/hide the left/right/menu navigation buttons depending on whether or not they
  29863. // are needed.
  29864. var enable = this._enableBtn(this._contentBox.w);
  29865. this._buttons.style("display", enable ? "" : "none");
  29866. // Position and size the navigation buttons and the tablist
  29867. this._leftBtn.layoutAlign = "left";
  29868. this._rightBtn.layoutAlign = "right";
  29869. this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
  29870. dijit.layout.layoutChildren(this.domNode, this._contentBox,
  29871. [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
  29872. // set proper scroll so that selected tab is visible
  29873. if(this._selectedTab){
  29874. if(this._anim && this._anim.status() == "playing"){
  29875. this._anim.stop();
  29876. }
  29877. var w = this.scrollNode,
  29878. sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
  29879. w.scrollLeft = sl;
  29880. }
  29881. // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
  29882. this._setButtonClass(this._getScroll());
  29883. this._postResize = true;
  29884. // Return my size so layoutChildren() can use it.
  29885. // Also avoids IE9 layout glitch on browser resize when scroll buttons present
  29886. return {h: this._contentBox.h, w: dim.w};
  29887. },
  29888. _getScroll: function(){
  29889. // summary:
  29890. // Returns the current scroll of the tabs where 0 means
  29891. // "scrolled all the way to the left" and some positive number, based on #
  29892. // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
  29893. var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
  29894. dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
  29895. + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
  29896. return sl;
  29897. },
  29898. _convertToScrollLeft: function(val){
  29899. // summary:
  29900. // Given a scroll value where 0 means "scrolled all the way to the left"
  29901. // and some positive number, based on # of pixels of possible scroll (ex: 1000)
  29902. // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
  29903. // to achieve that scroll.
  29904. //
  29905. // This method is to adjust for RTL funniness in various browsers and versions.
  29906. if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
  29907. return val;
  29908. }else{
  29909. var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
  29910. return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
  29911. }
  29912. },
  29913. onSelectChild: function(/*dijit._Widget*/ page){
  29914. // summary:
  29915. // Smoothly scrolls to a tab when it is selected.
  29916. var tab = this.pane2button[page.id];
  29917. if(!tab || !page){return;}
  29918. // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
  29919. var node = tab.domNode;
  29920. if(this._postResize && node != this._selectedTab){
  29921. this._selectedTab = node;
  29922. var sl = this._getScroll();
  29923. if(sl > node.offsetLeft ||
  29924. sl + dojo.style(this.scrollNode, "width") <
  29925. node.offsetLeft + dojo.style(node, "width")){
  29926. this.createSmoothScroll().play();
  29927. }
  29928. }
  29929. this.inherited(arguments);
  29930. },
  29931. _getScrollBounds: function(){
  29932. // summary:
  29933. // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
  29934. // tabs (respectively)
  29935. var children = this.getChildren(),
  29936. scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
  29937. containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
  29938. maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
  29939. tabsWidth = this._getTabsWidth();
  29940. if(children.length && tabsWidth > scrollNodeWidth){
  29941. // Scrolling should happen
  29942. return {
  29943. min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
  29944. max: this.isLeftToRight() ?
  29945. (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
  29946. maxPossibleScroll
  29947. };
  29948. }else{
  29949. // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
  29950. var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
  29951. return {
  29952. min: onlyScrollPosition,
  29953. max: onlyScrollPosition
  29954. };
  29955. }
  29956. },
  29957. _getScrollForSelectedTab: function(){
  29958. // summary:
  29959. // Returns the scroll value setting so that the selected tab
  29960. // will appear in the center
  29961. var w = this.scrollNode,
  29962. n = this._selectedTab,
  29963. scrollNodeWidth = dojo.style(this.scrollNode, "width"),
  29964. scrollBounds = this._getScrollBounds();
  29965. // TODO: scroll minimal amount (to either right or left) so that
  29966. // selected tab is fully visible, and just return if it's already visible?
  29967. var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
  29968. pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
  29969. // TODO:
  29970. // If scrolling close to the left side or right side, scroll
  29971. // all the way to the left or right. See this._minScroll.
  29972. // (But need to make sure that doesn't scroll the tab out of view...)
  29973. return pos;
  29974. },
  29975. createSmoothScroll: function(x){
  29976. // summary:
  29977. // Creates a dojo._Animation object that smoothly scrolls the tab list
  29978. // either to a fixed horizontal pixel value, or to the selected tab.
  29979. // description:
  29980. // If an number argument is passed to the function, that horizontal
  29981. // pixel position is scrolled to. Otherwise the currently selected
  29982. // tab is scrolled to.
  29983. // x: Integer?
  29984. // An optional pixel value to scroll to, indicating distance from left.
  29985. // Calculate position to scroll to
  29986. if(arguments.length > 0){
  29987. // position specified by caller, just make sure it's within bounds
  29988. var scrollBounds = this._getScrollBounds();
  29989. x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
  29990. }else{
  29991. // scroll to center the current tab
  29992. x = this._getScrollForSelectedTab();
  29993. }
  29994. if(this._anim && this._anim.status() == "playing"){
  29995. this._anim.stop();
  29996. }
  29997. var self = this,
  29998. w = this.scrollNode,
  29999. anim = new dojo._Animation({
  30000. beforeBegin: function(){
  30001. if(this.curve){ delete this.curve; }
  30002. var oldS = w.scrollLeft,
  30003. newS = self._convertToScrollLeft(x);
  30004. anim.curve = new dojo._Line(oldS, newS);
  30005. },
  30006. onAnimate: function(val){
  30007. w.scrollLeft = val;
  30008. }
  30009. });
  30010. this._anim = anim;
  30011. // Disable/enable left/right buttons according to new scroll position
  30012. this._setButtonClass(x);
  30013. return anim; // dojo._Animation
  30014. },
  30015. _getBtnNode: function(/*Event*/ e){
  30016. // summary:
  30017. // Gets a button DOM node from a mouse click event.
  30018. // e:
  30019. // The mouse click event.
  30020. var n = e.target;
  30021. while(n && !dojo.hasClass(n, "tabStripButton")){
  30022. n = n.parentNode;
  30023. }
  30024. return n;
  30025. },
  30026. doSlideRight: function(/*Event*/ e){
  30027. // summary:
  30028. // Scrolls the menu to the right.
  30029. // e:
  30030. // The mouse click event.
  30031. this.doSlide(1, this._getBtnNode(e));
  30032. },
  30033. doSlideLeft: function(/*Event*/ e){
  30034. // summary:
  30035. // Scrolls the menu to the left.
  30036. // e:
  30037. // The mouse click event.
  30038. this.doSlide(-1,this._getBtnNode(e));
  30039. },
  30040. doSlide: function(/*Number*/ direction, /*DomNode*/ node){
  30041. // summary:
  30042. // Scrolls the tab list to the left or right by 75% of the widget width.
  30043. // direction:
  30044. // If the direction is 1, the widget scrolls to the right, if it is
  30045. // -1, it scrolls to the left.
  30046. if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
  30047. var sWidth = dojo.style(this.scrollNode, "width");
  30048. var d = (sWidth * 0.75) * direction;
  30049. var to = this._getScroll() + d;
  30050. this._setButtonClass(to);
  30051. this.createSmoothScroll(to).play();
  30052. },
  30053. _setButtonClass: function(/*Number*/ scroll){
  30054. // summary:
  30055. // Disables the left scroll button if the tabs are scrolled all the way to the left,
  30056. // or the right scroll button in the opposite case.
  30057. // scroll: Integer
  30058. // amount of horizontal scroll
  30059. var scrollBounds = this._getScrollBounds();
  30060. this._leftBtn.set("disabled", scroll <= scrollBounds.min);
  30061. this._rightBtn.set("disabled", scroll >= scrollBounds.max);
  30062. }
  30063. });
  30064. dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
  30065. baseClass: "dijitTab tabStripButton",
  30066. 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"),
  30067. // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
  30068. // able to tab to the left/right/menu buttons
  30069. tabIndex: "",
  30070. // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
  30071. // either (this override avoids focus() call in FormWidget.js)
  30072. isFocusable: function(){ return false; }
  30073. });
  30074. dojo.declare("dijit.layout._ScrollingTabControllerButton",
  30075. [dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]);
  30076. dojo.declare(
  30077. "dijit.layout._ScrollingTabControllerMenuButton",
  30078. [dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin],
  30079. {
  30080. // id of the TabContainer itself
  30081. containerId: "",
  30082. // -1 so user can't tab into the button, but so that button can still be focused programatically.
  30083. // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
  30084. tabIndex: "-1",
  30085. isLoaded: function(){
  30086. // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
  30087. return false;
  30088. },
  30089. loadDropDown: function(callback){
  30090. this.dropDown = new dijit.Menu({
  30091. id: this.containerId + "_menu",
  30092. dir: this.dir,
  30093. lang: this.lang
  30094. });
  30095. var container = dijit.byId(this.containerId);
  30096. dojo.forEach(container.getChildren(), function(page){
  30097. var menuItem = new dijit.MenuItem({
  30098. id: page.id + "_stcMi",
  30099. label: page.title,
  30100. iconClass: page.iconClass,
  30101. dir: page.dir,
  30102. lang: page.lang,
  30103. onClick: function(){
  30104. container.selectChild(page);
  30105. }
  30106. });
  30107. this.dropDown.addChild(menuItem);
  30108. }, this);
  30109. callback();
  30110. },
  30111. closeDropDown: function(/*Boolean*/ focus){
  30112. this.inherited(arguments);
  30113. if(this.dropDown){
  30114. this.dropDown.destroyRecursive();
  30115. delete this.dropDown;
  30116. }
  30117. }
  30118. });
  30119. }
  30120. if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  30121. dojo._hasResource["dijit.layout.TabContainer"] = true;
  30122. dojo.provide("dijit.layout.TabContainer");
  30123. dojo.declare("dijit.layout.TabContainer",
  30124. dijit.layout._TabContainerBase,
  30125. {
  30126. // summary:
  30127. // A Container with tabs to select each child (only one of which is displayed at a time).
  30128. // description:
  30129. // A TabContainer is a container that has multiple panes, but shows only
  30130. // one pane at a time. There are a set of tabs corresponding to each pane,
  30131. // where each tab has the name (aka title) of the pane, and optionally a close button.
  30132. // useMenu: [const] Boolean
  30133. // True if a menu should be used to select tabs when they are too
  30134. // wide to fit the TabContainer, false otherwise.
  30135. useMenu: true,
  30136. // useSlider: [const] Boolean
  30137. // True if a slider should be used to select tabs when they are too
  30138. // wide to fit the TabContainer, false otherwise.
  30139. useSlider: true,
  30140. // controllerWidget: String
  30141. // An optional parameter to override the widget used to display the tab labels
  30142. controllerWidget: "",
  30143. _makeController: function(/*DomNode*/ srcNode){
  30144. // summary:
  30145. // Instantiate tablist controller widget and return reference to it.
  30146. // Callback from _TabContainerBase.postCreate().
  30147. // tags:
  30148. // protected extension
  30149. var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
  30150. TabController = dojo.getObject(this.controllerWidget);
  30151. return new TabController({
  30152. id: this.id + "_tablist",
  30153. dir: this.dir,
  30154. lang: this.lang,
  30155. tabPosition: this.tabPosition,
  30156. doLayout: this.doLayout,
  30157. containerId: this.id,
  30158. "class": cls,
  30159. nested: this.nested,
  30160. useMenu: this.useMenu,
  30161. useSlider: this.useSlider,
  30162. tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
  30163. }, srcNode);
  30164. },
  30165. postMixInProperties: function(){
  30166. this.inherited(arguments);
  30167. // Scrolling controller only works for horizontal non-nested tabs
  30168. if(!this.controllerWidget){
  30169. this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
  30170. "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
  30171. }
  30172. }
  30173. });
  30174. }
  30175. if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  30176. dojo._hasResource["dijit.TitlePane"] = true;
  30177. dojo.provide("dijit.TitlePane");
  30178. dojo.declare(
  30179. "dijit.TitlePane",
  30180. [dijit.layout.ContentPane, dijit._Templated, dijit._CssStateMixin],
  30181. {
  30182. // summary:
  30183. // A pane with a title on top, that can be expanded or collapsed.
  30184. //
  30185. // description:
  30186. // An accessible container with a title Heading, and a content
  30187. // section that slides open and closed. TitlePane is an extension to
  30188. // `dijit.layout.ContentPane`, providing all the useful content-control aspects from it.
  30189. //
  30190. // example:
  30191. // | // load a TitlePane from remote file:
  30192. // | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
  30193. // | foo.startup();
  30194. //
  30195. // example:
  30196. // | <!-- markup href example: -->
  30197. // | <div dojoType="dijit.TitlePane" href="foobar.html" title="Title"></div>
  30198. //
  30199. // example:
  30200. // | <!-- markup with inline data -->
  30201. // | <div dojoType="dijit.TitlePane" title="Title">
  30202. // | <p>I am content</p>
  30203. // | </div>
  30204. // title: String
  30205. // Title of the pane
  30206. title: "",
  30207. // open: Boolean
  30208. // Whether pane is opened or closed.
  30209. open: true,
  30210. // toggleable: Boolean
  30211. // Whether pane can be opened or closed by clicking the title bar.
  30212. toggleable: true,
  30213. // tabIndex: String
  30214. // Tabindex setting for the title (so users can tab to the title then
  30215. // use space/enter to open/close the title pane)
  30216. tabIndex: "0",
  30217. // duration: Integer
  30218. // Time in milliseconds to fade in/fade out
  30219. duration: dijit.defaultDuration,
  30220. // baseClass: [protected] String
  30221. // The root className to be placed on this widget's domNode.
  30222. baseClass: "dijitTitlePane",
  30223. 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"),
  30224. attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
  30225. title: { node: "titleNode", type: "innerHTML" },
  30226. tooltip: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
  30227. id:""
  30228. }),
  30229. buildRendering: function(){
  30230. this.inherited(arguments);
  30231. dojo.setSelectable(this.titleNode, false);
  30232. },
  30233. postCreate: function(){
  30234. this.inherited(arguments);
  30235. // Hover and focus effect on title bar, except for non-toggleable TitlePanes
  30236. // This should really be controlled from _setToggleableAttr() but _CssStateMixin
  30237. // doesn't provide a way to disconnect a previous _trackMouseState() call
  30238. if(this.toggleable){
  30239. this._trackMouseState(this.titleBarNode, "dijitTitlePaneTitle");
  30240. }
  30241. // setup open/close animations
  30242. var hideNode = this.hideNode, wipeNode = this.wipeNode;
  30243. this._wipeIn = dojo.fx.wipeIn({
  30244. node: this.wipeNode,
  30245. duration: this.duration,
  30246. beforeBegin: function(){
  30247. hideNode.style.display="";
  30248. }
  30249. });
  30250. this._wipeOut = dojo.fx.wipeOut({
  30251. node: this.wipeNode,
  30252. duration: this.duration,
  30253. onEnd: function(){
  30254. hideNode.style.display="none";
  30255. }
  30256. });
  30257. },
  30258. _setOpenAttr: function(/*Boolean*/ open, /*Boolean*/ animate){
  30259. // summary:
  30260. // Hook to make set("open", boolean) control the open/closed state of the pane.
  30261. // open: Boolean
  30262. // True if you want to open the pane, false if you want to close it.
  30263. dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
  30264. if(animation && animation.status() == "playing"){
  30265. animation.stop();
  30266. }
  30267. });
  30268. if(animate){
  30269. var anim = this[open ? "_wipeIn" : "_wipeOut"];
  30270. anim.play();
  30271. }else{
  30272. this.hideNode.style.display = this.wipeNode.style.display = open ? "" : "none";
  30273. }
  30274. // load content (if this is the first time we are opening the TitlePane
  30275. // and content is specified as an href, or href was set when hidden)
  30276. if(this._started){
  30277. if(open){
  30278. this._onShow();
  30279. }else{
  30280. this.onHide();
  30281. }
  30282. }
  30283. this.arrowNodeInner.innerHTML = open ? "-" : "+";
  30284. dijit.setWaiState(this.containerNode,"hidden", open ? "false" : "true");
  30285. dijit.setWaiState(this.focusNode, "pressed", open ? "true" : "false");
  30286. this._set("open", open);
  30287. this._setCss();
  30288. },
  30289. _setToggleableAttr: function(/*Boolean*/ canToggle){
  30290. // summary:
  30291. // Hook to make set("toggleable", boolean) work.
  30292. // canToggle: Boolean
  30293. // True to allow user to open/close pane by clicking title bar.
  30294. dijit.setWaiRole(this.focusNode, canToggle ? "button" : "heading");
  30295. if(canToggle){
  30296. // TODO: if canToggle is switched from true to false shouldn't we remove this setting?
  30297. dijit.setWaiState(this.focusNode, "controls", this.id+"_pane");
  30298. dojo.attr(this.focusNode, "tabIndex", this.tabIndex);
  30299. }else{
  30300. dojo.removeAttr(this.focusNode, "tabIndex");
  30301. }
  30302. this._set("toggleable", canToggle);
  30303. this._setCss();
  30304. },
  30305. _setContentAttr: function(/*String|DomNode|Nodelist*/ content){
  30306. // summary:
  30307. // Hook to make set("content", ...) work.
  30308. // Typically called when an href is loaded. Our job is to make the animation smooth.
  30309. if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
  30310. // we are currently *closing* the pane (or the pane is closed), so just let that continue
  30311. this.inherited(arguments);
  30312. }else{
  30313. if(this._wipeIn && this._wipeIn.status() == "playing"){
  30314. this._wipeIn.stop();
  30315. }
  30316. // freeze container at current height so that adding new content doesn't make it jump
  30317. dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });
  30318. // add the new content (erasing the old content, if any)
  30319. this.inherited(arguments);
  30320. // call _wipeIn.play() to animate from current height to new height
  30321. if(this._wipeIn){
  30322. this._wipeIn.play();
  30323. }else{
  30324. this.hideNode.style.display = "";
  30325. }
  30326. }
  30327. },
  30328. toggle: function(){
  30329. // summary:
  30330. // Switches between opened and closed state
  30331. // tags:
  30332. // private
  30333. this._setOpenAttr(!this.open, true);
  30334. },
  30335. _setCss: function(){
  30336. // summary:
  30337. // Set the open/close css state for the TitlePane
  30338. // tags:
  30339. // private
  30340. var node = this.titleBarNode || this.focusNode;
  30341. var oldCls = this._titleBarClass;
  30342. this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
  30343. dojo.replaceClass(node, this._titleBarClass, oldCls || "");
  30344. this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
  30345. },
  30346. _onTitleKey: function(/*Event*/ e){
  30347. // summary:
  30348. // Handler for when user hits a key
  30349. // tags:
  30350. // private
  30351. if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
  30352. if(this.toggleable){
  30353. this.toggle();
  30354. }
  30355. dojo.stopEvent(e);
  30356. }else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
  30357. this.containerNode.focus();
  30358. e.preventDefault();
  30359. }
  30360. },
  30361. _onTitleClick: function(){
  30362. // summary:
  30363. // Handler when user clicks the title bar
  30364. // tags:
  30365. // private
  30366. if(this.toggleable){
  30367. this.toggle();
  30368. }
  30369. },
  30370. setTitle: function(/*String*/ title){
  30371. // summary:
  30372. // Deprecated. Use set('title', ...) instead.
  30373. // tags:
  30374. // deprecated
  30375. dojo.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
  30376. this.set("title", title);
  30377. }
  30378. });
  30379. }
  30380. if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  30381. dojo._hasResource["dojo.DeferredList"] = true;
  30382. dojo.provide("dojo.DeferredList");
  30383. dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
  30384. // summary:
  30385. // Provides event handling for a group of Deferred objects.
  30386. // description:
  30387. // DeferredList takes an array of existing deferreds and returns a new deferred of its own
  30388. // this new deferred will typically have its callback fired when all of the deferreds in
  30389. // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
  30390. // fireOnOneErrback, will fire before all the deferreds as appropriate
  30391. //
  30392. // list:
  30393. // The list of deferreds to be synchronizied with this DeferredList
  30394. // fireOnOneCallback:
  30395. // Will cause the DeferredLists callback to be fired as soon as any
  30396. // of the deferreds in its list have been fired instead of waiting until
  30397. // the entire list has finished
  30398. // fireonOneErrback:
  30399. // Will cause the errback to fire upon any of the deferreds errback
  30400. // canceller:
  30401. // A deferred canceller function, see dojo.Deferred
  30402. var resultList = [];
  30403. dojo.Deferred.call(this);
  30404. var self = this;
  30405. if(list.length === 0 && !fireOnOneCallback){
  30406. this.resolve([0, []]);
  30407. }
  30408. var finished = 0;
  30409. dojo.forEach(list, function(item, i){
  30410. item.then(function(result){
  30411. if(fireOnOneCallback){
  30412. self.resolve([i, result]);
  30413. }else{
  30414. addResult(true, result);
  30415. }
  30416. },function(error){
  30417. if(fireOnOneErrback){
  30418. self.reject(error);
  30419. }else{
  30420. addResult(false, error);
  30421. }
  30422. if(consumeErrors){
  30423. return null;
  30424. }
  30425. throw error;
  30426. });
  30427. function addResult(succeeded, result){
  30428. resultList[i] = [succeeded, result];
  30429. finished++;
  30430. if(finished === list.length){
  30431. self.resolve(resultList);
  30432. }
  30433. }
  30434. });
  30435. };
  30436. dojo.DeferredList.prototype = new dojo.Deferred();
  30437. dojo.DeferredList.prototype.gatherResults= function(deferredList){
  30438. // summary:
  30439. // Gathers the results of the deferreds for packaging
  30440. // as the parameters to the Deferred Lists' callback
  30441. var d = new dojo.DeferredList(deferredList, false, true, false);
  30442. d.addCallback(function(results){
  30443. var ret = [];
  30444. dojo.forEach(results, function(result){
  30445. ret.push(result[1]);
  30446. });
  30447. return ret;
  30448. });
  30449. return d;
  30450. };
  30451. }
  30452. if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  30453. dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
  30454. dojo.provide("dijit.tree.TreeStoreModel");
  30455. dojo.declare(
  30456. "dijit.tree.TreeStoreModel",
  30457. null,
  30458. {
  30459. // summary:
  30460. // Implements dijit.Tree.model connecting to a store with a single
  30461. // root item. Any methods passed into the constructor will override
  30462. // the ones defined here.
  30463. // store: dojo.data.Store
  30464. // Underlying store
  30465. store: null,
  30466. // childrenAttrs: String[]
  30467. // One or more attribute names (attributes in the dojo.data item) that specify that item's children
  30468. childrenAttrs: ["children"],
  30469. // newItemIdAttr: String
  30470. // Name of attribute in the Object passed to newItem() that specifies the id.
  30471. //
  30472. // If newItemIdAttr is set then it's used when newItem() is called to see if an
  30473. // item with the same id already exists, and if so just links to the old item
  30474. // (so that the old item ends up with two parents).
  30475. //
  30476. // Setting this to null or "" will make every drop create a new item.
  30477. newItemIdAttr: "id",
  30478. // labelAttr: String
  30479. // If specified, get label for tree node from this attribute, rather
  30480. // than by calling store.getLabel()
  30481. labelAttr: "",
  30482. // root: [readonly] dojo.data.Item
  30483. // Pointer to the root item (read only, not a parameter)
  30484. root: null,
  30485. // query: anything
  30486. // Specifies datastore query to return the root item for the tree.
  30487. // Must only return a single item. Alternately can just pass in pointer
  30488. // to root item.
  30489. // example:
  30490. // | {id:'ROOT'}
  30491. query: null,
  30492. // deferItemLoadingUntilExpand: Boolean
  30493. // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
  30494. // until they are expanded. This allows for lazying loading where only one
  30495. // loadItem (and generally one network call, consequently) per expansion
  30496. // (rather than one for each child).
  30497. // This relies on partial loading of the children items; each children item of a
  30498. // fully loaded item should contain the label and info about having children.
  30499. deferItemLoadingUntilExpand: false,
  30500. constructor: function(/* Object */ args){
  30501. // summary:
  30502. // Passed the arguments listed above (store, etc)
  30503. // tags:
  30504. // private
  30505. dojo.mixin(this, args);
  30506. this.connects = [];
  30507. var store = this.store;
  30508. if(!store.getFeatures()['dojo.data.api.Identity']){
  30509. throw new Error("dijit.Tree: store must support dojo.data.Identity");
  30510. }
  30511. // if the store supports Notification, subscribe to the notification events
  30512. if(store.getFeatures()['dojo.data.api.Notification']){
  30513. this.connects = this.connects.concat([
  30514. dojo.connect(store, "onNew", this, "onNewItem"),
  30515. dojo.connect(store, "onDelete", this, "onDeleteItem"),
  30516. dojo.connect(store, "onSet", this, "onSetItem")
  30517. ]);
  30518. }
  30519. },
  30520. destroy: function(){
  30521. dojo.forEach(this.connects, dojo.disconnect);
  30522. // TODO: should cancel any in-progress processing of getRoot(), getChildren()
  30523. },
  30524. // =======================================================================
  30525. // Methods for traversing hierarchy
  30526. getRoot: function(onItem, onError){
  30527. // summary:
  30528. // Calls onItem with the root item for the tree, possibly a fabricated item.
  30529. // Calls onError on error.
  30530. if(this.root){
  30531. onItem(this.root);
  30532. }else{
  30533. this.store.fetch({
  30534. query: this.query,
  30535. onComplete: dojo.hitch(this, function(items){
  30536. if(items.length != 1){
  30537. throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
  30538. " items, but must return exactly one item");
  30539. }
  30540. this.root = items[0];
  30541. onItem(this.root);
  30542. }),
  30543. onError: onError
  30544. });
  30545. }
  30546. },
  30547. mayHaveChildren: function(/*dojo.data.Item*/ item){
  30548. // summary:
  30549. // Tells if an item has or may have children. Implementing logic here
  30550. // avoids showing +/- expando icon for nodes that we know don't have children.
  30551. // (For efficiency reasons we may not want to check if an element actually
  30552. // has children until user clicks the expando node)
  30553. return dojo.some(this.childrenAttrs, function(attr){
  30554. return this.store.hasAttribute(item, attr);
  30555. }, this);
  30556. },
  30557. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
  30558. // summary:
  30559. // Calls onComplete() with array of child items of given parent item, all loaded.
  30560. var store = this.store;
  30561. if(!store.isItemLoaded(parentItem)){
  30562. // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
  30563. // mode, so we will load it and just return the children (without loading each
  30564. // child item)
  30565. var getChildren = dojo.hitch(this, arguments.callee);
  30566. store.loadItem({
  30567. item: parentItem,
  30568. onItem: function(parentItem){
  30569. getChildren(parentItem, onComplete, onError);
  30570. },
  30571. onError: onError
  30572. });
  30573. return;
  30574. }
  30575. // get children of specified item
  30576. var childItems = [];
  30577. for(var i=0; i<this.childrenAttrs.length; i++){
  30578. var vals = store.getValues(parentItem, this.childrenAttrs[i]);
  30579. childItems = childItems.concat(vals);
  30580. }
  30581. // count how many items need to be loaded
  30582. var _waitCount = 0;
  30583. if(!this.deferItemLoadingUntilExpand){
  30584. dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
  30585. }
  30586. if(_waitCount == 0){
  30587. // all items are already loaded (or we aren't loading them). proceed...
  30588. onComplete(childItems);
  30589. }else{
  30590. // still waiting for some or all of the items to load
  30591. dojo.forEach(childItems, function(item, idx){
  30592. if(!store.isItemLoaded(item)){
  30593. store.loadItem({
  30594. item: item,
  30595. onItem: function(item){
  30596. childItems[idx] = item;
  30597. if(--_waitCount == 0){
  30598. // all nodes have been loaded, send them to the tree
  30599. onComplete(childItems);
  30600. }
  30601. },
  30602. onError: onError
  30603. });
  30604. }
  30605. });
  30606. }
  30607. },
  30608. // =======================================================================
  30609. // Inspecting items
  30610. isItem: function(/* anything */ something){
  30611. return this.store.isItem(something); // Boolean
  30612. },
  30613. fetchItemByIdentity: function(/* object */ keywordArgs){
  30614. this.store.fetchItemByIdentity(keywordArgs);
  30615. },
  30616. getIdentity: function(/* item */ item){
  30617. return this.store.getIdentity(item); // Object
  30618. },
  30619. getLabel: function(/*dojo.data.Item*/ item){
  30620. // summary:
  30621. // Get the label for an item
  30622. if(this.labelAttr){
  30623. return this.store.getValue(item,this.labelAttr); // String
  30624. }else{
  30625. return this.store.getLabel(item); // String
  30626. }
  30627. },
  30628. // =======================================================================
  30629. // Write interface
  30630. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  30631. // summary:
  30632. // Creates a new item. See `dojo.data.api.Write` for details on args.
  30633. // Used in drag & drop when item from external source dropped onto tree.
  30634. // description:
  30635. // Developers will need to override this method if new items get added
  30636. // to parents with multiple children attributes, in order to define which
  30637. // children attribute points to the new item.
  30638. var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
  30639. if(this.newItemIdAttr && args[this.newItemIdAttr]){
  30640. // Maybe there's already a corresponding item in the store; if so, reuse it.
  30641. this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
  30642. if(item){
  30643. // There's already a matching item in store, use it
  30644. this.pasteItem(item, null, parent, true, insertIndex);
  30645. }else{
  30646. // Create new item in the tree, based on the drag source.
  30647. LnewItem=this.store.newItem(args, pInfo);
  30648. if (LnewItem && (insertIndex!=undefined)){
  30649. // Move new item to desired position
  30650. this.pasteItem(LnewItem, parent, parent, false, insertIndex);
  30651. }
  30652. }
  30653. }});
  30654. }else{
  30655. // [as far as we know] there is no id so we must assume this is a new item
  30656. LnewItem=this.store.newItem(args, pInfo);
  30657. if (LnewItem && (insertIndex!=undefined)){
  30658. // Move new item to desired position
  30659. this.pasteItem(LnewItem, parent, parent, false, insertIndex);
  30660. }
  30661. }
  30662. },
  30663. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  30664. // summary:
  30665. // Move or copy an item from one parent item to another.
  30666. // Used in drag & drop
  30667. var store = this.store,
  30668. parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
  30669. // remove child from source item, and record the attribute that child occurred in
  30670. if(oldParentItem){
  30671. dojo.forEach(this.childrenAttrs, function(attr){
  30672. if(store.containsValue(oldParentItem, attr, childItem)){
  30673. if(!bCopy){
  30674. var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
  30675. return x != childItem;
  30676. });
  30677. store.setValues(oldParentItem, attr, values);
  30678. }
  30679. parentAttr = attr;
  30680. }
  30681. });
  30682. }
  30683. // modify target item's children attribute to include this item
  30684. if(newParentItem){
  30685. if(typeof insertIndex == "number"){
  30686. // call slice() to avoid modifying the original array, confusing the data store
  30687. var childItems = store.getValues(newParentItem, parentAttr).slice();
  30688. childItems.splice(insertIndex, 0, childItem);
  30689. store.setValues(newParentItem, parentAttr, childItems);
  30690. }else{
  30691. store.setValues(newParentItem, parentAttr,
  30692. store.getValues(newParentItem, parentAttr).concat(childItem));
  30693. }
  30694. }
  30695. },
  30696. // =======================================================================
  30697. // Callbacks
  30698. onChange: function(/*dojo.data.Item*/ item){
  30699. // summary:
  30700. // Callback whenever an item has changed, so that Tree
  30701. // can update the label, icon, etc. Note that changes
  30702. // to an item's children or parent(s) will trigger an
  30703. // onChildrenChange() so you can ignore those changes here.
  30704. // tags:
  30705. // callback
  30706. },
  30707. onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  30708. // summary:
  30709. // Callback to do notifications about new, updated, or deleted items.
  30710. // tags:
  30711. // callback
  30712. },
  30713. onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  30714. // summary:
  30715. // Callback when an item has been deleted.
  30716. // description:
  30717. // Note that there will also be an onChildrenChange() callback for the parent
  30718. // of this item.
  30719. // tags:
  30720. // callback
  30721. },
  30722. // =======================================================================
  30723. // Events from data store
  30724. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  30725. // summary:
  30726. // Handler for when new items appear in the store, either from a drop operation
  30727. // or some other way. Updates the tree view (if necessary).
  30728. // description:
  30729. // If the new item is a child of an existing item,
  30730. // calls onChildrenChange() with the new list of children
  30731. // for that existing item.
  30732. //
  30733. // tags:
  30734. // extension
  30735. // We only care about the new item if it has a parent that corresponds to a TreeNode
  30736. // we are currently displaying
  30737. if(!parentInfo){
  30738. return;
  30739. }
  30740. // Call onChildrenChange() on parent (ie, existing) item with new list of children
  30741. // In the common case, the new list of children is simply parentInfo.newValue or
  30742. // [ parentInfo.newValue ], although if items in the store has multiple
  30743. // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
  30744. // so call getChildren() to be sure to get right answer.
  30745. this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
  30746. this.onChildrenChange(parentInfo.item, children);
  30747. }));
  30748. },
  30749. onDeleteItem: function(/*Object*/ item){
  30750. // summary:
  30751. // Handler for delete notifications from underlying store
  30752. this.onDelete(item);
  30753. },
  30754. onSetItem: function(/* item */ item,
  30755. /* attribute-name-string */ attribute,
  30756. /* object | array */ oldValue,
  30757. /* object | array */ newValue){
  30758. // summary:
  30759. // Updates the tree view according to changes in the data store.
  30760. // description:
  30761. // Handles updates to an item's children by calling onChildrenChange(), and
  30762. // other updates to an item by calling onChange().
  30763. //
  30764. // See `onNewItem` for more details on handling updates to an item's children.
  30765. // tags:
  30766. // extension
  30767. if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
  30768. // item's children list changed
  30769. this.getChildren(item, dojo.hitch(this, function(children){
  30770. // See comments in onNewItem() about calling getChildren()
  30771. this.onChildrenChange(item, children);
  30772. }));
  30773. }else{
  30774. // item's label/icon/etc. changed.
  30775. this.onChange(item);
  30776. }
  30777. }
  30778. });
  30779. }
  30780. if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  30781. dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
  30782. dojo.provide("dijit.tree.ForestStoreModel");
  30783. dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
  30784. // summary:
  30785. // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
  30786. // a.k.a. a store that has multiple "top level" items.
  30787. //
  30788. // description
  30789. // Use this class to wrap a dojo.data store, making all the items matching the specified query
  30790. // appear as children of a fabricated "root item". If no query is specified then all the
  30791. // items returned by fetch() on the underlying store become children of the root item.
  30792. // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
  30793. //
  30794. // When using this class the developer must override a number of methods according to their app and
  30795. // data, including:
  30796. // - onNewRootItem
  30797. // - onAddToRoot
  30798. // - onLeaveRoot
  30799. // - onNewItem
  30800. // - onSetItem
  30801. // Parameters to constructor
  30802. // rootId: String
  30803. // ID of fabricated root item
  30804. rootId: "$root$",
  30805. // rootLabel: String
  30806. // Label of fabricated root item
  30807. rootLabel: "ROOT",
  30808. // query: String
  30809. // Specifies the set of children of the root item.
  30810. // example:
  30811. // | {type:'continent'}
  30812. query: null,
  30813. // End of parameters to constructor
  30814. constructor: function(params){
  30815. // summary:
  30816. // Sets up variables, etc.
  30817. // tags:
  30818. // private
  30819. // Make dummy root item
  30820. this.root = {
  30821. store: this,
  30822. root: true,
  30823. id: params.rootId,
  30824. label: params.rootLabel,
  30825. children: params.rootChildren // optional param
  30826. };
  30827. },
  30828. // =======================================================================
  30829. // Methods for traversing hierarchy
  30830. mayHaveChildren: function(/*dojo.data.Item*/ item){
  30831. // summary:
  30832. // Tells if an item has or may have children. Implementing logic here
  30833. // avoids showing +/- expando icon for nodes that we know don't have children.
  30834. // (For efficiency reasons we may not want to check if an element actually
  30835. // has children until user clicks the expando node)
  30836. // tags:
  30837. // extension
  30838. return item === this.root || this.inherited(arguments);
  30839. },
  30840. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
  30841. // summary:
  30842. // Calls onComplete() with array of child items of given parent item, all loaded.
  30843. if(parentItem === this.root){
  30844. if(this.root.children){
  30845. // already loaded, just return
  30846. callback(this.root.children);
  30847. }else{
  30848. this.store.fetch({
  30849. query: this.query,
  30850. onComplete: dojo.hitch(this, function(items){
  30851. this.root.children = items;
  30852. callback(items);
  30853. }),
  30854. onError: onError
  30855. });
  30856. }
  30857. }else{
  30858. this.inherited(arguments);
  30859. }
  30860. },
  30861. // =======================================================================
  30862. // Inspecting items
  30863. isItem: function(/* anything */ something){
  30864. return (something === this.root) ? true : this.inherited(arguments);
  30865. },
  30866. fetchItemByIdentity: function(/* object */ keywordArgs){
  30867. if(keywordArgs.identity == this.root.id){
  30868. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  30869. if(keywordArgs.onItem){
  30870. keywordArgs.onItem.call(scope, this.root);
  30871. }
  30872. }else{
  30873. this.inherited(arguments);
  30874. }
  30875. },
  30876. getIdentity: function(/* item */ item){
  30877. return (item === this.root) ? this.root.id : this.inherited(arguments);
  30878. },
  30879. getLabel: function(/* item */ item){
  30880. return (item === this.root) ? this.root.label : this.inherited(arguments);
  30881. },
  30882. // =======================================================================
  30883. // Write interface
  30884. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  30885. // summary:
  30886. // Creates a new item. See dojo.data.api.Write for details on args.
  30887. // Used in drag & drop when item from external source dropped onto tree.
  30888. if(parent === this.root){
  30889. this.onNewRootItem(args);
  30890. return this.store.newItem(args);
  30891. }else{
  30892. return this.inherited(arguments);
  30893. }
  30894. },
  30895. onNewRootItem: function(args){
  30896. // summary:
  30897. // User can override this method to modify a new element that's being
  30898. // added to the root of the tree, for example to add a flag like root=true
  30899. },
  30900. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  30901. // summary:
  30902. // Move or copy an item from one parent item to another.
  30903. // Used in drag & drop
  30904. if(oldParentItem === this.root){
  30905. if(!bCopy){
  30906. // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
  30907. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  30908. // that this element is no longer a child of the root node
  30909. this.onLeaveRoot(childItem);
  30910. }
  30911. }
  30912. dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
  30913. oldParentItem === this.root ? null : oldParentItem,
  30914. newParentItem === this.root ? null : newParentItem,
  30915. bCopy,
  30916. insertIndex
  30917. );
  30918. if(newParentItem === this.root){
  30919. // It's onAddToRoot()'s responsibility to modify the item so it matches
  30920. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  30921. // that this element is now a child of the root node
  30922. this.onAddToRoot(childItem);
  30923. }
  30924. },
  30925. // =======================================================================
  30926. // Handling for top level children
  30927. onAddToRoot: function(/* item */ item){
  30928. // summary:
  30929. // Called when item added to root of tree; user must override this method
  30930. // to modify the item so that it matches the query for top level items
  30931. // example:
  30932. // | store.setValue(item, "root", true);
  30933. // tags:
  30934. // extension
  30935. console.log(this, ": item ", item, " added to root");
  30936. },
  30937. onLeaveRoot: function(/* item */ item){
  30938. // summary:
  30939. // Called when item removed from root of tree; user must override this method
  30940. // to modify the item so it doesn't match the query for top level items
  30941. // example:
  30942. // | store.unsetAttribute(item, "root");
  30943. // tags:
  30944. // extension
  30945. console.log(this, ": item ", item, " removed from root");
  30946. },
  30947. // =======================================================================
  30948. // Events from data store
  30949. _requeryTop: function(){
  30950. // reruns the query for the children of the root node,
  30951. // sending out an onSet notification if those children have changed
  30952. var oldChildren = this.root.children || [];
  30953. this.store.fetch({
  30954. query: this.query,
  30955. onComplete: dojo.hitch(this, function(newChildren){
  30956. this.root.children = newChildren;
  30957. // If the list of children or the order of children has changed...
  30958. if(oldChildren.length != newChildren.length ||
  30959. dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
  30960. this.onChildrenChange(this.root, newChildren);
  30961. }
  30962. })
  30963. });
  30964. },
  30965. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  30966. // summary:
  30967. // Handler for when new items appear in the store. Developers should override this
  30968. // method to be more efficient based on their app/data.
  30969. // description:
  30970. // Note that the default implementation requeries the top level items every time
  30971. // a new item is created, since any new item could be a top level item (even in
  30972. // addition to being a child of another item, since items can have multiple parents).
  30973. //
  30974. // If developers can detect which items are possible top level items (based on the item and the
  30975. // parentInfo parameters), they should override this method to only call _requeryTop() for top
  30976. // level items. Often all top level items have parentInfo==null, but
  30977. // that will depend on which store you use and what your data is like.
  30978. // tags:
  30979. // extension
  30980. this._requeryTop();
  30981. this.inherited(arguments);
  30982. },
  30983. onDeleteItem: function(/*Object*/ item){
  30984. // summary:
  30985. // Handler for delete notifications from underlying store
  30986. // check if this was a child of root, and if so send notification that root's children
  30987. // have changed
  30988. if(dojo.indexOf(this.root.children, item) != -1){
  30989. this._requeryTop();
  30990. }
  30991. this.inherited(arguments);
  30992. },
  30993. onSetItem: function(/* item */ item,
  30994. /* attribute-name-string */ attribute,
  30995. /* object | array */ oldValue,
  30996. /* object | array */ newValue){
  30997. // summary:
  30998. // Updates the tree view according to changes to an item in the data store.
  30999. // Developers should override this method to be more efficient based on their app/data.
  31000. // description:
  31001. // Handles updates to an item's children by calling onChildrenChange(), and
  31002. // other updates to an item by calling onChange().
  31003. //
  31004. // Also, any change to any item re-executes the query for the tree's top-level items,
  31005. // since this modified item may have started/stopped matching the query for top level items.
  31006. //
  31007. // If possible, developers should override this function to only call _requeryTop() when
  31008. // the change to the item has caused it to stop/start being a top level item in the tree.
  31009. // tags:
  31010. // extension
  31011. this._requeryTop();
  31012. this.inherited(arguments);
  31013. }
  31014. });
  31015. }
  31016. if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  31017. dojo._hasResource["dijit.Tree"] = true;
  31018. dojo.provide("dijit.Tree");
  31019. dojo.declare(
  31020. "dijit._TreeNode",
  31021. [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
  31022. {
  31023. // summary:
  31024. // Single node within a tree. This class is used internally
  31025. // by Tree and should not be accessed directly.
  31026. // tags:
  31027. // private
  31028. // item: [const] dojo.data.Item
  31029. // the dojo.data entry this tree represents
  31030. item: null,
  31031. // isTreeNode: [protected] Boolean
  31032. // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
  31033. // should not be accessed directly.
  31034. isTreeNode: true,
  31035. // label: String
  31036. // Text of this tree node
  31037. label: "",
  31038. // isExpandable: [private] Boolean
  31039. // This node has children, so show the expando node (+ sign)
  31040. isExpandable: null,
  31041. // isExpanded: [readonly] Boolean
  31042. // This node is currently expanded (ie, opened)
  31043. isExpanded: false,
  31044. // state: [private] String
  31045. // Dynamic loading-related stuff.
  31046. // When an empty folder node appears, it is "UNCHECKED" first,
  31047. // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
  31048. state: "UNCHECKED",
  31049. 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"),
  31050. baseClass: "dijitTreeNode",
  31051. // For hover effect for tree node, and focus effect for label
  31052. cssStateNodes: {
  31053. rowNode: "dijitTreeRow",
  31054. labelNode: "dijitTreeLabel"
  31055. },
  31056. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  31057. label: {node: "labelNode", type: "innerText"},
  31058. tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
  31059. }),
  31060. buildRendering: function(){
  31061. this.inherited(arguments);
  31062. // set expand icon for leaf
  31063. this._setExpando();
  31064. // set icon and label class based on item
  31065. this._updateItemClasses(this.item);
  31066. if(this.isExpandable){
  31067. dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
  31068. }
  31069. //aria-selected should be false on all selectable elements.
  31070. this.setSelected(false);
  31071. },
  31072. _setIndentAttr: function(indent){
  31073. // summary:
  31074. // Tell this node how many levels it should be indented
  31075. // description:
  31076. // 0 for top level nodes, 1 for their children, 2 for their
  31077. // grandchildren, etc.
  31078. // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
  31079. var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
  31080. dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
  31081. dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
  31082. dojo.forEach(this.getChildren(), function(child){
  31083. child.set("indent", indent+1);
  31084. });
  31085. this._set("indent", indent);
  31086. },
  31087. markProcessing: function(){
  31088. // summary:
  31089. // Visually denote that tree is loading data, etc.
  31090. // tags:
  31091. // private
  31092. this.state = "LOADING";
  31093. this._setExpando(true);
  31094. },
  31095. unmarkProcessing: function(){
  31096. // summary:
  31097. // Clear markup from markProcessing() call
  31098. // tags:
  31099. // private
  31100. this._setExpando(false);
  31101. },
  31102. _updateItemClasses: function(item){
  31103. // summary:
  31104. // Set appropriate CSS classes for icon and label dom node
  31105. // (used to allow for item updates to change respective CSS)
  31106. // tags:
  31107. // private
  31108. var tree = this.tree, model = tree.model;
  31109. if(tree._v10Compat && item === model.root){
  31110. // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
  31111. item = null;
  31112. }
  31113. this._applyClassAndStyle(item, "icon", "Icon");
  31114. this._applyClassAndStyle(item, "label", "Label");
  31115. this._applyClassAndStyle(item, "row", "Row");
  31116. },
  31117. _applyClassAndStyle: function(item, lower, upper){
  31118. // summary:
  31119. // Set the appropriate CSS classes and styles for labels, icons and rows.
  31120. //
  31121. // item:
  31122. // The data item.
  31123. //
  31124. // lower:
  31125. // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
  31126. //
  31127. // upper:
  31128. // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
  31129. //
  31130. // tags:
  31131. // private
  31132. var clsName = "_" + lower + "Class";
  31133. var nodeName = lower + "Node";
  31134. var oldCls = this[clsName];
  31135. this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
  31136. dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
  31137. dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
  31138. },
  31139. _updateLayout: function(){
  31140. // summary:
  31141. // Set appropriate CSS classes for this.domNode
  31142. // tags:
  31143. // private
  31144. var parent = this.getParent();
  31145. if(!parent || parent.rowNode.style.display == "none"){
  31146. /* if we are hiding the root node then make every first level child look like a root node */
  31147. dojo.addClass(this.domNode, "dijitTreeIsRoot");
  31148. }else{
  31149. dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
  31150. }
  31151. },
  31152. _setExpando: function(/*Boolean*/ processing){
  31153. // summary:
  31154. // Set the right image for the expando node
  31155. // tags:
  31156. // private
  31157. var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
  31158. "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
  31159. _a11yStates = ["*","-","+","*"],
  31160. idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
  31161. // apply the appropriate class to the expando node
  31162. dojo.replaceClass(this.expandoNode, styles[idx], styles);
  31163. // provide a non-image based indicator for images-off mode
  31164. this.expandoNodeText.innerHTML = _a11yStates[idx];
  31165. },
  31166. expand: function(){
  31167. // summary:
  31168. // Show my children
  31169. // returns:
  31170. // Deferred that fires when expansion is complete
  31171. // If there's already an expand in progress or we are already expanded, just return
  31172. if(this._expandDeferred){
  31173. return this._expandDeferred; // dojo.Deferred
  31174. }
  31175. // cancel in progress collapse operation
  31176. this._wipeOut && this._wipeOut.stop();
  31177. // All the state information for when a node is expanded, maybe this should be
  31178. // set when the animation completes instead
  31179. this.isExpanded = true;
  31180. dijit.setWaiState(this.labelNode, "expanded", "true");
  31181. if(this.tree.showRoot || this !== this.tree.rootNode){
  31182. dijit.setWaiRole(this.containerNode, "group");
  31183. }
  31184. dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
  31185. this._setExpando();
  31186. this._updateItemClasses(this.item);
  31187. if(this == this.tree.rootNode){
  31188. dijit.setWaiState(this.tree.domNode, "expanded", "true");
  31189. }
  31190. var def,
  31191. wipeIn = dojo.fx.wipeIn({
  31192. node: this.containerNode, duration: dijit.defaultDuration,
  31193. onEnd: function(){
  31194. def.callback(true);
  31195. }
  31196. });
  31197. // Deferred that fires when expand is complete
  31198. def = (this._expandDeferred = new dojo.Deferred(function(){
  31199. // Canceller
  31200. wipeIn.stop();
  31201. }));
  31202. wipeIn.play();
  31203. return def; // dojo.Deferred
  31204. },
  31205. collapse: function(){
  31206. // summary:
  31207. // Collapse this node (if it's expanded)
  31208. if(!this.isExpanded){ return; }
  31209. // cancel in progress expand operation
  31210. if(this._expandDeferred){
  31211. this._expandDeferred.cancel();
  31212. delete this._expandDeferred;
  31213. }
  31214. this.isExpanded = false;
  31215. dijit.setWaiState(this.labelNode, "expanded", "false");
  31216. if(this == this.tree.rootNode){
  31217. dijit.setWaiState(this.tree.domNode, "expanded", "false");
  31218. }
  31219. dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
  31220. this._setExpando();
  31221. this._updateItemClasses(this.item);
  31222. if(!this._wipeOut){
  31223. this._wipeOut = dojo.fx.wipeOut({
  31224. node: this.containerNode, duration: dijit.defaultDuration
  31225. });
  31226. }
  31227. this._wipeOut.play();
  31228. },
  31229. // indent: Integer
  31230. // Levels from this node to the root node
  31231. indent: 0,
  31232. setChildItems: function(/* Object[] */ items){
  31233. // summary:
  31234. // Sets the child items of this node, removing/adding nodes
  31235. // from current children to match specified items[] array.
  31236. // Also, if this.persist == true, expands any children that were previously
  31237. // opened.
  31238. // returns:
  31239. // Deferred object that fires after all previously opened children
  31240. // have been expanded again (or fires instantly if there are no such children).
  31241. var tree = this.tree,
  31242. model = tree.model,
  31243. defs = []; // list of deferreds that need to fire before I am complete
  31244. // Orphan all my existing children.
  31245. // If items contains some of the same items as before then we will reattach them.
  31246. // Don't call this.removeChild() because that will collapse the tree etc.
  31247. dojo.forEach(this.getChildren(), function(child){
  31248. dijit._Container.prototype.removeChild.call(this, child);
  31249. }, this);
  31250. this.state = "LOADED";
  31251. if(items && items.length > 0){
  31252. this.isExpandable = true;
  31253. // Create _TreeNode widget for each specified tree node, unless one already
  31254. // exists and isn't being used (presumably it's from a DnD move and was recently
  31255. // released
  31256. dojo.forEach(items, function(item){
  31257. var id = model.getIdentity(item),
  31258. existingNodes = tree._itemNodesMap[id],
  31259. node;
  31260. if(existingNodes){
  31261. for(var i=0;i<existingNodes.length;i++){
  31262. if(existingNodes[i] && !existingNodes[i].getParent()){
  31263. node = existingNodes[i];
  31264. node.set('indent', this.indent+1);
  31265. break;
  31266. }
  31267. }
  31268. }
  31269. if(!node){
  31270. node = this.tree._createTreeNode({
  31271. item: item,
  31272. tree: tree,
  31273. isExpandable: model.mayHaveChildren(item),
  31274. label: tree.getLabel(item),
  31275. tooltip: tree.getTooltip(item),
  31276. dir: tree.dir,
  31277. lang: tree.lang,
  31278. indent: this.indent + 1
  31279. });
  31280. if(existingNodes){
  31281. existingNodes.push(node);
  31282. }else{
  31283. tree._itemNodesMap[id] = [node];
  31284. }
  31285. }
  31286. this.addChild(node);
  31287. // If node was previously opened then open it again now (this may trigger
  31288. // more data store accesses, recursively)
  31289. if(this.tree.autoExpand || this.tree._state(item)){
  31290. defs.push(tree._expandNode(node));
  31291. }
  31292. }, this);
  31293. // note that updateLayout() needs to be called on each child after
  31294. // _all_ the children exist
  31295. dojo.forEach(this.getChildren(), function(child, idx){
  31296. child._updateLayout();
  31297. });
  31298. }else{
  31299. this.isExpandable=false;
  31300. }
  31301. if(this._setExpando){
  31302. // change expando to/from dot or + icon, as appropriate
  31303. this._setExpando(false);
  31304. }
  31305. // Set leaf icon or folder icon, as appropriate
  31306. this._updateItemClasses(this.item);
  31307. // On initial tree show, make the selected TreeNode as either the root node of the tree,
  31308. // or the first child, if the root node is hidden
  31309. if(this == tree.rootNode){
  31310. var fc = this.tree.showRoot ? this : this.getChildren()[0];
  31311. if(fc){
  31312. fc.setFocusable(true);
  31313. tree.lastFocused = fc;
  31314. }else{
  31315. // fallback: no nodes in tree so focus on Tree <div> itself
  31316. tree.domNode.setAttribute("tabIndex", "0");
  31317. }
  31318. }
  31319. return new dojo.DeferredList(defs); // dojo.Deferred
  31320. },
  31321. getTreePath: function(){
  31322. var node = this;
  31323. var path = [];
  31324. while(node && node !== this.tree.rootNode){
  31325. path.unshift(node.item);
  31326. node = node.getParent();
  31327. }
  31328. path.unshift(this.tree.rootNode.item);
  31329. return path;
  31330. },
  31331. getIdentity: function() {
  31332. return this.tree.model.getIdentity(this.item);
  31333. },
  31334. removeChild: function(/* treeNode */ node){
  31335. this.inherited(arguments);
  31336. var children = this.getChildren();
  31337. if(children.length == 0){
  31338. this.isExpandable = false;
  31339. this.collapse();
  31340. }
  31341. dojo.forEach(children, function(child){
  31342. child._updateLayout();
  31343. });
  31344. },
  31345. makeExpandable: function(){
  31346. // summary:
  31347. // if this node wasn't already showing the expando node,
  31348. // turn it into one and call _setExpando()
  31349. // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
  31350. this.isExpandable = true;
  31351. this._setExpando(false);
  31352. },
  31353. _onLabelFocus: function(evt){
  31354. // summary:
  31355. // Called when this row is focused (possibly programatically)
  31356. // Note that we aren't using _onFocus() builtin to dijit
  31357. // because it's called when focus is moved to a descendant TreeNode.
  31358. // tags:
  31359. // private
  31360. this.tree._onNodeFocus(this);
  31361. },
  31362. setSelected: function(/*Boolean*/ selected){
  31363. // summary:
  31364. // A Tree has a (single) currently selected node.
  31365. // Mark that this node is/isn't that currently selected node.
  31366. // description:
  31367. // In particular, setting a node as selected involves setting tabIndex
  31368. // so that when user tabs to the tree, focus will go to that node (only).
  31369. dijit.setWaiState(this.labelNode, "selected", selected);
  31370. dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
  31371. },
  31372. setFocusable: function(/*Boolean*/ selected){
  31373. // summary:
  31374. // A Tree has a (single) node that's focusable.
  31375. // Mark that this node is/isn't that currently focsuable node.
  31376. // description:
  31377. // In particular, setting a node as selected involves setting tabIndex
  31378. // so that when user tabs to the tree, focus will go to that node (only).
  31379. this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
  31380. },
  31381. _onClick: function(evt){
  31382. // summary:
  31383. // Handler for onclick event on a node
  31384. // tags:
  31385. // private
  31386. this.tree._onClick(this, evt);
  31387. },
  31388. _onDblClick: function(evt){
  31389. // summary:
  31390. // Handler for ondblclick event on a node
  31391. // tags:
  31392. // private
  31393. this.tree._onDblClick(this, evt);
  31394. },
  31395. _onMouseEnter: function(evt){
  31396. // summary:
  31397. // Handler for onmouseenter event on a node
  31398. // tags:
  31399. // private
  31400. this.tree._onNodeMouseEnter(this, evt);
  31401. },
  31402. _onMouseLeave: function(evt){
  31403. // summary:
  31404. // Handler for onmouseenter event on a node
  31405. // tags:
  31406. // private
  31407. this.tree._onNodeMouseLeave(this, evt);
  31408. }
  31409. });
  31410. dojo.declare(
  31411. "dijit.Tree",
  31412. [dijit._Widget, dijit._Templated],
  31413. {
  31414. // summary:
  31415. // This widget displays hierarchical data from a store.
  31416. // store: [deprecated] String||dojo.data.Store
  31417. // Deprecated. Use "model" parameter instead.
  31418. // The store to get data to display in the tree.
  31419. store: null,
  31420. // model: dijit.Tree.model
  31421. // Interface to read tree data, get notifications of changes to tree data,
  31422. // and for handling drop operations (i.e drag and drop onto the tree)
  31423. model: null,
  31424. // query: [deprecated] anything
  31425. // Deprecated. User should specify query to the model directly instead.
  31426. // Specifies datastore query to return the root item or top items for the tree.
  31427. query: null,
  31428. // label: [deprecated] String
  31429. // Deprecated. Use dijit.tree.ForestStoreModel directly instead.
  31430. // Used in conjunction with query parameter.
  31431. // If a query is specified (rather than a root node id), and a label is also specified,
  31432. // then a fake root node is created and displayed, with this label.
  31433. label: "",
  31434. // showRoot: [const] Boolean
  31435. // Should the root node be displayed, or hidden?
  31436. showRoot: true,
  31437. // childrenAttr: [deprecated] String[]
  31438. // Deprecated. This information should be specified in the model.
  31439. // One ore more attributes that holds children of a tree node
  31440. childrenAttr: ["children"],
  31441. // paths: String[][] or Item[][]
  31442. // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
  31443. // Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
  31444. // returns a Deferred to indicate when the set is complete.
  31445. paths: [],
  31446. // path: String[] or Item[]
  31447. // Backward compatible singular variant of paths.
  31448. path: [],
  31449. // selectedItems: [readonly] Item[]
  31450. // The currently selected items in this tree.
  31451. // This property can only be set (via set('selectedItems', ...)) when that item is already
  31452. // visible in the tree. (I.e. the tree has already been expanded to show that node.)
  31453. // Should generally use `paths` attribute to set the selected items instead.
  31454. selectedItems: null,
  31455. // selectedItem: [readonly] Item
  31456. // Backward compatible singular variant of selectedItems.
  31457. selectedItem: null,
  31458. // openOnClick: Boolean
  31459. // If true, clicking a folder node's label will open it, rather than calling onClick()
  31460. openOnClick: false,
  31461. // openOnDblClick: Boolean
  31462. // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
  31463. openOnDblClick: false,
  31464. 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"),
  31465. // persist: Boolean
  31466. // Enables/disables use of cookies for state saving.
  31467. persist: true,
  31468. // autoExpand: Boolean
  31469. // Fully expand the tree on load. Overrides `persist`.
  31470. autoExpand: false,
  31471. // dndController: [protected] String
  31472. // Class name to use as as the dnd controller. Specifying this class enables DnD.
  31473. // Generally you should specify this as "dijit.tree.dndSource".
  31474. // Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
  31475. dndController: "dijit.tree._dndSelector",
  31476. // parameters to pull off of the tree and pass on to the dndController as its params
  31477. dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
  31478. //declare the above items so they can be pulled from the tree's markup
  31479. // onDndDrop: [protected] Function
  31480. // Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
  31481. // Generally this doesn't need to be set.
  31482. onDndDrop: null,
  31483. /*=====
  31484. itemCreator: function(nodes, target, source){
  31485. // summary:
  31486. // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
  31487. // dropped onto the tree. Developer must override this method to enable
  31488. // dropping from external sources onto this Tree, unless the Tree.model's items
  31489. // happen to look like {id: 123, name: "Apple" } with no other attributes.
  31490. // description:
  31491. // For each node in nodes[], which came from source, create a hash of name/value
  31492. // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
  31493. // nodes: DomNode[]
  31494. // The DOMNodes dragged from the source container
  31495. // target: DomNode
  31496. // The target TreeNode.rowNode
  31497. // source: dojo.dnd.Source
  31498. // The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
  31499. // returns: Object[]
  31500. // Array of name/value hashes for each new item to be added to the Tree, like:
  31501. // | [
  31502. // | { id: 123, label: "apple", foo: "bar" },
  31503. // | { id: 456, label: "pear", zaz: "bam" }
  31504. // | ]
  31505. // tags:
  31506. // extension
  31507. return [{}];
  31508. },
  31509. =====*/
  31510. itemCreator: null,
  31511. // onDndCancel: [protected] Function
  31512. // Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
  31513. // Generally this doesn't need to be set.
  31514. onDndCancel: null,
  31515. /*=====
  31516. checkAcceptance: function(source, nodes){
  31517. // summary:
  31518. // Checks if the Tree itself can accept nodes from this source
  31519. // source: dijit.tree._dndSource
  31520. // The source which provides items
  31521. // nodes: DOMNode[]
  31522. // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
  31523. // source is a dijit.Tree.
  31524. // tags:
  31525. // extension
  31526. return true; // Boolean
  31527. },
  31528. =====*/
  31529. checkAcceptance: null,
  31530. /*=====
  31531. checkItemAcceptance: function(target, source, position){
  31532. // summary:
  31533. // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
  31534. // description:
  31535. // In the base case, this is called to check if target can become a child of source.
  31536. // When betweenThreshold is set, position="before" or "after" means that we
  31537. // are asking if the source node can be dropped before/after the target node.
  31538. // target: DOMNode
  31539. // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
  31540. // Use dijit.getEnclosingWidget(target) to get the TreeNode.
  31541. // source: dijit.tree.dndSource
  31542. // The (set of) nodes we are dropping
  31543. // position: String
  31544. // "over", "before", or "after"
  31545. // tags:
  31546. // extension
  31547. return true; // Boolean
  31548. },
  31549. =====*/
  31550. checkItemAcceptance: null,
  31551. // dragThreshold: Integer
  31552. // Number of pixels mouse moves before it's considered the start of a drag operation
  31553. dragThreshold: 5,
  31554. // betweenThreshold: Integer
  31555. // Set to a positive value to allow drag and drop "between" nodes.
  31556. //
  31557. // If during DnD mouse is over a (target) node but less than betweenThreshold
  31558. // pixels from the bottom edge, dropping the the dragged node will make it
  31559. // the next sibling of the target node, rather than the child.
  31560. //
  31561. // Similarly, if mouse is over a target node but less that betweenThreshold
  31562. // pixels from the top edge, dropping the dragged node will make it
  31563. // the target node's previous sibling rather than the target node's child.
  31564. betweenThreshold: 0,
  31565. // _nodePixelIndent: Integer
  31566. // Number of pixels to indent tree nodes (relative to parent node).
  31567. // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
  31568. // and calling resize() or startup() on tree after it's in the DOM.
  31569. _nodePixelIndent: 19,
  31570. _publish: function(/*String*/ topicName, /*Object*/ message){
  31571. // summary:
  31572. // Publish a message for this widget/topic
  31573. dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
  31574. },
  31575. postMixInProperties: function(){
  31576. this.tree = this;
  31577. if(this.autoExpand){
  31578. // There's little point in saving opened/closed state of nodes for a Tree
  31579. // that initially opens all it's nodes.
  31580. this.persist = false;
  31581. }
  31582. this._itemNodesMap={};
  31583. if(!this.cookieName){
  31584. this.cookieName = this.id + "SaveStateCookie";
  31585. }
  31586. this._loadDeferred = new dojo.Deferred();
  31587. this.inherited(arguments);
  31588. },
  31589. postCreate: function(){
  31590. this._initState();
  31591. // Create glue between store and Tree, if not specified directly by user
  31592. if(!this.model){
  31593. this._store2model();
  31594. }
  31595. // monitor changes to items
  31596. this.connect(this.model, "onChange", "_onItemChange");
  31597. this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
  31598. this.connect(this.model, "onDelete", "_onItemDelete");
  31599. this._load();
  31600. this.inherited(arguments);
  31601. if(this.dndController){
  31602. if(dojo.isString(this.dndController)){
  31603. this.dndController = dojo.getObject(this.dndController);
  31604. }
  31605. var params={};
  31606. for(var i=0; i<this.dndParams.length;i++){
  31607. if(this[this.dndParams[i]]){
  31608. params[this.dndParams[i]] = this[this.dndParams[i]];
  31609. }
  31610. }
  31611. this.dndController = new this.dndController(this, params);
  31612. }
  31613. },
  31614. _store2model: function(){
  31615. // summary:
  31616. // User specified a store&query rather than model, so create model from store/query
  31617. this._v10Compat = true;
  31618. dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
  31619. var modelParams = {
  31620. id: this.id + "_ForestStoreModel",
  31621. store: this.store,
  31622. query: this.query,
  31623. childrenAttrs: this.childrenAttr
  31624. };
  31625. // Only override the model's mayHaveChildren() method if the user has specified an override
  31626. if(this.params.mayHaveChildren){
  31627. modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
  31628. }
  31629. if(this.params.getItemChildren){
  31630. modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
  31631. this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
  31632. });
  31633. }
  31634. this.model = new dijit.tree.ForestStoreModel(modelParams);
  31635. // For backwards compatibility, the visibility of the root node is controlled by
  31636. // whether or not the user has specified a label
  31637. this.showRoot = Boolean(this.label);
  31638. },
  31639. onLoad: function(){
  31640. // summary:
  31641. // Called when tree finishes loading and expanding.
  31642. // description:
  31643. // If persist == true the loading may encompass many levels of fetches
  31644. // from the data store, each asynchronous. Waits for all to finish.
  31645. // tags:
  31646. // callback
  31647. },
  31648. _load: function(){
  31649. // summary:
  31650. // Initial load of the tree.
  31651. // Load root node (possibly hidden) and it's children.
  31652. this.model.getRoot(
  31653. dojo.hitch(this, function(item){
  31654. var rn = (this.rootNode = this.tree._createTreeNode({
  31655. item: item,
  31656. tree: this,
  31657. isExpandable: true,
  31658. label: this.label || this.getLabel(item),
  31659. indent: this.showRoot ? 0 : -1
  31660. }));
  31661. if(!this.showRoot){
  31662. rn.rowNode.style.display="none";
  31663. // if root is not visible, move tree role to the invisible
  31664. // root node's containerNode, see #12135
  31665. dijit.setWaiRole(this.domNode, 'presentation');
  31666. dijit.setWaiRole(rn.labelNode, 'presentation');
  31667. dijit.setWaiRole(rn.containerNode, 'tree');
  31668. }
  31669. this.domNode.appendChild(rn.domNode);
  31670. var identity = this.model.getIdentity(item);
  31671. if(this._itemNodesMap[identity]){
  31672. this._itemNodesMap[identity].push(rn);
  31673. }else{
  31674. this._itemNodesMap[identity] = [rn];
  31675. }
  31676. rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
  31677. // load top level children and then fire onLoad() event
  31678. this._expandNode(rn).addCallback(dojo.hitch(this, function(){
  31679. this._loadDeferred.callback(true);
  31680. this.onLoad();
  31681. }));
  31682. }),
  31683. function(err){
  31684. console.error(this, ": error loading root: ", err);
  31685. }
  31686. );
  31687. },
  31688. getNodesByItem: function(/*dojo.data.Item or id*/ item){
  31689. // summary:
  31690. // Returns all tree nodes that refer to an item
  31691. // returns:
  31692. // Array of tree nodes that refer to passed item
  31693. if(!item){ return []; }
  31694. var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
  31695. // return a copy so widget don't get messed up by changes to returned array
  31696. return [].concat(this._itemNodesMap[identity]);
  31697. },
  31698. _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
  31699. this.set('selectedItems', [item]);
  31700. },
  31701. _setSelectedItemsAttr: function(/*dojo.data.Items or ids*/ items){
  31702. // summary:
  31703. // Select tree nodes related to passed items.
  31704. // WARNING: if model use multi-parented items or desired tree node isn't already loaded
  31705. // behavior is undefined. Use set('paths', ...) instead.
  31706. var tree = this;
  31707. this._loadDeferred.addCallback( dojo.hitch(this, function(){
  31708. var identities = dojo.map(items, function(item){
  31709. return (!item || dojo.isString(item)) ? item : tree.model.getIdentity(item);
  31710. });
  31711. var nodes = [];
  31712. dojo.forEach(identities, function(id){
  31713. nodes = nodes.concat(tree._itemNodesMap[id] || []);
  31714. });
  31715. this.set('selectedNodes', nodes);
  31716. }));
  31717. },
  31718. _setPathAttr: function(/*Item[] || String[]*/ path){
  31719. // summary:
  31720. // Singular variant of _setPathsAttr
  31721. if(path.length) {
  31722. return this.set("paths", [path]);
  31723. } else {
  31724. //Empty list is interpreted as "select nothing"
  31725. return this.set("paths", []);
  31726. }
  31727. },
  31728. _setPathsAttr: function(/*Item[][] || String[][]*/ paths){
  31729. // summary:
  31730. // Select the tree nodes identified by passed paths.
  31731. // paths:
  31732. // Array of arrays of items or item id's
  31733. // returns:
  31734. // Deferred to indicate when the set is complete
  31735. var tree = this;
  31736. // We may need to wait for some nodes to expand, so setting
  31737. // each path will involve a Deferred. We bring those deferreds
  31738. // together witha DeferredList.
  31739. return new dojo.DeferredList(dojo.map(paths, function(path){
  31740. var d = new dojo.Deferred();
  31741. // normalize path to use identity
  31742. path = dojo.map(path, function(item){
  31743. return dojo.isString(item) ? item : tree.model.getIdentity(item);
  31744. });
  31745. if(path.length){
  31746. // Wait for the tree to load, if it hasn't already.
  31747. tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
  31748. }else{
  31749. d.errback("Empty path");
  31750. }
  31751. return d;
  31752. })).addCallback(setNodes);
  31753. function selectPath(path, nodes, def){
  31754. // Traverse path; the next path component should be among "nodes".
  31755. var nextPath = path.shift();
  31756. var nextNode = dojo.filter(nodes, function(node){
  31757. return node.getIdentity() == nextPath;
  31758. })[0];
  31759. if(!!nextNode){
  31760. if(path.length){
  31761. tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
  31762. }else{
  31763. //Successfully reached the end of this path
  31764. def.callback(nextNode);
  31765. }
  31766. } else {
  31767. def.errback("Could not expand path at " + nextPath);
  31768. }
  31769. }
  31770. function setNodes(newNodes){
  31771. //After all expansion is finished, set the selection to
  31772. //the set of nodes successfully found.
  31773. tree.set("selectedNodes", dojo.map(
  31774. dojo.filter(newNodes,function(x){return x[0];}),
  31775. function(x){return x[1];}));
  31776. }
  31777. },
  31778. _setSelectedNodeAttr: function(node){
  31779. this.set('selectedNodes', [node]);
  31780. },
  31781. _setSelectedNodesAttr: function(nodes){
  31782. this._loadDeferred.addCallback( dojo.hitch(this, function(){
  31783. this.dndController.setSelection(nodes);
  31784. }));
  31785. },
  31786. ////////////// Data store related functions //////////////////////
  31787. // These just get passed to the model; they are here for back-compat
  31788. mayHaveChildren: function(/*dojo.data.Item*/ item){
  31789. // summary:
  31790. // Deprecated. This should be specified on the model itself.
  31791. //
  31792. // Overridable function to tell if an item has or may have children.
  31793. // Controls whether or not +/- expando icon is shown.
  31794. // (For efficiency reasons we may not want to check if an element actually
  31795. // has children until user clicks the expando node)
  31796. // tags:
  31797. // deprecated
  31798. },
  31799. getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
  31800. // summary:
  31801. // Deprecated. This should be specified on the model itself.
  31802. //
  31803. // Overridable function that return array of child items of given parent item,
  31804. // or if parentItem==null then return top items in tree
  31805. // tags:
  31806. // deprecated
  31807. },
  31808. ///////////////////////////////////////////////////////
  31809. // Functions for converting an item to a TreeNode
  31810. getLabel: function(/*dojo.data.Item*/ item){
  31811. // summary:
  31812. // Overridable function to get the label for a tree node (given the item)
  31813. // tags:
  31814. // extension
  31815. return this.model.getLabel(item); // String
  31816. },
  31817. getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31818. // summary:
  31819. // Overridable function to return CSS class name to display icon
  31820. // tags:
  31821. // extension
  31822. return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
  31823. },
  31824. getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31825. // summary:
  31826. // Overridable function to return CSS class name to display label
  31827. // tags:
  31828. // extension
  31829. },
  31830. getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31831. // summary:
  31832. // Overridable function to return CSS class name to display row
  31833. // tags:
  31834. // extension
  31835. },
  31836. getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31837. // summary:
  31838. // Overridable function to return CSS styles to display icon
  31839. // returns:
  31840. // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
  31841. // tags:
  31842. // extension
  31843. },
  31844. getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31845. // summary:
  31846. // Overridable function to return CSS styles to display label
  31847. // returns:
  31848. // Object suitable for input to dojo.style() like {color: "red", background: "green"}
  31849. // tags:
  31850. // extension
  31851. },
  31852. getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
  31853. // summary:
  31854. // Overridable function to return CSS styles to display row
  31855. // returns:
  31856. // Object suitable for input to dojo.style() like {background-color: "#bbb"}
  31857. // tags:
  31858. // extension
  31859. },
  31860. getTooltip: function(/*dojo.data.Item*/ item){
  31861. // summary:
  31862. // Overridable function to get the tooltip for a tree node (given the item)
  31863. // tags:
  31864. // extension
  31865. return ""; // String
  31866. },
  31867. /////////// Keyboard and Mouse handlers ////////////////////
  31868. _onKeyPress: function(/*Event*/ e){
  31869. // summary:
  31870. // Translates keypress events into commands for the controller
  31871. if(e.altKey){ return; }
  31872. var dk = dojo.keys;
  31873. var treeNode = dijit.getEnclosingWidget(e.target);
  31874. if(!treeNode){ return; }
  31875. var key = e.charOrCode;
  31876. if(typeof key == "string" && key != " "){ // handle printables (letter navigation)
  31877. // Check for key navigation.
  31878. if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
  31879. this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
  31880. dojo.stopEvent(e);
  31881. }
  31882. }else{ // handle non-printables (arrow keys)
  31883. // clear record of recent printables (being saved for multi-char letter navigation),
  31884. // because "a", down-arrow, "b" shouldn't search for "ab"
  31885. if(this._curSearch){
  31886. clearTimeout(this._curSearch.timer);
  31887. delete this._curSearch;
  31888. }
  31889. var map = this._keyHandlerMap;
  31890. if(!map){
  31891. // setup table mapping keys to events
  31892. map = {};
  31893. map[dk.ENTER]="_onEnterKey";
  31894. //On WebKit based browsers, the combination ctrl-enter
  31895. //does not get passed through. To allow accessible
  31896. //multi-select on those browsers, the space key is
  31897. //also used for selection.
  31898. map[dk.SPACE]= map[" "] = "_onEnterKey";
  31899. map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
  31900. map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
  31901. map[dk.UP_ARROW]="_onUpArrow";
  31902. map[dk.DOWN_ARROW]="_onDownArrow";
  31903. map[dk.HOME]="_onHomeKey";
  31904. map[dk.END]="_onEndKey";
  31905. this._keyHandlerMap = map;
  31906. }
  31907. if(this._keyHandlerMap[key]){
  31908. this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
  31909. dojo.stopEvent(e);
  31910. }
  31911. }
  31912. },
  31913. _onEnterKey: function(/*Object*/ message){
  31914. this._publish("execute", { item: message.item, node: message.node } );
  31915. this.dndController.userSelect(message.node, dojo.isCopyKey( message.evt ), message.evt.shiftKey);
  31916. this.onClick(message.item, message.node, message.evt);
  31917. },
  31918. _onDownArrow: function(/*Object*/ message){
  31919. // summary:
  31920. // down arrow pressed; get next visible node, set focus there
  31921. var node = this._getNextNode(message.node);
  31922. if(node && node.isTreeNode){
  31923. this.focusNode(node);
  31924. }
  31925. },
  31926. _onUpArrow: function(/*Object*/ message){
  31927. // summary:
  31928. // Up arrow pressed; move to previous visible node
  31929. var node = message.node;
  31930. // if younger siblings
  31931. var previousSibling = node.getPreviousSibling();
  31932. if(previousSibling){
  31933. node = previousSibling;
  31934. // if the previous node is expanded, dive in deep
  31935. while(node.isExpandable && node.isExpanded && node.hasChildren()){
  31936. // move to the last child
  31937. var children = node.getChildren();
  31938. node = children[children.length-1];
  31939. }
  31940. }else{
  31941. // if this is the first child, return the parent
  31942. // unless the parent is the root of a tree with a hidden root
  31943. var parent = node.getParent();
  31944. if(!(!this.showRoot && parent === this.rootNode)){
  31945. node = parent;
  31946. }
  31947. }
  31948. if(node && node.isTreeNode){
  31949. this.focusNode(node);
  31950. }
  31951. },
  31952. _onRightArrow: function(/*Object*/ message){
  31953. // summary:
  31954. // Right arrow pressed; go to child node
  31955. var node = message.node;
  31956. // if not expanded, expand, else move to 1st child
  31957. if(node.isExpandable && !node.isExpanded){
  31958. this._expandNode(node);
  31959. }else if(node.hasChildren()){
  31960. node = node.getChildren()[0];
  31961. if(node && node.isTreeNode){
  31962. this.focusNode(node);
  31963. }
  31964. }
  31965. },
  31966. _onLeftArrow: function(/*Object*/ message){
  31967. // summary:
  31968. // Left arrow pressed.
  31969. // If not collapsed, collapse, else move to parent.
  31970. var node = message.node;
  31971. if(node.isExpandable && node.isExpanded){
  31972. this._collapseNode(node);
  31973. }else{
  31974. var parent = node.getParent();
  31975. if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
  31976. this.focusNode(parent);
  31977. }
  31978. }
  31979. },
  31980. _onHomeKey: function(){
  31981. // summary:
  31982. // Home key pressed; get first visible node, and set focus there
  31983. var node = this._getRootOrFirstNode();
  31984. if(node){
  31985. this.focusNode(node);
  31986. }
  31987. },
  31988. _onEndKey: function(/*Object*/ message){
  31989. // summary:
  31990. // End key pressed; go to last visible node.
  31991. var node = this.rootNode;
  31992. while(node.isExpanded){
  31993. var c = node.getChildren();
  31994. node = c[c.length - 1];
  31995. }
  31996. if(node && node.isTreeNode){
  31997. this.focusNode(node);
  31998. }
  31999. },
  32000. // multiCharSearchDuration: Number
  32001. // If multiple characters are typed where each keystroke happens within
  32002. // multiCharSearchDuration of the previous keystroke,
  32003. // search for nodes matching all the keystrokes.
  32004. //
  32005. // For example, typing "ab" will search for entries starting with
  32006. // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
  32007. multiCharSearchDuration: 250,
  32008. _onLetterKeyNav: function(message){
  32009. // summary:
  32010. // Called when user presses a prinatable key; search for node starting with recently typed letters.
  32011. // message: Object
  32012. // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
  32013. // Branch depending on whether this key starts a new search, or modifies an existing search
  32014. var cs = this._curSearch;
  32015. if(cs){
  32016. // We are continuing a search. Ex: user has pressed 'a', and now has pressed
  32017. // 'b', so we want to search for nodes starting w/"ab".
  32018. cs.pattern = cs.pattern + message.key;
  32019. clearTimeout(cs.timer);
  32020. }else{
  32021. // We are starting a new search
  32022. cs = this._curSearch = {
  32023. pattern: message.key,
  32024. startNode: message.node
  32025. };
  32026. }
  32027. // set/reset timer to forget recent keystrokes
  32028. var self = this;
  32029. cs.timer = setTimeout(function(){
  32030. delete self._curSearch;
  32031. }, this.multiCharSearchDuration);
  32032. // Navigate to TreeNode matching keystrokes [entered so far].
  32033. var node = cs.startNode;
  32034. do{
  32035. node = this._getNextNode(node);
  32036. //check for last node, jump to first node if necessary
  32037. if(!node){
  32038. node = this._getRootOrFirstNode();
  32039. }
  32040. }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
  32041. if(node && node.isTreeNode){
  32042. // no need to set focus if back where we started
  32043. if(node !== cs.startNode){
  32044. this.focusNode(node);
  32045. }
  32046. }
  32047. },
  32048. isExpandoNode: function(node, widget){
  32049. // summary:
  32050. // check whether a dom node is the expandoNode for a particular TreeNode widget
  32051. return dojo.isDescendant(node, widget.expandoNode);
  32052. },
  32053. _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
  32054. // summary:
  32055. // Translates click events into commands for the controller to process
  32056. var domElement = e.target,
  32057. isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
  32058. if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
  32059. // expando node was clicked, or label of a folder node was clicked; open it
  32060. if(nodeWidget.isExpandable){
  32061. this._onExpandoClick({node:nodeWidget});
  32062. }
  32063. }else{
  32064. this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
  32065. this.onClick(nodeWidget.item, nodeWidget, e);
  32066. this.focusNode(nodeWidget);
  32067. }
  32068. dojo.stopEvent(e);
  32069. },
  32070. _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
  32071. // summary:
  32072. // Translates double-click events into commands for the controller to process
  32073. var domElement = e.target,
  32074. isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
  32075. if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
  32076. // expando node was clicked, or label of a folder node was clicked; open it
  32077. if(nodeWidget.isExpandable){
  32078. this._onExpandoClick({node:nodeWidget});
  32079. }
  32080. }else{
  32081. this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
  32082. this.onDblClick(nodeWidget.item, nodeWidget, e);
  32083. this.focusNode(nodeWidget);
  32084. }
  32085. dojo.stopEvent(e);
  32086. },
  32087. _onExpandoClick: function(/*Object*/ message){
  32088. // summary:
  32089. // User clicked the +/- icon; expand or collapse my children.
  32090. var node = message.node;
  32091. // If we are collapsing, we might be hiding the currently focused node.
  32092. // Also, clicking the expando node might have erased focus from the current node.
  32093. // For simplicity's sake just focus on the node with the expando.
  32094. this.focusNode(node);
  32095. if(node.isExpanded){
  32096. this._collapseNode(node);
  32097. }else{
  32098. this._expandNode(node);
  32099. }
  32100. },
  32101. onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
  32102. // summary:
  32103. // Callback when a tree node is clicked
  32104. // tags:
  32105. // callback
  32106. },
  32107. onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
  32108. // summary:
  32109. // Callback when a tree node is double-clicked
  32110. // tags:
  32111. // callback
  32112. },
  32113. onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
  32114. // summary:
  32115. // Callback when a node is opened
  32116. // tags:
  32117. // callback
  32118. },
  32119. onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
  32120. // summary:
  32121. // Callback when a node is closed
  32122. // tags:
  32123. // callback
  32124. },
  32125. _getNextNode: function(node){
  32126. // summary:
  32127. // Get next visible node
  32128. if(node.isExpandable && node.isExpanded && node.hasChildren()){
  32129. // if this is an expanded node, get the first child
  32130. return node.getChildren()[0]; // _TreeNode
  32131. }else{
  32132. // find a parent node with a sibling
  32133. while(node && node.isTreeNode){
  32134. var returnNode = node.getNextSibling();
  32135. if(returnNode){
  32136. return returnNode; // _TreeNode
  32137. }
  32138. node = node.getParent();
  32139. }
  32140. return null;
  32141. }
  32142. },
  32143. _getRootOrFirstNode: function(){
  32144. // summary:
  32145. // Get first visible node
  32146. return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
  32147. },
  32148. _collapseNode: function(/*_TreeNode*/ node){
  32149. // summary:
  32150. // Called when the user has requested to collapse the node
  32151. if(node._expandNodeDeferred){
  32152. delete node._expandNodeDeferred;
  32153. }
  32154. if(node.isExpandable){
  32155. if(node.state == "LOADING"){
  32156. // ignore clicks while we are in the process of loading data
  32157. return;
  32158. }
  32159. node.collapse();
  32160. this.onClose(node.item, node);
  32161. if(node.item){
  32162. this._state(node.item,false);
  32163. this._saveState();
  32164. }
  32165. }
  32166. },
  32167. _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
  32168. // summary:
  32169. // Called when the user has requested to expand the node
  32170. // recursive:
  32171. // Internal flag used when _expandNode() calls itself, don't set.
  32172. // returns:
  32173. // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
  32174. // that were previously opened too
  32175. if(node._expandNodeDeferred && !recursive){
  32176. // there's already an expand in progress (or completed), so just return
  32177. return node._expandNodeDeferred; // dojo.Deferred
  32178. }
  32179. var model = this.model,
  32180. item = node.item,
  32181. _this = this;
  32182. switch(node.state){
  32183. case "UNCHECKED":
  32184. // need to load all the children, and then expand
  32185. node.markProcessing();
  32186. // Setup deferred to signal when the load and expand are finished.
  32187. // Save that deferred in this._expandDeferred as a flag that operation is in progress.
  32188. var def = (node._expandNodeDeferred = new dojo.Deferred());
  32189. // Get the children
  32190. model.getChildren(
  32191. item,
  32192. function(items){
  32193. node.unmarkProcessing();
  32194. // Display the children and also start expanding any children that were previously expanded
  32195. // (if this.persist == true). The returned Deferred will fire when those expansions finish.
  32196. var scid = node.setChildItems(items);
  32197. // Call _expandNode() again but this time it will just to do the animation (default branch).
  32198. // The returned Deferred will fire when the animation completes.
  32199. // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
  32200. var ed = _this._expandNode(node, true);
  32201. // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
  32202. // signal that I am done.
  32203. scid.addCallback(function(){
  32204. ed.addCallback(function(){
  32205. def.callback();
  32206. })
  32207. });
  32208. },
  32209. function(err){
  32210. console.error(_this, ": error loading root children: ", err);
  32211. }
  32212. );
  32213. break;
  32214. default: // "LOADED"
  32215. // data is already loaded; just expand node
  32216. def = (node._expandNodeDeferred = node.expand());
  32217. this.onOpen(node.item, node);
  32218. if(item){
  32219. this._state(item, true);
  32220. this._saveState();
  32221. }
  32222. }
  32223. return def; // dojo.Deferred
  32224. },
  32225. ////////////////// Miscellaneous functions ////////////////
  32226. focusNode: function(/* _tree.Node */ node){
  32227. // summary:
  32228. // Focus on the specified node (which must be visible)
  32229. // tags:
  32230. // protected
  32231. // set focus so that the label will be voiced using screen readers
  32232. dijit.focus(node.labelNode);
  32233. },
  32234. _onNodeFocus: function(/*dijit._Widget*/ node){
  32235. // summary:
  32236. // Called when a TreeNode gets focus, either by user clicking
  32237. // it, or programatically by arrow key handling code.
  32238. // description:
  32239. // It marks that the current node is the selected one, and the previously
  32240. // selected node no longer is.
  32241. if(node && node != this.lastFocused){
  32242. if(this.lastFocused && !this.lastFocused._destroyed){
  32243. // mark that the previously focsable node is no longer focusable
  32244. this.lastFocused.setFocusable(false);
  32245. }
  32246. // mark that the new node is the currently selected one
  32247. node.setFocusable(true);
  32248. this.lastFocused = node;
  32249. }
  32250. },
  32251. _onNodeMouseEnter: function(/*dijit._Widget*/ node){
  32252. // summary:
  32253. // Called when mouse is over a node (onmouseenter event),
  32254. // this is monitored by the DND code
  32255. },
  32256. _onNodeMouseLeave: function(/*dijit._Widget*/ node){
  32257. // summary:
  32258. // Called when mouse leaves a node (onmouseleave event),
  32259. // this is monitored by the DND code
  32260. },
  32261. //////////////// Events from the model //////////////////////////
  32262. _onItemChange: function(/*Item*/ item){
  32263. // summary:
  32264. // Processes notification of a change to an item's scalar values like label
  32265. var model = this.model,
  32266. identity = model.getIdentity(item),
  32267. nodes = this._itemNodesMap[identity];
  32268. if(nodes){
  32269. var label = this.getLabel(item),
  32270. tooltip = this.getTooltip(item);
  32271. dojo.forEach(nodes, function(node){
  32272. node.set({
  32273. item: item, // theoretically could be new JS Object representing same item
  32274. label: label,
  32275. tooltip: tooltip
  32276. });
  32277. node._updateItemClasses(item);
  32278. });
  32279. }
  32280. },
  32281. _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
  32282. // summary:
  32283. // Processes notification of a change to an item's children
  32284. var model = this.model,
  32285. identity = model.getIdentity(parent),
  32286. parentNodes = this._itemNodesMap[identity];
  32287. if(parentNodes){
  32288. dojo.forEach(parentNodes,function(parentNode){
  32289. parentNode.setChildItems(newChildrenList);
  32290. });
  32291. }
  32292. },
  32293. _onItemDelete: function(/*Item*/ item){
  32294. // summary:
  32295. // Processes notification of a deletion of an item
  32296. var model = this.model,
  32297. identity = model.getIdentity(item),
  32298. nodes = this._itemNodesMap[identity];
  32299. if(nodes){
  32300. dojo.forEach(nodes,function(node){
  32301. // Remove node from set of selected nodes (if it's selected)
  32302. this.dndController.removeTreeNode(node);
  32303. var parent = node.getParent();
  32304. if(parent){
  32305. // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
  32306. parent.removeChild(node);
  32307. }
  32308. node.destroyRecursive();
  32309. }, this);
  32310. delete this._itemNodesMap[identity];
  32311. }
  32312. },
  32313. /////////////// Miscellaneous funcs
  32314. _initState: function(){
  32315. // summary:
  32316. // Load in which nodes should be opened automatically
  32317. if(this.persist){
  32318. var cookie = dojo.cookie(this.cookieName);
  32319. this._openedItemIds = {};
  32320. if(cookie){
  32321. dojo.forEach(cookie.split(','), function(item){
  32322. this._openedItemIds[item] = true;
  32323. }, this);
  32324. }
  32325. }
  32326. },
  32327. _state: function(item,expanded){
  32328. // summary:
  32329. // Query or set expanded state for an item,
  32330. if(!this.persist){
  32331. return false;
  32332. }
  32333. var id=this.model.getIdentity(item);
  32334. if(arguments.length === 1){
  32335. return this._openedItemIds[id];
  32336. }
  32337. if(expanded){
  32338. this._openedItemIds[id] = true;
  32339. }else{
  32340. delete this._openedItemIds[id];
  32341. }
  32342. },
  32343. _saveState: function(){
  32344. // summary:
  32345. // Create and save a cookie with the currently expanded nodes identifiers
  32346. if(!this.persist){
  32347. return;
  32348. }
  32349. var ary = [];
  32350. for(var id in this._openedItemIds){
  32351. ary.push(id);
  32352. }
  32353. dojo.cookie(this.cookieName, ary.join(","), {expires:365});
  32354. },
  32355. destroy: function(){
  32356. if(this._curSearch){
  32357. clearTimeout(this._curSearch.timer);
  32358. delete this._curSearch;
  32359. }
  32360. if(this.rootNode){
  32361. this.rootNode.destroyRecursive();
  32362. }
  32363. if(this.dndController && !dojo.isString(this.dndController)){
  32364. this.dndController.destroy();
  32365. }
  32366. this.rootNode = null;
  32367. this.inherited(arguments);
  32368. },
  32369. destroyRecursive: function(){
  32370. // A tree is treated as a leaf, not as a node with children (like a grid),
  32371. // but defining destroyRecursive for back-compat.
  32372. this.destroy();
  32373. },
  32374. resize: function(changeSize){
  32375. if(changeSize){
  32376. dojo.marginBox(this.domNode, changeSize);
  32377. }
  32378. // The only JS sizing involved w/tree is the indentation, which is specified
  32379. // in CSS and read in through this dummy indentDetector node (tree must be
  32380. // visible and attached to the DOM to read this)
  32381. this._nodePixelIndent = dojo._getMarginSize(this.tree.indentDetector).w;
  32382. if(this.tree.rootNode){
  32383. // If tree has already loaded, then reset indent for all the nodes
  32384. this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
  32385. }
  32386. },
  32387. _createTreeNode: function(/*Object*/ args){
  32388. // summary:
  32389. // creates a TreeNode
  32390. // description:
  32391. // Developers can override this method to define their own TreeNode class;
  32392. // However it will probably be removed in a future release in favor of a way
  32393. // of just specifying a widget for the label, rather than one that contains
  32394. // the children too.
  32395. return new dijit._TreeNode(args);
  32396. }
  32397. });
  32398. // For back-compat. TODO: remove in 2.0
  32399. }
  32400. if(!dojo._hasResource["dojox.xml.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  32401. dojo._hasResource["dojox.xml.parser"] = true;
  32402. dojo.provide("dojox.xml.parser");
  32403. //DOM type to int value for reference.
  32404. //Ints make for more compact code than full constant names.
  32405. //ELEMENT_NODE = 1;
  32406. //ATTRIBUTE_NODE = 2;
  32407. //TEXT_NODE = 3;
  32408. //CDATA_SECTION_NODE = 4;
  32409. //ENTITY_REFERENCE_NODE = 5;
  32410. //ENTITY_NODE = 6;
  32411. //PROCESSING_INSTRUCTION_NODE = 7;
  32412. //COMMENT_NODE = 8;
  32413. //DOCUMENT_NODE = 9;
  32414. //DOCUMENT_TYPE_NODE = 10;
  32415. //DOCUMENT_FRAGMENT_NODE = 11;
  32416. //NOTATION_NODE = 12;
  32417. dojox.xml.parser.parse = function(/*String?*/ str, /*String?*/ mimetype){
  32418. // summary:
  32419. // cross-browser implementation of creating an XML document object from null, empty string, and XML text..
  32420. //
  32421. // str:
  32422. // Optional text to create the document from. If not provided, an empty XML document will be created.
  32423. // If str is empty string "", then a new empty document will be created.
  32424. // mimetype:
  32425. // Optional mimetype of the text. Typically, this is text/xml. Will be defaulted to text/xml if not provided.
  32426. var _document = dojo.doc;
  32427. var doc;
  32428. mimetype = mimetype || "text/xml";
  32429. if(str && dojo.trim(str) && "DOMParser" in dojo.global){
  32430. //Handle parsing the text on Mozilla based browsers etc..
  32431. var parser = new DOMParser();
  32432. doc = parser.parseFromString(str, mimetype);
  32433. var de = doc.documentElement;
  32434. var errorNS = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
  32435. if(de.nodeName == "parsererror" && de.namespaceURI == errorNS){
  32436. var sourceText = de.getElementsByTagNameNS(errorNS, 'sourcetext')[0];
  32437. if(sourceText){
  32438. sourceText = sourceText.firstChild.data;
  32439. }
  32440. throw new Error("Error parsing text " + de.firstChild.data + " \n" + sourceText);
  32441. }
  32442. return doc;
  32443. }else if("ActiveXObject" in dojo.global){
  32444. //Handle IE.
  32445. var ms = function(n){ return "MSXML" + n + ".DOMDocument"; };
  32446. var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)];
  32447. dojo.some(dp, function(p){
  32448. try{
  32449. doc = new ActiveXObject(p);
  32450. }catch(e){ return false; }
  32451. return true;
  32452. });
  32453. if(str && doc){
  32454. doc.async = false;
  32455. doc.loadXML(str);
  32456. var pe = doc.parseError;
  32457. if(pe.errorCode !== 0){
  32458. throw new Error("Line: " + pe.line + "\n" +
  32459. "Col: " + pe.linepos + "\n" +
  32460. "Reason: " + pe.reason + "\n" +
  32461. "Error Code: " + pe.errorCode + "\n" +
  32462. "Source: " + pe.srcText);
  32463. }
  32464. }
  32465. if(doc){
  32466. return doc; //DOMDocument
  32467. }
  32468. }else if(_document.implementation && _document.implementation.createDocument){
  32469. if(str && dojo.trim(str) && _document.createElement){
  32470. //Everyone else that we couldn't get to work. Fallback case.
  32471. // FIXME: this may change all tags to uppercase!
  32472. var tmp = _document.createElement("xml");
  32473. tmp.innerHTML = str;
  32474. var xmlDoc = _document.implementation.createDocument("foo", "", null);
  32475. dojo.forEach(tmp.childNodes, function(child){
  32476. xmlDoc.importNode(child, true);
  32477. });
  32478. return xmlDoc; // DOMDocument
  32479. }else{
  32480. return _document.implementation.createDocument("", "", null); // DOMDocument
  32481. }
  32482. }
  32483. return null; // null
  32484. }
  32485. dojox.xml.parser.textContent = function(/*Node*/node, /*String?*/text){
  32486. // summary:
  32487. // Implementation of the DOM Level 3 attribute; scan node for text
  32488. // description:
  32489. // Implementation of the DOM Level 3 attribute; scan node for text
  32490. // This function can also update the text of a node by replacing all child
  32491. // content of the node.
  32492. // node:
  32493. // The node to get the text off of or set the text on.
  32494. // text:
  32495. // Optional argument of the text to apply to the node.
  32496. if(arguments.length>1){
  32497. var _document = node.ownerDocument || dojo.doc; //Preference is to get the node owning doc first or it may fail
  32498. dojox.xml.parser.replaceChildren(node, _document.createTextNode(text));
  32499. return text; // String
  32500. }else{
  32501. if(node.textContent !== undefined){ //FF 1.5 -- remove?
  32502. return node.textContent; // String
  32503. }
  32504. var _result = "";
  32505. if(node){
  32506. dojo.forEach(node.childNodes, function(child){
  32507. switch(child.nodeType){
  32508. case 1: // ELEMENT_NODE
  32509. case 5: // ENTITY_REFERENCE_NODE
  32510. _result += dojox.xml.parser.textContent(child);
  32511. break;
  32512. case 3: // TEXT_NODE
  32513. case 2: // ATTRIBUTE_NODE
  32514. case 4: // CDATA_SECTION_NODE
  32515. _result += child.nodeValue;
  32516. }
  32517. });
  32518. }
  32519. return _result; // String
  32520. }
  32521. }
  32522. dojox.xml.parser.replaceChildren = function(/*Element*/node, /*Node || Array*/ newChildren){
  32523. // summary:
  32524. // Removes all children of node and appends newChild. All the existing
  32525. // children will be destroyed.
  32526. // description:
  32527. // Removes all children of node and appends newChild. All the existing
  32528. // children will be destroyed.
  32529. // node:
  32530. // The node to modify the children on
  32531. // newChildren:
  32532. // The children to add to the node. It can either be a single Node or an
  32533. // array of Nodes.
  32534. var nodes = [];
  32535. if(dojo.isIE){
  32536. dojo.forEach(node.childNodes, function(child){
  32537. nodes.push(child);
  32538. });
  32539. }
  32540. dojox.xml.parser.removeChildren(node);
  32541. dojo.forEach(nodes, dojo.destroy);
  32542. if(!dojo.isArray(newChildren)){
  32543. node.appendChild(newChildren);
  32544. }else{
  32545. dojo.forEach(newChildren, function(child){
  32546. node.appendChild(child);
  32547. });
  32548. }
  32549. }
  32550. dojox.xml.parser.removeChildren = function(/*Element*/node){
  32551. // summary:
  32552. // removes all children from node and returns the count of children removed.
  32553. // The children nodes are not destroyed. Be sure to call dojo.destroy on them
  32554. // after they are not used anymore.
  32555. // node:
  32556. // The node to remove all the children from.
  32557. var count = node.childNodes.length;
  32558. while(node.hasChildNodes()){
  32559. node.removeChild(node.firstChild);
  32560. }
  32561. return count; // int
  32562. }
  32563. dojox.xml.parser.innerXML = function(/*Node*/node){
  32564. // summary:
  32565. // Implementation of MS's innerXML function.
  32566. // node:
  32567. // The node from which to generate the XML text representation.
  32568. if(node.innerXML){
  32569. return node.innerXML; // String
  32570. }else if(node.xml){
  32571. return node.xml; // String
  32572. }else if(typeof XMLSerializer != "undefined"){
  32573. return (new XMLSerializer()).serializeToString(node); // String
  32574. }
  32575. return null;
  32576. }
  32577. }
  32578. if(!dojo._hasResource["dojox.xml.DomParser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  32579. dojo._hasResource["dojox.xml.DomParser"] = true;
  32580. dojo.provide("dojox.xml.DomParser");
  32581. dojox.xml.DomParser=new (function(){
  32582. /**********************************************************
  32583. * The DomParser is a close-to (but not entirely)
  32584. * conforming XML parser based on regular
  32585. * expressions. It will take any XML fragment
  32586. * and return a lightweight JS structure that is
  32587. * similar to (but not exactly) the DOM specification.
  32588. *
  32589. * Getter and setter methods are NOT available; the goal
  32590. * was to keep the resulting object model entirely JS-like.
  32591. *
  32592. * All node types but document fragments are supported;
  32593. * all nodes support getElementsByTagName and
  32594. * getElementsByTagNameNS (with short names byName and
  32595. * byNameNS). The document node supports getElementById
  32596. * (byId), and all nodes support a supplimental
  32597. * childrenByName/childrenByNameNS method as well.
  32598. *
  32599. * The object model is intended to be a READONLY format;
  32600. * mutation events are NOT supported, and though you
  32601. * can change properties on a node-by-node basis, certain
  32602. * operations are not supported (such as changing the ID
  32603. * of an element).
  32604. **********************************************************/
  32605. // internal use only.
  32606. var nodeTypes={ ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9 };
  32607. // compile the regular expressions once.
  32608. var reTags=/<([^>\/\s+]*)([^>]*)>([^<]*)/g;
  32609. var reAttr=/([^=]*)=(("([^"]*)")|('([^']*)'))/g; // patch from tdedischew AT gmail, with additional grouping
  32610. var reEntity=/<!ENTITY\s+([^"]*)\s+"([^"]*)">/g;
  32611. var reCData=/<!\[CDATA\[([\u0001-\uFFFF]*?)\]\]>/g;
  32612. var reComments=/<!--([\u0001-\uFFFF]*?)-->/g;
  32613. var trim=/^\s+|\s+$/g;
  32614. var normalize=/\s+/g;
  32615. var egt=/\&gt;/g;
  32616. var elt=/\&lt;/g;
  32617. var equot=/\&quot;/g;
  32618. var eapos=/\&apos;/g;
  32619. var eamp=/\&amp;/g;
  32620. var dNs="_def_";
  32621. // create a root node.
  32622. function _doc(){
  32623. return new (function(){
  32624. var all={};
  32625. this.nodeType=nodeTypes.DOCUMENT;
  32626. this.nodeName="#document";
  32627. this.namespaces={};
  32628. this._nsPaths={};
  32629. this.childNodes=[];
  32630. this.documentElement=null;
  32631. // any element with an ID attribute will be added to the internal hashtable.
  32632. this._add=function(obj){
  32633. if(typeof(obj.id)!="undefined"){ all[obj.id]=obj; }
  32634. };
  32635. this._remove=function(id){
  32636. if(all[id]){ delete all[id]; }
  32637. };
  32638. this.byId=this.getElementById=function(id){ return all[id]; };
  32639. this.byName=this.getElementsByTagName=byName;
  32640. this.byNameNS=this.getElementsByTagNameNS=byNameNS;
  32641. this.childrenByName=childrenByName;
  32642. this.childrenByNameNS=childrenByNameNS;
  32643. })();
  32644. }
  32645. // functions attached to element nodes
  32646. function byName(name){
  32647. // return all descendants with name. Fully qualified (i.e. svg:svg)
  32648. function __(node, name, arr){
  32649. dojo.forEach(node.childNodes, function(c){
  32650. if(c.nodeType==nodeTypes.ELEMENT){
  32651. if(name=="*"){ arr.push(c); }
  32652. else if(c.nodeName==name){ arr.push(c); }
  32653. __(c, name, arr);
  32654. }
  32655. });
  32656. }
  32657. var a=[];
  32658. __(this, name, a);
  32659. return a;
  32660. }
  32661. function byNameNS(name, ns){
  32662. // return all descendants with name by namespace. If no namespace passed, the default is used.
  32663. function __(node, name, ns, arr){
  32664. dojo.forEach(node.childNodes, function(c){
  32665. if(c.nodeType==nodeTypes.ELEMENT){
  32666. if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
  32667. else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
  32668. __(c, name, ns, arr);
  32669. }
  32670. });
  32671. }
  32672. if(!ns){ ns=dNs; }
  32673. var a=[];
  32674. __(this, name, ns, a);
  32675. return a;
  32676. }
  32677. // Only child nodes with name.
  32678. function childrenByName(name){
  32679. var a=[];
  32680. dojo.forEach(this.childNodes, function(c){
  32681. if(c.nodeType==nodeTypes.ELEMENT){
  32682. if(name=="*"){ a.push(c); }
  32683. else if(c.nodeName==name){ a.push(c); }
  32684. }
  32685. });
  32686. return a;
  32687. }
  32688. function childrenByNameNS(name, ns){
  32689. var a=[];
  32690. dojo.forEach(this.childNodes, function(c){
  32691. if(c.nodeType==nodeTypes.ELEMENT){
  32692. if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
  32693. else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
  32694. }
  32695. });
  32696. return a;
  32697. }
  32698. function _createTextNode(v){
  32699. return {
  32700. nodeType:nodeTypes.TEXT,
  32701. nodeName:"#text",
  32702. nodeValue:v.replace(normalize," ").replace(egt,">").replace(elt,"<").replace(eapos,"'").replace(equot,'"').replace(eamp,"&")
  32703. };
  32704. }
  32705. // attribute functions
  32706. function getAttr(name){
  32707. for(var i=0; i<this.attributes.length; i++){
  32708. if(this.attributes[i].nodeName==name){
  32709. return this.attributes[i].nodeValue;
  32710. }
  32711. }
  32712. return null;
  32713. }
  32714. function getAttrNS(name, ns){
  32715. for(var i=0; i<this.attributes.length; i++){
  32716. if(this.ownerDocument._nsPaths[ns]==this.attributes[i].namespace
  32717. &&this.attributes[i].localName==name
  32718. ){
  32719. return this.attributes[i].nodeValue;
  32720. }
  32721. }
  32722. return null;
  32723. }
  32724. // note that you can only swap IDs using setAttribute, NOT with setAttributeNS.
  32725. function setAttr(name, val){
  32726. var old=null;
  32727. for(var i=0; i<this.attributes.length; i++){
  32728. if(this.attributes[i].nodeName==name){
  32729. old=this.attributes[i].nodeValue;
  32730. this.attributes[i].nodeValue=val;
  32731. break;
  32732. }
  32733. }
  32734. if(name=="id"){
  32735. if(old!=null){ this.ownerDocument._remove(old); }
  32736. this.ownerDocument._add(this);
  32737. }
  32738. }
  32739. function setAttrNS(name, val, ns){
  32740. for(var i=0; i<this.attributes.length; i++){
  32741. if(this.ownerDocument._nsPaths[ns]==this.attributes[i].namespace
  32742. &&this.attributes[i].localName==name
  32743. ){
  32744. this.attributes[i].nodeValue=val;
  32745. return;
  32746. }
  32747. }
  32748. }
  32749. // navigation
  32750. function prev(){
  32751. var p=this.parentNode;
  32752. if(p){
  32753. for(var i=0;i<p.childNodes.length;i++){
  32754. if(p.childNodes[i]==this&&i>0){
  32755. return p.childNodes[i-1];
  32756. }
  32757. }
  32758. }
  32759. return null;
  32760. }
  32761. function next(){
  32762. var p=this.parentNode;
  32763. if(p){
  32764. for(var i=0;i<p.childNodes.length;i++){
  32765. if(p.childNodes[i]==this&&(i+1)<p.childNodes.length){
  32766. return p.childNodes[i+1];
  32767. }
  32768. }
  32769. }
  32770. return null;
  32771. }
  32772. // the main method.
  32773. this.parse=function(/* String */str){
  32774. var root=_doc();
  32775. if(str==null){ return root; }
  32776. if(str.length==0){ return root; }
  32777. // preprocess custom entities
  32778. if(str.indexOf("<!ENTITY")>0){
  32779. var entity, eRe=[];
  32780. if(reEntity.test(str)){
  32781. reEntity.lastIndex=0;
  32782. // match entities
  32783. while((entity=reEntity.exec(str))!=null){
  32784. eRe.push({
  32785. entity:"&"+entity[1].replace(trim,"")+";",
  32786. expression:entity[2]
  32787. });
  32788. }
  32789. // replace instances in the document.
  32790. for(var i=0; i<eRe.length; i++){
  32791. str=str.replace(new RegExp(eRe[i].entity, "g"), eRe[i].expression);
  32792. }
  32793. }
  32794. }
  32795. // pre-parse for CData, and tokenize.
  32796. var cdSections=[], cdata;
  32797. while((cdata=reCData.exec(str))!=null){ cdSections.push(cdata[1]); }
  32798. for(var i=0; i<cdSections.length; i++){ str=str.replace(cdSections[i], i); }
  32799. // pre-parse for comments, and tokenize.
  32800. var comments=[], comment;
  32801. while((comment=reComments.exec(str))!=null){ comments.push(comment[1]); }
  32802. for(i=0; i<comments.length; i++){ str=str.replace(comments[i], i); }
  32803. // parse the document
  32804. var res, obj=root;
  32805. while((res=reTags.exec(str))!=null){
  32806. // closing tags.
  32807. if(res[2].charAt(0)=="/" && res[2].replace(trim, "").length>1){
  32808. if(obj.parentNode){
  32809. obj=obj.parentNode;
  32810. }
  32811. var text=(res[3]||"").replace(trim, "");
  32812. if(text.length>0) {
  32813. obj.childNodes.push(_createTextNode(text));
  32814. }
  32815. }
  32816. // open tags.
  32817. else if(res[1].length>0){
  32818. // figure out the type of node.
  32819. if(res[1].charAt(0)=="?"){
  32820. // processing instruction
  32821. var name=res[1].substr(1);
  32822. var target=res[2].substr(0,res[2].length-2);
  32823. obj.childNodes.push({
  32824. nodeType:nodeTypes.PROCESSING_INSTRUCTION,
  32825. nodeName:name,
  32826. nodeValue:target
  32827. });
  32828. }
  32829. else if(res[1].charAt(0)=="!"){
  32830. // CDATA; skip over any declaration elements.
  32831. if(res[1].indexOf("![CDATA[")==0){
  32832. var val=parseInt(res[1].replace("![CDATA[","").replace("]]",""));
  32833. obj.childNodes.push({
  32834. nodeType:nodeTypes.CDATA_SECTION,
  32835. nodeName:"#cdata-section",
  32836. nodeValue:cdSections[val]
  32837. });
  32838. }
  32839. // Comments.
  32840. else if(res[1].substr(0,3)=="!--"){
  32841. var val=parseInt(res[1].replace("!--","").replace("--",""));
  32842. obj.childNodes.push({
  32843. nodeType:nodeTypes.COMMENT,
  32844. nodeName:"#comment",
  32845. nodeValue:comments[val]
  32846. });
  32847. }
  32848. }
  32849. else {
  32850. // Elements (with attribute and text)
  32851. var name=res[1].replace(trim,"");
  32852. var o={
  32853. nodeType:nodeTypes.ELEMENT,
  32854. nodeName:name,
  32855. localName:name,
  32856. namespace:dNs,
  32857. ownerDocument:root,
  32858. attributes:[],
  32859. parentNode:null,
  32860. childNodes:[]
  32861. };
  32862. // check to see if it's namespaced.
  32863. if(name.indexOf(":")>-1){
  32864. var t=name.split(":");
  32865. o.namespace=t[0];
  32866. o.localName=t[1];
  32867. }
  32868. // set the function references.
  32869. o.byName=o.getElementsByTagName=byName;
  32870. o.byNameNS=o.getElementsByTagNameNS=byNameNS;
  32871. o.childrenByName=childrenByName;
  32872. o.childrenByNameNS=childrenByNameNS;
  32873. o.getAttribute=getAttr;
  32874. o.getAttributeNS=getAttrNS;
  32875. o.setAttribute=setAttr;
  32876. o.setAttributeNS=setAttrNS;
  32877. o.previous=o.previousSibling=prev;
  32878. o.next=o.nextSibling=next;
  32879. // parse the attribute string.
  32880. var attr;
  32881. while((attr=reAttr.exec(res[2]))!=null){
  32882. if(attr.length>0){
  32883. var name=attr[1].replace(trim,"");
  32884. var val=(attr[4]||attr[6]||"").replace(normalize," ")
  32885. .replace(egt,">")
  32886. .replace(elt,"<")
  32887. .replace(eapos,"'")
  32888. .replace(equot,'"')
  32889. .replace(eamp,"&");
  32890. if(name.indexOf("xmlns")==0){
  32891. if(name.indexOf(":")>0){
  32892. var ns=name.split(":");
  32893. root.namespaces[ns[1]]=val;
  32894. root._nsPaths[val]=ns[1];
  32895. } else {
  32896. root.namespaces[dNs]=val;
  32897. root._nsPaths[val]=dNs;
  32898. }
  32899. } else {
  32900. var ln=name;
  32901. var ns=dNs;
  32902. if(name.indexOf(":")>0){
  32903. var t=name.split(":");
  32904. ln=t[1];
  32905. ns=t[0];
  32906. }
  32907. o.attributes.push({
  32908. nodeType:nodeTypes.ATTRIBUTE,
  32909. nodeName:name,
  32910. localName:ln,
  32911. namespace:ns,
  32912. nodeValue:val
  32913. });
  32914. // only add id as a property.
  32915. if(ln=="id"){ o.id=val; }
  32916. }
  32917. }
  32918. }
  32919. root._add(o);
  32920. if(obj){
  32921. obj.childNodes.push(o);
  32922. o.parentNode=obj;
  32923. // if it's not a self-closing node.
  32924. if(res[2].charAt(res[2].length-1)!="/"){
  32925. obj=o;
  32926. }
  32927. }
  32928. var text=res[3];
  32929. if(text.length>0){
  32930. obj.childNodes.push(_createTextNode(text));
  32931. }
  32932. }
  32933. }
  32934. }
  32935. // set the document element
  32936. for(var i=0; i<root.childNodes.length; i++){
  32937. var e=root.childNodes[i];
  32938. if(e.nodeType==nodeTypes.ELEMENT){
  32939. root.documentElement=e;
  32940. break;
  32941. }
  32942. }
  32943. return root;
  32944. };
  32945. })();
  32946. }
  32947. if(!dojo._hasResource["dojox.collections._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  32948. dojo._hasResource["dojox.collections._base"] = true;
  32949. dojo.provide("dojox.collections._base");
  32950. dojox.collections.DictionaryEntry=function(/* string */k, /* object */v){
  32951. // summary
  32952. // return an object of type dojox.collections.DictionaryEntry
  32953. this.key=k;
  32954. this.value=v;
  32955. this.valueOf=function(){
  32956. return this.value; // object
  32957. };
  32958. this.toString=function(){
  32959. return String(this.value); // string
  32960. };
  32961. }
  32962. /* Iterators
  32963. * The collections.Iterators (Iterator and DictionaryIterator) are built to
  32964. * work with the Collections included in this module. However, they *can*
  32965. * be used with arrays and objects, respectively, should one choose to do so.
  32966. */
  32967. dojox.collections.Iterator=function(/* array */arr){
  32968. // summary
  32969. // return an object of type dojox.collections.Iterator
  32970. var a=arr;
  32971. var position=0;
  32972. this.element=a[position]||null;
  32973. this.atEnd=function(){
  32974. // summary
  32975. // Test to see if the internal cursor has reached the end of the internal collection.
  32976. return (position>=a.length); // bool
  32977. };
  32978. this.get=function(){
  32979. // summary
  32980. // Get the next member in the collection.
  32981. if(this.atEnd()){
  32982. return null; // object
  32983. }
  32984. this.element=a[position++];
  32985. return this.element; // object
  32986. };
  32987. this.map=function(/* function */fn, /* object? */scope){
  32988. // summary
  32989. // Functional iteration with optional scope.
  32990. return dojo.map(a, fn, scope);
  32991. };
  32992. this.reset=function(){
  32993. // summary
  32994. // reset the internal cursor.
  32995. position=0;
  32996. this.element=a[position];
  32997. };
  32998. }
  32999. /* Notes:
  33000. * The DictionaryIterator no longer supports a key and value property;
  33001. * the reality is that you can use this to iterate over a JS object
  33002. * being used as a hashtable.
  33003. */
  33004. dojox.collections.DictionaryIterator=function(/* object */obj){
  33005. // summary
  33006. // return an object of type dojox.collections.DictionaryIterator
  33007. var a=[]; // Create an indexing array
  33008. var testObject={};
  33009. for(var p in obj){
  33010. if(!testObject[p]){
  33011. a.push(obj[p]); // fill it up
  33012. }
  33013. }
  33014. var position=0;
  33015. this.element=a[position]||null;
  33016. this.atEnd=function(){
  33017. // summary
  33018. // Test to see if the internal cursor has reached the end of the internal collection.
  33019. return (position>=a.length); // bool
  33020. };
  33021. this.get=function(){
  33022. // summary
  33023. // Get the next member in the collection.
  33024. if(this.atEnd()){
  33025. return null; // object
  33026. }
  33027. this.element=a[position++];
  33028. return this.element; // object
  33029. };
  33030. this.map=function(/* function */fn, /* object? */scope){
  33031. // summary
  33032. // Functional iteration with optional scope.
  33033. return dojo.map(a, fn, scope);
  33034. };
  33035. this.reset=function() {
  33036. // summary
  33037. // reset the internal cursor.
  33038. position=0;
  33039. this.element=a[position];
  33040. };
  33041. };
  33042. }
  33043. if(!dojo._hasResource["dojox.collections.Dictionary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  33044. dojo._hasResource["dojox.collections.Dictionary"] = true;
  33045. dojo.provide("dojox.collections.Dictionary");
  33046. dojox.collections.Dictionary=function(/* dojox.collections.Dictionary? */dictionary){
  33047. // summary
  33048. // Returns an object of type dojox.collections.Dictionary
  33049. var items={};
  33050. this.count=0;
  33051. // comparator for property addition and access.
  33052. var testObject={};
  33053. this.add=function(/* string */k, /* object */v){
  33054. // summary
  33055. // Add a new item to the Dictionary.
  33056. var b=(k in items);
  33057. items[k]=new dojox.collections.DictionaryEntry(k,v);
  33058. if(!b){
  33059. this.count++;
  33060. }
  33061. };
  33062. this.clear=function(){
  33063. // summary
  33064. // Clears the internal dictionary.
  33065. items={};
  33066. this.count=0;
  33067. };
  33068. this.clone=function(){
  33069. // summary
  33070. // Returns a new instance of dojox.collections.Dictionary; note the the dictionary is a clone but items might not be.
  33071. return new dojox.collections.Dictionary(this); // dojox.collections.Dictionary
  33072. };
  33073. this.contains=this.containsKey=function(/* string */k){
  33074. // summary
  33075. // Check to see if the dictionary has an entry at key "k".
  33076. if(testObject[k]){
  33077. return false; // bool
  33078. }
  33079. return (items[k]!=null); // bool
  33080. };
  33081. this.containsValue=function(/* object */v){
  33082. // summary
  33083. // Check to see if the dictionary has an entry with value "v".
  33084. var e=this.getIterator();
  33085. while(e.get()){
  33086. if(e.element.value==v){
  33087. return true; // bool
  33088. }
  33089. }
  33090. return false; // bool
  33091. };
  33092. this.entry=function(/* string */k){
  33093. // summary
  33094. // Accessor method; similar to dojox.collections.Dictionary.item but returns the actual Entry object.
  33095. return items[k]; // dojox.collections.DictionaryEntry
  33096. };
  33097. this.forEach=function(/* function */ fn, /* object? */ scope){
  33098. // summary
  33099. // functional iterator, following the mozilla spec.
  33100. var a=[]; // Create an indexing array
  33101. for(var p in items) {
  33102. if(!testObject[p]){
  33103. a.push(items[p]); // fill it up
  33104. }
  33105. }
  33106. dojo.forEach(a, fn, scope);
  33107. };
  33108. this.getKeyList=function(){
  33109. // summary
  33110. // Returns an array of the keys in the dictionary.
  33111. return (this.getIterator()).map(function(entry){
  33112. return entry.key;
  33113. }); // array
  33114. };
  33115. this.getValueList=function(){
  33116. // summary
  33117. // Returns an array of the values in the dictionary.
  33118. return (this.getIterator()).map(function(entry){
  33119. return entry.value;
  33120. }); // array
  33121. };
  33122. this.item=function(/* string */k){
  33123. // summary
  33124. // Accessor method.
  33125. if(k in items){
  33126. return items[k].valueOf(); // object
  33127. }
  33128. return undefined; // object
  33129. };
  33130. this.getIterator=function(){
  33131. // summary
  33132. // Gets a dojox.collections.DictionaryIterator for iteration purposes.
  33133. return new dojox.collections.DictionaryIterator(items); // dojox.collections.DictionaryIterator
  33134. };
  33135. this.remove=function(/* string */k){
  33136. // summary
  33137. // Removes the item at k from the internal collection.
  33138. if(k in items && !testObject[k]){
  33139. delete items[k];
  33140. this.count--;
  33141. return true; // bool
  33142. }
  33143. return false; // bool
  33144. };
  33145. if (dictionary){
  33146. var e=dictionary.getIterator();
  33147. while(e.get()) {
  33148. this.add(e.element.key, e.element.value);
  33149. }
  33150. }
  33151. };
  33152. }
  33153. if(!dojo._hasResource["dojox.data.QueryReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  33154. dojo._hasResource["dojox.data.QueryReadStore"] = true;
  33155. dojo.provide("dojox.data.QueryReadStore");
  33156. dojo.declare("dojox.data.QueryReadStore",
  33157. null,
  33158. {
  33159. // summary:
  33160. // This class provides a store that is mainly intended to be used
  33161. // for loading data dynamically from the server, used i.e. for
  33162. // retreiving chunks of data from huge data stores on the server (by server-side filtering!).
  33163. // Upon calling the fetch() method of this store the data are requested from
  33164. // the server if they are not yet loaded for paging (or cached).
  33165. //
  33166. // For example used for a combobox which works on lots of data. It
  33167. // can be used to retreive the data partially upon entering the
  33168. // letters "ac" it returns only items like "action", "acting", etc.
  33169. //
  33170. // note:
  33171. // The field name "id" in a query is reserved for looking up data
  33172. // by id. This is necessary as before the first fetch, the store
  33173. // has no way of knowing which field the server will declare as
  33174. // identifier.
  33175. //
  33176. // example:
  33177. // | // The parameter "query" contains the data that are sent to the server.
  33178. // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
  33179. // | store.fetch({query:{name:'a'}, queryOptions:{ignoreCase:false}});
  33180. //
  33181. // | // Since "serverQuery" is given, it overrules and those data are
  33182. // | // sent to the server.
  33183. // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
  33184. // | store.fetch({serverQuery:{name:'a'}, queryOptions:{ignoreCase:false}});
  33185. //
  33186. // | <div dojoType="dojox.data.QueryReadStore"
  33187. // | jsId="store2"
  33188. // | url="../tests/stores/QueryReadStore.php"
  33189. // | requestMethod="post"></div>
  33190. // | <div dojoType="dojox.grid.data.DojoData"
  33191. // | jsId="model2"
  33192. // | store="store2"
  33193. // | sortFields="[{attribute: 'name', descending: true}]"
  33194. // | rowsPerPage="30"></div>
  33195. // | <div dojoType="dojox.Grid" id="grid2"
  33196. // | model="model2"
  33197. // | structure="gridLayout"
  33198. // | style="height:300px; width:800px;"></div>
  33199. //
  33200. // todo:
  33201. // - there is a bug in the paging, when i set start:2, count:5 after an initial fetch() and doClientPaging:true
  33202. // it returns 6 elemetns, though count=5, try it in QueryReadStore.html
  33203. // - add optional caching
  33204. // - when the first query searched for "a" and the next for a subset of
  33205. // the first, i.e. "ab" then we actually dont need a server request, if
  33206. // we have client paging, we just need to filter the items we already have
  33207. // that might also be tooo much logic
  33208. url:"",
  33209. requestMethod:"get",
  33210. //useCache:false,
  33211. // We use the name in the errors, once the name is fixed hardcode it, may be.
  33212. _className:"dojox.data.QueryReadStore",
  33213. // This will contain the items we have loaded from the server.
  33214. // The contents of this array is optimized to satisfy all read-api requirements
  33215. // and for using lesser storage, so the keys and their content need some explaination:
  33216. // this._items[0].i - the item itself
  33217. // this._items[0].r - a reference to the store, so we can identify the item
  33218. // securly. We set this reference right after receiving the item from the
  33219. // server.
  33220. _items:[],
  33221. // Store the last query that triggered xhr request to the server.
  33222. // So we can compare if the request changed and if we shall reload
  33223. // (this also depends on other factors, such as is caching used, etc).
  33224. _lastServerQuery:null,
  33225. // Store how many rows we have so that we can pass it to a clientPaging handler
  33226. _numRows:-1,
  33227. // Store a hash of the last server request. Actually I introduced this
  33228. // for testing, so I can check if no unnecessary requests were issued for
  33229. // client-side-paging.
  33230. lastRequestHash:null,
  33231. // summary:
  33232. // By default every request for paging is sent to the server.
  33233. doClientPaging:false,
  33234. // summary:
  33235. // By default all the sorting is done serverside before the data is returned
  33236. // which is the proper place to be doing it for really large datasets.
  33237. doClientSorting:false,
  33238. // Items by identify for Identify API
  33239. _itemsByIdentity:null,
  33240. // Identifier used
  33241. _identifier:null,
  33242. _features: {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true},
  33243. _labelAttr: "label",
  33244. constructor: function(/* Object */ params){
  33245. dojo.mixin(this,params);
  33246. },
  33247. getValue: function(/* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){
  33248. // According to the Read API comments in getValue() and exception is
  33249. // thrown when an item is not an item or the attribute not a string!
  33250. this._assertIsItem(item);
  33251. if(!dojo.isString(attribute)){
  33252. throw new Error(this._className+".getValue(): Invalid attribute, string expected!");
  33253. }
  33254. if(!this.hasAttribute(item, attribute)){
  33255. // read api says: return defaultValue "only if *item* does not have a value for *attribute*."
  33256. // Is this the case here? The attribute doesn't exist, but a defaultValue, sounds reasonable.
  33257. if(defaultValue){
  33258. return defaultValue;
  33259. }
  33260. }
  33261. return item.i[attribute];
  33262. },
  33263. getValues: function(/* item */ item, /* attribute-name-string */ attribute){
  33264. this._assertIsItem(item);
  33265. var ret = [];
  33266. if(this.hasAttribute(item, attribute)){
  33267. ret.push(item.i[attribute]);
  33268. }
  33269. return ret;
  33270. },
  33271. getAttributes: function(/* item */ item){
  33272. this._assertIsItem(item);
  33273. var ret = [];
  33274. for(var i in item.i){
  33275. ret.push(i);
  33276. }
  33277. return ret;
  33278. },
  33279. hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
  33280. // summary:
  33281. // See dojo.data.api.Read.hasAttribute()
  33282. return this.isItem(item) && typeof item.i[attribute]!="undefined";
  33283. },
  33284. containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value){
  33285. var values = this.getValues(item, attribute);
  33286. var len = values.length;
  33287. for(var i=0; i<len; i++){
  33288. if(values[i] == value){
  33289. return true;
  33290. }
  33291. }
  33292. return false;
  33293. },
  33294. isItem: function(/* anything */ something){
  33295. // Some basic tests, that are quick and easy to do here.
  33296. // >>> var store = new dojox.data.QueryReadStore({});
  33297. // >>> store.isItem("");
  33298. // false
  33299. //
  33300. // >>> var store = new dojox.data.QueryReadStore({});
  33301. // >>> store.isItem({});
  33302. // false
  33303. //
  33304. // >>> var store = new dojox.data.QueryReadStore({});
  33305. // >>> store.isItem(0);
  33306. // false
  33307. //
  33308. // >>> var store = new dojox.data.QueryReadStore({});
  33309. // >>> store.isItem({name:"me", label:"me too"});
  33310. // false
  33311. //
  33312. if(something){
  33313. return typeof something.r != "undefined" && something.r == this;
  33314. }
  33315. return false;
  33316. },
  33317. isItemLoaded: function(/* anything */ something){
  33318. // Currently we dont have any state that tells if an item is loaded or not
  33319. // if the item exists its also loaded.
  33320. // This might change when we start working with refs inside items ...
  33321. return this.isItem(something);
  33322. },
  33323. loadItem: function(/* object */ args){
  33324. if(this.isItemLoaded(args.item)){
  33325. return;
  33326. }
  33327. // Actually we have nothing to do here, or at least I dont know what to do here ...
  33328. },
  33329. fetch:function(/* Object? */ request){
  33330. // summary:
  33331. // See dojo.data.util.simpleFetch.fetch() this is just a copy and I adjusted
  33332. // only the paging, since it happens on the server if doClientPaging is
  33333. // false, thx to http://trac.dojotoolkit.org/ticket/4761 reporting this.
  33334. // Would be nice to be able to use simpleFetch() to reduce copied code,
  33335. // but i dont know how yet. Ideas please!
  33336. request = request || {};
  33337. if(!request.store){
  33338. request.store = this;
  33339. }
  33340. var self = this;
  33341. var _errorHandler = function(errorData, requestObject){
  33342. if(requestObject.onError){
  33343. var scope = requestObject.scope || dojo.global;
  33344. requestObject.onError.call(scope, errorData, requestObject);
  33345. }
  33346. };
  33347. var _fetchHandler = function(items, requestObject, numRows){
  33348. var oldAbortFunction = requestObject.abort || null;
  33349. var aborted = false;
  33350. var startIndex = requestObject.start?requestObject.start:0;
  33351. if(self.doClientPaging == false){
  33352. // For client paging we dont need no slicing of the result.
  33353. startIndex = 0;
  33354. }
  33355. var endIndex = requestObject.count?(startIndex + requestObject.count):items.length;
  33356. requestObject.abort = function(){
  33357. aborted = true;
  33358. if(oldAbortFunction){
  33359. oldAbortFunction.call(requestObject);
  33360. }
  33361. };
  33362. var scope = requestObject.scope || dojo.global;
  33363. if(!requestObject.store){
  33364. requestObject.store = self;
  33365. }
  33366. if(requestObject.onBegin){
  33367. requestObject.onBegin.call(scope, numRows, requestObject);
  33368. }
  33369. if(requestObject.sort && self.doClientSorting){
  33370. items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
  33371. }
  33372. if(requestObject.onItem){
  33373. for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
  33374. var item = items[i];
  33375. if(!aborted){
  33376. requestObject.onItem.call(scope, item, requestObject);
  33377. }
  33378. }
  33379. }
  33380. if(requestObject.onComplete && !aborted){
  33381. var subset = null;
  33382. if(!requestObject.onItem){
  33383. subset = items.slice(startIndex, endIndex);
  33384. }
  33385. requestObject.onComplete.call(scope, subset, requestObject);
  33386. }
  33387. };
  33388. this._fetchItems(request, _fetchHandler, _errorHandler);
  33389. return request; // Object
  33390. },
  33391. getFeatures: function(){
  33392. return this._features;
  33393. },
  33394. close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
  33395. // I have no idea if this is really needed ...
  33396. },
  33397. getLabel: function(/* item */ item){
  33398. // summary:
  33399. // See dojo.data.api.Read.getLabel()
  33400. if(this._labelAttr && this.isItem(item)){
  33401. return this.getValue(item, this._labelAttr); //String
  33402. }
  33403. return undefined; //undefined
  33404. },
  33405. getLabelAttributes: function(/* item */ item){
  33406. // summary:
  33407. // See dojo.data.api.Read.getLabelAttributes()
  33408. if(this._labelAttr){
  33409. return [this._labelAttr]; //array
  33410. }
  33411. return null; //null
  33412. },
  33413. _xhrFetchHandler: function(data, request, fetchHandler, errorHandler){
  33414. data = this._filterResponse(data);
  33415. if(data.label){
  33416. this._labelAttr = data.label;
  33417. }
  33418. var numRows = data.numRows || -1;
  33419. this._items = [];
  33420. // Store a ref to "this" in each item, so we can simply check if an item
  33421. // really origins form here (idea is from ItemFileReadStore, I just don't know
  33422. // how efficient the real storage use, garbage collection effort, etc. is).
  33423. dojo.forEach(data.items,function(e){
  33424. this._items.push({i:e, r:this});
  33425. },this);
  33426. var identifier = data.identifier;
  33427. this._itemsByIdentity = {};
  33428. if(identifier){
  33429. this._identifier = identifier;
  33430. var i;
  33431. for(i = 0; i < this._items.length; ++i){
  33432. var item = this._items[i].i;
  33433. var identity = item[identifier];
  33434. if(!this._itemsByIdentity[identity]){
  33435. this._itemsByIdentity[identity] = item;
  33436. }else{
  33437. throw new Error(this._className+": The json data as specified by: [" + this.url + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
  33438. }
  33439. }
  33440. }else{
  33441. this._identifier = Number;
  33442. for(i = 0; i < this._items.length; ++i){
  33443. this._items[i].n = i;
  33444. }
  33445. }
  33446. // TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize
  33447. // (does it really sanititze them) and store the data optimal. should we? for security reasons???
  33448. numRows = this._numRows = (numRows === -1) ? this._items.length : numRows;
  33449. fetchHandler(this._items, request, numRows);
  33450. this._numRows = numRows;
  33451. },
  33452. _fetchItems: function(request, fetchHandler, errorHandler){
  33453. // summary:
  33454. // The request contains the data as defined in the Read-API.
  33455. // Additionally there is following keyword "serverQuery".
  33456. //
  33457. // The *serverQuery* parameter, optional.
  33458. // This parameter contains the data that will be sent to the server.
  33459. // If this parameter is not given the parameter "query"'s
  33460. // data are sent to the server. This is done for some reasons:
  33461. // - to specify explicitly which data are sent to the server, they
  33462. // might also be a mix of what is contained in "query", "queryOptions"
  33463. // and the paging parameters "start" and "count" or may be even
  33464. // completely different things.
  33465. // - don't modify the request.query data, so the interface using this
  33466. // store can rely on unmodified data, as the combobox dijit currently
  33467. // does it, it compares if the query has changed
  33468. // - request.query is required by the Read-API
  33469. //
  33470. // I.e. the following examples might be sent via GET:
  33471. // fetch({query:{name:"abc"}, queryOptions:{ignoreCase:true}})
  33472. // the URL will become: /url.php?name=abc
  33473. //
  33474. // fetch({serverQuery:{q:"abc", c:true}, query:{name:"abc"}, queryOptions:{ignoreCase:true}})
  33475. // the URL will become: /url.php?q=abc&c=true
  33476. // // The serverQuery-parameter has overruled the query-parameter
  33477. // // but the query parameter stays untouched, but is not sent to the server!
  33478. // // The serverQuery contains more data than the query, so they might differ!
  33479. //
  33480. var serverQuery = request.serverQuery || request.query || {};
  33481. //Need to add start and count
  33482. if(!this.doClientPaging){
  33483. serverQuery.start = request.start || 0;
  33484. // Count might not be sent if not given.
  33485. if(request.count){
  33486. serverQuery.count = request.count;
  33487. }
  33488. }
  33489. if(!this.doClientSorting && request.sort){
  33490. var sortInfo = [];
  33491. dojo.forEach(request.sort, function(sort){
  33492. if(sort && sort.attribute){
  33493. sortInfo.push((sort.descending ? "-" : "") + sort.attribute);
  33494. }
  33495. });
  33496. serverQuery.sort = sortInfo.join(',');
  33497. }
  33498. // Compare the last query and the current query by simply json-encoding them,
  33499. // so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()???
  33500. if(this.doClientPaging && this._lastServerQuery !== null &&
  33501. dojo.toJson(serverQuery) == dojo.toJson(this._lastServerQuery)
  33502. ){
  33503. this._numRows = (this._numRows === -1) ? this._items.length : this._numRows;
  33504. fetchHandler(this._items, request, this._numRows);
  33505. }else{
  33506. var xhrFunc = this.requestMethod.toLowerCase() == "post" ? dojo.xhrPost : dojo.xhrGet;
  33507. var xhrHandler = xhrFunc({url:this.url, handleAs:"json-comment-optional", content:serverQuery, failOk: true});
  33508. request.abort = function(){
  33509. xhrHandler.cancel();
  33510. };
  33511. xhrHandler.addCallback(dojo.hitch(this, function(data){
  33512. this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
  33513. }));
  33514. xhrHandler.addErrback(function(error){
  33515. errorHandler(error, request);
  33516. });
  33517. // Generate the hash using the time in milliseconds and a randon number.
  33518. // Since Math.randon() returns something like: 0.23453463, we just remove the "0."
  33519. // probably just for esthetic reasons :-).
  33520. this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2);
  33521. this._lastServerQuery = dojo.mixin({}, serverQuery);
  33522. }
  33523. },
  33524. _filterResponse: function(data){
  33525. // summary:
  33526. // If the data from servers needs to be processed before it can be processed by this
  33527. // store, then this function should be re-implemented in subclass. This default
  33528. // implementation just return the data unchanged.
  33529. // data:
  33530. // The data received from server
  33531. return data;
  33532. },
  33533. _assertIsItem: function(/* item */ item){
  33534. // summary:
  33535. // It throws an error if item is not valid, so you can call it in every method that needs to
  33536. // throw an error when item is invalid.
  33537. // item:
  33538. // The item to test for being contained by the store.
  33539. if(!this.isItem(item)){
  33540. throw new Error(this._className+": Invalid item argument.");
  33541. }
  33542. },
  33543. _assertIsAttribute: function(/* attribute-name-string */ attribute){
  33544. // summary:
  33545. // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
  33546. // attribute:
  33547. // The attribute to test for being contained by the store.
  33548. if(typeof attribute !== "string"){
  33549. throw new Error(this._className+": Invalid attribute argument ('"+attribute+"').");
  33550. }
  33551. },
  33552. fetchItemByIdentity: function(/* Object */ keywordArgs){
  33553. // summary:
  33554. // See dojo.data.api.Identity.fetchItemByIdentity()
  33555. // See if we have already loaded the item with that id
  33556. // In case there hasn't been a fetch yet, _itemsByIdentity is null
  33557. // and thus a fetch will be triggered below.
  33558. if(this._itemsByIdentity){
  33559. var item = this._itemsByIdentity[keywordArgs.identity];
  33560. if(!(item === undefined)){
  33561. if(keywordArgs.onItem){
  33562. var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
  33563. keywordArgs.onItem.call(scope, {i:item, r:this});
  33564. }
  33565. return;
  33566. }
  33567. }
  33568. // Otherwise we need to go remote
  33569. // Set up error handler
  33570. var _errorHandler = function(errorData, requestObject){
  33571. var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
  33572. if(keywordArgs.onError){
  33573. keywordArgs.onError.call(scope, errorData);
  33574. }
  33575. };
  33576. // Set up fetch handler
  33577. var _fetchHandler = function(items, requestObject){
  33578. var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
  33579. try{
  33580. // There is supposed to be only one result
  33581. var item = null;
  33582. if(items && items.length == 1){
  33583. item = items[0];
  33584. }
  33585. // If no item was found, item is still null and we'll
  33586. // fire the onItem event with the null here
  33587. if(keywordArgs.onItem){
  33588. keywordArgs.onItem.call(scope, item);
  33589. }
  33590. }catch(error){
  33591. if(keywordArgs.onError){
  33592. keywordArgs.onError.call(scope, error);
  33593. }
  33594. }
  33595. };
  33596. // Construct query
  33597. var request = {serverQuery:{id:keywordArgs.identity}};
  33598. // Dispatch query
  33599. this._fetchItems(request, _fetchHandler, _errorHandler);
  33600. },
  33601. getIdentity: function(/* item */ item){
  33602. // summary:
  33603. // See dojo.data.api.Identity.getIdentity()
  33604. var identifier = null;
  33605. if(this._identifier === Number){
  33606. identifier = item.n; // Number
  33607. }else{
  33608. identifier = item.i[this._identifier];
  33609. }
  33610. return identifier;
  33611. },
  33612. getIdentityAttributes: function(/* item */ item){
  33613. // summary:
  33614. // See dojo.data.api.Identity.getIdentityAttributes()
  33615. return [this._identifier];
  33616. }
  33617. }
  33618. );
  33619. }
  33620. if(!dojo._hasResource["dojox.string.Builder"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  33621. dojo._hasResource["dojox.string.Builder"] = true;
  33622. dojo.provide("dojox.string.Builder");
  33623. dojox.string.Builder = function(/*String?*/str){
  33624. // summary:
  33625. // A fast buffer for creating large strings.
  33626. //
  33627. // length: Number
  33628. // The current length of the internal string.
  33629. // N.B. the public nature of the internal buffer is no longer
  33630. // needed because the IE-specific fork is no longer needed--TRT.
  33631. var b = "";
  33632. this.length = 0;
  33633. this.append = function(/* String... */s){
  33634. // summary: Append all arguments to the end of the buffer
  33635. if(arguments.length>1){
  33636. /*
  33637. This is a loop unroll was designed specifically for Firefox;
  33638. it would seem that static index access on an Arguments
  33639. object is a LOT faster than doing dynamic index access.
  33640. Therefore, we create a buffer string and take advantage
  33641. of JS's switch fallthrough. The peformance of this method
  33642. comes very close to straight up string concatenation (+=).
  33643. If the arguments object length is greater than 9, we fall
  33644. back to standard dynamic access.
  33645. This optimization seems to have no real effect on either
  33646. Safari or Opera, so we just use it for all.
  33647. It turns out also that this loop unroll can increase performance
  33648. significantly with Internet Explorer, particularly when
  33649. as many arguments are provided as possible.
  33650. Loop unroll per suggestion from Kris Zyp, implemented by
  33651. Tom Trenka.
  33652. Note: added empty string to force a string cast if needed.
  33653. */
  33654. var tmp="", l=arguments.length;
  33655. switch(l){
  33656. case 9: tmp=""+arguments[8]+tmp;
  33657. case 8: tmp=""+arguments[7]+tmp;
  33658. case 7: tmp=""+arguments[6]+tmp;
  33659. case 6: tmp=""+arguments[5]+tmp;
  33660. case 5: tmp=""+arguments[4]+tmp;
  33661. case 4: tmp=""+arguments[3]+tmp;
  33662. case 3: tmp=""+arguments[2]+tmp;
  33663. case 2: {
  33664. b+=""+arguments[0]+arguments[1]+tmp;
  33665. break;
  33666. }
  33667. default: {
  33668. var i=0;
  33669. while(i<arguments.length){
  33670. tmp += arguments[i++];
  33671. }
  33672. b += tmp;
  33673. }
  33674. }
  33675. } else {
  33676. b += s;
  33677. }
  33678. this.length = b.length;
  33679. return this; // dojox.string.Builder
  33680. };
  33681. this.concat = function(/*String...*/s){
  33682. // summary:
  33683. // Alias for append.
  33684. return this.append.apply(this, arguments); // dojox.string.Builder
  33685. };
  33686. this.appendArray = function(/*Array*/strings) {
  33687. // summary:
  33688. // Append an array of items to the internal buffer.
  33689. // Changed from String.prototype.concat.apply because of IE.
  33690. return this.append.apply(this, strings); // dojox.string.Builder
  33691. };
  33692. this.clear = function(){
  33693. // summary:
  33694. // Remove all characters from the buffer.
  33695. b = "";
  33696. this.length = 0;
  33697. return this; // dojox.string.Builder
  33698. };
  33699. this.replace = function(/* String */oldStr, /* String */ newStr){
  33700. // summary:
  33701. // Replace instances of one string with another in the buffer.
  33702. b = b.replace(oldStr,newStr);
  33703. this.length = b.length;
  33704. return this; // dojox.string.Builder
  33705. };
  33706. this.remove = function(/* Number */start, /* Number? */len){
  33707. // summary:
  33708. // Remove len characters starting at index start. If len
  33709. // is not provided, the end of the string is assumed.
  33710. if(len===undefined){ len = b.length; }
  33711. if(len == 0){ return this; }
  33712. b = b.substr(0, start) + b.substr(start+len);
  33713. this.length = b.length;
  33714. return this; // dojox.string.Builder
  33715. };
  33716. this.insert = function(/* Number */index, /* String */str){
  33717. // summary:
  33718. // Insert string str starting at index.
  33719. if(index == 0){
  33720. b = str + b;
  33721. }else{
  33722. b = b.slice(0, index) + str + b.slice(index);
  33723. }
  33724. this.length = b.length;
  33725. return this; // dojox.string.Builder
  33726. };
  33727. this.toString = function(){
  33728. // summary:
  33729. // Return the string representation of the internal buffer.
  33730. return b; // String
  33731. };
  33732. // initialize the buffer.
  33733. if(str){ this.append(str); }
  33734. };
  33735. }
  33736. if(!dojo._hasResource["dojox.string.tokenize"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  33737. dojo._hasResource["dojox.string.tokenize"] = true;
  33738. dojo.provide("dojox.string.tokenize");
  33739. dojox.string.tokenize = function(/*String*/ str, /*RegExp*/ re, /*Function?*/ parseDelim, /*Object?*/ instance){
  33740. // summary:
  33741. // Split a string by a regular expression with the ability to capture the delimeters
  33742. // parseDelim:
  33743. // Each group (excluding the 0 group) is passed as a parameter. If the function returns
  33744. // a value, it's added to the list of tokens.
  33745. // instance:
  33746. // Used as the "this" instance when calling parseDelim
  33747. var tokens = [];
  33748. var match, content, lastIndex = 0;
  33749. while(match = re.exec(str)){
  33750. content = str.slice(lastIndex, re.lastIndex - match[0].length);
  33751. if(content.length){
  33752. tokens.push(content);
  33753. }
  33754. if(parseDelim){
  33755. if(dojo.isOpera){
  33756. var copy = match.slice(0);
  33757. while(copy.length < match.length){
  33758. copy.push(null);
  33759. }
  33760. match = copy;
  33761. }
  33762. var parsed = parseDelim.apply(instance, match.slice(1).concat(tokens.length));
  33763. if(typeof parsed != "undefined"){
  33764. tokens.push(parsed);
  33765. }
  33766. }
  33767. lastIndex = re.lastIndex;
  33768. }
  33769. content = str.slice(lastIndex);
  33770. if(content.length){
  33771. tokens.push(content);
  33772. }
  33773. return tokens;
  33774. }
  33775. }
  33776. if(!dojo._hasResource["dojox.dtl._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  33777. dojo._hasResource["dojox.dtl._base"] = true;
  33778. dojo.provide("dojox.dtl._base");
  33779. dojo.experimental("dojox.dtl");
  33780. (function(){
  33781. var dd = dojox.dtl;
  33782. dd.TOKEN_BLOCK = -1;
  33783. dd.TOKEN_VAR = -2;
  33784. dd.TOKEN_COMMENT = -3;
  33785. dd.TOKEN_TEXT = 3;
  33786. dd._Context = dojo.extend(function(dict){
  33787. // summary: Pass one of these when rendering a template to tell the template what values to use.
  33788. if(dict){
  33789. dojo._mixin(this, dict);
  33790. if(dict.get){
  33791. // Preserve passed getter and restore prototype get
  33792. this._getter = dict.get;
  33793. delete this.get;
  33794. }
  33795. }
  33796. },
  33797. {
  33798. push: function(){
  33799. var last = this;
  33800. var context = dojo.delegate(this);
  33801. context.pop = function(){ return last; }
  33802. return context;
  33803. },
  33804. pop: function(){
  33805. throw new Error("pop() called on empty Context");
  33806. },
  33807. get: function(key, otherwise){
  33808. var n = this._normalize;
  33809. if(this._getter){
  33810. var got = this._getter(key);
  33811. if(typeof got != "undefined"){
  33812. return n(got);
  33813. }
  33814. }
  33815. if(typeof this[key] != "undefined"){
  33816. return n(this[key]);
  33817. }
  33818. return otherwise;
  33819. },
  33820. _normalize: function(value){
  33821. if(value instanceof Date){
  33822. value.year = value.getFullYear();
  33823. value.month = value.getMonth() + 1;
  33824. value.day = value.getDate();
  33825. value.date = value.year + "-" + ("0" + value.month).slice(-2) + "-" + ("0" + value.day).slice(-2);
  33826. value.hour = value.getHours();
  33827. value.minute = value.getMinutes();
  33828. value.second = value.getSeconds();
  33829. value.microsecond = value.getMilliseconds();
  33830. }
  33831. return value;
  33832. },
  33833. update: function(dict){
  33834. var context = this.push();
  33835. if(dict){
  33836. dojo._mixin(this, dict);
  33837. }
  33838. return context;
  33839. }
  33840. });
  33841. var smart_split_re = /("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+)/g;
  33842. var split_re = /\s+/g;
  33843. var split = function(/*String|RegExp?*/ splitter, /*Integer?*/ limit){
  33844. splitter = splitter || split_re;
  33845. if(!(splitter instanceof RegExp)){
  33846. splitter = new RegExp(splitter, "g");
  33847. }
  33848. if(!splitter.global){
  33849. throw new Error("You must use a globally flagged RegExp with split " + splitter);
  33850. }
  33851. splitter.exec(""); // Reset the global
  33852. var part, parts = [], lastIndex = 0, i = 0;
  33853. while(part = splitter.exec(this)){
  33854. parts.push(this.slice(lastIndex, splitter.lastIndex - part[0].length));
  33855. lastIndex = splitter.lastIndex;
  33856. if(limit && (++i > limit - 1)){
  33857. break;
  33858. }
  33859. }
  33860. parts.push(this.slice(lastIndex));
  33861. return parts;
  33862. }
  33863. dd.Token = function(token_type, contents){
  33864. this.token_type = token_type;
  33865. this.contents = new String(dojo.trim(contents));
  33866. this.contents.split = split;
  33867. this.split = function(){
  33868. return String.prototype.split.apply(this.contents, arguments);
  33869. }
  33870. }
  33871. dd.Token.prototype.split_contents = function(/*Integer?*/ limit){
  33872. var bit, bits = [], i = 0;
  33873. limit = limit || 999;
  33874. while(i++ < limit && (bit = smart_split_re.exec(this.contents))){
  33875. bit = bit[0];
  33876. if(bit.charAt(0) == '"' && bit.slice(-1) == '"'){
  33877. bits.push('"' + bit.slice(1, -1).replace('\\"', '"').replace('\\\\', '\\') + '"');
  33878. }else if(bit.charAt(0) == "'" && bit.slice(-1) == "'"){
  33879. bits.push("'" + bit.slice(1, -1).replace("\\'", "'").replace('\\\\', '\\') + "'");
  33880. }else{
  33881. bits.push(bit);
  33882. }
  33883. }
  33884. return bits;
  33885. }
  33886. var ddt = dd.text = {
  33887. _get: function(module, name, errorless){
  33888. // summary: Used to find both tags and filters
  33889. var params = dd.register.get(module, name.toLowerCase(), errorless);
  33890. if(!params){
  33891. if(!errorless){
  33892. throw new Error("No tag found for " + name);
  33893. }
  33894. return null;
  33895. }
  33896. var fn = params[1];
  33897. var require = params[2];
  33898. var parts;
  33899. if(fn.indexOf(":") != -1){
  33900. parts = fn.split(":");
  33901. fn = parts.pop();
  33902. }
  33903. dojo["require"](require);
  33904. var parent = dojo.getObject(require);
  33905. return parent[fn || name] || parent[name + "_"] || parent[fn + "_"];
  33906. },
  33907. getTag: function(name, errorless){
  33908. return ddt._get("tag", name, errorless);
  33909. },
  33910. getFilter: function(name, errorless){
  33911. return ddt._get("filter", name, errorless);
  33912. },
  33913. getTemplate: function(file){
  33914. return new dd.Template(ddt.getTemplateString(file));
  33915. },
  33916. getTemplateString: function(file){
  33917. return dojo._getText(file.toString()) || "";
  33918. },
  33919. _resolveLazy: function(location, sync, json){
  33920. if(sync){
  33921. if(json){
  33922. return dojo.fromJson(dojo._getText(location)) || {};
  33923. }else{
  33924. return dd.text.getTemplateString(location);
  33925. }
  33926. }else{
  33927. return dojo.xhrGet({
  33928. handleAs: (json) ? "json" : "text",
  33929. url: location
  33930. });
  33931. }
  33932. },
  33933. _resolveTemplateArg: function(arg, sync){
  33934. if(ddt._isTemplate(arg)){
  33935. if(!sync){
  33936. var d = new dojo.Deferred();
  33937. d.callback(arg);
  33938. return d;
  33939. }
  33940. return arg;
  33941. }
  33942. return ddt._resolveLazy(arg, sync);
  33943. },
  33944. _isTemplate: function(arg){
  33945. return (typeof arg == "undefined") || (typeof arg == "string" && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
  33946. },
  33947. _resolveContextArg: function(arg, sync){
  33948. if(arg.constructor == Object){
  33949. if(!sync){
  33950. var d = new dojo.Deferred;
  33951. d.callback(arg);
  33952. return d;
  33953. }
  33954. return arg;
  33955. }
  33956. return ddt._resolveLazy(arg, sync, true);
  33957. },
  33958. _re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
  33959. tokenize: function(str){
  33960. return dojox.string.tokenize(str, ddt._re, ddt._parseDelims);
  33961. },
  33962. _parseDelims: function(varr, load, tag){
  33963. if(varr){
  33964. return [dd.TOKEN_VAR, varr];
  33965. }else if(load){
  33966. var parts = dojo.trim(tag).split(/\s+/g);
  33967. for(var i = 0, part; part = parts[i]; i++){
  33968. dojo["require"](part);
  33969. }
  33970. }else{
  33971. return [dd.TOKEN_BLOCK, tag];
  33972. }
  33973. }
  33974. }
  33975. dd.Template = dojo.extend(function(/*String|dojo._Url*/ template, /*Boolean*/ isString){
  33976. // template:
  33977. // The string or location of the string to
  33978. // use as a template
  33979. var str = isString ? template : ddt._resolveTemplateArg(template, true) || "";
  33980. var tokens = ddt.tokenize(str);
  33981. var parser = new dd._Parser(tokens);
  33982. this.nodelist = parser.parse();
  33983. },
  33984. {
  33985. update: function(node, context){
  33986. // node: DOMNode|String|dojo.NodeList
  33987. // A node reference or set of nodes
  33988. // context: dojo._Url|String|Object
  33989. // The context object or location
  33990. return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
  33991. var content = this.render(new dd._Context(contextObject));
  33992. if(node.forEach){
  33993. node.forEach(function(item){
  33994. item.innerHTML = content;
  33995. });
  33996. }else{
  33997. dojo.byId(node).innerHTML = content;
  33998. }
  33999. return this;
  34000. });
  34001. },
  34002. render: function(context, /*concatenatable?*/ buffer){
  34003. buffer = buffer || this.getBuffer();
  34004. context = context || new dd._Context({});
  34005. return this.nodelist.render(context, buffer) + "";
  34006. },
  34007. getBuffer: function(){
  34008. return new dojox.string.Builder();
  34009. }
  34010. });
  34011. var qfRe = /\{\{\s*(.+?)\s*\}\}/g;
  34012. dd.quickFilter = function(str){
  34013. if(!str){
  34014. return new dd._NodeList();
  34015. }
  34016. if(str.indexOf("{%") == -1){
  34017. return new dd._QuickNodeList(dojox.string.tokenize(str, qfRe, function(token){
  34018. return new dd._Filter(token);
  34019. }));
  34020. }
  34021. }
  34022. dd._QuickNodeList = dojo.extend(function(contents){
  34023. this.contents = contents;
  34024. },
  34025. {
  34026. render: function(context, buffer){
  34027. for(var i=0, l=this.contents.length; i<l; i++){
  34028. if(this.contents[i].resolve){
  34029. buffer = buffer.concat(this.contents[i].resolve(context));
  34030. }else{
  34031. buffer = buffer.concat(this.contents[i]);
  34032. }
  34033. }
  34034. return buffer;
  34035. },
  34036. dummyRender: function(context){ return this.render(context, dd.Template.prototype.getBuffer()).toString(); },
  34037. clone: function(buffer){ return this; }
  34038. });
  34039. dd._Filter = dojo.extend(function(token){
  34040. // summary: Uses a string to find (and manipulate) a variable
  34041. if(!token) throw new Error("Filter must be called with variable name");
  34042. this.contents = token;
  34043. var cache = this._cache[token];
  34044. if(cache){
  34045. this.key = cache[0];
  34046. this.filters = cache[1];
  34047. }else{
  34048. this.filters = [];
  34049. dojox.string.tokenize(token, this._re, this._tokenize, this);
  34050. this._cache[token] = [this.key, this.filters];
  34051. }
  34052. },
  34053. {
  34054. _cache: {},
  34055. _re: /(?:^_\("([^\\"]*(?:\\.[^\\"])*)"\)|^"([^\\"]*(?:\\.[^\\"]*)*)"|^([a-zA-Z0-9_.]+)|\|(\w+)(?::(?:_\("([^\\"]*(?:\\.[^\\"])*)"\)|"([^\\"]*(?:\\.[^\\"]*)*)"|([a-zA-Z0-9_.]+)|'([^\\']*(?:\\.[^\\']*)*)'))?|^'([^\\']*(?:\\.[^\\']*)*)')/g,
  34056. _values: {
  34057. 0: '"', // _("text")
  34058. 1: '"', // "text"
  34059. 2: "", // variable
  34060. 8: '"' // 'text'
  34061. },
  34062. _args: {
  34063. 4: '"', // :_("text")
  34064. 5: '"', // :"text"
  34065. 6: "", // :variable
  34066. 7: "'"// :'text'
  34067. },
  34068. _tokenize: function(){
  34069. var pos, arg;
  34070. for(var i = 0, has = []; i < arguments.length; i++){
  34071. has[i] = (typeof arguments[i] != "undefined" && typeof arguments[i] == "string" && arguments[i]);
  34072. }
  34073. if(!this.key){
  34074. for(pos in this._values){
  34075. if(has[pos]){
  34076. this.key = this._values[pos] + arguments[pos] + this._values[pos];
  34077. break;
  34078. }
  34079. }
  34080. }else{
  34081. for(pos in this._args){
  34082. if(has[pos]){
  34083. var value = arguments[pos];
  34084. if(this._args[pos] == "'"){
  34085. value = value.replace(/\\'/g, "'");
  34086. }else if(this._args[pos] == '"'){
  34087. value = value.replace(/\\"/g, '"');
  34088. }
  34089. arg = [!this._args[pos], value];
  34090. break;
  34091. }
  34092. }
  34093. // Get a named filter
  34094. var fn = ddt.getFilter(arguments[3]);
  34095. if(!dojo.isFunction(fn)) throw new Error(arguments[3] + " is not registered as a filter");
  34096. this.filters.push([fn, arg]);
  34097. }
  34098. },
  34099. getExpression: function(){
  34100. return this.contents;
  34101. },
  34102. resolve: function(context){
  34103. if(typeof this.key == "undefined"){
  34104. return "";
  34105. }
  34106. var str = this.resolvePath(this.key, context);
  34107. for(var i = 0, filter; filter = this.filters[i]; i++){
  34108. // Each filter has the function in [0], a boolean in [1][0] of whether it's a variable or a string
  34109. // and [1][1] is either the variable name of the string content.
  34110. if(filter[1]){
  34111. if(filter[1][0]){
  34112. str = filter[0](str, this.resolvePath(filter[1][1], context));
  34113. }else{
  34114. str = filter[0](str, filter[1][1]);
  34115. }
  34116. }else{
  34117. str = filter[0](str);
  34118. }
  34119. }
  34120. return str;
  34121. },
  34122. resolvePath: function(path, context){
  34123. var current, parts;
  34124. var first = path.charAt(0);
  34125. var last = path.slice(-1);
  34126. if(!isNaN(parseInt(first))){
  34127. current = (path.indexOf(".") == -1) ? parseInt(path) : parseFloat(path);
  34128. }else if(first == '"' && first == last){
  34129. current = path.slice(1, -1);
  34130. }else{
  34131. if(path == "true"){ return true; }
  34132. if(path == "false"){ return false; }
  34133. if(path == "null" || path == "None"){ return null; }
  34134. parts = path.split(".");
  34135. current = context.get(parts[0]);
  34136. if(dojo.isFunction(current)){
  34137. var self = context.getThis && context.getThis();
  34138. if(current.alters_data){
  34139. current = "";
  34140. }else if(self){
  34141. current = current.call(self);
  34142. }else{
  34143. current = "";
  34144. }
  34145. }
  34146. for(var i = 1; i < parts.length; i++){
  34147. var part = parts[i];
  34148. if(current){
  34149. var base = current;
  34150. if(dojo.isObject(current) && part == "items" && typeof current[part] == "undefined"){
  34151. var items = [];
  34152. for(var key in current){
  34153. items.push([key, current[key]]);
  34154. }
  34155. current = items;
  34156. continue;
  34157. }
  34158. if(current.get && dojo.isFunction(current.get) && current.get.safe){
  34159. current = current.get(part);
  34160. }else if(typeof current[part] == "undefined"){
  34161. current = current[part];
  34162. break;
  34163. }else{
  34164. current = current[part];
  34165. }
  34166. if(dojo.isFunction(current)){
  34167. if(current.alters_data){
  34168. current = "";
  34169. }else{
  34170. current = current.call(base);
  34171. }
  34172. }else if(current instanceof Date){
  34173. current = dd._Context.prototype._normalize(current);
  34174. }
  34175. }else{
  34176. return "";
  34177. }
  34178. }
  34179. }
  34180. return current;
  34181. }
  34182. });
  34183. dd._TextNode = dd._Node = dojo.extend(function(/*Object*/ obj){
  34184. // summary: Basic catch-all node
  34185. this.contents = obj;
  34186. },
  34187. {
  34188. set: function(data){
  34189. this.contents = data;
  34190. return this;
  34191. },
  34192. render: function(context, buffer){
  34193. // summary: Adds content onto the buffer
  34194. return buffer.concat(this.contents);
  34195. },
  34196. isEmpty: function(){
  34197. return !dojo.trim(this.contents);
  34198. },
  34199. clone: function(){ return this; }
  34200. });
  34201. dd._NodeList = dojo.extend(function(/*Node[]*/ nodes){
  34202. // summary: Allows us to render a group of nodes
  34203. this.contents = nodes || [];
  34204. this.last = "";
  34205. },
  34206. {
  34207. push: function(node){
  34208. // summary: Add a new node to the list
  34209. this.contents.push(node);
  34210. return this;
  34211. },
  34212. concat: function(nodes){
  34213. this.contents = this.contents.concat(nodes);
  34214. return this;
  34215. },
  34216. render: function(context, buffer){
  34217. // summary: Adds all content onto the buffer
  34218. for(var i = 0; i < this.contents.length; i++){
  34219. buffer = this.contents[i].render(context, buffer);
  34220. if(!buffer) throw new Error("Template must return buffer");
  34221. }
  34222. return buffer;
  34223. },
  34224. dummyRender: function(context){
  34225. return this.render(context, dd.Template.prototype.getBuffer()).toString();
  34226. },
  34227. unrender: function(){ return arguments[1]; },
  34228. clone: function(){ return this; },
  34229. rtrim: function(){
  34230. while(1){
  34231. i = this.contents.length - 1;
  34232. if(this.contents[i] instanceof dd._TextNode && this.contents[i].isEmpty()){
  34233. this.contents.pop();
  34234. }else{
  34235. break;
  34236. }
  34237. }
  34238. return this;
  34239. }
  34240. });
  34241. dd._VarNode = dojo.extend(function(str){
  34242. // summary: A node to be processed as a variable
  34243. this.contents = new dd._Filter(str);
  34244. },
  34245. {
  34246. render: function(context, buffer){
  34247. var str = this.contents.resolve(context);
  34248. if(!str.safe){
  34249. str = dd._base.escape("" + str);
  34250. }
  34251. return buffer.concat(str);
  34252. }
  34253. });
  34254. dd._noOpNode = new function(){
  34255. // summary: Adds a no-op node. Useful in custom tags
  34256. this.render = this.unrender = function(){ return arguments[1]; }
  34257. this.clone = function(){ return this; }
  34258. }
  34259. dd._Parser = dojo.extend(function(tokens){
  34260. // summary: Parser used during initialization and for tag groups.
  34261. this.contents = tokens;
  34262. },
  34263. {
  34264. i: 0,
  34265. parse: function(/*Array?*/ stop_at){
  34266. // summary: Turns tokens into nodes
  34267. // description: Steps into tags are they're found. Blocks use the parse object
  34268. // to find their closing tag (the stop_at array). stop_at is inclusive, it
  34269. // returns the node that matched.
  34270. var terminators = {}, token;
  34271. stop_at = stop_at || [];
  34272. for(var i = 0; i < stop_at.length; i++){
  34273. terminators[stop_at[i]] = true;
  34274. }
  34275. var nodelist = new dd._NodeList();
  34276. while(this.i < this.contents.length){
  34277. token = this.contents[this.i++];
  34278. if(typeof token == "string"){
  34279. nodelist.push(new dd._TextNode(token));
  34280. }else{
  34281. var type = token[0];
  34282. var text = token[1];
  34283. if(type == dd.TOKEN_VAR){
  34284. nodelist.push(new dd._VarNode(text));
  34285. }else if(type == dd.TOKEN_BLOCK){
  34286. if(terminators[text]){
  34287. --this.i;
  34288. return nodelist;
  34289. }
  34290. var cmd = text.split(/\s+/g);
  34291. if(cmd.length){
  34292. cmd = cmd[0];
  34293. var fn = ddt.getTag(cmd);
  34294. if(fn){
  34295. nodelist.push(fn(this, new dd.Token(type, text)));
  34296. }
  34297. }
  34298. }
  34299. }
  34300. }
  34301. if(stop_at.length){
  34302. throw new Error("Could not find closing tag(s): " + stop_at.toString());
  34303. }
  34304. this.contents.length = 0;
  34305. return nodelist;
  34306. },
  34307. next_token: function(){
  34308. // summary: Returns the next token in the list.
  34309. var token = this.contents[this.i++];
  34310. return new dd.Token(token[0], token[1]);
  34311. },
  34312. delete_first_token: function(){
  34313. this.i++;
  34314. },
  34315. skip_past: function(endtag){
  34316. while(this.i < this.contents.length){
  34317. var token = this.contents[this.i++];
  34318. if(token[0] == dd.TOKEN_BLOCK && token[1] == endtag){
  34319. return;
  34320. }
  34321. }
  34322. throw new Error("Unclosed tag found when looking for " + endtag);
  34323. },
  34324. create_variable_node: function(expr){
  34325. return new dd._VarNode(expr);
  34326. },
  34327. create_text_node: function(expr){
  34328. return new dd._TextNode(expr || "");
  34329. },
  34330. getTemplate: function(file){
  34331. return new dd.Template(file);
  34332. }
  34333. });
  34334. dd.register = {
  34335. _registry: {
  34336. attributes: [],
  34337. tags: [],
  34338. filters: []
  34339. },
  34340. get: function(/*String*/ module, /*String*/ name){
  34341. var registry = dd.register._registry[module + "s"];
  34342. for(var i = 0, entry; entry = registry[i]; i++){
  34343. if(typeof entry[0] == "string"){
  34344. if(entry[0] == name){
  34345. return entry;
  34346. }
  34347. }else if(name.match(entry[0])){
  34348. return entry;
  34349. }
  34350. }
  34351. },
  34352. getAttributeTags: function(){
  34353. var tags = [];
  34354. var registry = dd.register._registry.attributes;
  34355. for(var i = 0, entry; entry = registry[i]; i++){
  34356. if(entry.length == 3){
  34357. tags.push(entry);
  34358. }else{
  34359. var fn = dojo.getObject(entry[1]);
  34360. if(fn && dojo.isFunction(fn)){
  34361. entry.push(fn);
  34362. tags.push(entry);
  34363. }
  34364. }
  34365. }
  34366. return tags;
  34367. },
  34368. _any: function(type, base, locations){
  34369. for(var path in locations){
  34370. for(var i = 0, fn; fn = locations[path][i]; i++){
  34371. var key = fn;
  34372. if(dojo.isArray(fn)){
  34373. key = fn[0];
  34374. fn = fn[1];
  34375. }
  34376. if(typeof key == "string"){
  34377. if(key.substr(0, 5) == "attr:"){
  34378. var attr = fn;
  34379. if(attr.substr(0, 5) == "attr:"){
  34380. attr = attr.slice(5);
  34381. }
  34382. dd.register._registry.attributes.push([attr.toLowerCase(), base + "." + path + "." + attr]);
  34383. }
  34384. key = key.toLowerCase()
  34385. }
  34386. dd.register._registry[type].push([
  34387. key,
  34388. fn,
  34389. base + "." + path
  34390. ]);
  34391. }
  34392. }
  34393. },
  34394. tags: function(/*String*/ base, /*Object*/ locations){
  34395. dd.register._any("tags", base, locations);
  34396. },
  34397. filters: function(/*String*/ base, /*Object*/ locations){
  34398. dd.register._any("filters", base, locations);
  34399. }
  34400. }
  34401. var escapeamp = /&/g;
  34402. var escapelt = /</g;
  34403. var escapegt = />/g;
  34404. var escapeqt = /'/g;
  34405. var escapedblqt = /"/g;
  34406. dd._base.escape = function(value){
  34407. // summary: Escapes a string's HTML
  34408. return dd.mark_safe(value.replace(escapeamp, '&amp;').replace(escapelt, '&lt;').replace(escapegt, '&gt;').replace(escapedblqt, '&quot;').replace(escapeqt, '&#39;'));
  34409. }
  34410. dd._base.safe = function(value){
  34411. if(typeof value == "string"){
  34412. value = new String(value);
  34413. }
  34414. if(typeof value == "object"){
  34415. value.safe = true;
  34416. }
  34417. return value;
  34418. }
  34419. dd.mark_safe = dd._base.safe;
  34420. dd.register.tags("dojox.dtl.tag", {
  34421. "date": ["now"],
  34422. "logic": ["if", "for", "ifequal", "ifnotequal"],
  34423. "loader": ["extends", "block", "include", "load", "ssi"],
  34424. "misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
  34425. "loop": ["cycle", "ifchanged", "regroup"]
  34426. });
  34427. dd.register.filters("dojox.dtl.filter", {
  34428. "dates": ["date", "time", "timesince", "timeuntil"],
  34429. "htmlstrings": ["linebreaks", "linebreaksbr", "removetags", "striptags"],
  34430. "integers": ["add", "get_digit"],
  34431. "lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
  34432. "logic": ["default", "default_if_none", "divisibleby", "yesno"],
  34433. "misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
  34434. "strings": ["addslashes", "capfirst", "center", "cut", "fix_ampersands", "floatformat", "iriencode", "linenumbers", "ljust", "lower", "make_list", "rjust", "slugify", "stringformat", "title", "truncatewords", "truncatewords_html", "upper", "urlencode", "urlize", "urlizetrunc", "wordcount", "wordwrap"]
  34435. });
  34436. dd.register.filters("dojox.dtl", {
  34437. "_base": ["escape", "safe"]
  34438. });
  34439. })();
  34440. }
  34441. if(!dojo._hasResource["dojox.dtl.filter.htmlstrings"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  34442. dojo._hasResource["dojox.dtl.filter.htmlstrings"] = true;
  34443. dojo.provide("dojox.dtl.filter.htmlstrings");
  34444. dojo.mixin(dojox.dtl.filter.htmlstrings, {
  34445. _linebreaksrn: /(\r\n|\n\r)/g,
  34446. _linebreaksn: /\n{2,}/g,
  34447. _linebreakss: /(^\s+|\s+$)/g,
  34448. _linebreaksbr: /\n/g,
  34449. _removetagsfind: /[a-z0-9]+/g,
  34450. _striptags: /<[^>]*?>/g,
  34451. linebreaks: function(value){
  34452. // summary: Converts newlines into <p> and <br />s
  34453. var output = [];
  34454. var dh = dojox.dtl.filter.htmlstrings;
  34455. value = value.replace(dh._linebreaksrn, "\n");
  34456. var parts = value.split(dh._linebreaksn);
  34457. for(var i = 0; i < parts.length; i++){
  34458. var part = parts[i].replace(dh._linebreakss, "").replace(dh._linebreaksbr, "<br />");
  34459. output.push("<p>" + part + "</p>");
  34460. }
  34461. return output.join("\n\n");
  34462. },
  34463. linebreaksbr: function(value){
  34464. // summary: Converts newlines into <br />s
  34465. var dh = dojox.dtl.filter.htmlstrings;
  34466. return value.replace(dh._linebreaksrn, "\n").replace(dh._linebreaksbr, "<br />");
  34467. },
  34468. removetags: function(value, arg){
  34469. // summary: Removes a space separated list of [X]HTML tags from the output"
  34470. var dh = dojox.dtl.filter.htmlstrings;
  34471. var tags = [];
  34472. var group;
  34473. while(group = dh._removetagsfind.exec(arg)){
  34474. tags.push(group[0]);
  34475. }
  34476. tags = "(" + tags.join("|") + ")";
  34477. return value.replace(new RegExp("</?\s*" + tags + "\s*[^>]*>", "gi"), "");
  34478. },
  34479. striptags: function(value){
  34480. // summary: Strips all [X]HTML tags
  34481. return value.replace(dojox.dtl.filter.htmlstrings._striptags, "");
  34482. }
  34483. });
  34484. }
  34485. if(!dojo._hasResource["dojox.string.sprintf"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  34486. dojo._hasResource["dojox.string.sprintf"] = true;
  34487. dojo.provide("dojox.string.sprintf");
  34488. dojox.string.sprintf = function(/*String*/ format, /*mixed...*/ filler){
  34489. for(var args = [], i = 1; i < arguments.length; i++){
  34490. args.push(arguments[i]);
  34491. }
  34492. var formatter = new dojox.string.sprintf.Formatter(format);
  34493. return formatter.format.apply(formatter, args);
  34494. }
  34495. dojox.string.sprintf.Formatter = function(/*String*/ format){
  34496. var tokens = [];
  34497. this._mapped = false;
  34498. this._format = format;
  34499. this._tokens = dojox.string.tokenize(format, this._re, this._parseDelim, this);
  34500. }
  34501. dojo.extend(dojox.string.sprintf.Formatter, {
  34502. _re: /\%(?:\(([\w_]+)\)|([1-9]\d*)\$)?([0 +\-\#]*)(\*|\d+)?(\.)?(\*|\d+)?[hlL]?([\%scdeEfFgGiouxX])/g,
  34503. _parseDelim: function(mapping, intmapping, flags, minWidth, period, precision, specifier){
  34504. if(mapping){
  34505. this._mapped = true;
  34506. }
  34507. return {
  34508. mapping: mapping,
  34509. intmapping: intmapping,
  34510. flags: flags,
  34511. _minWidth: minWidth, // May be dependent on parameters
  34512. period: period,
  34513. _precision: precision, // May be dependent on parameters
  34514. specifier: specifier
  34515. };
  34516. },
  34517. _specifiers: {
  34518. b: {
  34519. base: 2,
  34520. isInt: true
  34521. },
  34522. o: {
  34523. base: 8,
  34524. isInt: true
  34525. },
  34526. x: {
  34527. base: 16,
  34528. isInt: true
  34529. },
  34530. X: {
  34531. extend: ["x"],
  34532. toUpper: true
  34533. },
  34534. d: {
  34535. base: 10,
  34536. isInt: true
  34537. },
  34538. i: {
  34539. extend: ["d"]
  34540. },
  34541. u: {
  34542. extend: ["d"],
  34543. isUnsigned: true
  34544. },
  34545. c: {
  34546. setArg: function(token){
  34547. if(!isNaN(token.arg)){
  34548. var num = parseInt(token.arg);
  34549. if(num < 0 || num > 127){
  34550. throw new Error("invalid character code passed to %c in sprintf");
  34551. }
  34552. token.arg = isNaN(num) ? "" + num : String.fromCharCode(num);
  34553. }
  34554. }
  34555. },
  34556. s: {
  34557. setMaxWidth: function(token){
  34558. token.maxWidth = (token.period == ".") ? token.precision : -1;
  34559. }
  34560. },
  34561. e: {
  34562. isDouble: true,
  34563. doubleNotation: "e"
  34564. },
  34565. E: {
  34566. extend: ["e"],
  34567. toUpper: true
  34568. },
  34569. f: {
  34570. isDouble: true,
  34571. doubleNotation: "f"
  34572. },
  34573. F: {
  34574. extend: ["f"]
  34575. },
  34576. g: {
  34577. isDouble: true,
  34578. doubleNotation: "g"
  34579. },
  34580. G: {
  34581. extend: ["g"],
  34582. toUpper: true
  34583. }
  34584. },
  34585. format: function(/*mixed...*/ filler){
  34586. if(this._mapped && typeof filler != "object"){
  34587. throw new Error("format requires a mapping");
  34588. }
  34589. var str = "";
  34590. var position = 0;
  34591. for(var i = 0, token; i < this._tokens.length; i++){
  34592. token = this._tokens[i];
  34593. if(typeof token == "string"){
  34594. str += token;
  34595. }else{
  34596. if(this._mapped){
  34597. if(typeof filler[token.mapping] == "undefined"){
  34598. throw new Error("missing key " + token.mapping);
  34599. }
  34600. token.arg = filler[token.mapping];
  34601. }else{
  34602. if(token.intmapping){
  34603. var position = parseInt(token.intmapping) - 1;
  34604. }
  34605. if(position >= arguments.length){
  34606. throw new Error("got " + arguments.length + " printf arguments, insufficient for '" + this._format + "'");
  34607. }
  34608. token.arg = arguments[position++];
  34609. }
  34610. if(!token.compiled){
  34611. token.compiled = true;
  34612. token.sign = "";
  34613. token.zeroPad = false;
  34614. token.rightJustify = false;
  34615. token.alternative = false;
  34616. var flags = {};
  34617. for(var fi = token.flags.length; fi--;){
  34618. var flag = token.flags.charAt(fi);
  34619. flags[flag] = true;
  34620. switch(flag){
  34621. case " ":
  34622. token.sign = " ";
  34623. break;
  34624. case "+":
  34625. token.sign = "+";
  34626. break;
  34627. case "0":
  34628. token.zeroPad = (flags["-"]) ? false : true;
  34629. break;
  34630. case "-":
  34631. token.rightJustify = true;
  34632. token.zeroPad = false;
  34633. break;
  34634. case "\#":
  34635. token.alternative = true;
  34636. break;
  34637. default:
  34638. throw Error("bad formatting flag '" + token.flags.charAt(fi) + "'");
  34639. }
  34640. }
  34641. token.minWidth = (token._minWidth) ? parseInt(token._minWidth) : 0;
  34642. token.maxWidth = -1;
  34643. token.toUpper = false;
  34644. token.isUnsigned = false;
  34645. token.isInt = false;
  34646. token.isDouble = false;
  34647. token.precision = 1;
  34648. if(token.period == '.'){
  34649. if(token._precision){
  34650. token.precision = parseInt(token._precision);
  34651. }else{
  34652. token.precision = 0;
  34653. }
  34654. }
  34655. var mixins = this._specifiers[token.specifier];
  34656. if(typeof mixins == "undefined"){
  34657. throw new Error("unexpected specifier '" + token.specifier + "'");
  34658. }
  34659. if(mixins.extend){
  34660. dojo.mixin(mixins, this._specifiers[mixins.extend]);
  34661. delete mixins.extend;
  34662. }
  34663. dojo.mixin(token, mixins);
  34664. }
  34665. if(typeof token.setArg == "function"){
  34666. token.setArg(token);
  34667. }
  34668. if(typeof token.setMaxWidth == "function"){
  34669. token.setMaxWidth(token);
  34670. }
  34671. if(token._minWidth == "*"){
  34672. if(this._mapped){
  34673. throw new Error("* width not supported in mapped formats");
  34674. }
  34675. token.minWidth = parseInt(arguments[position++]);
  34676. if(isNaN(token.minWidth)){
  34677. throw new Error("the argument for * width at position " + position + " is not a number in " + this._format);
  34678. }
  34679. // negative width means rightJustify
  34680. if (token.minWidth < 0) {
  34681. token.rightJustify = true;
  34682. token.minWidth = -token.minWidth;
  34683. }
  34684. }
  34685. if(token._precision == "*" && token.period == "."){
  34686. if(this._mapped){
  34687. throw new Error("* precision not supported in mapped formats");
  34688. }
  34689. token.precision = parseInt(arguments[position++]);
  34690. if(isNaN(token.precision)){
  34691. throw Error("the argument for * precision at position " + position + " is not a number in " + this._format);
  34692. }
  34693. // negative precision means unspecified
  34694. if (token.precision < 0) {
  34695. token.precision = 1;
  34696. token.period = '';
  34697. }
  34698. }
  34699. if(token.isInt){
  34700. // a specified precision means no zero padding
  34701. if(token.period == '.'){
  34702. token.zeroPad = false;
  34703. }
  34704. this.formatInt(token);
  34705. }else if(token.isDouble){
  34706. if(token.period != '.'){
  34707. token.precision = 6;
  34708. }
  34709. this.formatDouble(token);
  34710. }
  34711. this.fitField(token);
  34712. str += "" + token.arg;
  34713. }
  34714. }
  34715. return str;
  34716. },
  34717. _zeros10: '0000000000',
  34718. _spaces10: ' ',
  34719. formatInt: function(token) {
  34720. var i = parseInt(token.arg);
  34721. if(!isFinite(i)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
  34722. // allow this only if arg is number
  34723. if(typeof token.arg != "number"){
  34724. throw new Error("format argument '" + token.arg + "' not an integer; parseInt returned " + i);
  34725. }
  34726. //return '' + i;
  34727. i = 0;
  34728. }
  34729. // if not base 10, make negatives be positive
  34730. // otherwise, (-10).toString(16) is '-a' instead of 'fffffff6'
  34731. if(i < 0 && (token.isUnsigned || token.base != 10)){
  34732. i = 0xffffffff + i + 1;
  34733. }
  34734. if(i < 0){
  34735. token.arg = (- i).toString(token.base);
  34736. this.zeroPad(token);
  34737. token.arg = "-" + token.arg;
  34738. }else{
  34739. token.arg = i.toString(token.base);
  34740. // need to make sure that argument 0 with precision==0 is formatted as ''
  34741. if(!i && !token.precision){
  34742. token.arg = "";
  34743. }else{
  34744. this.zeroPad(token);
  34745. }
  34746. if(token.sign){
  34747. token.arg = token.sign + token.arg;
  34748. }
  34749. }
  34750. if(token.base == 16){
  34751. if(token.alternative){
  34752. token.arg = '0x' + token.arg;
  34753. }
  34754. token.arg = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
  34755. }
  34756. if(token.base == 8){
  34757. if(token.alternative && token.arg.charAt(0) != '0'){
  34758. token.arg = '0' + token.arg;
  34759. }
  34760. }
  34761. },
  34762. formatDouble: function(token) {
  34763. var f = parseFloat(token.arg);
  34764. if(!isFinite(f)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
  34765. // allow this only if arg is number
  34766. if(typeof token.arg != "number"){
  34767. throw new Error("format argument '" + token.arg + "' not a float; parseFloat returned " + f);
  34768. }
  34769. // C99 says that for 'f':
  34770. // infinity -> '[-]inf' or '[-]infinity' ('[-]INF' or '[-]INFINITY' for 'F')
  34771. // NaN -> a string starting with 'nan' ('NAN' for 'F')
  34772. // this is not commonly implemented though.
  34773. //return '' + f;
  34774. f = 0;
  34775. }
  34776. switch(token.doubleNotation) {
  34777. case 'e': {
  34778. token.arg = f.toExponential(token.precision);
  34779. break;
  34780. }
  34781. case 'f': {
  34782. token.arg = f.toFixed(token.precision);
  34783. break;
  34784. }
  34785. case 'g': {
  34786. // C says use 'e' notation if exponent is < -4 or is >= prec
  34787. // ECMAScript for toPrecision says use exponential notation if exponent is >= prec,
  34788. // though step 17 of toPrecision indicates a test for < -6 to force exponential.
  34789. if(Math.abs(f) < 0.0001){
  34790. //print("forcing exponential notation for f=" + f);
  34791. token.arg = f.toExponential(token.precision > 0 ? token.precision - 1 : token.precision);
  34792. }else{
  34793. token.arg = f.toPrecision(token.precision);
  34794. }
  34795. // In C, unlike 'f', 'gG' removes trailing 0s from fractional part, unless alternative format flag ("#").
  34796. // But ECMAScript formats toPrecision as 0.00100000. So remove trailing 0s.
  34797. if(!token.alternative){
  34798. //print("replacing trailing 0 in '" + s + "'");
  34799. token.arg = token.arg.replace(/(\..*[^0])0*/, "$1");
  34800. // if fractional part is entirely 0, remove it and decimal point
  34801. token.arg = token.arg.replace(/\.0*e/, 'e').replace(/\.0$/,'');
  34802. }
  34803. break;
  34804. }
  34805. default: throw new Error("unexpected double notation '" + token.doubleNotation + "'");
  34806. }
  34807. // C says that exponent must have at least two digits.
  34808. // But ECMAScript does not; toExponential results in things like "1.000000e-8" and "1.000000e+8".
  34809. // Note that s.replace(/e([\+\-])(\d)/, "e$10$2") won't work because of the "$10" instead of "$1".
  34810. // And replace(re, func) isn't supported on IE50 or Safari1.
  34811. token.arg = token.arg.replace(/e\+(\d)$/, "e+0$1").replace(/e\-(\d)$/, "e-0$1");
  34812. // Ensure a '0' before the period.
  34813. // Opera implements (0.001).toString() as '0.001', but (0.001).toFixed(1) is '.001'
  34814. if(dojo.isOpera){
  34815. token.arg = token.arg.replace(/^\./, '0.');
  34816. }
  34817. // if alt, ensure a decimal point
  34818. if(token.alternative){
  34819. token.arg = token.arg.replace(/^(\d+)$/,"$1.");
  34820. token.arg = token.arg.replace(/^(\d+)e/,"$1.e");
  34821. }
  34822. if(f >= 0 && token.sign){
  34823. token.arg = token.sign + token.arg;
  34824. }
  34825. token.arg = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
  34826. },
  34827. zeroPad: function(token, /*Int*/ length) {
  34828. length = (arguments.length == 2) ? length : token.precision;
  34829. if(typeof token.arg != "string"){
  34830. token.arg = "" + token.arg;
  34831. }
  34832. var tenless = length - 10;
  34833. while(token.arg.length < tenless){
  34834. token.arg = (token.rightJustify) ? token.arg + this._zeros10 : this._zeros10 + token.arg;
  34835. }
  34836. var pad = length - token.arg.length;
  34837. token.arg = (token.rightJustify) ? token.arg + this._zeros10.substring(0, pad) : this._zeros10.substring(0, pad) + token.arg;
  34838. },
  34839. fitField: function(token) {
  34840. if(token.maxWidth >= 0 && token.arg.length > token.maxWidth){
  34841. return token.arg.substring(0, token.maxWidth);
  34842. }
  34843. if(token.zeroPad){
  34844. this.zeroPad(token, token.minWidth);
  34845. return;
  34846. }
  34847. this.spacePad(token);
  34848. },
  34849. spacePad: function(token, /*Int*/ length) {
  34850. length = (arguments.length == 2) ? length : token.minWidth;
  34851. if(typeof token.arg != 'string'){
  34852. token.arg = '' + token.arg;
  34853. }
  34854. var tenless = length - 10;
  34855. while(token.arg.length < tenless){
  34856. token.arg = (token.rightJustify) ? token.arg + this._spaces10 : this._spaces10 + token.arg;
  34857. }
  34858. var pad = length - token.arg.length;
  34859. token.arg = (token.rightJustify) ? token.arg + this._spaces10.substring(0, pad) : this._spaces10.substring(0, pad) + token.arg;
  34860. }
  34861. });
  34862. }
  34863. if(!dojo._hasResource["dojox.dtl.filter.strings"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  34864. dojo._hasResource["dojox.dtl.filter.strings"] = true;
  34865. dojo.provide("dojox.dtl.filter.strings");
  34866. dojo.mixin(dojox.dtl.filter.strings, {
  34867. _urlquote: function(/*String*/ url, /*String?*/ safe){
  34868. if(!safe){
  34869. safe = "/";
  34870. }
  34871. return dojox.string.tokenize(url, /([^\w-_.])/g, function(token){
  34872. if(safe.indexOf(token) == -1){
  34873. if(token == " "){
  34874. return "+";
  34875. }else{
  34876. return "%" + token.charCodeAt(0).toString(16).toUpperCase();
  34877. }
  34878. }
  34879. return token;
  34880. }).join("");
  34881. },
  34882. addslashes: function(value){
  34883. // summary: Adds slashes - useful for passing strings to JavaScript, for example.
  34884. return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'");
  34885. },
  34886. capfirst: function(value){
  34887. // summary: Capitalizes the first character of the value
  34888. value = "" + value;
  34889. return value.charAt(0).toUpperCase() + value.substring(1);
  34890. },
  34891. center: function(value, arg){
  34892. // summary: Centers the value in a field of a given width
  34893. arg = arg || value.length;
  34894. value = value + "";
  34895. var diff = arg - value.length;
  34896. if(diff % 2){
  34897. value = value + " ";
  34898. diff -= 1;
  34899. }
  34900. for(var i = 0; i < diff; i += 2){
  34901. value = " " + value + " ";
  34902. }
  34903. return value;
  34904. },
  34905. cut: function(value, arg){
  34906. // summary: Removes all values of arg from the given string
  34907. arg = arg + "" || "";
  34908. value = value + "";
  34909. return value.replace(new RegExp(arg, "g"), "");
  34910. },
  34911. _fix_ampersands: /&(?!(\w+|#\d+);)/g,
  34912. fix_ampersands: function(value){
  34913. // summary: Replaces ampersands with ``&amp;`` entities
  34914. return value.replace(dojox.dtl.filter.strings._fix_ampersands, "&amp;");
  34915. },
  34916. floatformat: function(value, arg){
  34917. // summary: Format a number according to arg
  34918. // description:
  34919. // If called without an argument, displays a floating point
  34920. // number as 34.2 -- but only if there's a point to be displayed.
  34921. // With a positive numeric argument, it displays that many decimal places
  34922. // always.
  34923. // With a negative numeric argument, it will display that many decimal
  34924. // places -- but only if there's places to be displayed.
  34925. arg = parseInt(arg || -1, 10);
  34926. value = parseFloat(value);
  34927. var m = value - value.toFixed(0);
  34928. if(!m && arg < 0){
  34929. return value.toFixed();
  34930. }
  34931. value = value.toFixed(Math.abs(arg));
  34932. return (arg < 0) ? parseFloat(value) + "" : value;
  34933. },
  34934. iriencode: function(value){
  34935. return dojox.dtl.filter.strings._urlquote(value, "/#%[]=:;$&()+,!");
  34936. },
  34937. linenumbers: function(value){
  34938. // summary: Displays text with line numbers
  34939. var df = dojox.dtl.filter;
  34940. var lines = value.split("\n");
  34941. var output = [];
  34942. var width = (lines.length + "").length;
  34943. for(var i = 0, line; i < lines.length; i++){
  34944. line = lines[i];
  34945. output.push(df.strings.ljust(i + 1, width) + ". " + dojox.dtl._base.escape(line));
  34946. }
  34947. return output.join("\n");
  34948. },
  34949. ljust: function(value, arg){
  34950. value = value + "";
  34951. arg = parseInt(arg, 10);
  34952. while(value.length < arg){
  34953. value = value + " ";
  34954. }
  34955. return value;
  34956. },
  34957. lower: function(value){
  34958. // summary: Converts a string into all lowercase
  34959. return (value + "").toLowerCase();
  34960. },
  34961. make_list: function(value){
  34962. // summary:
  34963. // Returns the value turned into a list. For an integer, it's a list of
  34964. // digits. For a string, it's a list of characters.
  34965. var output = [];
  34966. if(typeof value == "number"){
  34967. value = value + "";
  34968. }
  34969. if(value.charAt){
  34970. for(var i = 0; i < value.length; i++){
  34971. output.push(value.charAt(i));
  34972. }
  34973. return output;
  34974. }
  34975. if(typeof value == "object"){
  34976. for(var key in value){
  34977. output.push(value[key]);
  34978. }
  34979. return output;
  34980. }
  34981. return [];
  34982. },
  34983. rjust: function(value, arg){
  34984. value = value + "";
  34985. arg = parseInt(arg, 10);
  34986. while(value.length < arg){
  34987. value = " " + value;
  34988. }
  34989. return value;
  34990. },
  34991. slugify: function(value){
  34992. // summary: Converts to lowercase, removes
  34993. // non-alpha chars and converts spaces to hyphens
  34994. value = value.replace(/[^\w\s-]/g, "").toLowerCase();
  34995. return value.replace(/[\-\s]+/g, "-");
  34996. },
  34997. _strings: {},
  34998. stringformat: function(value, arg){
  34999. // summary:
  35000. // Formats the variable according to the argument, a string formatting specifier.
  35001. // This specifier uses Python string formating syntax, with the exception that
  35002. // the leading "%" is dropped.
  35003. arg = "" + arg;
  35004. var strings = dojox.dtl.filter.strings._strings;
  35005. if(!strings[arg]){
  35006. strings[arg] = new dojox.string.sprintf.Formatter("%" + arg);
  35007. }
  35008. return strings[arg].format(value);
  35009. },
  35010. title: function(value){
  35011. // summary: Converts a string into titlecase
  35012. var last, title = "";
  35013. for(var i = 0, current; i < value.length; i++){
  35014. current = value.charAt(i);
  35015. if(last == " " || last == "\n" || last == "\t" || !last){
  35016. title += current.toUpperCase();
  35017. }else{
  35018. title += current.toLowerCase();
  35019. }
  35020. last = current;
  35021. }
  35022. return title;
  35023. },
  35024. _truncatewords: /[ \n\r\t]/,
  35025. truncatewords: function(value, arg){
  35026. // summary: Truncates a string after a certain number of words
  35027. // arg: Integer
  35028. // Number of words to truncate after
  35029. arg = parseInt(arg, 10);
  35030. if(!arg){
  35031. return value;
  35032. }
  35033. for(var i = 0, j = value.length, count = 0, current, last; i < value.length; i++){
  35034. current = value.charAt(i);
  35035. if(dojox.dtl.filter.strings._truncatewords.test(last)){
  35036. if(!dojox.dtl.filter.strings._truncatewords.test(current)){
  35037. ++count;
  35038. if(count == arg){
  35039. return value.substring(0, j + 1);
  35040. }
  35041. }
  35042. }else if(!dojox.dtl.filter.strings._truncatewords.test(current)){
  35043. j = i;
  35044. }
  35045. last = current;
  35046. }
  35047. return value;
  35048. },
  35049. _truncate_words: /(&.*?;|<.*?>|(\w[\w\-]*))/g,
  35050. _truncate_tag: /<(\/)?([^ ]+?)(?: (\/)| .*?)?>/,
  35051. _truncate_singlets: { br: true, col: true, link: true, base: true, img: true, param: true, area: true, hr: true, input: true },
  35052. truncatewords_html: function(value, arg){
  35053. arg = parseInt(arg, 10);
  35054. if(arg <= 0){
  35055. return "";
  35056. }
  35057. var strings = dojox.dtl.filter.strings;
  35058. var words = 0;
  35059. var open = [];
  35060. var output = dojox.string.tokenize(value, strings._truncate_words, function(all, word){
  35061. if(word){
  35062. // It's an actual non-HTML word
  35063. ++words;
  35064. if(words < arg){
  35065. return word;
  35066. }else if(words == arg){
  35067. return word + " ...";
  35068. }
  35069. }
  35070. // Check for tag
  35071. var tag = all.match(strings._truncate_tag);
  35072. if(!tag || words >= arg){
  35073. // Don't worry about non tags or tags after our truncate point
  35074. return;
  35075. }
  35076. var closing = tag[1];
  35077. var tagname = tag[2].toLowerCase();
  35078. var selfclosing = tag[3];
  35079. if(closing || strings._truncate_singlets[tagname]){
  35080. }else if(closing){
  35081. var i = dojo.indexOf(open, tagname);
  35082. if(i != -1){
  35083. open = open.slice(i + 1);
  35084. }
  35085. }else{
  35086. open.unshift(tagname);
  35087. }
  35088. return all;
  35089. }).join("");
  35090. output = output.replace(/\s+$/g, "");
  35091. for(var i = 0, tag; tag = open[i]; i++){
  35092. output += "</" + tag + ">";
  35093. }
  35094. return output;
  35095. },
  35096. upper: function(value){
  35097. return value.toUpperCase();
  35098. },
  35099. urlencode: function(value){
  35100. return dojox.dtl.filter.strings._urlquote(value);
  35101. },
  35102. _urlize: /^((?:[(>]|&lt;)*)(.*?)((?:[.,)>\n]|&gt;)*)$/,
  35103. _urlize2: /^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/,
  35104. urlize: function(value){
  35105. return dojox.dtl.filter.strings.urlizetrunc(value);
  35106. },
  35107. urlizetrunc: function(value, arg){
  35108. arg = parseInt(arg);
  35109. return dojox.string.tokenize(value, /(\S+)/g, function(word){
  35110. var matches = dojox.dtl.filter.strings._urlize.exec(word);
  35111. if(!matches){
  35112. return word;
  35113. }
  35114. var lead = matches[1];
  35115. var middle = matches[2];
  35116. var trail = matches[3];
  35117. var startsWww = middle.indexOf("www.") == 0;
  35118. var hasAt = middle.indexOf("@") != -1;
  35119. var hasColon = middle.indexOf(":") != -1;
  35120. var startsHttp = middle.indexOf("http://") == 0;
  35121. var startsHttps = middle.indexOf("https://") == 0;
  35122. var firstAlpha = /[a-zA-Z0-9]/.test(middle.charAt(0));
  35123. var last4 = middle.substring(middle.length - 4);
  35124. var trimmed = middle;
  35125. if(arg > 3){
  35126. trimmed = trimmed.substring(0, arg - 3) + "...";
  35127. }
  35128. if(startsWww || (!hasAt && !startsHttp && middle.length && firstAlpha && (last4 == ".org" || last4 == ".net" || last4 == ".com"))){
  35129. return '<a href="http://' + middle + '" rel="nofollow">' + trimmed + '</a>';
  35130. }else if(startsHttp || startsHttps){
  35131. return '<a href="' + middle + '" rel="nofollow">' + trimmed + '</a>';
  35132. }else if(hasAt && !startsWww && !hasColon && dojox.dtl.filter.strings._urlize2.test(middle)){
  35133. return '<a href="mailto:' + middle + '">' + middle + '</a>';
  35134. }
  35135. return word;
  35136. }).join("");
  35137. },
  35138. wordcount: function(value){
  35139. value = dojo.trim(value);
  35140. if(!value){ return 0; }
  35141. return value.split(/\s+/g).length;
  35142. },
  35143. wordwrap: function(value, arg){
  35144. arg = parseInt(arg);
  35145. // summary: Wraps words at specified line length
  35146. var output = [];
  35147. var parts = value.split(/\s+/g);
  35148. if(parts.length){
  35149. var word = parts.shift();
  35150. output.push(word);
  35151. var pos = word.length - word.lastIndexOf("\n") - 1;
  35152. for(var i = 0; i < parts.length; i++){
  35153. word = parts[i];
  35154. if(word.indexOf("\n") != -1){
  35155. var lines = word.split(/\n/g);
  35156. }else{
  35157. var lines = [word];
  35158. }
  35159. pos += lines[0].length + 1;
  35160. if(arg && pos > arg){
  35161. output.push("\n");
  35162. pos = lines[lines.length - 1].length;
  35163. }else{
  35164. output.push(" ");
  35165. if(lines.length > 1){
  35166. pos = lines[lines.length - 1].length;
  35167. }
  35168. }
  35169. output.push(word);
  35170. }
  35171. }
  35172. return output.join("");
  35173. }
  35174. });
  35175. }
  35176. if(!dojo._hasResource["dojox.fx._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  35177. dojo._hasResource["dojox.fx._base"] = true;
  35178. dojo.provide("dojox.fx._base");
  35179. // summary: Experimental and extended Animations beyond Dojo Core / Base functionality.
  35180. // Provides advanced Lines, Animations, and convenience aliases.
  35181. dojo.mixin(dojox.fx, {
  35182. // anim: Function
  35183. // Alias of `dojo.anim` - the shorthand `dojo.animateProperty` with auto-play
  35184. anim: dojo.anim,
  35185. // animateProperty: Function
  35186. // Alias of `dojo.animateProperty` - animate any CSS property
  35187. animateProperty: dojo.animateProperty,
  35188. // fadeTo: Function
  35189. // Fade an element from an opacity to an opacity.
  35190. // Omit `start:` property to detect. `end:` property is required.
  35191. // Ultimately an alias to `dojo._fade`
  35192. fadeTo: dojo._fade,
  35193. // fadeIn: Function
  35194. // Alias of `dojo.fadeIn` - Fade a node in.
  35195. fadeIn: dojo.fadeIn,
  35196. // fadeOut: Function
  35197. // Alias of `dojo.fadeOut` - Fades a node out.
  35198. fadeOut: dojo.fadeOut,
  35199. // combine: Function
  35200. // Alias of `dojo.fx.combine` - Run an array of animations in parallel
  35201. combine: dojo.fx.combine,
  35202. // chain: Function
  35203. // Alias of `dojo.fx.chain` - Run an array of animations in sequence
  35204. chain: dojo.fx.chain,
  35205. // slideTo: Function
  35206. // Alias of `dojo.fx.slideTo` - Slide a node to a defined top/left coordinate
  35207. slideTo: dojo.fx.slideTo,
  35208. // wipeIn: Function
  35209. // Alias of `dojo.fx.wipeIn` - Wipe a node to visible
  35210. wipeIn: dojo.fx.wipeIn,
  35211. // wipeOut: Function
  35212. // Alias of `dojo.fx.wipeOut` - Wipe a node to non-visible
  35213. wipeOut: dojo.fx.wipeOut
  35214. });
  35215. dojox.fx.sizeTo = function(/* Object */args){
  35216. // summary:
  35217. // Creates an animation that will size a node
  35218. //
  35219. // description:
  35220. // Returns an animation that will size the target node
  35221. // defined in args Object about it's center to
  35222. // a width and height defined by (args.width, args.height),
  35223. // supporting an optional method: chain||combine mixin
  35224. // (defaults to chain).
  35225. //
  35226. // - works best on absolutely or relatively positioned elements
  35227. //
  35228. // example:
  35229. // | // size #myNode to 400px x 200px over 1 second
  35230. // | dojo.fx.sizeTo({
  35231. // | node:'myNode',
  35232. // | duration: 1000,
  35233. // | width: 400,
  35234. // | height: 200,
  35235. // | method: "combine"
  35236. // | }).play();
  35237. //
  35238. var node = args.node = dojo.byId(args.node),
  35239. abs = "absolute";
  35240. var method = args.method || "chain";
  35241. if(!args.duration){ args.duration = 500; } // default duration needed
  35242. if(method == "chain"){ args.duration = Math.floor(args.duration / 2); }
  35243. var top, newTop, left, newLeft, width, height = null;
  35244. var init = (function(n){
  35245. return function(){
  35246. var cs = dojo.getComputedStyle(n),
  35247. pos = cs.position,
  35248. w = cs.width,
  35249. h = cs.height
  35250. ;
  35251. top = (pos == abs ? n.offsetTop : parseInt(cs.top) || 0);
  35252. left = (pos == abs ? n.offsetLeft : parseInt(cs.left) || 0);
  35253. width = (w == "auto" ? 0 : parseInt(w));
  35254. height = (h == "auto" ? 0 : parseInt(h));
  35255. newLeft = left - Math.floor((args.width - width) / 2);
  35256. newTop = top - Math.floor((args.height - height) / 2);
  35257. if(pos != abs && pos != 'relative'){
  35258. var ret = dojo.coords(n, true);
  35259. top = ret.y;
  35260. left = ret.x;
  35261. n.style.position = abs;
  35262. n.style.top = top + "px";
  35263. n.style.left = left + "px";
  35264. }
  35265. }
  35266. })(node);
  35267. var anim1 = dojo.animateProperty(dojo.mixin({
  35268. properties: {
  35269. height: function(){
  35270. init();
  35271. return { end: args.height || 0, start: height };
  35272. },
  35273. top: function(){
  35274. return { start: top, end: newTop };
  35275. }
  35276. }
  35277. }, args));
  35278. var anim2 = dojo.animateProperty(dojo.mixin({
  35279. properties: {
  35280. width: function(){
  35281. return { start: width, end: args.width || 0 }
  35282. },
  35283. left: function(){
  35284. return { start: left, end: newLeft }
  35285. }
  35286. }
  35287. }, args));
  35288. var anim = dojo.fx[(args.method == "combine" ? "combine" : "chain")]([anim1, anim2]);
  35289. return anim; // dojo.Animation
  35290. };
  35291. dojox.fx.slideBy = function(/* Object */args){
  35292. // summary:
  35293. // Returns an animation to slide a node by a defined offset.
  35294. //
  35295. // description:
  35296. // Returns an animation that will slide a node (args.node) from it's
  35297. // current position to it's current posision plus the numbers defined
  35298. // in args.top and args.left. standard dojo.fx mixin's apply.
  35299. //
  35300. // example:
  35301. // | // slide domNode 50px down, and 22px left
  35302. // | dojox.fx.slideBy({
  35303. // | node: domNode, duration:400,
  35304. // | top: 50, left: -22
  35305. // | }).play();
  35306. var node = args.node = dojo.byId(args.node),
  35307. top, left;
  35308. var init = (function(n){
  35309. return function(){
  35310. var cs = dojo.getComputedStyle(n);
  35311. var pos = cs.position;
  35312. top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
  35313. left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
  35314. if(pos != 'absolute' && pos != 'relative'){
  35315. var ret = dojo.coords(n, true);
  35316. top = ret.y;
  35317. left = ret.x;
  35318. n.style.position = "absolute";
  35319. n.style.top = top + "px";
  35320. n.style.left = left + "px";
  35321. }
  35322. }
  35323. })(node);
  35324. init();
  35325. var _anim = dojo.animateProperty(dojo.mixin({
  35326. properties: {
  35327. // FIXME: is there a way to update the _Line after creation?
  35328. // null start values allow chaining to work, animateProperty will
  35329. // determine them for us (except in ie6? -- ugh)
  35330. top: top + (args.top || 0),
  35331. left: left + (args.left || 0)
  35332. }
  35333. }, args));
  35334. dojo.connect(_anim, "beforeBegin", _anim, init);
  35335. return _anim; // dojo.Animation
  35336. };
  35337. dojox.fx.crossFade = function(/* Object */args){
  35338. // summary:
  35339. // Returns an animation cross fading two element simultaneously
  35340. //
  35341. // args:
  35342. // args.nodes: Array - two element array of domNodes, or id's
  35343. //
  35344. // all other standard animation args mixins apply. args.node ignored.
  35345. //
  35346. // simple check for which node is visible, maybe too simple?
  35347. var node1 = args.nodes[0] = dojo.byId(args.nodes[0]),
  35348. op1 = dojo.style(node1,"opacity"),
  35349. node2 = args.nodes[1] = dojo.byId(args.nodes[1]),
  35350. op2 = dojo.style(node2, "opacity")
  35351. ;
  35352. var _anim = dojo.fx.combine([
  35353. dojo[(op1 == 0 ? "fadeIn" : "fadeOut")](dojo.mixin({
  35354. node: node1
  35355. },args)),
  35356. dojo[(op1 == 0 ? "fadeOut" : "fadeIn")](dojo.mixin({
  35357. node: node2
  35358. },args))
  35359. ]);
  35360. return _anim; // dojo.Animation
  35361. };
  35362. dojox.fx.highlight = function(/*Object*/ args){
  35363. // summary:
  35364. // Highlight a node
  35365. //
  35366. // description:
  35367. // Returns an animation that sets the node background to args.color
  35368. // then gradually fades back the original node background color
  35369. //
  35370. // example:
  35371. // | dojox.fx.highlight({ node:"foo" }).play();
  35372. var node = args.node = dojo.byId(args.node);
  35373. args.duration = args.duration || 400;
  35374. // Assign default color light yellow
  35375. var startColor = args.color || '#ffff99',
  35376. endColor = dojo.style(node, "backgroundColor")
  35377. ;
  35378. // safari "fix"
  35379. // safari reports rgba(0, 0, 0, 0) (black) as transparent color, while
  35380. // other browsers return "transparent", rendered as white by default by
  35381. // dojo.Color; now dojo.Color maps "transparent" to
  35382. // djConfig.transparentColor ([r, g, b]), if present; so we can use
  35383. // the color behind the effect node
  35384. if(endColor == "rgba(0, 0, 0, 0)"){
  35385. endColor = "transparent";
  35386. }
  35387. var anim = dojo.animateProperty(dojo.mixin({
  35388. properties: {
  35389. backgroundColor: { start: startColor, end: endColor }
  35390. }
  35391. }, args));
  35392. if(endColor == "transparent"){
  35393. dojo.connect(anim, "onEnd", anim, function(){
  35394. node.style.backgroundColor = endColor;
  35395. });
  35396. }
  35397. return anim; // dojo.Animation
  35398. };
  35399. dojox.fx.wipeTo = function(/*Object*/ args){
  35400. // summary:
  35401. // Animate a node wiping to a specific width or height
  35402. //
  35403. // description:
  35404. // Returns an animation that will expand the
  35405. // node defined in 'args' object from it's current to
  35406. // the height or width value given by the args object.
  35407. //
  35408. // default to height:, so leave height null and specify width:
  35409. // to wipeTo a width. note: this may be deprecated by a
  35410. //
  35411. // Note that the final value should not include
  35412. // units and should be an integer. Thus a valid args object
  35413. // would look something like this:
  35414. //
  35415. // | dojox.fx.wipeTo({ node: "nodeId", height: 200 }).play();
  35416. //
  35417. // Node must have no margin/border/padding, so put another
  35418. // node inside your target node for additional styling.
  35419. args.node = dojo.byId(args.node);
  35420. var node = args.node, s = node.style;
  35421. var dir = (args.width ? "width" : "height"),
  35422. endVal = args[dir],
  35423. props = {}
  35424. ;
  35425. props[dir] = {
  35426. // wrapped in functions so we wait till the last second to query (in case value has changed)
  35427. start: function(){
  35428. // start at current [computed] height, but use 1px rather than 0
  35429. // because 0 causes IE to display the whole panel
  35430. s.overflow = "hidden";
  35431. if(s.visibility == "hidden" || s.display == "none"){
  35432. s[dir] = "1px";
  35433. s.display = "";
  35434. s.visibility = "";
  35435. return 1;
  35436. }else{
  35437. var now = dojo.style(node,dir);
  35438. return Math.max(now, 1);
  35439. }
  35440. },
  35441. end: endVal
  35442. };
  35443. var anim = dojo.animateProperty(dojo.mixin({ properties: props }, args));
  35444. return anim; // dojo.Animation
  35445. };
  35446. }
  35447. if(!dojo._hasResource["dojox.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  35448. dojo._hasResource["dojox.fx"] = true;
  35449. dojo.provide("dojox.fx");
  35450. }
  35451. if(!dojo._hasResource["dojox.form.RangeSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  35452. dojo._hasResource["dojox.form.RangeSlider"] = true;
  35453. dojo.provide("dojox.form.RangeSlider");
  35454. (function(){
  35455. // make these functions once:
  35456. var sortReversed = function(a, b){ return b - a; },
  35457. sortForward = function(a, b){ return a - b; }
  35458. ;
  35459. dojo.declare("dojox.form._RangeSliderMixin", null, {
  35460. value: [0,100],
  35461. postMixInProperties: function(){
  35462. this.inherited(arguments);
  35463. this.value = dojo.map(this.value, function(i){ return parseInt(i, 10); });
  35464. },
  35465. postCreate: function(){
  35466. this.inherited(arguments);
  35467. // we sort the values!
  35468. // TODO: re-think, how to set the value
  35469. this.value.sort(this._isReversed() ? sortReversed : sortForward);
  35470. // define a custom constructor for a SliderMoverMax that points back to me
  35471. var _self = this;
  35472. var mover = dojo.declare(dijit.form._SliderMoverMax, {
  35473. constructor: function(){
  35474. this.widget = _self;
  35475. }
  35476. });
  35477. this._movableMax = new dojo.dnd.Moveable(this.sliderHandleMax,{ mover: mover });
  35478. dijit.setWaiState(this.focusNodeMax, "valuemin", this.minimum);
  35479. dijit.setWaiState(this.focusNodeMax, "valuemax", this.maximum);
  35480. // a dnd for the bar!
  35481. var barMover = dojo.declare(dijit.form._SliderBarMover, {
  35482. constructor: function(){
  35483. this.widget = _self;
  35484. }
  35485. });
  35486. this._movableBar = new dojo.dnd.Moveable(this.progressBar,{ mover: barMover });
  35487. },
  35488. destroy: function(){
  35489. this.inherited(arguments);
  35490. this._movableMax.destroy();
  35491. this._movableBar.destroy();
  35492. },
  35493. _onKeyPress: function(/*Event*/ e){
  35494. if(this.disabled || this.readOnly || e.altKey || e.ctrlKey){ return; }
  35495. var useMaxValue = e.target === this.sliderHandleMax;
  35496. var barFocus = e.target === this.progressBar;
  35497. var k = dojo.delegate(dojo.keys, this.isLeftToRight() ? {PREV_ARROW: dojo.keys.LEFT_ARROW, NEXT_ARROW: dojo.keys.RIGHT_ARROW}
  35498. : {PREV_ARROW: dojo.keys.RIGHT_ARROW, NEXT_ARROW: dojo.keys.LEFT_ARROW});
  35499. var delta = 0;
  35500. var down = false;
  35501. switch(e.keyCode){
  35502. case k.HOME : this._setValueAttr(this.minimum, true, useMaxValue);dojo.stopEvent(e);return;
  35503. case k.END : this._setValueAttr(this.maximum, true, useMaxValue);dojo.stopEvent(e);return;
  35504. case k.PREV_ARROW :
  35505. case k.DOWN_ARROW : down = true;
  35506. case k.NEXT_ARROW :
  35507. case k.UP_ARROW : delta = 1; break;
  35508. case k.PAGE_DOWN : down = true;
  35509. case k.PAGE_UP : delta = this.pageIncrement; break;
  35510. default : this.inherited(arguments);return;
  35511. }
  35512. if(down){delta = -delta;}
  35513. if(delta){
  35514. if(barFocus){
  35515. this._bumpValue([
  35516. { change: delta, useMaxValue: false },
  35517. { change: delta, useMaxValue: true }
  35518. ]);
  35519. }else{
  35520. this._bumpValue(delta, useMaxValue);
  35521. }
  35522. dojo.stopEvent(e);
  35523. }
  35524. },
  35525. _onHandleClickMax: function(e){
  35526. if(this.disabled || this.readOnly){ return; }
  35527. if(!dojo.isIE){
  35528. // make sure you get focus when dragging the handle
  35529. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  35530. dijit.focus(this.sliderHandleMax);
  35531. }
  35532. dojo.stopEvent(e);
  35533. },
  35534. _onClkIncBumper: function(){
  35535. this._setValueAttr(this._descending === false ? this.minimum : this.maximum, true, true);
  35536. },
  35537. _bumpValue: function(signedChange, useMaxValue){
  35538. // we pass an array to _setValueAttr when signedChange is an array
  35539. var value = dojo.isArray(signedChange) ? [
  35540. this._getBumpValue(signedChange[0].change, signedChange[0].useMaxValue),
  35541. this._getBumpValue(signedChange[1].change, signedChange[1].useMaxValue)
  35542. ]
  35543. : this._getBumpValue(signedChange, useMaxValue)
  35544. this._setValueAttr(value, true, useMaxValue);
  35545. },
  35546. _getBumpValue: function(signedChange, useMaxValue){
  35547. var idx = useMaxValue ? 1 : 0;
  35548. if( this._isReversed() ) {
  35549. idx = 1 - idx;
  35550. }
  35551. var s = dojo.getComputedStyle(this.sliderBarContainer),
  35552. c = dojo._getContentBox(this.sliderBarContainer, s),
  35553. count = this.discreteValues,
  35554. myValue = this.value[idx]
  35555. ;
  35556. if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
  35557. count--;
  35558. var value = (myValue - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
  35559. if(value < 0){ value = 0; }
  35560. if(value > count){ value = count; }
  35561. return value * (this.maximum - this.minimum) / count + this.minimum;
  35562. },
  35563. _onBarClick: function(e){
  35564. if(this.disabled || this.readOnly){ return; }
  35565. if(!dojo.isIE){
  35566. // make sure you get focus when dragging the handle
  35567. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  35568. dijit.focus(this.progressBar);
  35569. }
  35570. dojo.stopEvent(e);
  35571. },
  35572. _onRemainingBarClick: function(e){
  35573. if(this.disabled || this.readOnly){ return; }
  35574. if(!dojo.isIE){
  35575. // make sure you get focus when dragging the handle
  35576. // (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
  35577. dijit.focus(this.progressBar);
  35578. }
  35579. // now we set the min/max-value of the slider!
  35580. var abspos = dojo.coords(this.sliderBarContainer, true),
  35581. bar = dojo.coords(this.progressBar, true),
  35582. relMousePos = e[this._mousePixelCoord] - abspos[this._startingPixelCoord],
  35583. leftPos = bar[this._startingPixelCount],
  35584. rightPos = leftPos + bar[this._pixelCount],
  35585. isMaxVal = this._isReversed() ? relMousePos <= leftPos : relMousePos >= rightPos,
  35586. p = this._isReversed() ? abspos[this._pixelCount] - relMousePos : relMousePos
  35587. ;
  35588. this._setPixelValue(p, abspos[this._pixelCount], true, isMaxVal);
  35589. dojo.stopEvent(e);
  35590. },
  35591. _setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean*/ priorityChange, /*Boolean*/ isMaxVal){
  35592. if(this.disabled || this.readOnly){ return; }
  35593. var myValue = this._getValueByPixelValue(pixelValue, maxPixels);
  35594. this._setValueAttr(myValue, priorityChange, isMaxVal);
  35595. },
  35596. _getValueByPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels){
  35597. pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
  35598. var count = this.discreteValues;
  35599. if(count <= 1 || count == Infinity){ count = maxPixels; }
  35600. count--;
  35601. var pixelsPerValue = maxPixels / count;
  35602. var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
  35603. return (this.maximum-this.minimum)*wholeIncrements/count + this.minimum;
  35604. },
  35605. _setValueAttr: function(/*Array or Number*/ value, /*Boolean, optional*/ priorityChange, /*Boolean, optional*/ isMaxVal){
  35606. // we pass an array, when we move the slider with the bar
  35607. var actValue = this.value;
  35608. if(!dojo.isArray(value)){
  35609. if(isMaxVal){
  35610. if(this._isReversed()){
  35611. actValue[0] = value;
  35612. }else{
  35613. actValue[1] = value;
  35614. }
  35615. }else{
  35616. if(this._isReversed()){
  35617. actValue[1] = value;
  35618. }else{
  35619. actValue[0] = value;
  35620. }
  35621. }
  35622. }else{
  35623. actValue = value;
  35624. }
  35625. // we have to reset this values. don't know the reason for that
  35626. this._lastValueReported = "";
  35627. this.valueNode.value = this.value = value = actValue;
  35628. dijit.setWaiState(this.focusNode, "valuenow", actValue[0]);
  35629. dijit.setWaiState(this.focusNodeMax, "valuenow", actValue[1]);
  35630. this.value.sort(this._isReversed() ? sortReversed : sortForward);
  35631. // not calling the _setValueAttr-function of dijit.form.Slider, but the super-super-class (needed for the onchange-event!)
  35632. dijit.form._FormValueWidget.prototype._setValueAttr.apply(this, arguments);
  35633. this._printSliderBar(priorityChange, isMaxVal);
  35634. },
  35635. _printSliderBar: function(priorityChange, isMaxVal){
  35636. var percentMin = (this.value[0] - this.minimum) / (this.maximum - this.minimum);
  35637. var percentMax = (this.value[1] - this.minimum) / (this.maximum - this.minimum);
  35638. var percentMinSave = percentMin;
  35639. if(percentMin > percentMax){
  35640. percentMin = percentMax;
  35641. percentMax = percentMinSave;
  35642. }
  35643. var sliderHandleVal = this._isReversed() ? ((1-percentMin)*100) : (percentMin * 100);
  35644. var sliderHandleMaxVal = this._isReversed() ? ((1-percentMax)*100) : (percentMax * 100);
  35645. var progressBarVal = this._isReversed() ? ((1-percentMax)*100) : (percentMin * 100);
  35646. if (priorityChange && this.slideDuration > 0 && this.progressBar.style[this._progressPixelSize]){
  35647. // animate the slider
  35648. var percent = isMaxVal ? percentMax : percentMin;
  35649. var _this = this;
  35650. var props = {};
  35651. var start = parseFloat(this.progressBar.style[this._handleOffsetCoord]);
  35652. var duration = this.slideDuration / 10; // * (percent-start/100);
  35653. if(duration === 0){ return; }
  35654. if(duration < 0){ duration = 0 - duration; }
  35655. var propsHandle = {};
  35656. var propsHandleMax = {};
  35657. var propsBar = {};
  35658. // hui, a lot of animations :-)
  35659. propsHandle[this._handleOffsetCoord] = { start: this.sliderHandle.style[this._handleOffsetCoord], end: sliderHandleVal, units:"%"};
  35660. propsHandleMax[this._handleOffsetCoord] = { start: this.sliderHandleMax.style[this._handleOffsetCoord], end: sliderHandleMaxVal, units:"%"};
  35661. propsBar[this._handleOffsetCoord] = { start: this.progressBar.style[this._handleOffsetCoord], end: progressBarVal, units:"%"};
  35662. propsBar[this._progressPixelSize] = { start: this.progressBar.style[this._progressPixelSize], end: (percentMax - percentMin) * 100, units:"%"};
  35663. var animHandle = dojo.animateProperty({node: this.sliderHandle,duration: duration, properties: propsHandle});
  35664. var animHandleMax = dojo.animateProperty({node: this.sliderHandleMax,duration: duration, properties: propsHandleMax});
  35665. var animBar = dojo.animateProperty({node: this.progressBar,duration: duration, properties: propsBar});
  35666. var animCombine = dojo.fx.combine([animHandle, animHandleMax, animBar]);
  35667. animCombine.play();
  35668. }else{
  35669. this.sliderHandle.style[this._handleOffsetCoord] = sliderHandleVal + "%";
  35670. this.sliderHandleMax.style[this._handleOffsetCoord] = sliderHandleMaxVal + "%";
  35671. this.progressBar.style[this._handleOffsetCoord] = progressBarVal + "%";
  35672. this.progressBar.style[this._progressPixelSize] = ((percentMax - percentMin) * 100) + "%";
  35673. }
  35674. }
  35675. });
  35676. dojo.declare("dijit.form._SliderMoverMax", dijit.form._SliderMover, {
  35677. onMouseMove: function(e){
  35678. var widget = this.widget;
  35679. var abspos = widget._abspos;
  35680. if(!abspos){
  35681. abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
  35682. widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
  35683. widget._isReversed_ = widget._isReversed();
  35684. }
  35685. var coordEvent = e.touches ? e.touches[0] : e; // if multitouch take first touch for coords
  35686. var pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
  35687. widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false, true);
  35688. },
  35689. destroy: function(e){
  35690. dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
  35691. var widget = this.widget;
  35692. widget._abspos = null;
  35693. widget._setValueAttr(widget.value, true);
  35694. }
  35695. });
  35696. dojo.declare("dijit.form._SliderBarMover", dojo.dnd.Mover, {
  35697. onMouseMove: function(e){
  35698. var widget = this.widget;
  35699. if(widget.disabled || widget.readOnly){ return; }
  35700. var abspos = widget._abspos;
  35701. var bar = widget._bar;
  35702. var mouseOffset = widget._mouseOffset;
  35703. if(!abspos){
  35704. abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
  35705. widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
  35706. widget._getValueByPixelValue_ = dojo.hitch(widget, "_getValueByPixelValue");
  35707. widget._isReversed_ = widget._isReversed();
  35708. }
  35709. if(!bar){
  35710. bar = widget._bar = dojo.coords(widget.progressBar, true);
  35711. }
  35712. var coordEvent = e.touches ? e.touches[0] : e; // if multitouch take first touch for coords
  35713. if(!mouseOffset){
  35714. mouseOffset = widget._mouseOffset = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - bar[widget._startingPixelCount];
  35715. }
  35716. var pixelValueMin = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - mouseOffset,
  35717. pixelValueMax = pixelValueMin + bar[widget._pixelCount];
  35718. // we don't narrow the slider when it reaches the bumper!
  35719. // maybe there is a simpler way
  35720. pixelValues = [pixelValueMin, pixelValueMax]
  35721. ;
  35722. pixelValues.sort(sortForward);
  35723. if(pixelValues[0] <= 0){
  35724. pixelValues[0] = 0;
  35725. pixelValues[1] = bar[widget._pixelCount];
  35726. }
  35727. if(pixelValues[1] >= abspos[widget._pixelCount]){
  35728. pixelValues[1] = abspos[widget._pixelCount];
  35729. pixelValues[0] = abspos[widget._pixelCount] - bar[widget._pixelCount];
  35730. }
  35731. // getting the real values by pixel
  35732. var myValues = [
  35733. widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[0]) : pixelValues[0], abspos[widget._pixelCount]),
  35734. widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[1]) : pixelValues[1], abspos[widget._pixelCount])
  35735. ];
  35736. // and setting the value of the widget
  35737. widget._setValueAttr(myValues, false, false);
  35738. },
  35739. destroy: function(){
  35740. dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
  35741. var widget = this.widget;
  35742. widget._abspos = null;
  35743. widget._bar = null;
  35744. widget._mouseOffset = null;
  35745. widget._setValueAttr(widget.value, true);
  35746. }
  35747. });
  35748. dojo.declare("dojox.form.HorizontalRangeSlider",
  35749. [dijit.form.HorizontalSlider, dojox.form._RangeSliderMixin],
  35750. {
  35751. // summary:
  35752. // A form widget that allows one to select a range with two horizontally draggable images
  35753. templateString: dojo.cache("dojox.form", "resources/HorizontalRangeSlider.html", "<table class=\"dijit dijitReset dijitSlider dijitSliderH dojoxRangeSlider\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\" dojoAttachEvent=\"onkeypress:_onKeyPress,onkeyup:_onKeyUp\"\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\" tabIndex=\"-1\" 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 role=\"presentation\" class=\"dojoxRangeSliderBarContainer\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div dojoAttachPoint=\"sliderHandle\" tabIndex=\"${tabIndex}\" class=\"dijitSliderMoveable dijitSliderMoveableH\" dojoAttachEvent=\"onmousedown:_onHandleClick\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleH\"></div\n\t\t\t\t></div\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"progressBar,focusNode\" class=\"dijitSliderBar dijitSliderBarH dijitSliderProgressBar dijitSliderProgressBarH\" dojoAttachEvent=\"onmousedown:_onBarClick\"></div\n\t\t\t\t><div dojoAttachPoint=\"sliderHandleMax,focusNodeMax\" tabIndex=\"${tabIndex}\" class=\"dijitSliderMoveable dijitSliderMoveableH\" dojoAttachEvent=\"onmousedown:_onHandleClickMax\" role=\"sliderMax\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleH\"></div\n\t\t\t\t></div\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarH dijitSliderRemainingBar dijitSliderRemainingBarH\" dojoAttachEvent=\"onmousedown:_onRemainingBarClick\"></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\" tabIndex=\"-1\" 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")
  35754. }
  35755. );
  35756. dojo.declare("dojox.form.VerticalRangeSlider",
  35757. [dijit.form.VerticalSlider, dojox.form._RangeSliderMixin],
  35758. {
  35759. // summary:
  35760. // A form widget that allows one to select a range with two vertically draggable images
  35761. templateString: dojo.cache("dojox.form", "resources/VerticalRangeSlider.html", "<table class=\"dijitReset dijitSlider dijitSliderV dojoxRangeSlider\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" rules=\"none\"\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\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"decrementButton\" dojoAttachEvent=\"onclick: increment\"><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=\"onclick:_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\" style=\"text-align:center;height:100%;\"></td\n\t\t><td class=\"dijitReset\" style=\"height:100%;\"\n\t\t\t><input dojoAttachPoint=\"valueNode\" type=\"hidden\" ${!nameAttrSetting}\n\t\t\t/><center role=\"presentation\" style=\"position:relative;height:100%;\" dojoAttachPoint=\"sliderBarContainer\"\n\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"remainingBar\" class=\"dijitSliderBar dijitSliderBarV dijitSliderRemainingBar dijitSliderRemainingBarV\" dojoAttachEvent=\"onmousedown:_onRemainingBarClick\"\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandle\" tabIndex=\"${tabIndex}\" class=\"dijitSliderMoveable dijitSliderMoveableV\" dojoAttachEvent=\"onkeypress:_onKeyPress,onmousedown:_onHandleClick\" style=\"vertical-align:top;\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleV\"></div\n\t\t\t\t\t></div\n\t\t\t\t\t><div role=\"presentation\" dojoAttachPoint=\"progressBar,focusNode\" tabIndex=\"${tabIndex}\" class=\"dijitSliderBar dijitSliderBarV dijitSliderProgressBar dijitSliderProgressBarV\" dojoAttachEvent=\"onkeypress:_onKeyPress,onmousedown:_onBarClick\"\n\t\t\t\t\t></div\n\t\t\t\t\t><div dojoAttachPoint=\"sliderHandleMax,focusNodeMax\" tabIndex=\"${tabIndex}\" class=\"dijitSliderMoveable dijitSliderMoveableV\" dojoAttachEvent=\"onkeypress:_onKeyPress,onmousedown:_onHandleClickMax\" style=\"vertical-align:top;\" role=\"slider\" valuemin=\"${minimum}\" valuemax=\"${maximum}\"\n\t\t\t\t\t\t><div class=\"dijitSliderImageHandle dijitSliderImageHandleV\"></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\" style=\"text-align:center;height:100%;\"></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=\"onclick:_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\" tabIndex=\"-1\" style=\"display:none\" dojoAttachPoint=\"incrementButton\" dojoAttachEvent=\"onclick: decrement\"><span class=\"dijitSliderButtonInner\">-</span></div\n\t\t></td\n\t\t><td class=\"dijitReset\"></td\n\t></tr\n></table>\n")
  35762. }
  35763. );
  35764. })();
  35765. }
  35766. if(!dojo._hasResource["dojox.fx._core"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  35767. dojo._hasResource["dojox.fx._core"] = true;
  35768. dojo.provide("dojox.fx._core");
  35769. dojox.fx._Line = function(start, end){
  35770. // summary: a custom _Line to accomodate multi-dimensional values
  35771. //
  35772. // description:
  35773. // a normal dojo._Line is the curve, and does Line(start,end)
  35774. // for propertyAnimation. as we make more complicatied animations, we realize
  35775. // some properties can have 2, or 4 values relevant (x,y) or (t,l,r,b) for example
  35776. //
  35777. // this function provides support for those Lines, and is ported directly from 0.4
  35778. // this is a lot of extra code for something so seldom used, so we'll put it here as
  35779. // and optional core addition. you can create a new line, and use it during onAnimate
  35780. // as you see fit.
  35781. //
  35782. // start: Integer|Array
  35783. // An Integer (or an Array of integers) to use as a starting point
  35784. // end: Integer|Array
  35785. // An Integer (or an Array of integers) to use as an ending point
  35786. //
  35787. // example: see dojox.fx.smoothScroll
  35788. //
  35789. // example:
  35790. // | // this is 10 .. 100 and 50 .. 500
  35791. // | var curve = new dojox.fx._Line([10,50],[100,500]);
  35792. // | // dojo.Animation.onAnimate is called at every step of the animation
  35793. // | // to define current values. this _Line returns an array
  35794. // | // at each step. arguments[0] and [1] in this example.
  35795. //
  35796. this.start = start;
  35797. this.end = end;
  35798. var isArray = dojo.isArray(start),
  35799. d = (isArray ? [] : end - start);
  35800. if(isArray){
  35801. // multi-dimensional branch
  35802. dojo.forEach(this.start, function(s, i){
  35803. d[i] = this.end[i] - s;
  35804. }, this);
  35805. this.getValue = function(/*float*/ n){
  35806. var res = [];
  35807. dojo.forEach(this.start, function(s, i){
  35808. res[i] = (d[i] * n) + s;
  35809. }, this);
  35810. return res; // Array
  35811. }
  35812. }else{
  35813. // single value branch, document here for both branches:
  35814. this.getValue = function(/*float*/ n){
  35815. // summary: Returns the point on the line, or an array of points
  35816. // n: a floating point number greater than 0 and less than 1
  35817. // returns: Mixed
  35818. return (d * n) + this.start; // Decimal
  35819. }
  35820. }
  35821. };
  35822. }
  35823. if(!dojo._hasResource["dojox.json.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  35824. dojo._hasResource["dojox.json.query"] = true;
  35825. dojo.provide("dojox.json.query");
  35826. (function(){
  35827. dojox.json._slice = function(obj,start,end,step){
  35828. // handles slice operations: [3:6:2]
  35829. var len=obj.length,results = [];
  35830. end = end || len;
  35831. start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
  35832. end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
  35833. for(var i=start; i<end; i+=step){
  35834. results.push(obj[i]);
  35835. }
  35836. return results;
  35837. }
  35838. dojox.json._find = function e(obj,name){
  35839. // handles ..name, .*, [*], [val1,val2], [val]
  35840. // name can be a property to search for, undefined for full recursive, or an array for picking by index
  35841. var results = [];
  35842. function walk(obj){
  35843. if(name){
  35844. if(name===true && !(obj instanceof Array)){
  35845. //recursive object search
  35846. results.push(obj);
  35847. }else if(obj[name]){
  35848. // found the name, add to our results
  35849. results.push(obj[name]);
  35850. }
  35851. }
  35852. for(var i in obj){
  35853. var val = obj[i];
  35854. if(!name){
  35855. // if we don't have a name we are just getting all the properties values (.* or [*])
  35856. results.push(val);
  35857. }else if(val && typeof val == 'object'){
  35858. walk(val);
  35859. }
  35860. }
  35861. }
  35862. if(name instanceof Array){
  35863. // this is called when multiple items are in the brackets: [3,4,5]
  35864. if(name.length==1){
  35865. // this can happen as a result of the parser becoming confused about commas
  35866. // in the brackets like [@.func(4,2)]. Fixing the parser would require recursive
  35867. // analsys, very expensive, but this fixes the problem nicely.
  35868. return obj[name[0]];
  35869. }
  35870. for(var i = 0; i < name.length; i++){
  35871. results.push(obj[name[i]]);
  35872. }
  35873. }else{
  35874. // otherwise we expanding
  35875. walk(obj);
  35876. }
  35877. return results;
  35878. }
  35879. dojox.json._distinctFilter = function(array, callback){
  35880. // does the filter with removal of duplicates in O(n)
  35881. var outArr = [];
  35882. var primitives = {};
  35883. for(var i=0,l=array.length; i<l; ++i){
  35884. var value = array[i];
  35885. if(callback(value, i, array)){
  35886. if((typeof value == 'object') && value){
  35887. // with objects we prevent duplicates with a marker property
  35888. if(!value.__included){
  35889. value.__included = true;
  35890. outArr.push(value);
  35891. }
  35892. }else if(!primitives[value + typeof value]){
  35893. // with primitives we prevent duplicates by putting it in a map
  35894. primitives[value + typeof value] = true;
  35895. outArr.push(value);
  35896. }
  35897. }
  35898. }
  35899. for(i=0,l=outArr.length; i<l; ++i){
  35900. // cleanup the marker properties
  35901. if(outArr[i]){
  35902. delete outArr[i].__included;
  35903. }
  35904. }
  35905. return outArr;
  35906. }
  35907. dojox.json.query = function(/*String*/query,/*Object?*/obj){
  35908. // summary:
  35909. // Performs a JSONQuery on the provided object and returns the results.
  35910. // If no object is provided (just a query), it returns a "compiled" function that evaluates objects
  35911. // according to the provided query.
  35912. // query:
  35913. // Query string
  35914. // obj:
  35915. // Target of the JSONQuery
  35916. //
  35917. // description:
  35918. // JSONQuery provides a comprehensive set of data querying tools including filtering,
  35919. // recursive search, sorting, mapping, range selection, and powerful expressions with
  35920. // wildcard string comparisons and various operators. JSONQuery generally supersets
  35921. // JSONPath and provides syntax that matches and behaves like JavaScript where
  35922. // possible.
  35923. //
  35924. // JSONQuery evaluations begin with the provided object, which can referenced with
  35925. // $. From
  35926. // the starting object, various operators can be successively applied, each operating
  35927. // on the result of the last operation.
  35928. //
  35929. // Supported Operators:
  35930. // --------------------
  35931. // * .property - This will return the provided property of the object, behaving exactly
  35932. // like JavaScript.
  35933. // * [expression] - This returns the property name/index defined by the evaluation of
  35934. // the provided expression, behaving exactly like JavaScript.
  35935. // * [?expression] - This will perform a filter operation on an array, returning all the
  35936. // items in an array that match the provided expression. This operator does not
  35937. // need to be in brackets, you can simply use ?expression, but since it does not
  35938. // have any containment, no operators can be used afterwards when used
  35939. // without brackets.
  35940. // * [^?expression] - This will perform a distinct filter operation on an array. This behaves
  35941. // as [?expression] except that it will remove any duplicate values/objects from the
  35942. // result set.
  35943. // * [/expression], [\expression], [/expression, /expression] - This performs a sort
  35944. // operation on an array, with sort based on the provide expression. Multiple comma delimited sort
  35945. // expressions can be provided for multiple sort orders (first being highest priority). /
  35946. // indicates ascending order and \ indicates descending order
  35947. // * [=expression] - This performs a map operation on an array, creating a new array
  35948. // with each item being the evaluation of the expression for each item in the source array.
  35949. // * [start:end:step] - This performs an array slice/range operation, returning the elements
  35950. // from the optional start index to the optional end index, stepping by the optional step number.
  35951. // * [expr,expr] - This a union operator, returning an array of all the property/index values from
  35952. // the evaluation of the comma delimited expressions.
  35953. // * .* or [*] - This returns the values of all the properties of the current object.
  35954. // * $ - This is the root object, If a JSONQuery expression does not being with a $,
  35955. // it will be auto-inserted at the beginning.
  35956. // * @ - This is the current object in filter, sort, and map expressions. This is generally
  35957. // not necessary, names are auto-converted to property references of the current object
  35958. // in expressions.
  35959. // * ..property - Performs a recursive search for the given property name, returning
  35960. // an array of all values with such a property name in the current object and any subobjects
  35961. // * expr = expr - Performs a comparison (like JS's ==). When comparing to
  35962. // a string, the comparison string may contain wildcards * (matches any number of
  35963. // characters) and ? (matches any single character).
  35964. // * expr ~ expr - Performs a string comparison with case insensitivity.
  35965. // * ..[?expression] - This will perform a deep search filter operation on all the objects and
  35966. // subobjects of the current data. Rather than only searching an array, this will search
  35967. // property values, arrays, and their children.
  35968. // * $1,$2,$3, etc. - These are references to extra parameters passed to the query
  35969. // function or the evaluator function.
  35970. // * +, -, /, *, &, |, %, (, ), <, >, <=, >=, != - These operators behave just as they do
  35971. // in JavaScript.
  35972. //
  35973. //
  35974. //
  35975. // | dojox.json.query(queryString,object)
  35976. // and
  35977. // | dojox.json.query(queryString)(object)
  35978. // always return identical results. The first one immediately evaluates, the second one returns a
  35979. // function that then evaluates the object.
  35980. //
  35981. // example:
  35982. // | dojox.json.query("foo",{foo:"bar"})
  35983. // This will return "bar".
  35984. //
  35985. // example:
  35986. // | evaluator = dojox.json.query("?foo='bar'&rating>3");
  35987. // This creates a function that finds all the objects in an array with a property
  35988. // foo that is equals to "bar" and with a rating property with a value greater
  35989. // than 3.
  35990. // | evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
  35991. // This returns:
  35992. // | {foo:"bar",rating:4}
  35993. //
  35994. // example:
  35995. // | evaluator = dojox.json.query("$[?price<15.00][\rating][0:10]");
  35996. // This finds objects in array with a price less than 15.00 and sorts then
  35997. // by rating, highest rated first, and returns the first ten items in from this
  35998. // filtered and sorted list.
  35999. var depth = 0;
  36000. var str = [];
  36001. query = query.replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){
  36002. depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth
  36003. return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable
  36004. (t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings
  36005. t;
  36006. });
  36007. var prefix = '';
  36008. function call(name){
  36009. // creates a function call and puts the expression so far in a parameter for a call
  36010. prefix = name + "(" + prefix;
  36011. }
  36012. function makeRegex(t,a,b,c,d,e,f,g){
  36013. // creates a regular expression matcher for when wildcards and ignore case is used
  36014. return str[g].match(/[\*\?]/) || f == '~' ?
  36015. "/^" + str[g].substring(1,str[g].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,"[\\w\\W]$1") + (f == '~' ? '$/i' : '$/') + ".test(" + a + ")" :
  36016. t;
  36017. }
  36018. query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){
  36019. throw new Error("Unsafe function call");
  36020. });
  36021. query = query.replace(/([^=]=)([^=])/g,"$1=$2"). // change the equals to comparisons
  36022. replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){
  36023. return t.charAt(0) == '.' ? t : // leave .prop alone
  36024. t == '@' ? "$obj" :// the reference to the current object
  36025. (t.match(/:|^(\$|Math|true|false|null)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer
  36026. }).
  36027. replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){
  36028. var oper = t.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr
  36029. if(oper){
  36030. var prefix = '';
  36031. if(t.match(/^\./)){
  36032. // recursive object search
  36033. call("dojox.json._find");
  36034. prefix = ",true)";
  36035. }
  36036. call(oper[1].match(/\=/) ? "dojo.map" : oper[1].match(/\^/) ? "dojox.json._distinctFilter" : "dojo.filter");
  36037. return prefix + ",function($obj){return " + oper[2] + "})";
  36038. }
  36039. oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr]
  36040. if(oper){
  36041. // make a copy of the array and then sort it using the sorting expression
  36042. return ".concat().sort(function(a,b){" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){
  36043. return "var av= " + b.replace(/\$obj/,"a") + ",bv= " + b.replace(/\$obj/,"b") + // FIXME: Should check to make sure the $obj token isn't followed by characters
  36044. ";if(av>bv||bv==null){return " + (a== "/" ? 1 : -1) +";}\n" +
  36045. "if(bv>av||av==null){return " + (a== "/" ? -1 : 1) +";}\n";
  36046. }) + "return 0;})";
  36047. }
  36048. oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3]
  36049. if(oper){
  36050. call("dojox.json._slice");
  36051. return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")";
  36052. }
  36053. if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*]
  36054. call("dojox.json._find");
  36055. return (t.charAt(1) == '.' ?
  36056. ",'" + b + "'" : // ..prop
  36057. t.match(/,/) ?
  36058. "," + t : // [prop1,prop2]
  36059. "") + ")"; // [*]
  36060. }
  36061. return t;
  36062. }).
  36063. replace(/(\$obj\s*((\.\s*[\w_$]+\s*)|(\[\s*`([0-9]+)\s*`\]))*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching
  36064. replace(/`([0-9]+)\s*(==|~)\s*(\$obj\s*((\.\s*[\w_$]+)|(\[\s*`([0-9]+)\s*`\]))*)/g,function(t,a,b,c,d,e,f,g){ // and do it for reverse =
  36065. return makeRegex(t,c,d,e,f,g,b,a);
  36066. });
  36067. query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){
  36068. //restore the strings
  36069. return a == ']' ? ']' : str[a];
  36070. });
  36071. // create a function within this scope (so it can use expand and slice)
  36072. var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}");
  36073. for(var i = 0;i<arguments.length-1;i++){
  36074. arguments[i] = arguments[i+1];
  36075. }
  36076. return obj ? executor.apply(this,arguments) : executor;
  36077. };
  36078. })();
  36079. }
  36080. if(!dojo._hasResource["dojox.html._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  36081. dojo._hasResource["dojox.html._base"] = true;
  36082. /*
  36083. Status: dont know where this will all live exactly
  36084. Need to pull in the implementation of the various helper methods
  36085. Some can be static method, others maybe methods of the ContentSetter (?)
  36086. Gut the ContentPane, replace its _setContent with our own call to dojox.html.set()
  36087. */
  36088. dojo.provide("dojox.html._base");
  36089. (function() {
  36090. if(dojo.isIE){
  36091. var alphaImageLoader = /(AlphaImageLoader\([^)]*?src=(['"]))(?![a-z]+:|\/)([^\r\n;}]+?)(\2[^)]*\)\s*[;}]?)/g;
  36092. }
  36093. // css at-rules must be set before any css declarations according to CSS spec
  36094. // match:
  36095. // @import 'http://dojotoolkit.org/dojo.css';
  36096. // @import 'you/never/thought/' print;
  36097. // @import url("it/would/work") tv, screen;
  36098. // @import url(/did/you/now.css);
  36099. // but not:
  36100. // @namespace dojo "http://dojotoolkit.org/dojo.css"; /* namespace URL should always be a absolute URI */
  36101. // @charset 'utf-8';
  36102. // @media print{ #menuRoot {display:none;} }
  36103. // we adjust all paths that dont start on '/' or contains ':'
  36104. //(?![a-z]+:|\/)
  36105. var cssPaths = /(?:(?:@import\s*(['"])(?![a-z]+:|\/)([^\r\n;{]+?)\1)|url\(\s*(['"]?)(?![a-z]+:|\/)([^\r\n;]+?)\3\s*\))([a-z, \s]*[;}]?)/g;
  36106. var adjustCssPaths = dojox.html._adjustCssPaths = function(cssUrl, cssText){
  36107. // summary:
  36108. // adjusts relative paths in cssText to be relative to cssUrl
  36109. // a path is considered relative if it doesn't start with '/' and not contains ':'
  36110. // description:
  36111. // Say we fetch a HTML page from level1/page.html
  36112. // It has some inline CSS:
  36113. // @import "css/page.css" tv, screen;
  36114. // ...
  36115. // background-image: url(images/aplhaimage.png);
  36116. //
  36117. // as we fetched this HTML and therefore this CSS
  36118. // from level1/page.html, these paths needs to be adjusted to:
  36119. // @import 'level1/css/page.css' tv, screen;
  36120. // ...
  36121. // background-image: url(level1/images/alphaimage.png);
  36122. //
  36123. // In IE it will also adjust relative paths in AlphaImageLoader()
  36124. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/alphaimage.png');
  36125. // will be adjusted to:
  36126. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='level1/images/alphaimage.png');
  36127. //
  36128. // Please note that any relative paths in AlphaImageLoader in external css files wont work, as
  36129. // the paths in AlphaImageLoader is MUST be declared relative to the HTML page,
  36130. // not relative to the CSS file that declares it
  36131. if(!cssText || !cssUrl){ return; }
  36132. // support the ImageAlphaFilter if it exists, most people use it in IE 6 for transparent PNGs
  36133. // We are NOT going to kill it in IE 7 just because the PNGs work there. Somebody might have
  36134. // other uses for it.
  36135. // If user want to disable css filter in IE6 he/she should
  36136. // unset filter in a declaration that just IE 6 doesn't understands
  36137. // like * > .myselector { filter:none; }
  36138. if(alphaImageLoader){
  36139. cssText = cssText.replace(alphaImageLoader, function(ignore, pre, delim, url, post){
  36140. return pre + (new dojo._Url(cssUrl, './'+url).toString()) + post;
  36141. });
  36142. }
  36143. return cssText.replace(cssPaths, function(ignore, delimStr, strUrl, delimUrl, urlUrl, media){
  36144. if(strUrl){
  36145. return '@import "' + (new dojo._Url(cssUrl, './'+strUrl).toString()) + '"' + media;
  36146. }else{
  36147. return 'url(' + (new dojo._Url(cssUrl, './'+urlUrl).toString()) + ')' + media;
  36148. }
  36149. });
  36150. };
  36151. // attributepaths one tag can have multiple paths, example:
  36152. // <input src="..." style="url(..)"/> or <a style="url(..)" href="..">
  36153. // <img style='filter:progid...AlphaImageLoader(src="noticeTheSrcHereRunsThroughHtmlSrc")' src="img">
  36154. var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi;
  36155. var adjustHtmlPaths = dojox.html._adjustHtmlPaths = function(htmlUrl, cont){
  36156. var url = htmlUrl || "./";
  36157. return cont.replace(htmlAttrPaths,
  36158. function(tag, start, name, delim, relUrl, delim2, cssText, end){
  36159. return start + (name ?
  36160. (name + '=' + delim + (new dojo._Url(url, relUrl).toString()) + delim)
  36161. : ('style=' + delim2 + adjustCssPaths(url, cssText) + delim2)
  36162. ) + end;
  36163. }
  36164. );
  36165. };
  36166. var snarfStyles = dojox.html._snarfStyles = function (/*String*/cssUrl, /*String*/cont, /*Array*/styles){
  36167. /**************** cut out all <style> and <link rel="stylesheet" href=".."> **************/
  36168. // also return any attributes from this tag (might be a media attribute)
  36169. // if cssUrl is set it will adjust paths accordingly
  36170. styles.attributes = [];
  36171. return cont.replace(/(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi,
  36172. function(ignore, styleAttr, cssText, linkAttr, delim, href){
  36173. // trim attribute
  36174. var i, attr = (styleAttr||linkAttr||"").replace(/^\s*([\s\S]*?)\s*$/i, "$1");
  36175. if(cssText){
  36176. i = styles.push(cssUrl ? adjustCssPaths(cssUrl, cssText) : cssText);
  36177. }else{
  36178. i = styles.push('@import "' + href + '";');
  36179. attr = attr.replace(/\s*(?:rel|href)=(['"])?[^\s]*\1\s*/gi, ""); // remove rel=... and href=...
  36180. }
  36181. if(attr){
  36182. attr = attr.split(/\s+/);// split on both "\n", "\t", " " etc
  36183. var atObj = {}, tmp;
  36184. for(var j = 0, e = attr.length; j < e; j++){
  36185. tmp = attr[j].split('='); // split name='value'
  36186. atObj[tmp[0]] = tmp[1].replace(/^\s*['"]?([\s\S]*?)['"]?\s*$/, "$1"); // trim and remove ''
  36187. }
  36188. styles.attributes[i - 1] = atObj;
  36189. }
  36190. return "";
  36191. }
  36192. );
  36193. };
  36194. var snarfScripts = dojox.html._snarfScripts = function(cont, byRef){
  36195. // summary
  36196. // strips out script tags from cont
  36197. // invoke with
  36198. // byRef = {errBack:function(){/*add your download error code here*/, downloadRemote: true(default false)}}
  36199. // byRef will have {code: 'jscode'} when this scope leaves
  36200. byRef.code = "";
  36201. //Update script tags nested in comments so that the script tag collector doesn't pick
  36202. //them up.
  36203. cont = cont.replace(/<[!][-][-](.|\s)*?[-][-]>/g,
  36204. function(comment){
  36205. return comment.replace(/<(\/?)script\b/ig,"&lt;$1Script");
  36206. }
  36207. );
  36208. function download(src){
  36209. if(byRef.downloadRemote){
  36210. // console.debug('downloading',src);
  36211. //Fix up src, in case there were entity character encodings in it.
  36212. //Probably only need to worry about a subset.
  36213. src = src.replace(/&([a-z0-9#]+);/g, function(m, name) {
  36214. switch(name) {
  36215. case "amp" : return "&";
  36216. case "gt" : return ">";
  36217. case "lt" : return "<";
  36218. default:
  36219. return name.charAt(0)=="#" ? String.fromCharCode(name.substring(1)) : "&"+name+";";
  36220. }
  36221. });
  36222. dojo.xhrGet({
  36223. url: src,
  36224. sync: true,
  36225. load: function(code){
  36226. byRef.code += code+";";
  36227. },
  36228. error: byRef.errBack
  36229. });
  36230. }
  36231. }
  36232. // match <script>, <script type="text/..., but not <script type="dojo(/method)...
  36233. return cont.replace(/<script\s*(?![^>]*type=['"]?(?:dojo\/|text\/html\b))(?:[^>]*?(?:src=(['"]?)([^>]*?)\1[^>]*)?)*>([\s\S]*?)<\/script>/gi,
  36234. function(ignore, delim, src, code){
  36235. if(src){
  36236. download(src);
  36237. }else{
  36238. byRef.code += code;
  36239. }
  36240. return "";
  36241. }
  36242. );
  36243. };
  36244. var evalInGlobal = dojox.html.evalInGlobal = function(code, appendNode){
  36245. // we do our own eval here as dojo.eval doesn't eval in global crossbrowser
  36246. // This work X browser but but it relies on a DOM
  36247. // plus it doesn't return anything, thats unrelevant here but not for dojo core
  36248. appendNode = appendNode || dojo.doc.body;
  36249. var n = appendNode.ownerDocument.createElement('script');
  36250. n.type = "text/javascript";
  36251. appendNode.appendChild(n);
  36252. n.text = code; // DOM 1 says this should work
  36253. };
  36254. dojo.declare("dojox.html._ContentSetter", [dojo.html._ContentSetter], {
  36255. // adjustPaths: Boolean
  36256. // Adjust relative paths in html string content to point to this page
  36257. // Only useful if you grab content from a another folder than the current one
  36258. adjustPaths: false,
  36259. referencePath: ".",
  36260. renderStyles: false,
  36261. executeScripts: false,
  36262. scriptHasHooks: false,
  36263. scriptHookReplacement: null,
  36264. _renderStyles: function(styles){
  36265. // insert css from content into document head
  36266. this._styleNodes = [];
  36267. var st, att, cssText, doc = this.node.ownerDocument;
  36268. var head = doc.getElementsByTagName('head')[0];
  36269. for(var i = 0, e = styles.length; i < e; i++){
  36270. cssText = styles[i]; att = styles.attributes[i];
  36271. st = doc.createElement('style');
  36272. st.setAttribute("type", "text/css"); // this is required in CSS spec!
  36273. for(var x in att){
  36274. st.setAttribute(x, att[x]);
  36275. }
  36276. this._styleNodes.push(st);
  36277. head.appendChild(st); // must insert into DOM before setting cssText
  36278. if(st.styleSheet){ // IE
  36279. st.styleSheet.cssText = cssText;
  36280. }else{ // w3c
  36281. st.appendChild(doc.createTextNode(cssText));
  36282. }
  36283. }
  36284. },
  36285. empty: function() {
  36286. this.inherited("empty", arguments);
  36287. // empty out the styles array from any previous use
  36288. this._styles = [];
  36289. },
  36290. onBegin: function() {
  36291. // summary
  36292. // Called after instantiation, but before set();
  36293. // It allows modification of any of the object properties - including the node and content
  36294. // provided - before the set operation actually takes place
  36295. // This implementation extends that of dojo.html._ContentSetter
  36296. // to add handling for adjustPaths, renderStyles on the html string content before it is set
  36297. this.inherited("onBegin", arguments);
  36298. var cont = this.content,
  36299. node = this.node;
  36300. var styles = this._styles;// init vars
  36301. if(dojo.isString(cont)){
  36302. if(this.adjustPaths && this.referencePath){
  36303. cont = adjustHtmlPaths(this.referencePath, cont);
  36304. }
  36305. if(this.renderStyles || this.cleanContent){
  36306. cont = snarfStyles(this.referencePath, cont, styles);
  36307. }
  36308. // because of a bug in IE, script tags that is first in html hierarchy doesnt make it into the DOM
  36309. // when content is innerHTML'ed, so we can't use dojo.query to retrieve scripts from DOM
  36310. if(this.executeScripts){
  36311. var _t = this;
  36312. var byRef = {
  36313. downloadRemote: true,
  36314. errBack:function(e){
  36315. _t._onError.call(_t, 'Exec', 'Error downloading remote script in "'+_t.id+'"', e);
  36316. }
  36317. };
  36318. cont = snarfScripts(cont, byRef);
  36319. this._code = byRef.code;
  36320. }
  36321. }
  36322. this.content = cont;
  36323. },
  36324. onEnd: function() {
  36325. // summary
  36326. // Called after set(), when the new content has been pushed into the node
  36327. // It provides an opportunity for post-processing before handing back the node to the caller
  36328. // This implementation extends that of dojo.html._ContentSetter
  36329. var code = this._code,
  36330. styles = this._styles;
  36331. // clear old stylenodes from the DOM
  36332. // these were added by the last set call
  36333. // (in other words, if you dont keep and reuse the ContentSetter for a particular node
  36334. // .. you'll have no practical way to do this)
  36335. if(this._styleNodes && this._styleNodes.length){
  36336. while(this._styleNodes.length){
  36337. dojo.destroy(this._styleNodes.pop());
  36338. }
  36339. }
  36340. // render new style nodes
  36341. if(this.renderStyles && styles && styles.length){
  36342. this._renderStyles(styles);
  36343. }
  36344. if(this.executeScripts && code){
  36345. if(this.cleanContent){
  36346. // clean JS from html comments and other crap that browser
  36347. // parser takes care of in a normal page load
  36348. code = code.replace(/(<!--|(?:\/\/)?-->|<!\[CDATA\[|\]\]>)/g, '');
  36349. }
  36350. if(this.scriptHasHooks){
  36351. // replace _container_ with this.scriptHookReplace()
  36352. // the scriptHookReplacement can be a string
  36353. // or a function, which when invoked returns the string you want to substitute in
  36354. code = code.replace(/_container_(?!\s*=[^=])/g, this.scriptHookReplacement);
  36355. }
  36356. try{
  36357. evalInGlobal(code, this.node);
  36358. }catch(e){
  36359. this._onError('Exec', 'Error eval script in '+this.id+', '+e.message, e);
  36360. }
  36361. }
  36362. this.inherited("onEnd", arguments);
  36363. },
  36364. tearDown: function() {
  36365. this.inherited(arguments);
  36366. delete this._styles;
  36367. // only tear down -or another set() - will explicitly throw away the
  36368. // references to the style nodes we added
  36369. if(this._styleNodes && this._styleNodes.length){
  36370. while(this._styleNodes.length){
  36371. dojo.destroy(this._styleNodes.pop());
  36372. }
  36373. }
  36374. delete this._styleNodes;
  36375. // reset the defaults from the prototype
  36376. dojo.mixin(this, dojo.getObject(this.declaredClass).prototype);
  36377. }
  36378. });
  36379. dojox.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
  36380. // TODO: add all the other options
  36381. // summary:
  36382. // inserts (replaces) the given content into the given node
  36383. // node:
  36384. // the parent element that will receive the content
  36385. // cont:
  36386. // the content to be set on the parent element.
  36387. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  36388. // params:
  36389. // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
  36390. // example:
  36391. // A safe string/node/nodelist content replacement/injection with hooks for extension
  36392. // Example Usage:
  36393. // dojo.html.set(node, "some string");
  36394. // dojo.html.set(node, contentNode, {options});
  36395. // dojo.html.set(node, myNode.childNodes, {options});
  36396. if(!params){
  36397. // simple and fast
  36398. return dojo.html._setNodeContent(node, cont, true);
  36399. }else{
  36400. // more options but slower
  36401. var op = new dojox.html._ContentSetter(dojo.mixin(
  36402. params,
  36403. { content: cont, node: node }
  36404. ));
  36405. return op.set();
  36406. }
  36407. };
  36408. })();
  36409. }
  36410. if(!dojo._hasResource["dojox.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  36411. dojo._hasResource["dojox.layout.ContentPane"] = true;
  36412. dojo.provide("dojox.layout.ContentPane");
  36413. dojo.declare("dojox.layout.ContentPane", dijit.layout.ContentPane, {
  36414. // summary:
  36415. // An extended version of dijit.layout.ContentPane.
  36416. // Supports infile scripts and external ones declared by <script src=''
  36417. // relative path adjustments (content fetched from a different folder)
  36418. // <style> and <link rel='stylesheet' href='..'> tags,
  36419. // css paths inside cssText is adjusted (if you set adjustPaths = true)
  36420. //
  36421. // NOTE that dojo.require in script in the fetched file isn't recommended
  36422. // Many widgets need to be required at page load to work properly
  36423. // adjustPaths: Boolean
  36424. // Adjust relative paths in html string content to point to this page.
  36425. // Only useful if you grab content from a another folder then the current one
  36426. adjustPaths: false,
  36427. // cleanContent: Boolean
  36428. // summary:
  36429. // cleans content to make it less likely to generate DOM/JS errors.
  36430. // description:
  36431. // useful if you send ContentPane a complete page, instead of a html fragment
  36432. // scans for
  36433. //
  36434. // * title Node, remove
  36435. // * DOCTYPE tag, remove
  36436. cleanContent: false,
  36437. // renderStyles: Boolean
  36438. // trigger/load styles in the content
  36439. renderStyles: false,
  36440. // executeScripts: Boolean
  36441. // Execute (eval) scripts that is found in the content
  36442. executeScripts: true,
  36443. // scriptHasHooks: Boolean
  36444. // replace keyword '_container_' in scripts with 'dijit.byId(this.id)'
  36445. // NOTE this name might change in the near future
  36446. scriptHasHooks: false,
  36447. /*======
  36448. // ioMethod: dojo.xhrGet|dojo.xhrPost
  36449. // reference to the method that should grab the content
  36450. ioMethod: dojo.xhrGet,
  36451. // ioArgs: Object
  36452. // makes it possible to add custom args to xhrGet, like ioArgs.headers['X-myHeader'] = 'true'
  36453. ioArgs: {},
  36454. ======*/
  36455. constructor: function(){
  36456. // init per instance properties, initializer doesn't work here because how things is hooked up in dijit._Widget
  36457. this.ioArgs = {};
  36458. this.ioMethod = dojo.xhrGet;
  36459. },
  36460. onExecError: function(e){
  36461. // summary:
  36462. // event callback, called on script error or on java handler error
  36463. // overide and return your own html string if you want a some text
  36464. // displayed within the ContentPane
  36465. },
  36466. _setContent: function(cont){
  36467. // override dijit.layout.ContentPane._setContent, to enable path adjustments
  36468. var setter = this._contentSetter;
  36469. if(! (setter && setter instanceof dojox.html._ContentSetter)) {
  36470. setter = this._contentSetter = new dojox.html._ContentSetter({
  36471. node: this.containerNode,
  36472. _onError: dojo.hitch(this, this._onError),
  36473. onContentError: dojo.hitch(this, function(e){
  36474. // fires if a domfault occurs when we are appending this.errorMessage
  36475. // like for instance if domNode is a UL and we try append a DIV
  36476. var errMess = this.onContentError(e);
  36477. try{
  36478. this.containerNode.innerHTML = errMess;
  36479. }catch(e){
  36480. console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
  36481. }
  36482. })/*,
  36483. _onError */
  36484. });
  36485. };
  36486. // stash the params for the contentSetter to allow inheritance to work for _setContent
  36487. this._contentSetterParams = {
  36488. adjustPaths: Boolean(this.adjustPaths && (this.href||this.referencePath)),
  36489. referencePath: this.href || this.referencePath,
  36490. renderStyles: this.renderStyles,
  36491. executeScripts: this.executeScripts,
  36492. scriptHasHooks: this.scriptHasHooks,
  36493. scriptHookReplacement: "dijit.byId('"+this.id+"')"
  36494. };
  36495. this.inherited("_setContent", arguments);
  36496. }
  36497. // could put back _renderStyles by wrapping/aliasing dojox.html._ContentSetter.prototype._renderStyles
  36498. });
  36499. }
  36500. if(!dojo._hasResource["dojox.layout.ResizeHandle"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  36501. dojo._hasResource["dojox.layout.ResizeHandle"] = true;
  36502. dojo.provide("dojox.layout.ResizeHandle");
  36503. dojo.experimental("dojox.layout.ResizeHandle");
  36504. dojo.declare("dojox.layout.ResizeHandle",
  36505. [dijit._Widget, dijit._Templated],
  36506. {
  36507. // summary: A dragable handle used to resize an attached node.
  36508. //
  36509. // description:
  36510. // The handle on the bottom-right corner of FloatingPane or other widgets that allows
  36511. // the widget to be resized.
  36512. // Typically not used directly.
  36513. //
  36514. // targetId: String
  36515. // id of the Widget OR DomNode that I will size
  36516. targetId: "",
  36517. // targetContainer: DomNode
  36518. // over-ride targetId and attch this handle directly to a reference of a DomNode
  36519. targetContainer: null,
  36520. // resizeAxis: String
  36521. // one of: x|y|xy limit resizing to a single axis, default to xy ...
  36522. resizeAxis: "xy",
  36523. // activeResize: Boolean
  36524. // if true, node will size realtime with mouse movement,
  36525. // if false, node will create virtual node, and only resize target on mouseUp
  36526. activeResize: false,
  36527. // activeResizeClass: String
  36528. // css class applied to virtual resize node.
  36529. activeResizeClass: "dojoxResizeHandleClone",
  36530. // animateSizing: Boolean
  36531. // only applicable if activeResize = false. onMouseup, animate the node to the
  36532. // new size
  36533. animateSizing: true,
  36534. // animateMethod: String
  36535. // one of "chain" or "combine" ... visual effect only. combine will "scale"
  36536. // node to size, "chain" will alter width, then height
  36537. animateMethod: "chain",
  36538. // animateDuration: Integer
  36539. // time in MS to run sizing animation. if animateMethod="chain", total animation
  36540. // playtime is 2*animateDuration
  36541. animateDuration: 225,
  36542. // minHeight: Integer
  36543. // smallest height in px resized node can be
  36544. minHeight: 100,
  36545. // minWidth: Integer
  36546. // smallest width in px resize node can be
  36547. minWidth: 100,
  36548. // constrainMax: Boolean
  36549. // Toggle if this widget cares about the maxHeight and maxWidth
  36550. // parameters.
  36551. constrainMax: false,
  36552. // maxHeight: Integer
  36553. // Largest height size in px the resize node can become.
  36554. maxHeight:0,
  36555. // maxWidth: Integer
  36556. // Largest width size in px the reize node can become.
  36557. maxWidth:0,
  36558. // fixedAspect: Boolean
  36559. // Toggle to enable this widget to maintain the aspect
  36560. // ratio of the attached node.
  36561. fixedAspect: false,
  36562. // intermediateChanges: Boolean
  36563. // Toggle to enable/disable this widget from firing onResize
  36564. // events at every step of a resize. If `activeResize` is true,
  36565. // and this is false, onResize only fires _after_ the drop
  36566. // operation. Animated resizing is not affected by this setting.
  36567. intermediateChanges: false,
  36568. // startTopic: String
  36569. // The name of the topic this resizehandle publishes when resize is starting
  36570. startTopic: "/dojo/resize/start",
  36571. // endTopic: String
  36572. // The name of the topic this resizehandle publishes when resize is complete
  36573. endTopic:"/dojo/resize/stop",
  36574. templateString: '<div dojoAttachPoint="resizeHandle" class="dojoxResizeHandle"><div></div></div>',
  36575. postCreate: function(){
  36576. // summary: setup our one major listener upon creation
  36577. this.connect(this.resizeHandle, "onmousedown", "_beginSizing");
  36578. if(!this.activeResize){
  36579. // there shall be only a single resize rubberbox that at the top
  36580. // level so that we can overlay it on anything whenever the user
  36581. // resizes something. Since there is only one mouse pointer he
  36582. // can't at once resize multiple things interactively.
  36583. this._resizeHelper = dijit.byId('dojoxGlobalResizeHelper');
  36584. if(!this._resizeHelper){
  36585. this._resizeHelper = new dojox.layout._ResizeHelper({
  36586. id: 'dojoxGlobalResizeHelper'
  36587. }).placeAt(dojo.body());
  36588. dojo.addClass(this._resizeHelper.domNode, this.activeResizeClass);
  36589. }
  36590. }else{ this.animateSizing = false; }
  36591. if(!this.minSize){
  36592. this.minSize = { w: this.minWidth, h: this.minHeight };
  36593. }
  36594. if(this.constrainMax){
  36595. this.maxSize = { w: this.maxWidth, h: this.maxHeight }
  36596. }
  36597. // should we modify the css for the cursor hover to n-resize nw-resize and w-resize?
  36598. this._resizeX = this._resizeY = false;
  36599. var addClass = dojo.partial(dojo.addClass, this.resizeHandle);
  36600. switch(this.resizeAxis.toLowerCase()){
  36601. case "xy" :
  36602. this._resizeX = this._resizeY = true;
  36603. // FIXME: need logic to determine NW or NE class to see
  36604. // based on which [todo] corner is clicked
  36605. addClass("dojoxResizeNW");
  36606. break;
  36607. case "x" :
  36608. this._resizeX = true;
  36609. addClass("dojoxResizeW");
  36610. break;
  36611. case "y" :
  36612. this._resizeY = true;
  36613. addClass("dojoxResizeN");
  36614. break;
  36615. }
  36616. },
  36617. _beginSizing: function(/*Event*/ e){
  36618. // summary: setup movement listeners and calculate initial size
  36619. if(this._isSizing){ return false; }
  36620. dojo.publish(this.startTopic, [ this ]);
  36621. this.targetWidget = dijit.byId(this.targetId);
  36622. this.targetDomNode = this.targetWidget ? this.targetWidget.domNode : dojo.byId(this.targetId);
  36623. if(this.targetContainer){ this.targetDomNode = this.targetContainer; }
  36624. if(!this.targetDomNode){ return false; }
  36625. if(!this.activeResize){
  36626. var c = dojo.position(this.targetDomNode, true);
  36627. console.log(c);
  36628. console.log(dojo.window.getBox());
  36629. this._resizeHelper.resize({l: c.x, t: c.y, w: c.w, h: c.h});
  36630. this._resizeHelper.show();
  36631. if(!this.isLeftToRight()){
  36632. this._resizeHelper.startPosition = {l: c.x, t: c.y};
  36633. }
  36634. }
  36635. this._isSizing = true;
  36636. this.startPoint = { x:e.clientX, y:e.clientY};
  36637. // FIXME: this is funky: marginBox adds height, contentBox ignores padding (expected, but foo!)
  36638. var mb = this.targetWidget ? dojo.marginBox(this.targetDomNode) : dojo.contentBox(this.targetDomNode);
  36639. this.startSize = { w:mb.w, h:mb.h };
  36640. if(!this.isLeftToRight() && dojo.style(this.targetDomNode, "position") == "absolute"){
  36641. this.startPosition = {l: mb.l, t: mb.t};
  36642. }
  36643. if(this.fixedAspect){
  36644. var max, val;
  36645. if(mb.w > mb.h){
  36646. max = "w";
  36647. val = mb.w / mb.h
  36648. }else{
  36649. max = "h";
  36650. val = mb.h / mb.w
  36651. }
  36652. this._aspect = { prop: max };
  36653. this._aspect[max] = val;
  36654. }
  36655. this._pconnects = [];
  36656. this._pconnects.push(dojo.connect(dojo.doc,"onmousemove",this,"_updateSizing"));
  36657. this._pconnects.push(dojo.connect(dojo.doc,"onmouseup", this, "_endSizing"));
  36658. dojo.stopEvent(e);
  36659. },
  36660. _updateSizing: function(/*Event*/ e){
  36661. // summary: called when moving the ResizeHandle ... determines
  36662. // new size based on settings/position and sets styles.
  36663. if(this.activeResize){
  36664. this._changeSizing(e);
  36665. }else{
  36666. var tmp = this._getNewCoords(e, this._resizeHelper.startPosition);
  36667. if(tmp === false){ return; }
  36668. this._resizeHelper.resize(tmp);
  36669. }
  36670. e.preventDefault();
  36671. },
  36672. _getNewCoords: function(/* Event */ e, /* Object */startPosition){
  36673. // On IE, if you move the mouse above/to the left of the object being resized,
  36674. // sometimes clientX/Y aren't set, apparently. Just ignore the event.
  36675. try{
  36676. if(!e.clientX || !e.clientY){ return false; }
  36677. }catch(e){
  36678. // sometimes you get an exception accessing above fields...
  36679. return false;
  36680. }
  36681. this._activeResizeLastEvent = e;
  36682. var dx = (this.isLeftToRight()? this.startPoint.x - e.clientX: e.clientX - this.startPoint.x),
  36683. dy = this.startPoint.y - e.clientY,
  36684. newW = this.startSize.w - (this._resizeX ? dx : 0),
  36685. newH = this.startSize.h - (this._resizeY ? dy : 0)
  36686. ;
  36687. var newSize = this._checkConstraints(newW, newH); // Object
  36688. startPosition = (startPosition || this.startPosition);
  36689. if(newSize && startPosition && this._resizeX){
  36690. // adjust x position for RtoL
  36691. newSize.l = startPosition.l + dx;
  36692. if(newSize.w != newW){
  36693. newSize.l += (newW - newSize.w);
  36694. }
  36695. newSize.t = startPosition.t;
  36696. }
  36697. return newSize;
  36698. },
  36699. _checkConstraints: function(newW, newH){
  36700. // summary: filter through the various possible constaint possibilities.
  36701. // minimum size check
  36702. if(this.minSize){
  36703. var tm = this.minSize;
  36704. if(newW < tm.w){
  36705. newW = tm.w;
  36706. }
  36707. if(newH < tm.h){
  36708. newH = tm.h;
  36709. }
  36710. }
  36711. // maximum size check:
  36712. if(this.constrainMax && this.maxSize){
  36713. var ms = this.maxSize;
  36714. if(newW > ms.w){
  36715. newW = ms.w;
  36716. }
  36717. if(newH > ms.h){
  36718. newH = ms.h;
  36719. }
  36720. }
  36721. if(this.fixedAspect){
  36722. var ta = this._aspect[this._aspect.prop];
  36723. if(newW < newH){
  36724. newH = newW * ta;
  36725. }else if(newH < newW){
  36726. newW = newH * ta;
  36727. }
  36728. }
  36729. return { w: newW, h: newH }; // Object
  36730. },
  36731. _changeSizing: function(/*Event*/ e){
  36732. // summary: apply sizing information based on information in (e) to attached node
  36733. var tmp = this._getNewCoords(e);
  36734. if(tmp === false){ return; }
  36735. if(this.targetWidget && dojo.isFunction(this.targetWidget.resize)){
  36736. this.targetWidget.resize(tmp);
  36737. }else{
  36738. if(this.animateSizing){
  36739. var anim = dojo.fx[this.animateMethod]([
  36740. dojo.animateProperty({
  36741. node: this.targetDomNode,
  36742. properties: {
  36743. width: { start: this.startSize.w, end: tmp.w }
  36744. },
  36745. duration: this.animateDuration
  36746. }),
  36747. dojo.animateProperty({
  36748. node: this.targetDomNode,
  36749. properties: {
  36750. height: { start: this.startSize.h, end: tmp.h }
  36751. },
  36752. duration: this.animateDuration
  36753. })
  36754. ]);
  36755. anim.play();
  36756. }else{
  36757. dojo.style(this.targetDomNode,{
  36758. width: tmp.w + "px",
  36759. height: tmp.h + "px"
  36760. });
  36761. }
  36762. }
  36763. if(this.intermediateChanges){
  36764. this.onResize(e);
  36765. }
  36766. },
  36767. _endSizing: function(/*Event*/ e){
  36768. // summary: disconnect listenrs and cleanup sizing
  36769. dojo.forEach(this._pconnects, dojo.disconnect);
  36770. var pub = dojo.partial(dojo.publish, this.endTopic, [ this ]);
  36771. if(!this.activeResize){
  36772. this._resizeHelper.hide();
  36773. this._changeSizing(e);
  36774. setTimeout(pub, this.animateDuration + 15);
  36775. }else{
  36776. pub();
  36777. }
  36778. this._isSizing = false;
  36779. this.onResize(e);
  36780. },
  36781. onResize: function(e){
  36782. // summary: Stub fired when sizing is done. Fired once
  36783. // after resize, or often when `intermediateChanges` is
  36784. // set to true.
  36785. }
  36786. });
  36787. dojo.declare("dojox.layout._ResizeHelper",
  36788. dijit._Widget,
  36789. {
  36790. // summary: A global private resize helper shared between any
  36791. // `dojox.layout.ResizeHandle` with activeSizing off.
  36792. show: function(){
  36793. // summary: show helper to start resizing
  36794. dojo.fadeIn({
  36795. node: this.domNode,
  36796. duration: 120,
  36797. beforeBegin: function(n){ dojo.style(n, "display", "") }
  36798. }).play();
  36799. },
  36800. hide: function(){
  36801. // summary: hide helper after resizing is complete
  36802. dojo.fadeOut({
  36803. node: this.domNode,
  36804. duration: 250,
  36805. onEnd: function(n){ dojo.style(n, "display", "none") }
  36806. }).play();
  36807. },
  36808. resize: function(/* Object */dim){
  36809. // summary: size the widget and place accordingly
  36810. // FIXME: this is off when padding present
  36811. dojo.marginBox(this.domNode, dim);
  36812. }
  36813. });
  36814. }
  36815. if(!dojo._hasResource["dojox.layout.FloatingPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  36816. dojo._hasResource["dojox.layout.FloatingPane"] = true;
  36817. dojo.provide("dojox.layout.FloatingPane");
  36818. dojo.experimental("dojox.layout.FloatingPane");
  36819. dojo.declare("dojox.layout.FloatingPane",
  36820. [ dojox.layout.ContentPane, dijit._Templated ],
  36821. {
  36822. // summary:
  36823. // A non-modal Floating window.
  36824. //
  36825. // description:
  36826. // Makes a `dojox.layout.ContentPane` float and draggable by it's title [similar to TitlePane]
  36827. // and over-rides onClick to onDblClick for wipeIn/Out of containerNode
  36828. // provides minimize(dock) / show() and hide() methods, and resize [almost]
  36829. //
  36830. // closable: Boolean
  36831. // Allow closure of this Node
  36832. closable: true,
  36833. // dockable: Boolean
  36834. // Allow minimizing of pane if true
  36835. dockable: true,
  36836. // resizable: Boolean
  36837. // Allow resizing of pane true if true
  36838. resizable: false,
  36839. // maxable: Boolean
  36840. // Horrible param name for "Can you maximize this floating pane?"
  36841. maxable: false,
  36842. // resizeAxis: String
  36843. // One of: x | xy | y to limit pane's sizing direction
  36844. resizeAxis: "xy",
  36845. // title: String
  36846. // Title to use in the header
  36847. title: "",
  36848. // dockTo: DomNode?
  36849. // if empty, will create private layout.Dock that scrolls with viewport
  36850. // on bottom span of viewport.
  36851. dockTo: "",
  36852. // duration: Integer
  36853. // Time is MS to spend toggling in/out node
  36854. duration: 400,
  36855. /*=====
  36856. // iconSrc: String
  36857. // [not implemented yet] will be either icon in titlepane to left
  36858. // of Title, and/or icon show when docked in a fisheye-like dock
  36859. // or maybe dockIcon would be better?
  36860. iconSrc: null,
  36861. =====*/
  36862. // contentClass: String
  36863. // The className to give to the inner node which has the content
  36864. contentClass: "dojoxFloatingPaneContent",
  36865. // animation holders for toggle
  36866. _showAnim: null,
  36867. _hideAnim: null,
  36868. // node in the dock (if docked)
  36869. _dockNode: null,
  36870. // privates:
  36871. _restoreState: {},
  36872. _allFPs: [],
  36873. _startZ: 100,
  36874. templateString: dojo.cache("dojox.layout", "resources/FloatingPane.html", "<div class=\"dojoxFloatingPane\" id=\"${id}\">\n\t<div tabindex=\"0\" role=\"button\" class=\"dojoxFloatingPaneTitle\" dojoAttachPoint=\"focusNode\">\n\t\t<span dojoAttachPoint=\"closeNode\" dojoAttachEvent=\"onclick: close\" class=\"dojoxFloatingCloseIcon\"></span>\n\t\t<span dojoAttachPoint=\"maxNode\" dojoAttachEvent=\"onclick: maximize\" class=\"dojoxFloatingMaximizeIcon\">&thinsp;</span>\n\t\t<span dojoAttachPoint=\"restoreNode\" dojoAttachEvent=\"onclick: _restore\" class=\"dojoxFloatingRestoreIcon\">&thinsp;</span>\t\n\t\t<span dojoAttachPoint=\"dockNode\" dojoAttachEvent=\"onclick: minimize\" class=\"dojoxFloatingMinimizeIcon\">&thinsp;</span>\n\t\t<span dojoAttachPoint=\"titleNode\" class=\"dijitInline dijitTitleNode\"></span>\n\t</div>\n\t<div dojoAttachPoint=\"canvas\" class=\"dojoxFloatingPaneCanvas\">\n\t\t<div dojoAttachPoint=\"containerNode\" role=\"region\" tabindex=\"-1\" class=\"${contentClass}\">\n\t\t</div>\n\t\t<span dojoAttachPoint=\"resizeHandle\" class=\"dojoxFloatingResizeHandle\"></span>\n\t</div>\n</div>\n"),
  36875. attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
  36876. title: { type:"innerHTML", node:"titleNode" }
  36877. }),
  36878. postCreate: function(){
  36879. this.inherited(arguments);
  36880. new dojo.dnd.Moveable(this.domNode,{ handle: this.focusNode });
  36881. //this._listener = dojo.subscribe("/dnd/move/start",this,"bringToTop");
  36882. if(!this.dockable){ this.dockNode.style.display = "none"; }
  36883. if(!this.closable){ this.closeNode.style.display = "none"; }
  36884. if(!this.maxable){
  36885. this.maxNode.style.display = "none";
  36886. this.restoreNode.style.display = "none";
  36887. }
  36888. if(!this.resizable){
  36889. this.resizeHandle.style.display = "none";
  36890. }else{
  36891. this.domNode.style.width = dojo.marginBox(this.domNode).w + "px";
  36892. }
  36893. this._allFPs.push(this);
  36894. this.domNode.style.position = "absolute";
  36895. this.bgIframe = new dijit.BackgroundIframe(this.domNode);
  36896. this._naturalState = dojo.coords(this.domNode);
  36897. },
  36898. startup: function(){
  36899. if(this._started){ return; }
  36900. this.inherited(arguments);
  36901. if(this.resizable){
  36902. if(dojo.isIE){
  36903. this.canvas.style.overflow = "auto";
  36904. }else{
  36905. this.containerNode.style.overflow = "auto";
  36906. }
  36907. this._resizeHandle = new dojox.layout.ResizeHandle({
  36908. targetId: this.id,
  36909. resizeAxis: this.resizeAxis
  36910. },this.resizeHandle);
  36911. }
  36912. if(this.dockable){
  36913. // FIXME: argh.
  36914. var tmpName = this.dockTo;
  36915. if(this.dockTo){
  36916. this.dockTo = dijit.byId(this.dockTo);
  36917. }else{
  36918. this.dockTo = dijit.byId('dojoxGlobalFloatingDock');
  36919. }
  36920. if(!this.dockTo){
  36921. var tmpId, tmpNode;
  36922. // we need to make our dock node, and position it against
  36923. // .dojoxDockDefault .. this is a lot. either dockto="node"
  36924. // and fail if node doesn't exist or make the global one
  36925. // once, and use it on empty OR invalid dockTo="" node?
  36926. if(tmpName){
  36927. tmpId = tmpName;
  36928. tmpNode = dojo.byId(tmpName);
  36929. }else{
  36930. tmpNode = dojo.create('div', null, dojo.body());
  36931. dojo.addClass(tmpNode,"dojoxFloatingDockDefault");
  36932. tmpId = 'dojoxGlobalFloatingDock';
  36933. }
  36934. this.dockTo = new dojox.layout.Dock({ id: tmpId, autoPosition: "south" }, tmpNode);
  36935. this.dockTo.startup();
  36936. }
  36937. if((this.domNode.style.display == "none")||(this.domNode.style.visibility == "hidden")){
  36938. // If the FP is created dockable and non-visible, start up docked.
  36939. this.minimize();
  36940. }
  36941. }
  36942. this.connect(this.focusNode,"onmousedown","bringToTop");
  36943. this.connect(this.domNode, "onmousedown","bringToTop");
  36944. // Initial resize to give child the opportunity to lay itself out
  36945. this.resize(dojo.coords(this.domNode));
  36946. this._started = true;
  36947. },
  36948. setTitle: function(/* String */ title){
  36949. // summary: Update the Title bar with a new string
  36950. dojo.deprecated("pane.setTitle", "Use pane.set('title', someTitle)", "2.0");
  36951. this.set("title", title);
  36952. // this.setTitle = dojo.hitch(this, "setTitle") ??
  36953. },
  36954. close: function(){
  36955. // summary: Close and destroy this widget
  36956. if(!this.closable){ return; }
  36957. dojo.unsubscribe(this._listener);
  36958. this.hide(dojo.hitch(this,function(){
  36959. this.destroyRecursive();
  36960. }));
  36961. },
  36962. hide: function(/* Function? */ callback){
  36963. // summary: Close, but do not destroy this FloatingPane
  36964. dojo.fadeOut({
  36965. node:this.domNode,
  36966. duration:this.duration,
  36967. onEnd: dojo.hitch(this,function() {
  36968. this.domNode.style.display = "none";
  36969. this.domNode.style.visibility = "hidden";
  36970. if(this.dockTo && this.dockable){
  36971. this.dockTo._positionDock(null);
  36972. }
  36973. if(callback){
  36974. callback();
  36975. }
  36976. })
  36977. }).play();
  36978. },
  36979. show: function(/* Function? */callback){
  36980. // summary: Show the FloatingPane
  36981. var anim = dojo.fadeIn({node:this.domNode, duration:this.duration,
  36982. beforeBegin: dojo.hitch(this,function(){
  36983. this.domNode.style.display = "";
  36984. this.domNode.style.visibility = "visible";
  36985. if (this.dockTo && this.dockable) { this.dockTo._positionDock(null); }
  36986. if (typeof callback == "function") { callback(); }
  36987. this._isDocked = false;
  36988. if (this._dockNode) {
  36989. this._dockNode.destroy();
  36990. this._dockNode = null;
  36991. }
  36992. })
  36993. }).play();
  36994. this.resize(dojo.coords(this.domNode));
  36995. this._onShow(); // lazy load trigger
  36996. },
  36997. minimize: function(){
  36998. // summary: Hide and dock the FloatingPane
  36999. if(!this._isDocked){ this.hide(dojo.hitch(this,"_dock")); }
  37000. },
  37001. maximize: function(){
  37002. // summary: Make this FloatingPane full-screen (viewport)
  37003. if(this._maximized){ return; }
  37004. this._naturalState = dojo.position(this.domNode);
  37005. if(this._isDocked){
  37006. this.show();
  37007. setTimeout(dojo.hitch(this,"maximize"),this.duration);
  37008. }
  37009. dojo.addClass(this.focusNode,"floatingPaneMaximized");
  37010. this.resize(dojo.window.getBox());
  37011. this._maximized = true;
  37012. },
  37013. _restore: function(){
  37014. if(this._maximized){
  37015. this.resize(this._naturalState);
  37016. dojo.removeClass(this.focusNode,"floatingPaneMaximized");
  37017. this._maximized = false;
  37018. }
  37019. },
  37020. _dock: function(){
  37021. if(!this._isDocked && this.dockable){
  37022. this._dockNode = this.dockTo.addNode(this);
  37023. this._isDocked = true;
  37024. }
  37025. },
  37026. resize: function(/* Object */dim){
  37027. // summary: Size the FloatingPane and place accordingly
  37028. dim = dim || this._naturalState;
  37029. this._naturalState = dim;
  37030. // From the ResizeHandle we only get width and height information
  37031. var dns = this.domNode.style;
  37032. if("t" in dim){ dns.top = dim.t + "px"; }
  37033. if("l" in dim){ dns.left = dim.l + "px"; }
  37034. dns.width = dim.w + "px";
  37035. dns.height = dim.h + "px";
  37036. // Now resize canvas
  37037. var mbCanvas = { l: 0, t: 0, w: dim.w, h: (dim.h - this.focusNode.offsetHeight) };
  37038. dojo.marginBox(this.canvas, mbCanvas);
  37039. // If the single child can resize, forward resize event to it so it can
  37040. // fit itself properly into the content area
  37041. this._checkIfSingleChild();
  37042. if(this._singleChild && this._singleChild.resize){
  37043. this._singleChild.resize(mbCanvas);
  37044. }
  37045. },
  37046. bringToTop: function(){
  37047. // summary: bring this FloatingPane above all other panes
  37048. var windows = dojo.filter(
  37049. this._allFPs,
  37050. function(i){
  37051. return i !== this;
  37052. },
  37053. this);
  37054. windows.sort(function(a, b){
  37055. return a.domNode.style.zIndex - b.domNode.style.zIndex;
  37056. });
  37057. windows.push(this);
  37058. dojo.forEach(windows, function(w, x){
  37059. w.domNode.style.zIndex = this._startZ + (x * 2);
  37060. dojo.removeClass(w.domNode, "dojoxFloatingPaneFg");
  37061. }, this);
  37062. dojo.addClass(this.domNode, "dojoxFloatingPaneFg");
  37063. },
  37064. destroy: function(){
  37065. // summary: Destroy this FloatingPane completely
  37066. this._allFPs.splice(dojo.indexOf(this._allFPs, this), 1);
  37067. if(this._resizeHandle){
  37068. this._resizeHandle.destroy();
  37069. }
  37070. this.inherited(arguments);
  37071. }
  37072. });
  37073. dojo.declare("dojox.layout.Dock",
  37074. [dijit._Widget,dijit._Templated],
  37075. {
  37076. // summary:
  37077. // A widget that attaches to a node and keeps track of incoming / outgoing FloatingPanes
  37078. // and handles layout
  37079. templateString: '<div class="dojoxDock"><ul dojoAttachPoint="containerNode" class="dojoxDockList"></ul></div>',
  37080. // private _docked: array of panes currently in our dock
  37081. _docked: [],
  37082. _inPositioning: false,
  37083. autoPosition: false,
  37084. addNode: function(refNode){
  37085. // summary: Instert a dockNode refernce into the dock
  37086. var div = dojo.create('li', null, this.containerNode),
  37087. node = new dojox.layout._DockNode({
  37088. title: refNode.title,
  37089. paneRef: refNode
  37090. }, div)
  37091. ;
  37092. node.startup();
  37093. return node;
  37094. },
  37095. startup: function(){
  37096. if (this.id == "dojoxGlobalFloatingDock" || this.isFixedDock) {
  37097. // attach window.onScroll, and a position like in presentation/dialog
  37098. this.connect(window, 'onresize', "_positionDock");
  37099. this.connect(window, 'onscroll', "_positionDock");
  37100. if(dojo.isIE){
  37101. this.connect(this.domNode, "onresize", "_positionDock");
  37102. }
  37103. }
  37104. this._positionDock(null);
  37105. this.inherited(arguments);
  37106. },
  37107. _positionDock: function(/* Event? */e){
  37108. if(!this._inPositioning){
  37109. if(this.autoPosition == "south"){
  37110. // Give some time for scrollbars to appear/disappear
  37111. setTimeout(dojo.hitch(this, function() {
  37112. this._inPositiononing = true;
  37113. var viewport = dojo.window.getBox();
  37114. var s = this.domNode.style;
  37115. s.left = viewport.l + "px";
  37116. s.width = (viewport.w-2) + "px";
  37117. s.top = (viewport.h + viewport.t) - this.domNode.offsetHeight + "px";
  37118. this._inPositioning = false;
  37119. }), 125);
  37120. }
  37121. }
  37122. }
  37123. });
  37124. dojo.declare("dojox.layout._DockNode",
  37125. [dijit._Widget,dijit._Templated],
  37126. {
  37127. // summary:
  37128. // dojox.layout._DockNode is a private widget used to keep track of
  37129. // which pane is docked.
  37130. //
  37131. // title: String
  37132. // Shown in dock icon. should read parent iconSrc?
  37133. title: "",
  37134. // paneRef: Widget
  37135. // reference to the FloatingPane we reprasent in any given dock
  37136. paneRef: null,
  37137. templateString:
  37138. '<li dojoAttachEvent="onclick: restore" class="dojoxDockNode">'+
  37139. '<span dojoAttachPoint="restoreNode" class="dojoxDockRestoreButton" dojoAttachEvent="onclick: restore"></span>'+
  37140. '<span class="dojoxDockTitleNode" dojoAttachPoint="titleNode">${title}</span>'+
  37141. '</li>',
  37142. restore: function(){
  37143. // summary: remove this dock item from parent dock, and call show() on reffed floatingpane
  37144. this.paneRef.show();
  37145. this.paneRef.bringToTop();
  37146. this.destroy();
  37147. }
  37148. });
  37149. }
  37150. if(!dojo._hasResource["dojox.layout.ExpandoPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  37151. dojo._hasResource["dojox.layout.ExpandoPane"] = true;
  37152. dojo.provide("dojox.layout.ExpandoPane");
  37153. dojo.experimental("dojox.layout.ExpandoPane"); // just to show it can be done?
  37154. dojo.declare("dojox.layout.ExpandoPane",
  37155. [dijit.layout.ContentPane, dijit._Templated, dijit._Contained, dijit._Container],
  37156. {
  37157. // summary: An experimental collapsing-pane for dijit.layout.BorderContainer
  37158. //
  37159. // description:
  37160. // Works just like a ContentPane inside of a borderContainer. Will expand/collapse on
  37161. // command, and supports having Layout Children as direct descendants
  37162. //
  37163. //maxHeight: "",
  37164. //maxWidth: "",
  37165. //splitter: false,
  37166. attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
  37167. title: { node: "titleNode", type: "innerHTML" }
  37168. }),
  37169. templateString: dojo.cache("dojox.layout", "resources/ExpandoPane.html", "<div class=\"dojoxExpandoPane\">\n\t<div dojoAttachPoint=\"titleWrapper\" class=\"dojoxExpandoTitle\">\n\t\t<div class=\"dojoxExpandoIcon\" dojoAttachPoint=\"iconNode\" dojoAttachEvent=\"onclick:toggle\"><span class=\"a11yNode\">X</span></div>\t\t\t\n\t\t<span class=\"dojoxExpandoTitleNode\" dojoAttachPoint=\"titleNode\">${title}</span>\n\t</div>\n\t<div class=\"dojoxExpandoWrapper\" dojoAttachPoint=\"cwrapper\" dojoAttachEvent=\"ondblclick:_trap\">\n\t\t<div class=\"dojoxExpandoContent\" dojoAttachPoint=\"containerNode\"></div>\n\t</div>\n</div>\n"),
  37170. // easeOut: String|Function
  37171. // easing function used to hide pane
  37172. easeOut: "dojo._DefaultEasing",
  37173. // easeIn: String|Function
  37174. // easing function use to show pane
  37175. easeIn: "dojo._DefaultEasing",
  37176. // duration: Integer
  37177. // duration to run show/hide animations
  37178. duration: 420,
  37179. // startExpanded: Boolean
  37180. // Does this widget start in an open (true) or closed (false) state
  37181. startExpanded: true,
  37182. // previewOpacity: Float
  37183. // A value from 0 .. 1 indicating the opacity to use on the container
  37184. // when only showing a preview
  37185. previewOpacity: 0.75,
  37186. // previewOnDblClick: Boolean
  37187. // If true, will override the default behavior of a double-click calling a full toggle.
  37188. // If false, a double-click will cause the preview to popup
  37189. previewOnDblClick: false,
  37190. baseClass: "dijitExpandoPane",
  37191. postCreate: function(){
  37192. this.inherited(arguments);
  37193. this._animConnects = [];
  37194. this._isHorizontal = true;
  37195. if(dojo.isString(this.easeOut)){
  37196. this.easeOut = dojo.getObject(this.easeOut);
  37197. }
  37198. if(dojo.isString(this.easeIn)){
  37199. this.easeIn = dojo.getObject(this.easeIn);
  37200. }
  37201. var thisClass = "", rtl = !this.isLeftToRight();
  37202. if(this.region){
  37203. switch(this.region){
  37204. case "trailing" :
  37205. case "right" :
  37206. thisClass = rtl ? "Left" : "Right";
  37207. break;
  37208. case "leading" :
  37209. case "left" :
  37210. thisClass = rtl ? "Right" : "Left";
  37211. break;
  37212. case "top" :
  37213. thisClass = "Top";
  37214. break;
  37215. case "bottom" :
  37216. thisClass = "Bottom";
  37217. break;
  37218. }
  37219. dojo.addClass(this.domNode, "dojoxExpando" + thisClass);
  37220. dojo.addClass(this.iconNode, "dojoxExpandoIcon" + thisClass);
  37221. this._isHorizontal = /top|bottom/.test(this.region);
  37222. }
  37223. dojo.style(this.domNode, {
  37224. overflow: "hidden",
  37225. padding:0
  37226. });
  37227. this.connect(this.domNode, "ondblclick", this.previewOnDblClick ? "preview" : "toggle");
  37228. if(this.previewOnDblClick){
  37229. this.connect(this.getParent(), "_layoutChildren", dojo.hitch(this, function(){
  37230. this._isonlypreview = false;
  37231. }));
  37232. }
  37233. },
  37234. _startupSizes: function(){
  37235. this._container = this.getParent();
  37236. this._closedSize = this._titleHeight = dojo.marginBox(this.titleWrapper).h;
  37237. if(this.splitter){
  37238. // find our splitter and tie into it's drag logic
  37239. var myid = this.id;
  37240. dijit.registry.filter(function(w){
  37241. return w && w.child && w.child.id == myid;
  37242. }).forEach(dojo.hitch(this,function(w){
  37243. this.connect(w,"_stopDrag","_afterResize");
  37244. }));
  37245. }
  37246. this._currentSize = dojo.contentBox(this.domNode); // TODO: can compute this from passed in value to resize(), see _LayoutWidget for example
  37247. this._showSize = this._currentSize[(this._isHorizontal ? "h" : "w")];
  37248. this._setupAnims();
  37249. if(this.startExpanded){
  37250. this._showing = true;
  37251. }else{
  37252. this._showing = false;
  37253. this._hideWrapper();
  37254. this._hideAnim.gotoPercent(99,true);
  37255. }
  37256. this._hasSizes = true;
  37257. },
  37258. _afterResize: function(e){
  37259. var tmp = this._currentSize; // the old size
  37260. this._currentSize = dojo.marginBox(this.domNode); // the new size
  37261. var n = this._currentSize[(this._isHorizontal ? "h" : "w")]
  37262. if(n > this._titleHeight){
  37263. if(!this._showing){
  37264. this._showing = !this._showing;
  37265. this._showEnd();
  37266. }
  37267. this._showSize = n;
  37268. this._setupAnims();
  37269. }else{
  37270. this._showSize = tmp[(this._isHorizontal ? "h" : "w")];
  37271. this._showing = false;
  37272. this._hideWrapper();
  37273. this._hideAnim.gotoPercent(89,true);
  37274. }
  37275. },
  37276. _setupAnims: function(){
  37277. // summary: Create the show and hide animations
  37278. dojo.forEach(this._animConnects, dojo.disconnect);
  37279. var _common = {
  37280. node:this.domNode,
  37281. duration:this.duration
  37282. },
  37283. isHorizontal = this._isHorizontal,
  37284. showProps = {},
  37285. hideProps = {},
  37286. dimension = isHorizontal ? "height" : "width"
  37287. ;
  37288. showProps[dimension] = {
  37289. end: this._showSize
  37290. };
  37291. hideProps[dimension] = {
  37292. end: this._closedSize
  37293. };
  37294. this._showAnim = dojo.animateProperty(dojo.mixin(_common,{
  37295. easing:this.easeIn,
  37296. properties: showProps
  37297. }));
  37298. this._hideAnim = dojo.animateProperty(dojo.mixin(_common,{
  37299. easing:this.easeOut,
  37300. properties: hideProps
  37301. }));
  37302. this._animConnects = [
  37303. dojo.connect(this._showAnim, "onEnd", this, "_showEnd"),
  37304. dojo.connect(this._hideAnim, "onEnd", this, "_hideEnd")
  37305. ];
  37306. },
  37307. preview: function(){
  37308. // summary: Expand this pane in preview mode (does not affect surrounding layout)
  37309. if(!this._showing){
  37310. this._isonlypreview = !this._isonlypreview;
  37311. }
  37312. this.toggle();
  37313. },
  37314. toggle: function(){
  37315. // summary: Toggle this pane's visibility
  37316. if(this._showing){
  37317. this._hideWrapper();
  37318. this._showAnim && this._showAnim.stop();
  37319. this._hideAnim.play();
  37320. }else{
  37321. this._hideAnim && this._hideAnim.stop();
  37322. this._showAnim.play();
  37323. }
  37324. this._showing = !this._showing;
  37325. },
  37326. _hideWrapper: function(){
  37327. // summary: Set the Expando state to "closed"
  37328. dojo.addClass(this.domNode, "dojoxExpandoClosed");
  37329. dojo.style(this.cwrapper,{
  37330. visibility: "hidden",
  37331. opacity: "0",
  37332. overflow: "hidden"
  37333. });
  37334. },
  37335. _showEnd: function(){
  37336. // summary: Common animation onEnd code - "unclose"
  37337. dojo.style(this.cwrapper, {
  37338. opacity: 0,
  37339. visibility:"visible"
  37340. });
  37341. dojo.anim(this.cwrapper, {
  37342. opacity: this._isonlypreview ? this.previewOpacity : 1
  37343. }, 227);
  37344. dojo.removeClass(this.domNode, "dojoxExpandoClosed");
  37345. if(!this._isonlypreview){
  37346. setTimeout(dojo.hitch(this._container, "layout"), 15);
  37347. }else{
  37348. this._previewShowing = true;
  37349. this.resize();
  37350. }
  37351. },
  37352. _hideEnd: function(){
  37353. // summary: Callback for the hide animation - "close"
  37354. // every time we hide, reset the "only preview" state
  37355. if(!this._isonlypreview){
  37356. setTimeout(dojo.hitch(this._container, "layout"), 25);
  37357. }else{
  37358. this._previewShowing = false;
  37359. }
  37360. this._isonlypreview = false;
  37361. },
  37362. resize: function(/* Object? */newSize){
  37363. // summary:
  37364. // we aren't a layout widget, but need to act like one:
  37365. // newSize: Object
  37366. // The size object to resize to
  37367. if(!this._hasSizes){ this._startupSizes(newSize); }
  37368. // compute size of container (ie, size left over after title bar)
  37369. var currentSize = dojo.marginBox(this.domNode);
  37370. this._contentBox = {
  37371. w: newSize && "w" in newSize ? newSize.w : currentSize.w,
  37372. h: (newSize && "h" in newSize ? newSize.h : currentSize.h) - this._titleHeight
  37373. };
  37374. dojo.style(this.containerNode, "height", this._contentBox.h + "px");
  37375. if(newSize){
  37376. dojo.marginBox(this.domNode, newSize);
  37377. }
  37378. this._layoutChildren();
  37379. },
  37380. _trap: function(e){
  37381. // summary: Trap stray events
  37382. dojo.stopEvent(e);
  37383. }
  37384. });
  37385. }
  37386. if(!dojo._hasResource["dojox.color._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  37387. dojo._hasResource["dojox.color._base"] = true;
  37388. dojo.provide("dojox.color._base");
  37389. // alias all the dojo.Color mechanisms
  37390. dojox.color.Color=dojo.Color;
  37391. dojox.color.blend=dojo.blendColors;
  37392. dojox.color.fromRgb=dojo.colorFromRgb;
  37393. dojox.color.fromHex=dojo.colorFromHex;
  37394. dojox.color.fromArray=dojo.colorFromArray;
  37395. dojox.color.fromString=dojo.colorFromString;
  37396. // alias the dojo.colors mechanisms
  37397. dojox.color.greyscale=dojo.colors.makeGrey;
  37398. // static methods
  37399. dojo.mixin(dojox.color, {
  37400. fromCmy: function(/* Object|Array|int */cyan, /*int*/magenta, /*int*/yellow){
  37401. // summary
  37402. // Create a dojox.color.Color from a CMY defined color.
  37403. // All colors should be expressed as 0-100 (percentage)
  37404. if(dojo.isArray(cyan)){
  37405. magenta=cyan[1], yellow=cyan[2], cyan=cyan[0];
  37406. } else if(dojo.isObject(cyan)){
  37407. magenta=cyan.m, yellow=cyan.y, cyan=cyan.c;
  37408. }
  37409. cyan/=100, magenta/=100, yellow/=100;
  37410. var r=1-cyan, g=1-magenta, b=1-yellow;
  37411. return new dojox.color.Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color
  37412. },
  37413. fromCmyk: function(/* Object|Array|int */cyan, /*int*/magenta, /*int*/yellow, /*int*/black){
  37414. // summary
  37415. // Create a dojox.color.Color from a CMYK defined color.
  37416. // All colors should be expressed as 0-100 (percentage)
  37417. if(dojo.isArray(cyan)){
  37418. magenta=cyan[1], yellow=cyan[2], black=cyan[3], cyan=cyan[0];
  37419. } else if(dojo.isObject(cyan)){
  37420. magenta=cyan.m, yellow=cyan.y, black=cyan.b, cyan=cyan.c;
  37421. }
  37422. cyan/=100, magenta/=100, yellow/=100, black/=100;
  37423. var r,g,b;
  37424. r = 1-Math.min(1, cyan*(1-black)+black);
  37425. g = 1-Math.min(1, magenta*(1-black)+black);
  37426. b = 1-Math.min(1, yellow*(1-black)+black);
  37427. return new dojox.color.Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color
  37428. },
  37429. fromHsl: function(/* Object|Array|int */hue, /* int */saturation, /* int */luminosity){
  37430. // summary
  37431. // Create a dojox.color.Color from an HSL defined color.
  37432. // hue from 0-359 (degrees), saturation and luminosity 0-100.
  37433. if(dojo.isArray(hue)){
  37434. saturation=hue[1], luminosity=hue[2], hue=hue[0];
  37435. } else if(dojo.isObject(hue)){
  37436. saturation=hue.s, luminosity=hue.l, hue=hue.h;
  37437. }
  37438. saturation/=100;
  37439. luminosity/=100;
  37440. while(hue<0){ hue+=360; }
  37441. while(hue>=360){ hue-=360; }
  37442. var r, g, b;
  37443. if(hue<120){
  37444. r=(120-hue)/60, g=hue/60, b=0;
  37445. } else if (hue<240){
  37446. r=0, g=(240-hue)/60, b=(hue-120)/60;
  37447. } else {
  37448. r=(hue-240)/60, g=0, b=(360-hue)/60;
  37449. }
  37450. r=2*saturation*Math.min(r, 1)+(1-saturation);
  37451. g=2*saturation*Math.min(g, 1)+(1-saturation);
  37452. b=2*saturation*Math.min(b, 1)+(1-saturation);
  37453. if(luminosity<0.5){
  37454. r*=luminosity, g*=luminosity, b*=luminosity;
  37455. }else{
  37456. r=(1-luminosity)*r+2*luminosity-1;
  37457. g=(1-luminosity)*g+2*luminosity-1;
  37458. b=(1-luminosity)*b+2*luminosity-1;
  37459. }
  37460. return new dojox.color.Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color
  37461. },
  37462. fromHsv: function(/* Object|Array|int */hue, /* int */saturation, /* int */value){
  37463. // summary
  37464. // Create a dojox.color.Color from an HSV defined color.
  37465. // hue from 0-359 (degrees), saturation and value 0-100.
  37466. if(dojo.isArray(hue)){
  37467. saturation=hue[1], value=hue[2], hue=hue[0];
  37468. } else if (dojo.isObject(hue)){
  37469. saturation=hue.s, value=hue.v, hue=hue.h;
  37470. }
  37471. if(hue==360){ hue=0; }
  37472. saturation/=100;
  37473. value/=100;
  37474. var r, g, b;
  37475. if(saturation==0){
  37476. r=value, b=value, g=value;
  37477. }else{
  37478. var hTemp=hue/60, i=Math.floor(hTemp), f=hTemp-i;
  37479. var p=value*(1-saturation);
  37480. var q=value*(1-(saturation*f));
  37481. var t=value*(1-(saturation*(1-f)));
  37482. switch(i){
  37483. case 0:{ r=value, g=t, b=p; break; }
  37484. case 1:{ r=q, g=value, b=p; break; }
  37485. case 2:{ r=p, g=value, b=t; break; }
  37486. case 3:{ r=p, g=q, b=value; break; }
  37487. case 4:{ r=t, g=p, b=value; break; }
  37488. case 5:{ r=value, g=p, b=q; break; }
  37489. }
  37490. }
  37491. return new dojox.color.Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color
  37492. }
  37493. });
  37494. // Conversions directly on dojox.color.Color
  37495. dojo.extend(dojox.color.Color, {
  37496. toCmy: function(){
  37497. // summary
  37498. // Convert this Color to a CMY definition.
  37499. var cyan=1-(this.r/255), magenta=1-(this.g/255), yellow=1-(this.b/255);
  37500. return { c:Math.round(cyan*100), m:Math.round(magenta*100), y:Math.round(yellow*100) }; // Object
  37501. },
  37502. toCmyk: function(){
  37503. // summary
  37504. // Convert this Color to a CMYK definition.
  37505. var cyan, magenta, yellow, black;
  37506. var r=this.r/255, g=this.g/255, b=this.b/255;
  37507. black = Math.min(1-r, 1-g, 1-b);
  37508. cyan = (1-r-black)/(1-black);
  37509. magenta = (1-g-black)/(1-black);
  37510. yellow = (1-b-black)/(1-black);
  37511. return { c:Math.round(cyan*100), m:Math.round(magenta*100), y:Math.round(yellow*100), b:Math.round(black*100) }; // Object
  37512. },
  37513. toHsl: function(){
  37514. // summary
  37515. // Convert this Color to an HSL definition.
  37516. var r=this.r/255, g=this.g/255, b=this.b/255;
  37517. var min = Math.min(r, b, g), max = Math.max(r, g, b);
  37518. var delta = max-min;
  37519. var h=0, s=0, l=(min+max)/2;
  37520. if(l>0 && l<1){
  37521. s = delta/((l<0.5)?(2*l):(2-2*l));
  37522. }
  37523. if(delta>0){
  37524. if(max==r && max!=g){
  37525. h+=(g-b)/delta;
  37526. }
  37527. if(max==g && max!=b){
  37528. h+=(2+(b-r)/delta);
  37529. }
  37530. if(max==b && max!=r){
  37531. h+=(4+(r-g)/delta);
  37532. }
  37533. h*=60;
  37534. }
  37535. return { h:h, s:Math.round(s*100), l:Math.round(l*100) }; // Object
  37536. },
  37537. toHsv: function(){
  37538. // summary
  37539. // Convert this Color to an HSV definition.
  37540. var r=this.r/255, g=this.g/255, b=this.b/255;
  37541. var min = Math.min(r, b, g), max = Math.max(r, g, b);
  37542. var delta = max-min;
  37543. var h = null, s = (max==0)?0:(delta/max);
  37544. if(s==0){
  37545. h = 0;
  37546. }else{
  37547. if(r==max){
  37548. h = 60*(g-b)/delta;
  37549. }else if(g==max){
  37550. h = 120 + 60*(b-r)/delta;
  37551. }else{
  37552. h = 240 + 60*(r-g)/delta;
  37553. }
  37554. if(h<0){ h+=360; }
  37555. }
  37556. return { h:h, s:Math.round(s*100), v:Math.round(max*100) }; // Object
  37557. }
  37558. });
  37559. }
  37560. if(!dojo._hasResource["dojox.color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  37561. dojo._hasResource["dojox.color"] = true;
  37562. dojo.provide("dojox.color");
  37563. }
  37564. if(!dojo._hasResource["dojox.widget.ColorPicker"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  37565. dojo._hasResource["dojox.widget.ColorPicker"] = true;
  37566. dojo.provide("dojox.widget.ColorPicker");
  37567. dojo.experimental("dojox.widget.ColorPicker"); // level: beta //TODO: which?
  37568. (function(d){
  37569. var webSafeFromHex = function(hex){
  37570. // stub, this is planned later:
  37571. return hex;
  37572. };
  37573. dojo.declare("dojox.widget.ColorPicker",
  37574. dijit.form._FormWidget,
  37575. {
  37576. // summary: a HSV color picker - similar to Photoshop picker
  37577. //
  37578. // description:
  37579. // Provides an interactive HSV ColorPicker similar to
  37580. // PhotoShop's color selction tool. This is an enhanced
  37581. // version of the default dijit.ColorPalette, though provides
  37582. // no accessibility.
  37583. //
  37584. // example:
  37585. // | var picker = new dojox.widget.ColorPicker({
  37586. // | // a couple of example toggles:
  37587. // | animatePoint:false,
  37588. // | showHsv: false,
  37589. // | webSafe: false,
  37590. // | showRgb: false
  37591. // | });
  37592. //
  37593. // example:
  37594. // | <!-- markup: -->
  37595. // | <div dojoType="dojox.widget.ColorPicker"></div>
  37596. //
  37597. // showRgb: Boolean
  37598. // show/update RGB input nodes
  37599. showRgb: true,
  37600. // showHsv: Boolean
  37601. // show/update HSV input nodes
  37602. showHsv: true,
  37603. // showHex: Boolean
  37604. // show/update Hex value field
  37605. showHex: true,
  37606. // webSafe: Boolean
  37607. // deprecated? or just use a toggle to show/hide that node, too?
  37608. webSafe: true,
  37609. // animatePoint: Boolean
  37610. // toggle to use slideTo (true) or just place the cursor (false) on click
  37611. animatePoint: true,
  37612. // slideDuration: Integer
  37613. // time in ms picker node will slide to next location (non-dragging) when animatePoint=true
  37614. slideDuration: 250,
  37615. // liveUpdate: Boolean
  37616. // Set to true to fire onChange in an indeterminate way
  37617. liveUpdate: false,
  37618. // PICKER_HUE_H: int
  37619. // Height of the hue picker, used to calculate positions
  37620. PICKER_HUE_H: 150,
  37621. // PICKER_SAT_VAL_H: int
  37622. // Height of the 2d picker, used to calculate positions
  37623. PICKER_SAT_VAL_H: 150,
  37624. // PICKER_SAT_VAL_W: int
  37625. // Width of the 2d picker, used to calculate positions
  37626. PICKER_SAT_VAL_W: 150,
  37627. // PICKER_HUE_SELECTOR_H: int
  37628. // Height of the hue selector DOM node, used to calc offsets so that selection
  37629. // is center of the image node.
  37630. PICKER_HUE_SELECTOR_H: 8,
  37631. // PICKER_SAT_SELECTOR_H: int
  37632. // Height of the saturation selector DOM node, used to calc offsets so that selection
  37633. // is center of the image node.
  37634. PICKER_SAT_SELECTOR_H: 10,
  37635. // PICKER_SAT_SELECTOR_W: int
  37636. // Width of the saturation selector DOM node, used to calc offsets so that selection
  37637. // is center of the image node.
  37638. PICKER_SAT_SELECTOR_W: 10,
  37639. // value: String
  37640. // Default color for this component. Only hex values are accepted as incoming/returned
  37641. // values. Adjust this value with `.attr`, eg: dijit.byId("myPicker").attr("value", "#ededed");
  37642. // to cause the points to adjust and the values to reflect the current color.
  37643. value: "#ffffff",
  37644. _underlay: d.moduleUrl("dojox.widget","ColorPicker/images/underlay.png"),
  37645. _hueUnderlay: d.moduleUrl("dojox.widget","ColorPicker/images/hue.png"),
  37646. _pickerPointer: d.moduleUrl("dojox.widget","ColorPicker/images/pickerPointer.png"),
  37647. _huePickerPointer: d.moduleUrl("dojox.widget","ColorPicker/images/hueHandle.png"),
  37648. _huePickerPointerAlly: d.moduleUrl("dojox.widget","ColorPicker/images/hueHandleA11y.png"),
  37649. // don't change to d.moduleUrl, build won't intern it.
  37650. templateString: dojo.cache("dojox.widget", "ColorPicker/ColorPicker.html", "<table class=\"dojoxColorPicker\" dojoAttachEvent=\"onkeypress: _handleKey\" cellpadding=\"0\" cellspacing=\"0\">\n\t<tr>\n\t\t<td valign=\"top\" class=\"dojoxColorPickerRightPad\">\n\t\t\t<div class=\"dojoxColorPickerBox\">\n\t\t\t\t<!-- Forcing ABS in style attr due to dojo DND issue with not picking it up form the class. -->\n\t\t\t\t<img role=\"status\" title=\"${saturationPickerTitle}\" alt=\"${saturationPickerTitle}\" class=\"dojoxColorPickerPoint\" src=\"${_pickerPointer}\" tabIndex=\"0\" dojoAttachPoint=\"cursorNode\" style=\"position: absolute; top: 0px; left: 0px;\">\n\t\t\t\t<img role=\"presentation\" alt=\"\" dojoAttachPoint=\"colorUnderlay\" dojoAttachEvent=\"onclick: _setPoint, onmousedown: _stopDrag\" class=\"dojoxColorPickerUnderlay\" src=\"${_underlay}\" ondragstart=\"return false\">\n\t\t\t</div>\n\t\t</td>\n\t\t<td valign=\"top\" class=\"dojoxColorPickerRightPad\">\n\t\t\t<div class=\"dojoxHuePicker\">\n\t\t\t\t<!-- Forcing ABS in style attr due to dojo DND issue with not picking it up form the class. -->\n\t\t\t\t<img role=\"status\" dojoAttachPoint=\"hueCursorNode\" tabIndex=\"0\" class=\"dojoxHuePickerPoint\" title=\"${huePickerTitle}\" alt=\"${huePickerTitle}\" src=\"${_huePickerPointer}\" style=\"position: absolute; top: 0px; left: 0px;\">\n\t\t\t\t<div class=\"dojoxHuePickerUnderlay\" dojoAttachPoint=\"hueNode\">\n\t\t\t\t <img role=\"presentation\" alt=\"\" dojoAttachEvent=\"onclick: _setHuePoint, onmousedown: _stopDrag\" src=\"${_hueUnderlay}\">\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</td>\n\t\t<td valign=\"top\">\n\t\t\t<table cellpadding=\"0\" cellspacing=\"0\">\n\t\t\t\t<tr>\n\t\t\t\t\t<td valign=\"top\" class=\"dojoxColorPickerPreviewContainer\">\n\t\t\t\t\t\t<table cellpadding=\"0\" cellspacing=\"0\">\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td valign=\"top\" class=\"dojoxColorPickerRightPad\">\n\t\t\t\t\t\t\t\t\t<div dojoAttachPoint=\"previewNode\" class=\"dojoxColorPickerPreview\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td valign=\"top\">\n\t\t\t\t\t\t\t\t\t<div dojoAttachPoint=\"safePreviewNode\" class=\"dojoxColorPickerWebSafePreview\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td valign=\"bottom\">\n\t\t\t\t\t\t<table class=\"dojoxColorPickerOptional\" cellpadding=\"0\" cellspacing=\"0\">\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<div class=\"dijitInline dojoxColorPickerRgb\" dojoAttachPoint=\"rgbNode\">\n\t\t\t\t\t\t\t\t\t\t<table cellpadding=\"1\" cellspacing=\"1\">\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_r\">${redLabel}</label></td><td><input id=\"${_uId}_r\" dojoAttachPoint=\"Rval\" size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"></td></tr>\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_g\">${greenLabel}</label></td><td><input id=\"${_uId}_g\" dojoAttachPoint=\"Gval\" size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"></td></tr>\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_b\">${blueLabel}</label></td><td><input id=\"${_uId}_b\" dojoAttachPoint=\"Bval\" size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"></td></tr>\n\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<div class=\"dijitInline dojoxColorPickerHsv\" dojoAttachPoint=\"hsvNode\">\n\t\t\t\t\t\t\t\t\t\t<table cellpadding=\"1\" cellspacing=\"1\">\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_h\">${hueLabel}</label></td><td><input id=\"${_uId}_h\" dojoAttachPoint=\"Hval\"size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"> ${degLabel}</td></tr>\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_s\">${saturationLabel}</label></td><td><input id=\"${_uId}_s\" dojoAttachPoint=\"Sval\" size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"> ${percentSign}</td></tr>\n\t\t\t\t\t\t\t\t\t\t<tr><td><label for=\"${_uId}_v\">${valueLabel}</label></td><td><input id=\"${_uId}_v\" dojoAttachPoint=\"Vval\" size=\"1\" dojoAttachEvent=\"onchange: _colorInputChange\"> ${percentSign}</td></tr>\n\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colspan=\"2\">\n\t\t\t\t\t\t\t\t\t<div class=\"dojoxColorPickerHex\" dojoAttachPoint=\"hexNode\" aria-live=\"polite\">\t\n\t\t\t\t\t\t\t\t\t\t<label for=\"${_uId}_hex\">&nbsp;${hexLabel}&nbsp;</label><input id=\"${_uId}_hex\" dojoAttachPoint=\"hexCode, focusNode, valueNode\" size=\"6\" class=\"dojoxColorPickerHexCode\" dojoAttachEvent=\"onchange: _colorInputChange\">\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</table>\n\t\t</td>\n\t</tr>\n</table>\n\n"),
  37651. postMixInProperties: function(){
  37652. if(dojo.hasClass(dojo.body(), "dijit_a11y")){
  37653. // Use the pointer that will show up in high contrast.
  37654. this._huePickerPointer = this._huePickerPointerAlly;
  37655. }
  37656. this._uId = dijit.getUniqueId(this.id);
  37657. dojo.mixin(this, dojo.i18n.getLocalization("dojox.widget", "ColorPicker"));
  37658. dojo.mixin(this, dojo.i18n.getLocalization("dojo.cldr", "number"));
  37659. this.inherited(arguments);
  37660. },
  37661. postCreate: function(){
  37662. // summary:
  37663. // As quickly as we can, set up ie6 alpha-filter support for our
  37664. // underlay. we don't do image handles (done in css), just the 'core'
  37665. // of this widget: the underlay.
  37666. this.inherited(arguments);
  37667. if(d.isIE < 7){
  37668. this.colorUnderlay.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this._underlay+"', sizingMethod='scale')";
  37669. this.colorUnderlay.src = this._blankGif.toString();
  37670. }
  37671. // hide toggle-able nodes:
  37672. if(!this.showRgb){ this.rgbNode.style.visibility = "hidden"; }
  37673. if(!this.showHsv){ this.hsvNode.style.visibility = "hidden"; }
  37674. if(!this.showHex){ this.hexNode.style.visibility = "hidden"; }
  37675. if(!this.webSafe){ this.safePreviewNode.style.visibility = "hidden"; }
  37676. },
  37677. startup: function(){
  37678. if(this._started){
  37679. return;
  37680. }
  37681. this._started = true;
  37682. this.set("value", this.value);
  37683. this._mover = new d.dnd.move.boxConstrainedMoveable(this.cursorNode, {
  37684. box: {
  37685. t: -(this.PICKER_SAT_SELECTOR_H/2),
  37686. l: -(this.PICKER_SAT_SELECTOR_W/2),
  37687. w:this.PICKER_SAT_VAL_W,
  37688. h:this.PICKER_SAT_VAL_H
  37689. }
  37690. });
  37691. this._hueMover = new d.dnd.move.boxConstrainedMoveable(this.hueCursorNode, {
  37692. box: {
  37693. t: -(this.PICKER_HUE_SELECTOR_H/2),
  37694. l:0,
  37695. w:0,
  37696. h:this.PICKER_HUE_H
  37697. }
  37698. });
  37699. this._subs = [];
  37700. // no dnd/move/move published ... use a timer:
  37701. this._subs.push(d.subscribe("/dnd/move/stop", d.hitch(this, "_clearTimer")));
  37702. this._subs.push(d.subscribe("/dnd/move/start", d.hitch(this, "_setTimer")));
  37703. // Bind to up, down, left and right arrows on the hue and saturation nodes.
  37704. this._keyListeners = [];
  37705. this._connects.push(dijit.typematic.addKeyListener(this.hueCursorNode,{
  37706. charOrCode: dojo.keys.UP_ARROW,
  37707. shiftKey: false,
  37708. metaKey: false,
  37709. ctrlKey: false,
  37710. altKey: false
  37711. }, this, dojo.hitch(this, this._updateHueCursorNode), 25, 25));
  37712. this._connects.push(dijit.typematic.addKeyListener(this.hueCursorNode,{
  37713. charOrCode: dojo.keys.DOWN_ARROW,
  37714. shiftKey: false,
  37715. metaKey: false,
  37716. ctrlKey: false,
  37717. altKey: false
  37718. }, this, dojo.hitch(this, this._updateHueCursorNode), 25, 25));
  37719. this._connects.push(dijit.typematic.addKeyListener(this.cursorNode,{
  37720. charOrCode: dojo.keys.UP_ARROW,
  37721. shiftKey: false,
  37722. metaKey: false,
  37723. ctrlKey: false,
  37724. altKey: false
  37725. }, this, dojo.hitch(this, this._updateCursorNode), 25, 25));
  37726. this._connects.push(dijit.typematic.addKeyListener(this.cursorNode,{
  37727. charOrCode: dojo.keys.DOWN_ARROW,
  37728. shiftKey: false,
  37729. metaKey: false,
  37730. ctrlKey: false,
  37731. altKey: false
  37732. }, this, dojo.hitch(this, this._updateCursorNode), 25, 25));
  37733. this._connects.push(dijit.typematic.addKeyListener(this.cursorNode,{
  37734. charOrCode: dojo.keys.LEFT_ARROW,
  37735. shiftKey: false,
  37736. metaKey: false,
  37737. ctrlKey: false,
  37738. altKey: false
  37739. }, this, dojo.hitch(this, this._updateCursorNode), 25, 25));
  37740. this._connects.push(dijit.typematic.addKeyListener(this.cursorNode,{
  37741. charOrCode: dojo.keys.RIGHT_ARROW,
  37742. shiftKey: false,
  37743. metaKey: false,
  37744. ctrlKey: false,
  37745. altKey: false
  37746. }, this, dojo.hitch(this, this._updateCursorNode), 25, 25));
  37747. },
  37748. _setValueAttr: function(value){
  37749. if(!this._started){ return; }
  37750. this.setColor(value, true);
  37751. },
  37752. setColor: function(/* String */color, force){
  37753. // summary: Set a color on a picker. Usually used to set
  37754. // initial color as an alternative to passing defaultColor option
  37755. // to the constructor.
  37756. var col = dojox.color.fromString(color);
  37757. this._updatePickerLocations(col);
  37758. this._updateColorInputs(col);
  37759. this._updateValue(col, force);
  37760. },
  37761. _setTimer: function(/* d.dnd.Mover */mover){
  37762. // FIXME: should I assume this? focus on mouse down so on mouse up
  37763. dijit.focus(mover.node);
  37764. d.setSelectable(this.domNode,false);
  37765. this._timer = setInterval(d.hitch(this, "_updateColor"), 45);
  37766. },
  37767. _clearTimer: function(/* d.dnd.Mover */mover){
  37768. clearInterval(this._timer);
  37769. this._timer = null;
  37770. this.onChange(this.value);
  37771. d.setSelectable(this.domNode,true);
  37772. },
  37773. _setHue: function(/* Decimal */h){
  37774. // summary:
  37775. // Sets a natural color background for the
  37776. // underlay image against closest hue value (full saturation)
  37777. // h: 0..360
  37778. d.style(this.colorUnderlay, "backgroundColor", dojox.color.fromHsv(h,100,100).toHex());
  37779. },
  37780. _updateHueCursorNode: function(count, node, e){
  37781. // summary:
  37782. // Function used by the typematic code to handle cursor position and update
  37783. // via keyboard.
  37784. // count:
  37785. // -1 means stop, anything else is just how many times it was called.
  37786. // node:
  37787. // The node generating the event.
  37788. // e:
  37789. // The event.
  37790. if(count !== -1){
  37791. var y = dojo.style(this.hueCursorNode, "top");
  37792. var selCenter = (this.PICKER_HUE_SELECTOR_H/2);
  37793. // Account for our offset
  37794. y += selCenter;
  37795. var update = false;
  37796. if(e.charOrCode == dojo.keys.UP_ARROW){
  37797. if(y > 0){
  37798. y -= 1;
  37799. update = true;
  37800. }
  37801. }else if(e.charOrCode == dojo.keys.DOWN_ARROW){
  37802. if(y < this.PICKER_HUE_H){
  37803. y += 1;
  37804. update = true;
  37805. }
  37806. }
  37807. y -= selCenter;
  37808. if(update){
  37809. dojo.style(this.hueCursorNode, "top", y + "px");
  37810. }
  37811. }else{
  37812. this._updateColor(true);
  37813. }
  37814. },
  37815. _updateCursorNode: function(count, node, e){
  37816. // summary:
  37817. // Function used by the typematic code to handle cursor position and update
  37818. // via keyboard.
  37819. // count:
  37820. // -1 means stop, anything else is just how many times it was called.
  37821. // node:
  37822. // The node generating the event.
  37823. // e:
  37824. // The event.
  37825. var selCenterH = this.PICKER_SAT_SELECTOR_H/2;
  37826. var selCenterW = this.PICKER_SAT_SELECTOR_W/2;
  37827. if(count !== -1){
  37828. var y = dojo.style(this.cursorNode, "top");
  37829. var x = dojo.style(this.cursorNode, "left");
  37830. // Account for our offsets to center
  37831. y += selCenterH;
  37832. x += selCenterW;
  37833. var update = false;
  37834. if(e.charOrCode == dojo.keys.UP_ARROW){
  37835. if(y > 0){
  37836. y -= 1;
  37837. update = true;
  37838. }
  37839. }else if(e.charOrCode == dojo.keys.DOWN_ARROW){
  37840. if(y < this.PICKER_SAT_VAL_H){
  37841. y += 1;
  37842. update = true;
  37843. }
  37844. }else if(e.charOrCode == dojo.keys.LEFT_ARROW){
  37845. if(x > 0){
  37846. x -= 1;
  37847. update = true;
  37848. }
  37849. }else if(e.charOrCode == dojo.keys.RIGHT_ARROW){
  37850. if(x < this.PICKER_SAT_VAL_W){
  37851. x += 1;
  37852. update = true;
  37853. }
  37854. }
  37855. if(update){
  37856. // Account for our offsets to center
  37857. y -= selCenterH;
  37858. x -= selCenterW;
  37859. dojo.style(this.cursorNode, "top", y + "px");
  37860. dojo.style(this.cursorNode, "left", x + "px");
  37861. }
  37862. }else{
  37863. this._updateColor(true);
  37864. }
  37865. },
  37866. _updateColor: function(){
  37867. // summary: update the previewNode color, and input values [optional]
  37868. var hueSelCenter = this.PICKER_HUE_SELECTOR_H/2,
  37869. satSelCenterH = this.PICKER_SAT_SELECTOR_H/2,
  37870. satSelCenterW = this.PICKER_SAT_SELECTOR_W/2;
  37871. var _huetop = d.style(this.hueCursorNode,"top") + hueSelCenter,
  37872. _pickertop = d.style(this.cursorNode,"top") + satSelCenterH,
  37873. _pickerleft = d.style(this.cursorNode,"left") + satSelCenterW,
  37874. h = Math.round(360 - (_huetop / this.PICKER_HUE_H * 360)),
  37875. col = dojox.color.fromHsv(h, _pickerleft / this.PICKER_SAT_VAL_W * 100, 100 - (_pickertop / this.PICKER_SAT_VAL_H * 100))
  37876. ;
  37877. this._updateColorInputs(col);
  37878. this._updateValue(col, true);
  37879. // update hue, not all the pickers
  37880. if (h!=this._hue) {
  37881. this._setHue(h);
  37882. }
  37883. },
  37884. _colorInputChange: function(e){
  37885. //summary: updates picker position and inputs
  37886. // according to rgb, hex or hsv input changes
  37887. var col, hasit = false;
  37888. switch (e.target) {
  37889. //transform to hsv to pixels
  37890. case this.hexCode:
  37891. col = dojox.color.fromString(e.target.value);
  37892. hasit = true;
  37893. break;
  37894. case this.Rval:
  37895. case this.Gval:
  37896. case this.Bval:
  37897. col = dojox.color.fromArray([this.Rval.value, this.Gval.value, this.Bval.value]);
  37898. hasit = true;
  37899. break;
  37900. case this.Hval:
  37901. case this.Sval:
  37902. case this.Vval:
  37903. col = dojox.color.fromHsv(this.Hval.value, this.Sval.value, this.Vval.value);
  37904. hasit = true;
  37905. break;
  37906. }
  37907. if(hasit){
  37908. this._updatePickerLocations(col);
  37909. this._updateColorInputs(col);
  37910. this._updateValue(col, true);
  37911. }
  37912. },
  37913. _updateValue: function(/* dojox.color.Color */col, /* Boolean */fireChange){
  37914. // summary: updates the value of the widget
  37915. // can cancel reverse onChange by specifying second param
  37916. var hex = col.toHex();
  37917. this.value = this.valueNode.value = hex;
  37918. // anytime we muck with the color, fire onChange?
  37919. if(fireChange && (!this._timer || this.liveUpdate)) {
  37920. this.onChange(hex);
  37921. }
  37922. },
  37923. _updatePickerLocations: function(/* dojox.color.Color */col){
  37924. //summary: update handles on the pickers acording to color values
  37925. //
  37926. var hueSelCenter = this.PICKER_HUE_SELECTOR_H/2,
  37927. satSelCenterH = this.PICKER_SAT_SELECTOR_H/2,
  37928. satSelCenterW = this.PICKER_SAT_SELECTOR_W/2;
  37929. var hsv = col.toHsv(),
  37930. ypos = Math.round(this.PICKER_HUE_H - hsv.h / 360 * this.PICKER_HUE_H) - hueSelCenter,
  37931. newLeft = Math.round(hsv.s / 100 * this.PICKER_SAT_VAL_W) - satSelCenterW,
  37932. newTop = Math.round(this.PICKER_SAT_VAL_H - hsv.v / 100 * this.PICKER_SAT_VAL_H) - satSelCenterH
  37933. ;
  37934. if (this.animatePoint) {
  37935. d.fx.slideTo({
  37936. node: this.hueCursorNode,
  37937. duration: this.slideDuration,
  37938. top: ypos,
  37939. left: 0
  37940. }).play();
  37941. d.fx.slideTo({
  37942. node: this.cursorNode,
  37943. duration: this.slideDuration,
  37944. top: newTop,
  37945. left: newLeft
  37946. }).play();
  37947. }
  37948. else {
  37949. d.style(this.hueCursorNode, "top", ypos + "px");
  37950. d.style(this.cursorNode, {
  37951. left: newLeft + "px",
  37952. top: newTop + "px"
  37953. });
  37954. }
  37955. // limit hue calculations to only when it changes
  37956. if (hsv.h != this._hue) {
  37957. this._setHue(hsv.h);
  37958. }
  37959. },
  37960. _updateColorInputs: function(/* dojox.color.Color */col){
  37961. //summary: updates color inputs that were changed through other inputs
  37962. //or by clicking on the picker
  37963. var hex = col.toHex();
  37964. if (this.showRgb) {
  37965. this.Rval.value = col.r;
  37966. this.Gval.value = col.g;
  37967. this.Bval.value = col.b;
  37968. }
  37969. if (this.showHsv) {
  37970. var hsv = col.toHsv();
  37971. this.Hval.value = Math.round((hsv.h)); // convert to 0..360
  37972. this.Sval.value = Math.round(hsv.s);
  37973. this.Vval.value = Math.round(hsv.v);
  37974. }
  37975. if (this.showHex) {
  37976. this.hexCode.value = hex;
  37977. }
  37978. this.previewNode.style.backgroundColor = hex;
  37979. if (this.webSafe) {
  37980. this.safePreviewNode.style.backgroundColor = webSafeFromHex(hex);
  37981. }
  37982. },
  37983. _setHuePoint: function(/* Event */evt){
  37984. // summary: set the hue picker handle on relative y coordinates
  37985. var selCenter = (this.PICKER_HUE_SELECTOR_H/2);
  37986. var ypos = evt.layerY - selCenter;
  37987. if(this.animatePoint){
  37988. d.fx.slideTo({
  37989. node: this.hueCursorNode,
  37990. duration:this.slideDuration,
  37991. top: ypos,
  37992. left: 0,
  37993. onEnd: d.hitch(this, function() {this._updateColor(true); dijit.focus(this.hueCursorNode);})
  37994. }).play();
  37995. }else{
  37996. d.style(this.hueCursorNode, "top", ypos + "px");
  37997. this._updateColor(false);
  37998. }
  37999. },
  38000. _setPoint: function(/* Event */evt){
  38001. // summary: set our picker point based on relative x/y coordinates
  38002. // evt.preventDefault();
  38003. var satSelCenterH = this.PICKER_SAT_SELECTOR_H/2;
  38004. var satSelCenterW = this.PICKER_SAT_SELECTOR_W/2;
  38005. var newTop = evt.layerY - satSelCenterH;
  38006. var newLeft = evt.layerX - satSelCenterW;
  38007. if(evt){ dijit.focus(evt.target); }
  38008. if(this.animatePoint){
  38009. d.fx.slideTo({
  38010. node: this.cursorNode,
  38011. duration: this.slideDuration,
  38012. top: newTop,
  38013. left: newLeft,
  38014. onEnd: d.hitch(this, function() {this._updateColor(true); dijit.focus(this.cursorNode);})
  38015. }).play();
  38016. }else{
  38017. d.style(this.cursorNode, {
  38018. left: newLeft + "px",
  38019. top: newTop + "px"
  38020. });
  38021. this._updateColor(false);
  38022. }
  38023. },
  38024. _handleKey: function(/* Event */e){
  38025. // FIXME: not implemented YET
  38026. // var keys = d.keys;
  38027. },
  38028. focus: function(){
  38029. // summary:
  38030. // Put focus on this widget, only if focus isn't set on it already.
  38031. if(!this._focused){
  38032. dijit.focus(this.focusNode);
  38033. }
  38034. },
  38035. _stopDrag: function(e){
  38036. // summary:
  38037. // Function to hald the mouse down default
  38038. // to disable draggong of images out of the color
  38039. // picker.
  38040. dojo.stopEvent(e);
  38041. },
  38042. destroy: function(){
  38043. // summary:
  38044. // Over-ride to clean up subscriptions, etc.
  38045. this.inherited(arguments);
  38046. dojo.forEach(this._subs, function(sub){
  38047. dojo.unsubscribe(sub);
  38048. });
  38049. delete this._subs;
  38050. }
  38051. });
  38052. })(dojo);
  38053. }
  38054. if(!dojo._hasResource["dojox.uuid._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  38055. dojo._hasResource["dojox.uuid._base"] = true;
  38056. dojo.provide("dojox.uuid._base");
  38057. // Public constants:
  38058. dojox.uuid.NIL_UUID = "00000000-0000-0000-0000-000000000000";
  38059. dojox.uuid.version = {
  38060. // Enumeration for the different UUID versions.
  38061. UNKNOWN: 0,
  38062. TIME_BASED: 1,
  38063. DCE_SECURITY: 2,
  38064. NAME_BASED_MD5: 3,
  38065. RANDOM: 4,
  38066. NAME_BASED_SHA1: 5 };
  38067. dojox.uuid.variant = {
  38068. // Enumeration for the different UUID variants.
  38069. NCS: "0",
  38070. DCE: "10",
  38071. MICROSOFT: "110",
  38072. UNKNOWN: "111" };
  38073. dojox.uuid.assert = function(/*Boolean*/ booleanValue, /*String?*/ message){
  38074. // summary:
  38075. // Throws an exception if the assertion fails.
  38076. // description:
  38077. // If the asserted condition is true, this method does nothing. If the
  38078. // condition is false, we throw an error with a error message.
  38079. // booleanValue: Must be true for the assertion to succeed.
  38080. // message: A string describing the assertion.
  38081. // throws: Throws an Error if 'booleanValue' is false.
  38082. if(!booleanValue){
  38083. if(!message){
  38084. message = "An assert statement failed.\n" +
  38085. "The method dojox.uuid.assert() was called with a 'false' value.\n";
  38086. }
  38087. throw new Error(message);
  38088. }
  38089. };
  38090. dojox.uuid.generateNilUuid = function(){
  38091. // summary:
  38092. // This function returns the Nil UUID: "00000000-0000-0000-0000-000000000000".
  38093. // description:
  38094. // The Nil UUID is described in section 4.1.7 of
  38095. // RFC 4122: http://tools.ietf.org/html/rfc4122#section-4.1.7
  38096. // examples:
  38097. // var string = dojox.uuid.generateNilUuid();
  38098. return dojox.uuid.NIL_UUID; // String
  38099. };
  38100. dojox.uuid.isValid = function(/*String*/ uuidString){
  38101. // summary:
  38102. // Returns true if the UUID was initialized with a valid value.
  38103. uuidString = uuidString.toString();
  38104. var valid = (dojo.isString(uuidString) &&
  38105. (uuidString.length == 36) &&
  38106. (uuidString == uuidString.toLowerCase()));
  38107. if(valid){
  38108. var arrayOfParts = uuidString.split("-");
  38109. valid = ((arrayOfParts.length == 5) &&
  38110. (arrayOfParts[0].length == 8) &&
  38111. (arrayOfParts[1].length == 4) &&
  38112. (arrayOfParts[2].length == 4) &&
  38113. (arrayOfParts[3].length == 4) &&
  38114. (arrayOfParts[4].length == 12));
  38115. var HEX_RADIX = 16;
  38116. for (var i in arrayOfParts) {
  38117. var part = arrayOfParts[i];
  38118. var integer = parseInt(part, HEX_RADIX);
  38119. valid = valid && isFinite(integer);
  38120. }
  38121. }
  38122. return valid; // boolean
  38123. };
  38124. dojox.uuid.getVariant = function(/*String*/ uuidString){
  38125. // summary:
  38126. // Returns a variant code that indicates what type of UUID this is.
  38127. // Returns one of the enumerated dojox.uuid.variant values.
  38128. // example:
  38129. // var variant = dojox.uuid.getVariant("3b12f1df-5232-4804-897e-917bf397618a");
  38130. // dojox.uuid.assert(variant == dojox.uuid.variant.DCE);
  38131. // example:
  38132. // "3b12f1df-5232-4804-897e-917bf397618a"
  38133. // ^
  38134. // |
  38135. // (variant "10__" == DCE)
  38136. if(!dojox.uuid._ourVariantLookupTable){
  38137. var variant = dojox.uuid.variant;
  38138. var lookupTable = [];
  38139. lookupTable[0x0] = variant.NCS; // 0000
  38140. lookupTable[0x1] = variant.NCS; // 0001
  38141. lookupTable[0x2] = variant.NCS; // 0010
  38142. lookupTable[0x3] = variant.NCS; // 0011
  38143. lookupTable[0x4] = variant.NCS; // 0100
  38144. lookupTable[0x5] = variant.NCS; // 0101
  38145. lookupTable[0x6] = variant.NCS; // 0110
  38146. lookupTable[0x7] = variant.NCS; // 0111
  38147. lookupTable[0x8] = variant.DCE; // 1000
  38148. lookupTable[0x9] = variant.DCE; // 1001
  38149. lookupTable[0xA] = variant.DCE; // 1010
  38150. lookupTable[0xB] = variant.DCE; // 1011
  38151. lookupTable[0xC] = variant.MICROSOFT; // 1100
  38152. lookupTable[0xD] = variant.MICROSOFT; // 1101
  38153. lookupTable[0xE] = variant.UNKNOWN; // 1110
  38154. lookupTable[0xF] = variant.UNKNOWN; // 1111
  38155. dojox.uuid._ourVariantLookupTable = lookupTable;
  38156. }
  38157. uuidString = uuidString.toString();
  38158. var variantCharacter = uuidString.charAt(19);
  38159. var HEX_RADIX = 16;
  38160. var variantNumber = parseInt(variantCharacter, HEX_RADIX);
  38161. dojox.uuid.assert((variantNumber >= 0) && (variantNumber <= 16));
  38162. return dojox.uuid._ourVariantLookupTable[variantNumber]; // dojox.uuid.variant
  38163. };
  38164. dojox.uuid.getVersion = function(/*String*/ uuidString){
  38165. // summary:
  38166. // Returns a version number that indicates what type of UUID this is.
  38167. // Returns one of the enumerated dojox.uuid.version values.
  38168. // example:
  38169. // var version = dojox.uuid.getVersion("b4308fb0-86cd-11da-a72b-0800200c9a66");
  38170. // dojox.uuid.assert(version == dojox.uuid.version.TIME_BASED);
  38171. // exceptions:
  38172. // Throws an Error if this is not a DCE Variant UUID.
  38173. var errorMessage = "dojox.uuid.getVersion() was not passed a DCE Variant UUID.";
  38174. dojox.uuid.assert(dojox.uuid.getVariant(uuidString) == dojox.uuid.variant.DCE, errorMessage);
  38175. uuidString = uuidString.toString();
  38176. // "b4308fb0-86cd-11da-a72b-0800200c9a66"
  38177. // ^
  38178. // |
  38179. // (version 1 == TIME_BASED)
  38180. var versionCharacter = uuidString.charAt(14);
  38181. var HEX_RADIX = 16;
  38182. var versionNumber = parseInt(versionCharacter, HEX_RADIX);
  38183. return versionNumber; // dojox.uuid.version
  38184. };
  38185. dojox.uuid.getNode = function(/*String*/ uuidString){
  38186. // summary:
  38187. // If this is a version 1 UUID (a time-based UUID), getNode() returns a
  38188. // 12-character string with the "node" or "pseudonode" portion of the UUID,
  38189. // which is the rightmost 12 characters.
  38190. // exceptions:
  38191. // Throws an Error if this is not a version 1 UUID.
  38192. var errorMessage = "dojox.uuid.getNode() was not passed a TIME_BASED UUID.";
  38193. dojox.uuid.assert(dojox.uuid.getVersion(uuidString) == dojox.uuid.version.TIME_BASED, errorMessage);
  38194. uuidString = uuidString.toString();
  38195. var arrayOfStrings = uuidString.split('-');
  38196. var nodeString = arrayOfStrings[4];
  38197. return nodeString; // String (a 12-character string, which will look something like "917bf397618a")
  38198. };
  38199. dojox.uuid.getTimestamp = function(/*String*/ uuidString, /*String?*/ returnType){
  38200. // summary:
  38201. // If this is a version 1 UUID (a time-based UUID), this method returns
  38202. // the timestamp value encoded in the UUID. The caller can ask for the
  38203. // timestamp to be returned either as a JavaScript Date object or as a
  38204. // 15-character string of hex digits.
  38205. // returnType: Any of these five values: "string", String, "hex", "date", Date
  38206. // returns:
  38207. // Returns the timestamp value as a JavaScript Date object or a 15-character string of hex digits.
  38208. // examples:
  38209. // var uuidString = "b4308fb0-86cd-11da-a72b-0800200c9a66";
  38210. // var date, string, hexString;
  38211. // date = dojox.uuid.getTimestamp(uuidString); // returns a JavaScript Date
  38212. // date = dojox.uuid.getTimestamp(uuidString, Date); //
  38213. // string = dojox.uuid.getTimestamp(uuidString, String); // "Mon, 16 Jan 2006 20:21:41 GMT"
  38214. // hexString = dojox.uuid.getTimestamp(uuidString, "hex"); // "1da86cdb4308fb0"
  38215. // exceptions:
  38216. // Throws an Error if this is not a version 1 UUID.
  38217. var errorMessage = "dojox.uuid.getTimestamp() was not passed a TIME_BASED UUID.";
  38218. dojox.uuid.assert(dojox.uuid.getVersion(uuidString) == dojox.uuid.version.TIME_BASED, errorMessage);
  38219. uuidString = uuidString.toString();
  38220. if(!returnType){returnType = null};
  38221. switch(returnType){
  38222. case "string":
  38223. case String:
  38224. return dojox.uuid.getTimestamp(uuidString, Date).toUTCString(); // String (e.g. "Mon, 16 Jan 2006 20:21:41 GMT")
  38225. break;
  38226. case "hex":
  38227. // Return a 15-character string of hex digits containing the
  38228. // timestamp for this UUID, with the high-order bits first.
  38229. var arrayOfStrings = uuidString.split('-');
  38230. var hexTimeLow = arrayOfStrings[0];
  38231. var hexTimeMid = arrayOfStrings[1];
  38232. var hexTimeHigh = arrayOfStrings[2];
  38233. // Chop off the leading "1" character, which is the UUID
  38234. // version number for time-based UUIDs.
  38235. hexTimeHigh = hexTimeHigh.slice(1);
  38236. var timestampAsHexString = hexTimeHigh + hexTimeMid + hexTimeLow;
  38237. dojox.uuid.assert(timestampAsHexString.length == 15);
  38238. return timestampAsHexString; // String (e.g. "1da86cdb4308fb0")
  38239. break;
  38240. case null: // no returnType was specified, so default to Date
  38241. case "date":
  38242. case Date:
  38243. // Return a JavaScript Date object.
  38244. var GREGORIAN_CHANGE_OFFSET_IN_HOURS = 3394248;
  38245. var HEX_RADIX = 16;
  38246. var arrayOfParts = uuidString.split('-');
  38247. var timeLow = parseInt(arrayOfParts[0], HEX_RADIX);
  38248. var timeMid = parseInt(arrayOfParts[1], HEX_RADIX);
  38249. var timeHigh = parseInt(arrayOfParts[2], HEX_RADIX);
  38250. var hundredNanosecondIntervalsSince1582 = timeHigh & 0x0FFF;
  38251. hundredNanosecondIntervalsSince1582 <<= 16;
  38252. hundredNanosecondIntervalsSince1582 += timeMid;
  38253. // What we really want to do next is shift left 32 bits, but the
  38254. // result will be too big to fit in an int, so we'll multiply by 2^32,
  38255. // and the result will be a floating point approximation.
  38256. hundredNanosecondIntervalsSince1582 *= 0x100000000;
  38257. hundredNanosecondIntervalsSince1582 += timeLow;
  38258. var millisecondsSince1582 = hundredNanosecondIntervalsSince1582 / 10000;
  38259. // Again, this will be a floating point approximation.
  38260. // We can make things exact later if we need to.
  38261. var secondsPerHour = 60 * 60;
  38262. var hoursBetween1582and1970 = GREGORIAN_CHANGE_OFFSET_IN_HOURS;
  38263. var secondsBetween1582and1970 = hoursBetween1582and1970 * secondsPerHour;
  38264. var millisecondsBetween1582and1970 = secondsBetween1582and1970 * 1000;
  38265. var millisecondsSince1970 = millisecondsSince1582 - millisecondsBetween1582and1970;
  38266. var timestampAsDate = new Date(millisecondsSince1970);
  38267. return timestampAsDate; // Date
  38268. break;
  38269. default:
  38270. // we got passed something other than a valid returnType
  38271. dojox.uuid.assert(false, "dojox.uuid.getTimestamp was not passed a valid returnType: " + returnType);
  38272. break;
  38273. }
  38274. };
  38275. }
  38276. if(!dojo._hasResource["dojox.uuid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  38277. dojo._hasResource["dojox.uuid"] = true;
  38278. dojo.provide("dojox.uuid");
  38279. }
  38280. if(!dojo._hasResource["dojox.uuid.generateRandomUuid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  38281. dojo._hasResource["dojox.uuid.generateRandomUuid"] = true;
  38282. dojo.provide("dojox.uuid.generateRandomUuid");
  38283. dojox.uuid.generateRandomUuid = function(){
  38284. // summary:
  38285. // This function generates random UUIDs, meaning "version 4" UUIDs.
  38286. // description:
  38287. // A typical generated value would be something like this:
  38288. // "3b12f1df-5232-4804-897e-917bf397618a"
  38289. //
  38290. // For more information about random UUIDs, see sections 4.4 and
  38291. // 4.5 of RFC 4122: http://tools.ietf.org/html/rfc4122#section-4.4
  38292. //
  38293. // This generator function is designed to be small and fast,
  38294. // but not necessarily good.
  38295. //
  38296. // Small: This generator has a small footprint. Once comments are
  38297. // stripped, it's only about 25 lines of code, and it doesn't
  38298. // dojo.require() any other modules.
  38299. //
  38300. // Fast: This generator can generate lots of new UUIDs fairly quickly
  38301. // (at least, more quickly than the other dojo UUID generators).
  38302. //
  38303. // Not necessarily good: We use Math.random() as our source
  38304. // of randomness, which may or may not provide much randomness.
  38305. // examples:
  38306. // var string = dojox.uuid.generateRandomUuid();
  38307. var HEX_RADIX = 16;
  38308. function _generateRandomEightCharacterHexString(){
  38309. // Make random32bitNumber be a randomly generated floating point number
  38310. // between 0 and (4,294,967,296 - 1), inclusive.
  38311. var random32bitNumber = Math.floor( (Math.random() % 1) * Math.pow(2, 32) );
  38312. var eightCharacterHexString = random32bitNumber.toString(HEX_RADIX);
  38313. while(eightCharacterHexString.length < 8){
  38314. eightCharacterHexString = "0" + eightCharacterHexString;
  38315. }
  38316. return eightCharacterHexString; // for example: "3B12F1DF"
  38317. }
  38318. var hyphen = "-";
  38319. var versionCodeForRandomlyGeneratedUuids = "4"; // 8 == binary2hex("0100")
  38320. var variantCodeForDCEUuids = "8"; // 8 == binary2hex("1000")
  38321. var a = _generateRandomEightCharacterHexString();
  38322. var b = _generateRandomEightCharacterHexString();
  38323. b = b.substring(0, 4) + hyphen + versionCodeForRandomlyGeneratedUuids + b.substring(5, 8);
  38324. var c = _generateRandomEightCharacterHexString();
  38325. c = variantCodeForDCEUuids + c.substring(1, 4) + hyphen + c.substring(4, 8);
  38326. var d = _generateRandomEightCharacterHexString();
  38327. var returnValue = a + hyphen + b + hyphen + c + d;
  38328. returnValue = returnValue.toLowerCase();
  38329. return returnValue; // String
  38330. };
  38331. }
  38332. dojo.i18n._preloadLocalizations("dojo.nls.buxdojo", ["ROOT","ar","az","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hr","hu","it","it-it","ja","ja-jp","ko","ko-kr","nb","nl","nl-nl","no","pl","pt","pt-br","pt-pt","ro","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]);