simpleeditor-debug.js 309 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494
  1. /*
  2. Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.8.0r4
  6. */
  7. (function() {
  8. var Dom = YAHOO.util.Dom,
  9. Event = YAHOO.util.Event,
  10. Lang = YAHOO.lang;
  11. /**
  12. * @module editor
  13. * @description <p>Creates a rich custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar</p>
  14. * @class ToolbarButtonAdvanced
  15. * @namespace YAHOO.widget
  16. * @requires yahoo, dom, element, event, container_core, menu, button
  17. *
  18. * Provides a toolbar button based on the button and menu widgets.
  19. * @constructor
  20. * @class ToolbarButtonAdvanced
  21. * @param {String/HTMLElement} el The element to turn into a button.
  22. * @param {Object} attrs Object liternal containing configuration parameters.
  23. */
  24. if (YAHOO.widget.Button) {
  25. YAHOO.widget.ToolbarButtonAdvanced = YAHOO.widget.Button;
  26. /**
  27. * @property buttonType
  28. * @private
  29. * @description Tells if the Button is a Rich Button or a Simple Button
  30. */
  31. YAHOO.widget.ToolbarButtonAdvanced.prototype.buttonType = 'rich';
  32. /**
  33. * @method checkValue
  34. * @param {String} value The value of the option that we want to mark as selected
  35. * @description Select an option by value
  36. */
  37. YAHOO.widget.ToolbarButtonAdvanced.prototype.checkValue = function(value) {
  38. var _menuItems = this.getMenu().getItems();
  39. if (_menuItems.length === 0) {
  40. this.getMenu()._onBeforeShow();
  41. _menuItems = this.getMenu().getItems();
  42. }
  43. for (var i = 0; i < _menuItems.length; i++) {
  44. _menuItems[i].cfg.setProperty('checked', false);
  45. if (_menuItems[i].value == value) {
  46. _menuItems[i].cfg.setProperty('checked', true);
  47. }
  48. }
  49. };
  50. } else {
  51. YAHOO.widget.ToolbarButtonAdvanced = function() {};
  52. }
  53. /**
  54. * @description <p>Creates a basic custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar</p><p>Provides a toolbar button based on the button and menu widgets, &lt;select&gt; elements are used in place of menu's.</p>
  55. * @class ToolbarButton
  56. * @namespace YAHOO.widget
  57. * @requires yahoo, dom, element, event
  58. * @extends YAHOO.util.Element
  59. *
  60. *
  61. * @constructor
  62. * @param {String/HTMLElement} el The element to turn into a button.
  63. * @param {Object} attrs Object liternal containing configuration parameters.
  64. */
  65. YAHOO.widget.ToolbarButton = function(el, attrs) {
  66. YAHOO.log('ToolbarButton Initalizing', 'info', 'ToolbarButton');
  67. YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar');
  68. if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
  69. attrs = el;
  70. }
  71. var local_attrs = (attrs || {});
  72. var oConfig = {
  73. element: null,
  74. attributes: local_attrs
  75. };
  76. if (!oConfig.attributes.type) {
  77. oConfig.attributes.type = 'push';
  78. }
  79. oConfig.element = document.createElement('span');
  80. oConfig.element.setAttribute('unselectable', 'on');
  81. oConfig.element.className = 'yui-button yui-' + oConfig.attributes.type + '-button';
  82. oConfig.element.innerHTML = '<span class="first-child"><a href="#">LABEL</a></span>';
  83. oConfig.element.firstChild.firstChild.tabIndex = '-1';
  84. oConfig.attributes.id = (oConfig.attributes.id || Dom.generateId());
  85. oConfig.element.id = oConfig.attributes.id;
  86. YAHOO.widget.ToolbarButton.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
  87. };
  88. YAHOO.extend(YAHOO.widget.ToolbarButton, YAHOO.util.Element, {
  89. /**
  90. * @property buttonType
  91. * @private
  92. * @description Tells if the Button is a Rich Button or a Simple Button
  93. */
  94. buttonType: 'normal',
  95. /**
  96. * @method _handleMouseOver
  97. * @private
  98. * @description Adds classes to the button elements on mouseover (hover)
  99. */
  100. _handleMouseOver: function() {
  101. if (!this.get('disabled')) {
  102. this.addClass('yui-button-hover');
  103. this.addClass('yui-' + this.get('type') + '-button-hover');
  104. }
  105. },
  106. /**
  107. * @method _handleMouseOut
  108. * @private
  109. * @description Removes classes from the button elements on mouseout (hover)
  110. */
  111. _handleMouseOut: function() {
  112. this.removeClass('yui-button-hover');
  113. this.removeClass('yui-' + this.get('type') + '-button-hover');
  114. },
  115. /**
  116. * @method checkValue
  117. * @param {String} value The value of the option that we want to mark as selected
  118. * @description Select an option by value
  119. */
  120. checkValue: function(value) {
  121. if (this.get('type') == 'menu') {
  122. var opts = this._button.options;
  123. for (var i = 0; i < opts.length; i++) {
  124. if (opts[i].value == value) {
  125. opts.selectedIndex = i;
  126. }
  127. }
  128. }
  129. },
  130. /**
  131. * @method init
  132. * @description The ToolbarButton class's initialization method
  133. */
  134. init: function(p_oElement, p_oAttributes) {
  135. YAHOO.widget.ToolbarButton.superclass.init.call(this, p_oElement, p_oAttributes);
  136. this.on('mouseover', this._handleMouseOver, this, true);
  137. this.on('mouseout', this._handleMouseOut, this, true);
  138. this.on('click', function(ev) {
  139. Event.stopEvent(ev);
  140. return false;
  141. }, this, true);
  142. },
  143. /**
  144. * @method initAttributes
  145. * @description Initializes all of the configuration attributes used to create
  146. * the toolbar.
  147. * @param {Object} attr Object literal specifying a set of
  148. * configuration attributes used to create the toolbar.
  149. */
  150. initAttributes: function(attr) {
  151. YAHOO.widget.ToolbarButton.superclass.initAttributes.call(this, attr);
  152. /**
  153. * @attribute value
  154. * @description The value of the button
  155. * @type String
  156. */
  157. this.setAttributeConfig('value', {
  158. value: attr.value
  159. });
  160. /**
  161. * @attribute menu
  162. * @description The menu attribute, see YAHOO.widget.Button
  163. * @type Object
  164. */
  165. this.setAttributeConfig('menu', {
  166. value: attr.menu || false
  167. });
  168. /**
  169. * @attribute type
  170. * @description The type of button to create: push, menu, color, select, spin
  171. * @type String
  172. */
  173. this.setAttributeConfig('type', {
  174. value: attr.type,
  175. writeOnce: true,
  176. method: function(type) {
  177. var el, opt;
  178. if (!this._button) {
  179. this._button = this.get('element').getElementsByTagName('a')[0];
  180. }
  181. switch (type) {
  182. case 'select':
  183. case 'menu':
  184. el = document.createElement('select');
  185. el.id = this.get('id');
  186. var menu = this.get('menu');
  187. for (var i = 0; i < menu.length; i++) {
  188. opt = document.createElement('option');
  189. opt.innerHTML = menu[i].text;
  190. opt.value = menu[i].value;
  191. if (menu[i].checked) {
  192. opt.selected = true;
  193. }
  194. el.appendChild(opt);
  195. }
  196. this._button.parentNode.replaceChild(el, this._button);
  197. Event.on(el, 'change', this._handleSelect, this, true);
  198. this._button = el;
  199. break;
  200. }
  201. }
  202. });
  203. /**
  204. * @attribute disabled
  205. * @description Set the button into a disabled state
  206. * @type String
  207. */
  208. this.setAttributeConfig('disabled', {
  209. value: attr.disabled || false,
  210. method: function(disabled) {
  211. if (disabled) {
  212. this.addClass('yui-button-disabled');
  213. this.addClass('yui-' + this.get('type') + '-button-disabled');
  214. } else {
  215. this.removeClass('yui-button-disabled');
  216. this.removeClass('yui-' + this.get('type') + '-button-disabled');
  217. }
  218. if ((this.get('type') == 'menu') || (this.get('type') == 'select')) {
  219. this._button.disabled = disabled;
  220. }
  221. }
  222. });
  223. /**
  224. * @attribute label
  225. * @description The text label for the button
  226. * @type String
  227. */
  228. this.setAttributeConfig('label', {
  229. value: attr.label,
  230. method: function(label) {
  231. if (!this._button) {
  232. this._button = this.get('element').getElementsByTagName('a')[0];
  233. }
  234. if (this.get('type') == 'push') {
  235. this._button.innerHTML = label;
  236. }
  237. }
  238. });
  239. /**
  240. * @attribute title
  241. * @description The title of the button
  242. * @type String
  243. */
  244. this.setAttributeConfig('title', {
  245. value: attr.title
  246. });
  247. /**
  248. * @config container
  249. * @description The container that the button is rendered to, handled by Toolbar
  250. * @type String
  251. */
  252. this.setAttributeConfig('container', {
  253. value: null,
  254. writeOnce: true,
  255. method: function(cont) {
  256. this.appendTo(cont);
  257. }
  258. });
  259. },
  260. /**
  261. * @private
  262. * @method _handleSelect
  263. * @description The event fired when a change event gets fired on a select element
  264. * @param {Event} ev The change event.
  265. */
  266. _handleSelect: function(ev) {
  267. var tar = Event.getTarget(ev);
  268. var value = tar.options[tar.selectedIndex].value;
  269. this.fireEvent('change', {type: 'change', value: value });
  270. },
  271. /**
  272. * @method getMenu
  273. * @description A stub function to mimic YAHOO.widget.Button's getMenu method
  274. */
  275. getMenu: function() {
  276. return this.get('menu');
  277. },
  278. /**
  279. * @method destroy
  280. * @description Destroy the button
  281. */
  282. destroy: function() {
  283. Event.purgeElement(this.get('element'), true);
  284. this.get('element').parentNode.removeChild(this.get('element'));
  285. //Brutal Object Destroy
  286. for (var i in this) {
  287. if (Lang.hasOwnProperty(this, i)) {
  288. this[i] = null;
  289. }
  290. }
  291. },
  292. /**
  293. * @method fireEvent
  294. * @description Overridden fireEvent method to prevent DOM events from firing if the button is disabled.
  295. */
  296. fireEvent: function(p_sType, p_aArgs) {
  297. // Disabled buttons should not respond to DOM events
  298. if (this.DOM_EVENTS[p_sType] && this.get('disabled')) {
  299. Event.stopEvent(p_aArgs);
  300. return;
  301. }
  302. YAHOO.widget.ToolbarButton.superclass.fireEvent.call(this, p_sType, p_aArgs);
  303. },
  304. /**
  305. * @method toString
  306. * @description Returns a string representing the toolbar.
  307. * @return {String}
  308. */
  309. toString: function() {
  310. return 'ToolbarButton (' + this.get('id') + ')';
  311. }
  312. });
  313. })();
  314. /**
  315. * @module editor
  316. * @description <p>Creates a rich Toolbar widget based on Button. Primarily used with the Rich Text Editor</p>
  317. * @namespace YAHOO.widget
  318. * @requires yahoo, dom, element, event, toolbarbutton
  319. * @optional container_core, dragdrop
  320. */
  321. (function() {
  322. var Dom = YAHOO.util.Dom,
  323. Event = YAHOO.util.Event,
  324. Lang = YAHOO.lang;
  325. var getButton = function(id) {
  326. var button = id;
  327. if (Lang.isString(id)) {
  328. button = this.getButtonById(id);
  329. }
  330. if (Lang.isNumber(id)) {
  331. button = this.getButtonByIndex(id);
  332. }
  333. if ((!(button instanceof YAHOO.widget.ToolbarButton)) && (!(button instanceof YAHOO.widget.ToolbarButtonAdvanced))) {
  334. button = this.getButtonByValue(id);
  335. }
  336. if ((button instanceof YAHOO.widget.ToolbarButton) || (button instanceof YAHOO.widget.ToolbarButtonAdvanced)) {
  337. return button;
  338. }
  339. return false;
  340. };
  341. /**
  342. * Provides a rich toolbar widget based on the button and menu widgets
  343. * @constructor
  344. * @class Toolbar
  345. * @extends YAHOO.util.Element
  346. * @param {String/HTMLElement} el The element to turn into a toolbar.
  347. * @param {Object} attrs Object liternal containing configuration parameters.
  348. */
  349. YAHOO.widget.Toolbar = function(el, attrs) {
  350. YAHOO.log('Toolbar Initalizing', 'info', 'Toolbar');
  351. YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar');
  352. if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
  353. attrs = el;
  354. }
  355. var local_attrs = {};
  356. if (attrs) {
  357. Lang.augmentObject(local_attrs, attrs); //Break the config reference
  358. }
  359. var oConfig = {
  360. element: null,
  361. attributes: local_attrs
  362. };
  363. if (Lang.isString(el) && Dom.get(el)) {
  364. oConfig.element = Dom.get(el);
  365. } else if (Lang.isObject(el) && Dom.get(el) && Dom.get(el).nodeType) {
  366. oConfig.element = Dom.get(el);
  367. }
  368. if (!oConfig.element) {
  369. YAHOO.log('No element defined, creating toolbar container', 'warn', 'Toolbar');
  370. oConfig.element = document.createElement('DIV');
  371. oConfig.element.id = Dom.generateId();
  372. if (local_attrs.container && Dom.get(local_attrs.container)) {
  373. YAHOO.log('Container found in config appending to it (' + Dom.get(local_attrs.container).id + ')', 'info', 'Toolbar');
  374. Dom.get(local_attrs.container).appendChild(oConfig.element);
  375. }
  376. }
  377. if (!oConfig.element.id) {
  378. oConfig.element.id = ((Lang.isString(el)) ? el : Dom.generateId());
  379. YAHOO.log('No element ID defined for toolbar container, creating..', 'warn', 'Toolbar');
  380. }
  381. YAHOO.log('Initing toolbar with id: ' + oConfig.element.id, 'info', 'Toolbar');
  382. var fs = document.createElement('fieldset');
  383. var lg = document.createElement('legend');
  384. lg.innerHTML = 'Toolbar';
  385. fs.appendChild(lg);
  386. var cont = document.createElement('DIV');
  387. oConfig.attributes.cont = cont;
  388. Dom.addClass(cont, 'yui-toolbar-subcont');
  389. fs.appendChild(cont);
  390. oConfig.element.appendChild(fs);
  391. oConfig.element.tabIndex = -1;
  392. oConfig.attributes.element = oConfig.element;
  393. oConfig.attributes.id = oConfig.element.id;
  394. this._configuredButtons = [];
  395. YAHOO.widget.Toolbar.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
  396. };
  397. YAHOO.extend(YAHOO.widget.Toolbar, YAHOO.util.Element, {
  398. /**
  399. * @protected
  400. * @property _configuredButtons
  401. * @type Array
  402. */
  403. _configuredButtons: null,
  404. /**
  405. * @method _addMenuClasses
  406. * @private
  407. * @description This method is called from Menu's renderEvent to add a few more classes to the menu items
  408. * @param {String} ev The event that fired.
  409. * @param {Array} na Array of event information.
  410. * @param {Object} o Button config object.
  411. */
  412. _addMenuClasses: function(ev, na, o) {
  413. Dom.addClass(this.element, 'yui-toolbar-' + o.get('value') + '-menu');
  414. if (Dom.hasClass(o._button.parentNode.parentNode, 'yui-toolbar-select')) {
  415. Dom.addClass(this.element, 'yui-toolbar-select-menu');
  416. }
  417. var items = this.getItems();
  418. for (var i = 0; i < items.length; i++) {
  419. Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-').toLowerCase() : items[i]._oText.nodeValue.replace(/ /g, '-').toLowerCase()));
  420. Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-') : items[i]._oText.nodeValue.replace(/ /g, '-')));
  421. }
  422. },
  423. /**
  424. * @property buttonType
  425. * @description The default button to use
  426. * @type Object
  427. */
  428. buttonType: YAHOO.widget.ToolbarButton,
  429. /**
  430. * @property dd
  431. * @description The DragDrop instance associated with the Toolbar
  432. * @type Object
  433. */
  434. dd: null,
  435. /**
  436. * @property _colorData
  437. * @description Object reference containing colors hex and text values.
  438. * @type Object
  439. */
  440. _colorData: {
  441. /* {{{ _colorData */
  442. '#111111': 'Obsidian',
  443. '#2D2D2D': 'Dark Gray',
  444. '#434343': 'Shale',
  445. '#5B5B5B': 'Flint',
  446. '#737373': 'Gray',
  447. '#8B8B8B': 'Concrete',
  448. '#A2A2A2': 'Gray',
  449. '#B9B9B9': 'Titanium',
  450. '#000000': 'Black',
  451. '#D0D0D0': 'Light Gray',
  452. '#E6E6E6': 'Silver',
  453. '#FFFFFF': 'White',
  454. '#BFBF00': 'Pumpkin',
  455. '#FFFF00': 'Yellow',
  456. '#FFFF40': 'Banana',
  457. '#FFFF80': 'Pale Yellow',
  458. '#FFFFBF': 'Butter',
  459. '#525330': 'Raw Siena',
  460. '#898A49': 'Mildew',
  461. '#AEA945': 'Olive',
  462. '#7F7F00': 'Paprika',
  463. '#C3BE71': 'Earth',
  464. '#E0DCAA': 'Khaki',
  465. '#FCFAE1': 'Cream',
  466. '#60BF00': 'Cactus',
  467. '#80FF00': 'Chartreuse',
  468. '#A0FF40': 'Green',
  469. '#C0FF80': 'Pale Lime',
  470. '#DFFFBF': 'Light Mint',
  471. '#3B5738': 'Green',
  472. '#668F5A': 'Lime Gray',
  473. '#7F9757': 'Yellow',
  474. '#407F00': 'Clover',
  475. '#8A9B55': 'Pistachio',
  476. '#B7C296': 'Light Jade',
  477. '#E6EBD5': 'Breakwater',
  478. '#00BF00': 'Spring Frost',
  479. '#00FF80': 'Pastel Green',
  480. '#40FFA0': 'Light Emerald',
  481. '#80FFC0': 'Sea Foam',
  482. '#BFFFDF': 'Sea Mist',
  483. '#033D21': 'Dark Forrest',
  484. '#438059': 'Moss',
  485. '#7FA37C': 'Medium Green',
  486. '#007F40': 'Pine',
  487. '#8DAE94': 'Yellow Gray Green',
  488. '#ACC6B5': 'Aqua Lung',
  489. '#DDEBE2': 'Sea Vapor',
  490. '#00BFBF': 'Fog',
  491. '#00FFFF': 'Cyan',
  492. '#40FFFF': 'Turquoise Blue',
  493. '#80FFFF': 'Light Aqua',
  494. '#BFFFFF': 'Pale Cyan',
  495. '#033D3D': 'Dark Teal',
  496. '#347D7E': 'Gray Turquoise',
  497. '#609A9F': 'Green Blue',
  498. '#007F7F': 'Seaweed',
  499. '#96BDC4': 'Green Gray',
  500. '#B5D1D7': 'Soapstone',
  501. '#E2F1F4': 'Light Turquoise',
  502. '#0060BF': 'Summer Sky',
  503. '#0080FF': 'Sky Blue',
  504. '#40A0FF': 'Electric Blue',
  505. '#80C0FF': 'Light Azure',
  506. '#BFDFFF': 'Ice Blue',
  507. '#1B2C48': 'Navy',
  508. '#385376': 'Biscay',
  509. '#57708F': 'Dusty Blue',
  510. '#00407F': 'Sea Blue',
  511. '#7792AC': 'Sky Blue Gray',
  512. '#A8BED1': 'Morning Sky',
  513. '#DEEBF6': 'Vapor',
  514. '#0000BF': 'Deep Blue',
  515. '#0000FF': 'Blue',
  516. '#4040FF': 'Cerulean Blue',
  517. '#8080FF': 'Evening Blue',
  518. '#BFBFFF': 'Light Blue',
  519. '#212143': 'Deep Indigo',
  520. '#373E68': 'Sea Blue',
  521. '#444F75': 'Night Blue',
  522. '#00007F': 'Indigo Blue',
  523. '#585E82': 'Dockside',
  524. '#8687A4': 'Blue Gray',
  525. '#D2D1E1': 'Light Blue Gray',
  526. '#6000BF': 'Neon Violet',
  527. '#8000FF': 'Blue Violet',
  528. '#A040FF': 'Violet Purple',
  529. '#C080FF': 'Violet Dusk',
  530. '#DFBFFF': 'Pale Lavender',
  531. '#302449': 'Cool Shale',
  532. '#54466F': 'Dark Indigo',
  533. '#655A7F': 'Dark Violet',
  534. '#40007F': 'Violet',
  535. '#726284': 'Smoky Violet',
  536. '#9E8FA9': 'Slate Gray',
  537. '#DCD1DF': 'Violet White',
  538. '#BF00BF': 'Royal Violet',
  539. '#FF00FF': 'Fuchsia',
  540. '#FF40FF': 'Magenta',
  541. '#FF80FF': 'Orchid',
  542. '#FFBFFF': 'Pale Magenta',
  543. '#4A234A': 'Dark Purple',
  544. '#794A72': 'Medium Purple',
  545. '#936386': 'Cool Granite',
  546. '#7F007F': 'Purple',
  547. '#9D7292': 'Purple Moon',
  548. '#C0A0B6': 'Pale Purple',
  549. '#ECDAE5': 'Pink Cloud',
  550. '#BF005F': 'Hot Pink',
  551. '#FF007F': 'Deep Pink',
  552. '#FF409F': 'Grape',
  553. '#FF80BF': 'Electric Pink',
  554. '#FFBFDF': 'Pink',
  555. '#451528': 'Purple Red',
  556. '#823857': 'Purple Dino',
  557. '#A94A76': 'Purple Gray',
  558. '#7F003F': 'Rose',
  559. '#BC6F95': 'Antique Mauve',
  560. '#D8A5BB': 'Cool Marble',
  561. '#F7DDE9': 'Pink Granite',
  562. '#C00000': 'Apple',
  563. '#FF0000': 'Fire Truck',
  564. '#FF4040': 'Pale Red',
  565. '#FF8080': 'Salmon',
  566. '#FFC0C0': 'Warm Pink',
  567. '#441415': 'Sepia',
  568. '#82393C': 'Rust',
  569. '#AA4D4E': 'Brick',
  570. '#800000': 'Brick Red',
  571. '#BC6E6E': 'Mauve',
  572. '#D8A3A4': 'Shrimp Pink',
  573. '#F8DDDD': 'Shell Pink',
  574. '#BF5F00': 'Dark Orange',
  575. '#FF7F00': 'Orange',
  576. '#FF9F40': 'Grapefruit',
  577. '#FFBF80': 'Canteloupe',
  578. '#FFDFBF': 'Wax',
  579. '#482C1B': 'Dark Brick',
  580. '#855A40': 'Dirt',
  581. '#B27C51': 'Tan',
  582. '#7F3F00': 'Nutmeg',
  583. '#C49B71': 'Mustard',
  584. '#E1C4A8': 'Pale Tan',
  585. '#FDEEE0': 'Marble'
  586. /* }}} */
  587. },
  588. /**
  589. * @property _colorPicker
  590. * @description The HTML Element containing the colorPicker
  591. * @type HTMLElement
  592. */
  593. _colorPicker: null,
  594. /**
  595. * @property STR_COLLAPSE
  596. * @description String for Toolbar Collapse Button
  597. * @type String
  598. */
  599. STR_COLLAPSE: 'Collapse Toolbar',
  600. /**
  601. * @property STR_EXPAND
  602. * @description String for Toolbar Collapse Button - Expand
  603. * @type String
  604. */
  605. STR_EXPAND: 'Expand Toolbar',
  606. /**
  607. * @property STR_SPIN_LABEL
  608. * @description String for spinbutton dynamic label. Note the {VALUE} will be replaced with YAHOO.lang.substitute
  609. * @type String
  610. */
  611. STR_SPIN_LABEL: 'Spin Button with value {VALUE}. Use Control Shift Up Arrow and Control Shift Down arrow keys to increase or decrease the value.',
  612. /**
  613. * @property STR_SPIN_UP
  614. * @description String for spinbutton up
  615. * @type String
  616. */
  617. STR_SPIN_UP: 'Click to increase the value of this input',
  618. /**
  619. * @property STR_SPIN_DOWN
  620. * @description String for spinbutton down
  621. * @type String
  622. */
  623. STR_SPIN_DOWN: 'Click to decrease the value of this input',
  624. /**
  625. * @property _titlebar
  626. * @description Object reference to the titlebar
  627. * @type HTMLElement
  628. */
  629. _titlebar: null,
  630. /**
  631. * @property browser
  632. * @description Standard browser detection
  633. * @type Object
  634. */
  635. browser: YAHOO.env.ua,
  636. /**
  637. * @protected
  638. * @property _buttonList
  639. * @description Internal property list of current buttons in the toolbar
  640. * @type Array
  641. */
  642. _buttonList: null,
  643. /**
  644. * @protected
  645. * @property _buttonGroupList
  646. * @description Internal property list of current button groups in the toolbar
  647. * @type Array
  648. */
  649. _buttonGroupList: null,
  650. /**
  651. * @protected
  652. * @property _sep
  653. * @description Internal reference to the separator HTML Element for cloning
  654. * @type HTMLElement
  655. */
  656. _sep: null,
  657. /**
  658. * @protected
  659. * @property _sepCount
  660. * @description Internal refernce for counting separators, so we can give them a useful class name for styling
  661. * @type Number
  662. */
  663. _sepCount: null,
  664. /**
  665. * @protected
  666. * @property draghandle
  667. * @type HTMLElement
  668. */
  669. _dragHandle: null,
  670. /**
  671. * @protected
  672. * @property _toolbarConfigs
  673. * @type Object
  674. */
  675. _toolbarConfigs: {
  676. renderer: true
  677. },
  678. /**
  679. * @protected
  680. * @property CLASS_CONTAINER
  681. * @description Default CSS class to apply to the toolbar container element
  682. * @type String
  683. */
  684. CLASS_CONTAINER: 'yui-toolbar-container',
  685. /**
  686. * @protected
  687. * @property CLASS_DRAGHANDLE
  688. * @description Default CSS class to apply to the toolbar's drag handle element
  689. * @type String
  690. */
  691. CLASS_DRAGHANDLE: 'yui-toolbar-draghandle',
  692. /**
  693. * @protected
  694. * @property CLASS_SEPARATOR
  695. * @description Default CSS class to apply to all separators in the toolbar
  696. * @type String
  697. */
  698. CLASS_SEPARATOR: 'yui-toolbar-separator',
  699. /**
  700. * @protected
  701. * @property CLASS_DISABLED
  702. * @description Default CSS class to apply when the toolbar is disabled
  703. * @type String
  704. */
  705. CLASS_DISABLED: 'yui-toolbar-disabled',
  706. /**
  707. * @protected
  708. * @property CLASS_PREFIX
  709. * @description Default prefix for dynamically created class names
  710. * @type String
  711. */
  712. CLASS_PREFIX: 'yui-toolbar',
  713. /**
  714. * @method init
  715. * @description The Toolbar class's initialization method
  716. */
  717. init: function(p_oElement, p_oAttributes) {
  718. YAHOO.widget.Toolbar.superclass.init.call(this, p_oElement, p_oAttributes);
  719. },
  720. /**
  721. * @method initAttributes
  722. * @description Initializes all of the configuration attributes used to create
  723. * the toolbar.
  724. * @param {Object} attr Object literal specifying a set of
  725. * configuration attributes used to create the toolbar.
  726. */
  727. initAttributes: function(attr) {
  728. YAHOO.widget.Toolbar.superclass.initAttributes.call(this, attr);
  729. this.addClass(this.CLASS_CONTAINER);
  730. /**
  731. * @attribute buttonType
  732. * @description The buttonType to use (advanced or basic)
  733. * @type String
  734. */
  735. this.setAttributeConfig('buttonType', {
  736. value: attr.buttonType || 'basic',
  737. writeOnce: true,
  738. validator: function(type) {
  739. switch (type) {
  740. case 'advanced':
  741. case 'basic':
  742. return true;
  743. }
  744. return false;
  745. },
  746. method: function(type) {
  747. if (type == 'advanced') {
  748. if (YAHOO.widget.Button) {
  749. this.buttonType = YAHOO.widget.ToolbarButtonAdvanced;
  750. } else {
  751. YAHOO.log('Can not find YAHOO.widget.Button', 'error', 'Toolbar');
  752. this.buttonType = YAHOO.widget.ToolbarButton;
  753. }
  754. } else {
  755. this.buttonType = YAHOO.widget.ToolbarButton;
  756. }
  757. }
  758. });
  759. /**
  760. * @attribute buttons
  761. * @description Object specifying the buttons to include in the toolbar
  762. * Example:
  763. * <code><pre>
  764. * {
  765. * { id: 'b3', type: 'button', label: 'Underline', value: 'underline' },
  766. * { type: 'separator' },
  767. * { id: 'b4', type: 'menu', label: 'Align', value: 'align',
  768. * menu: [
  769. * { text: "Left", value: 'alignleft' },
  770. * { text: "Center", value: 'aligncenter' },
  771. * { text: "Right", value: 'alignright' }
  772. * ]
  773. * }
  774. * }
  775. * </pre></code>
  776. * @type Array
  777. */
  778. this.setAttributeConfig('buttons', {
  779. value: [],
  780. writeOnce: true,
  781. method: function(data) {
  782. var i, button, buttons, len, b;
  783. for (i in data) {
  784. if (Lang.hasOwnProperty(data, i)) {
  785. if (data[i].type == 'separator') {
  786. this.addSeparator();
  787. } else if (data[i].group !== undefined) {
  788. buttons = this.addButtonGroup(data[i]);
  789. if (buttons) {
  790. len = buttons.length;
  791. for(b = 0; b < len; b++) {
  792. if (buttons[b]) {
  793. this._configuredButtons[this._configuredButtons.length] = buttons[b].id;
  794. }
  795. }
  796. }
  797. } else {
  798. button = this.addButton(data[i]);
  799. if (button) {
  800. this._configuredButtons[this._configuredButtons.length] = button.id;
  801. }
  802. }
  803. }
  804. }
  805. }
  806. });
  807. /**
  808. * @attribute disabled
  809. * @description Boolean indicating if the toolbar should be disabled. It will also disable the draggable attribute if it is on.
  810. * @default false
  811. * @type Boolean
  812. */
  813. this.setAttributeConfig('disabled', {
  814. value: false,
  815. method: function(disabled) {
  816. if (this.get('disabled') === disabled) {
  817. return false;
  818. }
  819. if (disabled) {
  820. this.addClass(this.CLASS_DISABLED);
  821. this.set('draggable', false);
  822. this.disableAllButtons();
  823. } else {
  824. this.removeClass(this.CLASS_DISABLED);
  825. if (this._configs.draggable._initialConfig.value) {
  826. //Draggable by default, set it back
  827. this.set('draggable', true);
  828. }
  829. this.resetAllButtons();
  830. }
  831. }
  832. });
  833. /**
  834. * @config cont
  835. * @description The container for the toolbar.
  836. * @type HTMLElement
  837. */
  838. this.setAttributeConfig('cont', {
  839. value: attr.cont,
  840. readOnly: true
  841. });
  842. /**
  843. * @attribute grouplabels
  844. * @description Boolean indicating if the toolbar should show the group label's text string.
  845. * @default true
  846. * @type Boolean
  847. */
  848. this.setAttributeConfig('grouplabels', {
  849. value: ((attr.grouplabels === false) ? false : true),
  850. method: function(grouplabels) {
  851. if (grouplabels) {
  852. Dom.removeClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
  853. } else {
  854. Dom.addClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
  855. }
  856. }
  857. });
  858. /**
  859. * @attribute titlebar
  860. * @description Boolean indicating if the toolbar should have a titlebar. If
  861. * passed a string, it will use that as the titlebar text
  862. * @default false
  863. * @type Boolean or String
  864. */
  865. this.setAttributeConfig('titlebar', {
  866. value: false,
  867. method: function(titlebar) {
  868. if (titlebar) {
  869. if (this._titlebar && this._titlebar.parentNode) {
  870. this._titlebar.parentNode.removeChild(this._titlebar);
  871. }
  872. this._titlebar = document.createElement('DIV');
  873. this._titlebar.tabIndex = '-1';
  874. Event.on(this._titlebar, 'focus', function() {
  875. this._handleFocus();
  876. }, this, true);
  877. Dom.addClass(this._titlebar, this.CLASS_PREFIX + '-titlebar');
  878. if (Lang.isString(titlebar)) {
  879. var h2 = document.createElement('h2');
  880. h2.tabIndex = '-1';
  881. h2.innerHTML = '<a href="#" tabIndex="0">' + titlebar + '</a>';
  882. this._titlebar.appendChild(h2);
  883. Event.on(h2.firstChild, 'click', function(ev) {
  884. Event.stopEvent(ev);
  885. });
  886. Event.on([h2, h2.firstChild], 'focus', function() {
  887. this._handleFocus();
  888. }, this, true);
  889. }
  890. if (this.get('firstChild')) {
  891. this.insertBefore(this._titlebar, this.get('firstChild'));
  892. } else {
  893. this.appendChild(this._titlebar);
  894. }
  895. if (this.get('collapse')) {
  896. this.set('collapse', true);
  897. }
  898. } else if (this._titlebar) {
  899. if (this._titlebar && this._titlebar.parentNode) {
  900. this._titlebar.parentNode.removeChild(this._titlebar);
  901. }
  902. }
  903. }
  904. });
  905. /**
  906. * @attribute collapse
  907. * @description Boolean indicating if the the titlebar should have a collapse button.
  908. * The collapse button will not remove the toolbar, it will minimize it to the titlebar
  909. * @default false
  910. * @type Boolean
  911. */
  912. this.setAttributeConfig('collapse', {
  913. value: false,
  914. method: function(collapse) {
  915. if (this._titlebar) {
  916. var collapseEl = null;
  917. var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
  918. if (collapse) {
  919. if (el.length > 0) {
  920. //There is already a collapse button
  921. return true;
  922. }
  923. collapseEl = document.createElement('SPAN');
  924. collapseEl.innerHTML = 'X';
  925. collapseEl.title = this.STR_COLLAPSE;
  926. Dom.addClass(collapseEl, 'collapse');
  927. this._titlebar.appendChild(collapseEl);
  928. Event.addListener(collapseEl, 'click', function() {
  929. if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
  930. this.collapse(false); //Expand Toolbar
  931. } else {
  932. this.collapse(); //Collapse Toolbar
  933. }
  934. }, this, true);
  935. } else {
  936. collapseEl = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
  937. if (collapseEl[0]) {
  938. if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
  939. //We are closed, reopen the titlebar..
  940. this.collapse(false); //Expand Toolbar
  941. }
  942. collapseEl[0].parentNode.removeChild(collapseEl[0]);
  943. }
  944. }
  945. }
  946. }
  947. });
  948. /**
  949. * @attribute draggable
  950. * @description Boolean indicating if the toolbar should be draggable.
  951. * @default false
  952. * @type Boolean
  953. */
  954. this.setAttributeConfig('draggable', {
  955. value: (attr.draggable || false),
  956. method: function(draggable) {
  957. if (draggable && !this.get('titlebar')) {
  958. YAHOO.log('Dragging enabled', 'info', 'Toolbar');
  959. if (!this._dragHandle) {
  960. this._dragHandle = document.createElement('SPAN');
  961. this._dragHandle.innerHTML = '|';
  962. this._dragHandle.setAttribute('title', 'Click to drag the toolbar');
  963. this._dragHandle.id = this.get('id') + '_draghandle';
  964. Dom.addClass(this._dragHandle, this.CLASS_DRAGHANDLE);
  965. if (this.get('cont').hasChildNodes()) {
  966. this.get('cont').insertBefore(this._dragHandle, this.get('cont').firstChild);
  967. } else {
  968. this.get('cont').appendChild(this._dragHandle);
  969. }
  970. this.dd = new YAHOO.util.DD(this.get('id'));
  971. this.dd.setHandleElId(this._dragHandle.id);
  972. }
  973. } else {
  974. YAHOO.log('Dragging disabled', 'info', 'Toolbar');
  975. if (this._dragHandle) {
  976. this._dragHandle.parentNode.removeChild(this._dragHandle);
  977. this._dragHandle = null;
  978. this.dd = null;
  979. }
  980. }
  981. if (this._titlebar) {
  982. if (draggable) {
  983. this.dd = new YAHOO.util.DD(this.get('id'));
  984. this.dd.setHandleElId(this._titlebar);
  985. Dom.addClass(this._titlebar, 'draggable');
  986. } else {
  987. Dom.removeClass(this._titlebar, 'draggable');
  988. if (this.dd) {
  989. this.dd.unreg();
  990. this.dd = null;
  991. }
  992. }
  993. }
  994. },
  995. validator: function(value) {
  996. var ret = true;
  997. if (!YAHOO.util.DD) {
  998. ret = false;
  999. }
  1000. return ret;
  1001. }
  1002. });
  1003. },
  1004. /**
  1005. * @method addButtonGroup
  1006. * @description Add a new button group to the toolbar. (uses addButton)
  1007. * @param {Object} oGroup Object literal reference to the Groups Config (contains an array of button configs as well as the group label)
  1008. */
  1009. addButtonGroup: function(oGroup) {
  1010. if (!this.get('element')) {
  1011. this._queue[this._queue.length] = ['addButtonGroup', arguments];
  1012. return false;
  1013. }
  1014. if (!this.hasClass(this.CLASS_PREFIX + '-grouped')) {
  1015. this.addClass(this.CLASS_PREFIX + '-grouped');
  1016. }
  1017. var div = document.createElement('DIV');
  1018. Dom.addClass(div, this.CLASS_PREFIX + '-group');
  1019. Dom.addClass(div, this.CLASS_PREFIX + '-group-' + oGroup.group);
  1020. if (oGroup.label) {
  1021. var label = document.createElement('h3');
  1022. label.innerHTML = oGroup.label;
  1023. div.appendChild(label);
  1024. }
  1025. if (!this.get('grouplabels')) {
  1026. Dom.addClass(this.get('cont'), this.CLASS_PREFIX, '-nogrouplabels');
  1027. }
  1028. this.get('cont').appendChild(div);
  1029. //For accessibility, let's put all of the group buttons in an Unordered List
  1030. var ul = document.createElement('ul');
  1031. div.appendChild(ul);
  1032. if (!this._buttonGroupList) {
  1033. this._buttonGroupList = {};
  1034. }
  1035. this._buttonGroupList[oGroup.group] = ul;
  1036. //An array of the button ids added to this group
  1037. //This is used for destruction later...
  1038. var addedButtons = [],
  1039. button;
  1040. for (var i = 0; i < oGroup.buttons.length; i++) {
  1041. var li = document.createElement('li');
  1042. li.className = this.CLASS_PREFIX + '-groupitem';
  1043. ul.appendChild(li);
  1044. if ((oGroup.buttons[i].type !== undefined) && oGroup.buttons[i].type == 'separator') {
  1045. this.addSeparator(li);
  1046. } else {
  1047. oGroup.buttons[i].container = li;
  1048. button = this.addButton(oGroup.buttons[i]);
  1049. if (button) {
  1050. addedButtons[addedButtons.length] = button.id;
  1051. }
  1052. }
  1053. }
  1054. return addedButtons;
  1055. },
  1056. /**
  1057. * @method addButtonToGroup
  1058. * @description Add a new button to a toolbar group. Buttons supported:
  1059. * push, split, menu, select, color, spin
  1060. * @param {Object} oButton Object literal reference to the Button's Config
  1061. * @param {String} group The Group identifier passed into the initial config
  1062. * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
  1063. */
  1064. addButtonToGroup: function(oButton, group, after) {
  1065. var groupCont = this._buttonGroupList[group],
  1066. li = document.createElement('li');
  1067. li.className = this.CLASS_PREFIX + '-groupitem';
  1068. oButton.container = li;
  1069. this.addButton(oButton, after);
  1070. groupCont.appendChild(li);
  1071. },
  1072. /**
  1073. * @method addButton
  1074. * @description Add a new button to the toolbar. Buttons supported:
  1075. * push, split, menu, select, color, spin
  1076. * @param {Object} oButton Object literal reference to the Button's Config
  1077. * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
  1078. */
  1079. addButton: function(oButton, after) {
  1080. if (!this.get('element')) {
  1081. this._queue[this._queue.length] = ['addButton', arguments];
  1082. return false;
  1083. }
  1084. if (!this._buttonList) {
  1085. this._buttonList = [];
  1086. }
  1087. YAHOO.log('Adding button of type: ' + oButton.type, 'info', 'Toolbar');
  1088. if (!oButton.container) {
  1089. oButton.container = this.get('cont');
  1090. }
  1091. if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
  1092. if (Lang.isArray(oButton.menu)) {
  1093. for (var i in oButton.menu) {
  1094. if (Lang.hasOwnProperty(oButton.menu, i)) {
  1095. var funcObject = {
  1096. fn: function(ev, x, oMenu) {
  1097. if (!oButton.menucmd) {
  1098. oButton.menucmd = oButton.value;
  1099. }
  1100. oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
  1101. },
  1102. scope: this
  1103. };
  1104. oButton.menu[i].onclick = funcObject;
  1105. }
  1106. }
  1107. }
  1108. }
  1109. var _oButton = {}, skip = false;
  1110. for (var o in oButton) {
  1111. if (Lang.hasOwnProperty(oButton, o)) {
  1112. if (!this._toolbarConfigs[o]) {
  1113. _oButton[o] = oButton[o];
  1114. }
  1115. }
  1116. }
  1117. if (oButton.type == 'select') {
  1118. _oButton.type = 'menu';
  1119. }
  1120. if (oButton.type == 'spin') {
  1121. _oButton.type = 'push';
  1122. }
  1123. if (_oButton.type == 'color') {
  1124. if (YAHOO.widget.Overlay) {
  1125. _oButton = this._makeColorButton(_oButton);
  1126. } else {
  1127. skip = true;
  1128. }
  1129. }
  1130. if (_oButton.menu) {
  1131. if ((YAHOO.widget.Overlay) && (oButton.menu instanceof YAHOO.widget.Overlay)) {
  1132. oButton.menu.showEvent.subscribe(function() {
  1133. this._button = _oButton;
  1134. });
  1135. } else {
  1136. for (var m = 0; m < _oButton.menu.length; m++) {
  1137. if (!_oButton.menu[m].value) {
  1138. _oButton.menu[m].value = _oButton.menu[m].text;
  1139. }
  1140. }
  1141. if (this.browser.webkit) {
  1142. _oButton.focusmenu = false;
  1143. }
  1144. }
  1145. }
  1146. if (skip) {
  1147. oButton = false;
  1148. } else {
  1149. //Add to .get('buttons') manually
  1150. this._configs.buttons.value[this._configs.buttons.value.length] = oButton;
  1151. var tmp = new this.buttonType(_oButton);
  1152. tmp.get('element').tabIndex = '-1';
  1153. tmp.get('element').setAttribute('role', 'button');
  1154. tmp._selected = true;
  1155. if (this.get('disabled')) {
  1156. //Toolbar is disabled, disable the new button too!
  1157. tmp.set('disabled', true);
  1158. }
  1159. if (!oButton.id) {
  1160. oButton.id = tmp.get('id');
  1161. }
  1162. YAHOO.log('Button created (' + oButton.type + ')', 'info', 'Toolbar');
  1163. if (after) {
  1164. var el = tmp.get('element');
  1165. var nextSib = null;
  1166. if (after.get) {
  1167. nextSib = after.get('element').nextSibling;
  1168. } else if (after.nextSibling) {
  1169. nextSib = after.nextSibling;
  1170. }
  1171. if (nextSib) {
  1172. nextSib.parentNode.insertBefore(el, nextSib);
  1173. }
  1174. }
  1175. tmp.addClass(this.CLASS_PREFIX + '-' + tmp.get('value'));
  1176. var icon = document.createElement('span');
  1177. icon.className = this.CLASS_PREFIX + '-icon';
  1178. tmp.get('element').insertBefore(icon, tmp.get('firstChild'));
  1179. if (tmp._button.tagName.toLowerCase() == 'button') {
  1180. tmp.get('element').setAttribute('unselectable', 'on');
  1181. //Replace the Button HTML Element with an a href if it exists
  1182. var a = document.createElement('a');
  1183. a.innerHTML = tmp._button.innerHTML;
  1184. a.href = '#';
  1185. a.tabIndex = '-1';
  1186. Event.on(a, 'click', function(ev) {
  1187. Event.stopEvent(ev);
  1188. });
  1189. tmp._button.parentNode.replaceChild(a, tmp._button);
  1190. tmp._button = a;
  1191. }
  1192. if (oButton.type == 'select') {
  1193. if (tmp._button.tagName.toLowerCase() == 'select') {
  1194. icon.parentNode.removeChild(icon);
  1195. var iel = tmp._button,
  1196. parEl = tmp.get('element');
  1197. parEl.parentNode.replaceChild(iel, parEl);
  1198. //The 'element' value is currently the orphaned element
  1199. //In order for "destroy" to execute we need to get('element') to reference the correct node.
  1200. //I'm not sure if there is a direct approach to setting this value.
  1201. tmp._configs.element.value = iel;
  1202. } else {
  1203. //Don't put a class on it if it's a real select element
  1204. tmp.addClass(this.CLASS_PREFIX + '-select');
  1205. }
  1206. }
  1207. if (oButton.type == 'spin') {
  1208. if (!Lang.isArray(oButton.range)) {
  1209. oButton.range = [ 10, 100 ];
  1210. }
  1211. this._makeSpinButton(tmp, oButton);
  1212. }
  1213. tmp.get('element').setAttribute('title', tmp.get('label'));
  1214. if (oButton.type != 'spin') {
  1215. if ((YAHOO.widget.Overlay) && (_oButton.menu instanceof YAHOO.widget.Overlay)) {
  1216. var showPicker = function(ev) {
  1217. var exec = true;
  1218. if (ev.keyCode && (ev.keyCode == 9)) {
  1219. exec = false;
  1220. }
  1221. if (exec) {
  1222. if (this._colorPicker) {
  1223. this._colorPicker._button = oButton.value;
  1224. }
  1225. var menuEL = tmp.getMenu().element;
  1226. if (Dom.getStyle(menuEL, 'visibility') == 'hidden') {
  1227. tmp.getMenu().show();
  1228. } else {
  1229. tmp.getMenu().hide();
  1230. }
  1231. }
  1232. YAHOO.util.Event.stopEvent(ev);
  1233. };
  1234. tmp.on('mousedown', showPicker, oButton, this);
  1235. tmp.on('keydown', showPicker, oButton, this);
  1236. } else if ((oButton.type != 'menu') && (oButton.type != 'select')) {
  1237. tmp.on('keypress', this._buttonClick, oButton, this);
  1238. tmp.on('mousedown', function(ev) {
  1239. YAHOO.util.Event.stopEvent(ev);
  1240. this._buttonClick(ev, oButton);
  1241. }, oButton, this);
  1242. tmp.on('click', function(ev) {
  1243. YAHOO.util.Event.stopEvent(ev);
  1244. });
  1245. } else {
  1246. //Stop the mousedown event so we can trap the selection in the editor!
  1247. tmp.on('mousedown', function(ev) {
  1248. YAHOO.util.Event.stopEvent(ev);
  1249. });
  1250. tmp.on('click', function(ev) {
  1251. YAHOO.util.Event.stopEvent(ev);
  1252. });
  1253. tmp.on('change', function(ev) {
  1254. if (!ev.target) {
  1255. if (!oButton.menucmd) {
  1256. oButton.menucmd = oButton.value;
  1257. }
  1258. oButton.value = ev.value;
  1259. this._buttonClick(ev, oButton);
  1260. }
  1261. }, this, true);
  1262. var self = this;
  1263. //Hijack the mousedown event in the menu and make it fire a button click..
  1264. tmp.on('appendTo', function() {
  1265. var tmp = this;
  1266. if (tmp.getMenu() && tmp.getMenu().mouseDownEvent) {
  1267. tmp.getMenu().mouseDownEvent.subscribe(function(ev, args) {
  1268. YAHOO.log('mouseDownEvent', 'warn', 'Toolbar');
  1269. var oMenu = args[1];
  1270. YAHOO.util.Event.stopEvent(args[0]);
  1271. tmp._onMenuClick(args[0], tmp);
  1272. if (!oButton.menucmd) {
  1273. oButton.menucmd = oButton.value;
  1274. }
  1275. oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
  1276. self._buttonClick.call(self, args[1], oButton);
  1277. tmp._hideMenu();
  1278. return false;
  1279. });
  1280. tmp.getMenu().clickEvent.subscribe(function(ev, args) {
  1281. YAHOO.log('clickEvent', 'warn', 'Toolbar');
  1282. YAHOO.util.Event.stopEvent(args[0]);
  1283. });
  1284. tmp.getMenu().mouseUpEvent.subscribe(function(ev, args) {
  1285. YAHOO.log('mouseUpEvent', 'warn', 'Toolbar');
  1286. YAHOO.util.Event.stopEvent(args[0]);
  1287. });
  1288. }
  1289. });
  1290. }
  1291. } else {
  1292. //Stop the mousedown event so we can trap the selection in the editor!
  1293. tmp.on('mousedown', function(ev) {
  1294. YAHOO.util.Event.stopEvent(ev);
  1295. });
  1296. tmp.on('click', function(ev) {
  1297. YAHOO.util.Event.stopEvent(ev);
  1298. });
  1299. }
  1300. if (this.browser.ie) {
  1301. /*
  1302. //Add a couple of new events for IE
  1303. tmp.DOM_EVENTS.focusin = true;
  1304. tmp.DOM_EVENTS.focusout = true;
  1305. //Stop them so we don't loose focus in the Editor
  1306. tmp.on('focusin', function(ev) {
  1307. YAHOO.util.Event.stopEvent(ev);
  1308. }, oButton, this);
  1309. tmp.on('focusout', function(ev) {
  1310. YAHOO.util.Event.stopEvent(ev);
  1311. }, oButton, this);
  1312. tmp.on('click', function(ev) {
  1313. YAHOO.util.Event.stopEvent(ev);
  1314. }, oButton, this);
  1315. */
  1316. }
  1317. if (this.browser.webkit) {
  1318. //This will keep the document from gaining focus and the editor from loosing it..
  1319. //Forcefully remove the focus calls in button!
  1320. tmp.hasFocus = function() {
  1321. return true;
  1322. };
  1323. }
  1324. this._buttonList[this._buttonList.length] = tmp;
  1325. if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
  1326. if (Lang.isArray(oButton.menu)) {
  1327. YAHOO.log('Button type is (' + oButton.type + '), doing extra renderer work.', 'info', 'Toolbar');
  1328. var menu = tmp.getMenu();
  1329. if (menu && menu.renderEvent) {
  1330. menu.renderEvent.subscribe(this._addMenuClasses, tmp);
  1331. if (oButton.renderer) {
  1332. menu.renderEvent.subscribe(oButton.renderer, tmp);
  1333. }
  1334. }
  1335. }
  1336. }
  1337. }
  1338. return oButton;
  1339. },
  1340. /**
  1341. * @method addSeparator
  1342. * @description Add a new button separator to the toolbar.
  1343. * @param {HTMLElement} cont Optional HTML element to insert this button into.
  1344. * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
  1345. */
  1346. addSeparator: function(cont, after) {
  1347. if (!this.get('element')) {
  1348. this._queue[this._queue.length] = ['addSeparator', arguments];
  1349. return false;
  1350. }
  1351. var sepCont = ((cont) ? cont : this.get('cont'));
  1352. if (!this.get('element')) {
  1353. this._queue[this._queue.length] = ['addSeparator', arguments];
  1354. return false;
  1355. }
  1356. if (this._sepCount === null) {
  1357. this._sepCount = 0;
  1358. }
  1359. if (!this._sep) {
  1360. YAHOO.log('Separator does not yet exist, creating', 'info', 'Toolbar');
  1361. this._sep = document.createElement('SPAN');
  1362. Dom.addClass(this._sep, this.CLASS_SEPARATOR);
  1363. this._sep.innerHTML = '|';
  1364. }
  1365. YAHOO.log('Separator does exist, cloning', 'info', 'Toolbar');
  1366. var _sep = this._sep.cloneNode(true);
  1367. this._sepCount++;
  1368. Dom.addClass(_sep, this.CLASS_SEPARATOR + '-' + this._sepCount);
  1369. if (after) {
  1370. var nextSib = null;
  1371. if (after.get) {
  1372. nextSib = after.get('element').nextSibling;
  1373. } else if (after.nextSibling) {
  1374. nextSib = after.nextSibling;
  1375. } else {
  1376. nextSib = after;
  1377. }
  1378. if (nextSib) {
  1379. if (nextSib == after) {
  1380. nextSib.parentNode.appendChild(_sep);
  1381. } else {
  1382. nextSib.parentNode.insertBefore(_sep, nextSib);
  1383. }
  1384. }
  1385. } else {
  1386. sepCont.appendChild(_sep);
  1387. }
  1388. return _sep;
  1389. },
  1390. /**
  1391. * @method _createColorPicker
  1392. * @private
  1393. * @description Creates the core DOM reference to the color picker menu item.
  1394. * @param {String} id the id of the toolbar to prefix this DOM container with.
  1395. */
  1396. _createColorPicker: function(id) {
  1397. if (Dom.get(id + '_colors')) {
  1398. Dom.get(id + '_colors').parentNode.removeChild(Dom.get(id + '_colors'));
  1399. }
  1400. var picker = document.createElement('div');
  1401. picker.className = 'yui-toolbar-colors';
  1402. picker.id = id + '_colors';
  1403. picker.style.display = 'none';
  1404. Event.on(window, 'load', function() {
  1405. document.body.appendChild(picker);
  1406. }, this, true);
  1407. this._colorPicker = picker;
  1408. var html = '';
  1409. for (var i in this._colorData) {
  1410. if (Lang.hasOwnProperty(this._colorData, i)) {
  1411. html += '<a style="background-color: ' + i + '" href="#">' + i.replace('#', '') + '</a>';
  1412. }
  1413. }
  1414. html += '<span><em>X</em><strong></strong></span>';
  1415. window.setTimeout(function() {
  1416. picker.innerHTML = html;
  1417. }, 0);
  1418. Event.on(picker, 'mouseover', function(ev) {
  1419. var picker = this._colorPicker;
  1420. var em = picker.getElementsByTagName('em')[0];
  1421. var strong = picker.getElementsByTagName('strong')[0];
  1422. var tar = Event.getTarget(ev);
  1423. if (tar.tagName.toLowerCase() == 'a') {
  1424. em.style.backgroundColor = tar.style.backgroundColor;
  1425. strong.innerHTML = this._colorData['#' + tar.innerHTML] + '<br>' + tar.innerHTML;
  1426. }
  1427. }, this, true);
  1428. Event.on(picker, 'focus', function(ev) {
  1429. Event.stopEvent(ev);
  1430. });
  1431. Event.on(picker, 'click', function(ev) {
  1432. Event.stopEvent(ev);
  1433. });
  1434. Event.on(picker, 'mousedown', function(ev) {
  1435. Event.stopEvent(ev);
  1436. var tar = Event.getTarget(ev);
  1437. if (tar.tagName.toLowerCase() == 'a') {
  1438. var retVal = this.fireEvent('colorPickerClicked', { type: 'colorPickerClicked', target: this, button: this._colorPicker._button, color: tar.innerHTML, colorName: this._colorData['#' + tar.innerHTML] } );
  1439. if (retVal !== false) {
  1440. var info = {
  1441. color: tar.innerHTML,
  1442. colorName: this._colorData['#' + tar.innerHTML],
  1443. value: this._colorPicker._button
  1444. };
  1445. this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
  1446. }
  1447. this.getButtonByValue(this._colorPicker._button).getMenu().hide();
  1448. }
  1449. }, this, true);
  1450. },
  1451. /**
  1452. * @method _resetColorPicker
  1453. * @private
  1454. * @description Clears the currently selected color or mouseover color in the color picker.
  1455. */
  1456. _resetColorPicker: function() {
  1457. var em = this._colorPicker.getElementsByTagName('em')[0];
  1458. var strong = this._colorPicker.getElementsByTagName('strong')[0];
  1459. em.style.backgroundColor = 'transparent';
  1460. strong.innerHTML = '';
  1461. },
  1462. /**
  1463. * @method _makeColorButton
  1464. * @private
  1465. * @description Called to turn a "color" button into a menu button with an Overlay for the menu.
  1466. * @param {Object} _oButton <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
  1467. */
  1468. _makeColorButton: function(_oButton) {
  1469. if (!this._colorPicker) {
  1470. this._createColorPicker(this.get('id'));
  1471. }
  1472. _oButton.type = 'color';
  1473. _oButton.menu = new YAHOO.widget.Overlay(this.get('id') + '_' + _oButton.value + '_menu', { visible: false, position: 'absolute', iframe: true });
  1474. _oButton.menu.setBody('');
  1475. _oButton.menu.render(this.get('cont'));
  1476. Dom.addClass(_oButton.menu.element, 'yui-button-menu');
  1477. Dom.addClass(_oButton.menu.element, 'yui-color-button-menu');
  1478. _oButton.menu.beforeShowEvent.subscribe(function() {
  1479. _oButton.menu.cfg.setProperty('zindex', 5); //Re Adjust the overlays zIndex.. not sure why.
  1480. _oButton.menu.cfg.setProperty('context', [this.getButtonById(_oButton.id).get('element'), 'tl', 'bl']); //Re Adjust the overlay.. not sure why.
  1481. //Move the DOM reference of the color picker to the Overlay that we are about to show.
  1482. this._resetColorPicker();
  1483. var _p = this._colorPicker;
  1484. if (_p.parentNode) {
  1485. _p.parentNode.removeChild(_p);
  1486. }
  1487. _oButton.menu.setBody('');
  1488. _oButton.menu.appendToBody(_p);
  1489. this._colorPicker.style.display = 'block';
  1490. }, this, true);
  1491. return _oButton;
  1492. },
  1493. /**
  1494. * @private
  1495. * @method _makeSpinButton
  1496. * @description Create a button similar to an OS Spin button.. It has an up/down arrow combo to scroll through a range of int values.
  1497. * @param {Object} _button <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
  1498. * @param {Object} oButton Object literal containing the buttons initial config
  1499. */
  1500. _makeSpinButton: function(_button, oButton) {
  1501. _button.addClass(this.CLASS_PREFIX + '-spinbutton');
  1502. var self = this,
  1503. _par = _button._button.parentNode.parentNode, //parentNode of Button Element for appending child
  1504. range = oButton.range,
  1505. _b1 = document.createElement('a'),
  1506. _b2 = document.createElement('a');
  1507. _b1.href = '#';
  1508. _b2.href = '#';
  1509. _b1.tabIndex = '-1';
  1510. _b2.tabIndex = '-1';
  1511. //Setup the up and down arrows
  1512. _b1.className = 'up';
  1513. _b1.title = this.STR_SPIN_UP;
  1514. _b1.innerHTML = this.STR_SPIN_UP;
  1515. _b2.className = 'down';
  1516. _b2.title = this.STR_SPIN_DOWN;
  1517. _b2.innerHTML = this.STR_SPIN_DOWN;
  1518. //Append them to the container
  1519. _par.appendChild(_b1);
  1520. _par.appendChild(_b2);
  1521. var label = YAHOO.lang.substitute(this.STR_SPIN_LABEL, { VALUE: _button.get('label') });
  1522. _button.set('title', label);
  1523. var cleanVal = function(value) {
  1524. value = ((value < range[0]) ? range[0] : value);
  1525. value = ((value > range[1]) ? range[1] : value);
  1526. return value;
  1527. };
  1528. var br = this.browser;
  1529. var tbar = false;
  1530. var strLabel = this.STR_SPIN_LABEL;
  1531. if (this._titlebar && this._titlebar.firstChild) {
  1532. tbar = this._titlebar.firstChild;
  1533. }
  1534. var _intUp = function(ev) {
  1535. YAHOO.util.Event.stopEvent(ev);
  1536. if (!_button.get('disabled') && (ev.keyCode != 9)) {
  1537. var value = parseInt(_button.get('label'), 10);
  1538. value++;
  1539. value = cleanVal(value);
  1540. _button.set('label', ''+value);
  1541. var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
  1542. _button.set('title', label);
  1543. if (!br.webkit && tbar) {
  1544. //tbar.focus(); //We do this for accessibility, on the re-focus of the element, a screen reader will re-read the title that was just changed
  1545. //_button.focus();
  1546. }
  1547. self._buttonClick(ev, oButton);
  1548. }
  1549. };
  1550. var _intDown = function(ev) {
  1551. YAHOO.util.Event.stopEvent(ev);
  1552. if (!_button.get('disabled') && (ev.keyCode != 9)) {
  1553. var value = parseInt(_button.get('label'), 10);
  1554. value--;
  1555. value = cleanVal(value);
  1556. _button.set('label', ''+value);
  1557. var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
  1558. _button.set('title', label);
  1559. if (!br.webkit && tbar) {
  1560. //tbar.focus(); //We do this for accessibility, on the re-focus of the element, a screen reader will re-read the title that was just changed
  1561. //_button.focus();
  1562. }
  1563. self._buttonClick(ev, oButton);
  1564. }
  1565. };
  1566. var _intKeyUp = function(ev) {
  1567. if (ev.keyCode == 38) {
  1568. _intUp(ev);
  1569. } else if (ev.keyCode == 40) {
  1570. _intDown(ev);
  1571. } else if (ev.keyCode == 107 && ev.shiftKey) { //Plus Key
  1572. _intUp(ev);
  1573. } else if (ev.keyCode == 109 && ev.shiftKey) { //Minus Key
  1574. _intDown(ev);
  1575. }
  1576. };
  1577. //Handle arrow keys..
  1578. _button.on('keydown', _intKeyUp, this, true);
  1579. //Listen for the click on the up button and act on it
  1580. //Listen for the click on the down button and act on it
  1581. Event.on(_b1, 'mousedown',function(ev) {
  1582. Event.stopEvent(ev);
  1583. }, this, true);
  1584. Event.on(_b2, 'mousedown', function(ev) {
  1585. Event.stopEvent(ev);
  1586. }, this, true);
  1587. Event.on(_b1, 'click', _intUp, this, true);
  1588. Event.on(_b2, 'click', _intDown, this, true);
  1589. },
  1590. /**
  1591. * @protected
  1592. * @method _buttonClick
  1593. * @description Click handler for all buttons in the toolbar.
  1594. * @param {String} ev The event that was passed in.
  1595. * @param {Object} info Object literal of information about the button that was clicked.
  1596. */
  1597. _buttonClick: function(ev, info) {
  1598. var doEvent = true;
  1599. if (ev && ev.type == 'keypress') {
  1600. if (ev.keyCode == 9) {
  1601. doEvent = false;
  1602. } else if ((ev.keyCode === 13) || (ev.keyCode === 0) || (ev.keyCode === 32)) {
  1603. } else {
  1604. doEvent = false;
  1605. }
  1606. }
  1607. if (doEvent) {
  1608. var fireNextEvent = true,
  1609. retValue = false;
  1610. info.isSelected = this.isSelected(info.id);
  1611. if (info.value) {
  1612. YAHOO.log('fireEvent::' + info.value + 'Click', 'info', 'Toolbar');
  1613. retValue = this.fireEvent(info.value + 'Click', { type: info.value + 'Click', target: this.get('element'), button: info });
  1614. if (retValue === false) {
  1615. fireNextEvent = false;
  1616. }
  1617. }
  1618. if (info.menucmd && fireNextEvent) {
  1619. YAHOO.log('fireEvent::' + info.menucmd + 'Click', 'info', 'Toolbar');
  1620. retValue = this.fireEvent(info.menucmd + 'Click', { type: info.menucmd + 'Click', target: this.get('element'), button: info });
  1621. if (retValue === false) {
  1622. fireNextEvent = false;
  1623. }
  1624. }
  1625. if (fireNextEvent) {
  1626. YAHOO.log('fireEvent::buttonClick', 'info', 'Toolbar');
  1627. this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
  1628. }
  1629. if (info.type == 'select') {
  1630. var button = this.getButtonById(info.id);
  1631. if (button.buttonType == 'rich') {
  1632. var txt = info.value;
  1633. for (var i = 0; i < info.menu.length; i++) {
  1634. if (info.menu[i].value == info.value) {
  1635. txt = info.menu[i].text;
  1636. break;
  1637. }
  1638. }
  1639. button.set('label', '<span class="yui-toolbar-' + info.menucmd + '-' + (info.value).replace(/ /g, '-').toLowerCase() + '">' + txt + '</span>');
  1640. var _items = button.getMenu().getItems();
  1641. for (var m = 0; m < _items.length; m++) {
  1642. if (_items[m].value.toLowerCase() == info.value.toLowerCase()) {
  1643. _items[m].cfg.setProperty('checked', true);
  1644. } else {
  1645. _items[m].cfg.setProperty('checked', false);
  1646. }
  1647. }
  1648. }
  1649. }
  1650. if (ev) {
  1651. Event.stopEvent(ev);
  1652. }
  1653. }
  1654. },
  1655. /**
  1656. * @private
  1657. * @property _keyNav
  1658. * @description Flag to determine if the arrow nav listeners have been attached
  1659. * @type Boolean
  1660. */
  1661. _keyNav: null,
  1662. /**
  1663. * @private
  1664. * @property _navCounter
  1665. * @description Internal counter for walking the buttons in the toolbar with the arrow keys
  1666. * @type Number
  1667. */
  1668. _navCounter: null,
  1669. /**
  1670. * @private
  1671. * @method _navigateButtons
  1672. * @description Handles the navigation/focus of toolbar buttons with the Arrow Keys
  1673. * @param {Event} ev The Key Event
  1674. */
  1675. _navigateButtons: function(ev) {
  1676. switch (ev.keyCode) {
  1677. case 37:
  1678. case 39:
  1679. if (ev.keyCode == 37) {
  1680. this._navCounter--;
  1681. } else {
  1682. this._navCounter++;
  1683. }
  1684. if (this._navCounter > (this._buttonList.length - 1)) {
  1685. this._navCounter = 0;
  1686. }
  1687. if (this._navCounter < 0) {
  1688. this._navCounter = (this._buttonList.length - 1);
  1689. }
  1690. if (this._buttonList[this._navCounter]) {
  1691. var el = this._buttonList[this._navCounter].get('element');
  1692. if (this.browser.ie) {
  1693. el = this._buttonList[this._navCounter].get('element').getElementsByTagName('a')[0];
  1694. }
  1695. if (this._buttonList[this._navCounter].get('disabled')) {
  1696. this._navigateButtons(ev);
  1697. } else {
  1698. el.focus();
  1699. }
  1700. }
  1701. break;
  1702. }
  1703. },
  1704. /**
  1705. * @private
  1706. * @method _handleFocus
  1707. * @description Sets up the listeners for the arrow key navigation
  1708. */
  1709. _handleFocus: function() {
  1710. if (!this._keyNav) {
  1711. var ev = 'keypress';
  1712. if (this.browser.ie) {
  1713. ev = 'keydown';
  1714. }
  1715. Event.on(this.get('element'), ev, this._navigateButtons, this, true);
  1716. this._keyNav = true;
  1717. this._navCounter = -1;
  1718. }
  1719. },
  1720. /**
  1721. * @method getButtonById
  1722. * @description Gets a button instance from the toolbar by is Dom id.
  1723. * @param {String} id The Dom id to query for.
  1724. * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
  1725. */
  1726. getButtonById: function(id) {
  1727. var len = this._buttonList.length;
  1728. for (var i = 0; i < len; i++) {
  1729. if (this._buttonList[i] && this._buttonList[i].get('id') == id) {
  1730. return this._buttonList[i];
  1731. }
  1732. }
  1733. return false;
  1734. },
  1735. /**
  1736. * @method getButtonByValue
  1737. * @description Gets a button instance or a menuitem instance from the toolbar by it's value.
  1738. * @param {String} value The button value to query for.
  1739. * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> or <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a>}
  1740. */
  1741. getButtonByValue: function(value) {
  1742. var _buttons = this.get('buttons');
  1743. if (!_buttons) {
  1744. return false;
  1745. }
  1746. var len = _buttons.length;
  1747. for (var i = 0; i < len; i++) {
  1748. if (_buttons[i].group !== undefined) {
  1749. for (var m = 0; m < _buttons[i].buttons.length; m++) {
  1750. if ((_buttons[i].buttons[m].value == value) || (_buttons[i].buttons[m].menucmd == value)) {
  1751. return this.getButtonById(_buttons[i].buttons[m].id);
  1752. }
  1753. if (_buttons[i].buttons[m].menu) { //Menu Button, loop through the values
  1754. for (var s = 0; s < _buttons[i].buttons[m].menu.length; s++) {
  1755. if (_buttons[i].buttons[m].menu[s].value == value) {
  1756. return this.getButtonById(_buttons[i].buttons[m].id);
  1757. }
  1758. }
  1759. }
  1760. }
  1761. } else {
  1762. if ((_buttons[i].value == value) || (_buttons[i].menucmd == value)) {
  1763. return this.getButtonById(_buttons[i].id);
  1764. }
  1765. if (_buttons[i].menu) { //Menu Button, loop through the values
  1766. for (var j = 0; j < _buttons[i].menu.length; j++) {
  1767. if (_buttons[i].menu[j].value == value) {
  1768. return this.getButtonById(_buttons[i].id);
  1769. }
  1770. }
  1771. }
  1772. }
  1773. }
  1774. return false;
  1775. },
  1776. /**
  1777. * @method getButtonByIndex
  1778. * @description Gets a button instance from the toolbar by is index in _buttonList.
  1779. * @param {Number} index The index of the button in _buttonList.
  1780. * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
  1781. */
  1782. getButtonByIndex: function(index) {
  1783. if (this._buttonList[index]) {
  1784. return this._buttonList[index];
  1785. } else {
  1786. return false;
  1787. }
  1788. },
  1789. /**
  1790. * @method getButtons
  1791. * @description Returns an array of buttons in the current toolbar
  1792. * @return {Array}
  1793. */
  1794. getButtons: function() {
  1795. return this._buttonList;
  1796. },
  1797. /**
  1798. * @method disableButton
  1799. * @description Disables a button in the toolbar.
  1800. * @param {String/Number} id Disable a button by it's id, index or value.
  1801. * @return {Boolean}
  1802. */
  1803. disableButton: function(id) {
  1804. var button = getButton.call(this, id);
  1805. if (button) {
  1806. button.set('disabled', true);
  1807. } else {
  1808. return false;
  1809. }
  1810. },
  1811. /**
  1812. * @method enableButton
  1813. * @description Enables a button in the toolbar.
  1814. * @param {String/Number} id Enable a button by it's id, index or value.
  1815. * @return {Boolean}
  1816. */
  1817. enableButton: function(id) {
  1818. if (this.get('disabled')) {
  1819. return false;
  1820. }
  1821. var button = getButton.call(this, id);
  1822. if (button) {
  1823. if (button.get('disabled')) {
  1824. button.set('disabled', false);
  1825. }
  1826. } else {
  1827. return false;
  1828. }
  1829. },
  1830. /**
  1831. * @method isSelected
  1832. * @description Tells if a button is selected or not.
  1833. * @param {String/Number} id A button by it's id, index or value.
  1834. * @return {Boolean}
  1835. */
  1836. isSelected: function(id) {
  1837. var button = getButton.call(this, id);
  1838. if (button) {
  1839. return button._selected;
  1840. }
  1841. return false;
  1842. },
  1843. /**
  1844. * @method selectButton
  1845. * @description Selects a button in the toolbar.
  1846. * @param {String/Number} id Select a button by it's id, index or value.
  1847. * @param {String} value If this is a Menu Button, check this item in the menu
  1848. * @return {Boolean}
  1849. */
  1850. selectButton: function(id, value) {
  1851. var button = getButton.call(this, id);
  1852. if (button) {
  1853. button.addClass('yui-button-selected');
  1854. button.addClass('yui-button-' + button.get('value') + '-selected');
  1855. button._selected = true;
  1856. if (value) {
  1857. if (button.buttonType == 'rich') {
  1858. var _items = button.getMenu().getItems();
  1859. for (var m = 0; m < _items.length; m++) {
  1860. if (_items[m].value == value) {
  1861. _items[m].cfg.setProperty('checked', true);
  1862. button.set('label', '<span class="yui-toolbar-' + button.get('value') + '-' + (value).replace(/ /g, '-').toLowerCase() + '">' + _items[m]._oText.nodeValue + '</span>');
  1863. } else {
  1864. _items[m].cfg.setProperty('checked', false);
  1865. }
  1866. }
  1867. }
  1868. }
  1869. } else {
  1870. return false;
  1871. }
  1872. },
  1873. /**
  1874. * @method deselectButton
  1875. * @description Deselects a button in the toolbar.
  1876. * @param {String/Number} id Deselect a button by it's id, index or value.
  1877. * @return {Boolean}
  1878. */
  1879. deselectButton: function(id) {
  1880. var button = getButton.call(this, id);
  1881. if (button) {
  1882. button.removeClass('yui-button-selected');
  1883. button.removeClass('yui-button-' + button.get('value') + '-selected');
  1884. button.removeClass('yui-button-hover');
  1885. button._selected = false;
  1886. } else {
  1887. return false;
  1888. }
  1889. },
  1890. /**
  1891. * @method deselectAllButtons
  1892. * @description Deselects all buttons in the toolbar.
  1893. * @return {Boolean}
  1894. */
  1895. deselectAllButtons: function() {
  1896. var len = this._buttonList.length;
  1897. for (var i = 0; i < len; i++) {
  1898. this.deselectButton(this._buttonList[i]);
  1899. }
  1900. },
  1901. /**
  1902. * @method disableAllButtons
  1903. * @description Disables all buttons in the toolbar.
  1904. * @return {Boolean}
  1905. */
  1906. disableAllButtons: function() {
  1907. if (this.get('disabled')) {
  1908. return false;
  1909. }
  1910. var len = this._buttonList.length;
  1911. for (var i = 0; i < len; i++) {
  1912. this.disableButton(this._buttonList[i]);
  1913. }
  1914. },
  1915. /**
  1916. * @method enableAllButtons
  1917. * @description Enables all buttons in the toolbar.
  1918. * @return {Boolean}
  1919. */
  1920. enableAllButtons: function() {
  1921. if (this.get('disabled')) {
  1922. return false;
  1923. }
  1924. var len = this._buttonList.length;
  1925. for (var i = 0; i < len; i++) {
  1926. this.enableButton(this._buttonList[i]);
  1927. }
  1928. },
  1929. /**
  1930. * @method resetAllButtons
  1931. * @description Resets all buttons to their initial state.
  1932. * @param {Object} _ex Except these buttons
  1933. * @return {Boolean}
  1934. */
  1935. resetAllButtons: function(_ex) {
  1936. if (!Lang.isObject(_ex)) {
  1937. _ex = {};
  1938. }
  1939. if (this.get('disabled') || !this._buttonList) {
  1940. return false;
  1941. }
  1942. var len = this._buttonList.length;
  1943. for (var i = 0; i < len; i++) {
  1944. var _button = this._buttonList[i];
  1945. if (_button) {
  1946. var disabled = _button._configs.disabled._initialConfig.value;
  1947. if (_ex[_button.get('id')]) {
  1948. this.enableButton(_button);
  1949. this.selectButton(_button);
  1950. } else {
  1951. if (disabled) {
  1952. this.disableButton(_button);
  1953. } else {
  1954. this.enableButton(_button);
  1955. }
  1956. this.deselectButton(_button);
  1957. }
  1958. }
  1959. }
  1960. },
  1961. /**
  1962. * @method destroyButton
  1963. * @description Destroy a button in the toolbar.
  1964. * @param {String/Number} id Destroy a button by it's id or index.
  1965. * @return {Boolean}
  1966. */
  1967. destroyButton: function(id) {
  1968. var button = getButton.call(this, id);
  1969. if (button) {
  1970. var thisID = button.get('id'),
  1971. new_list = [], i = 0,
  1972. len = this._buttonList.length;
  1973. button.destroy();
  1974. for (i = 0; i < len; i++) {
  1975. if (this._buttonList[i].get('id') != thisID) {
  1976. new_list[new_list.length]= this._buttonList[i];
  1977. }
  1978. }
  1979. this._buttonList = new_list;
  1980. } else {
  1981. return false;
  1982. }
  1983. },
  1984. /**
  1985. * @method destroy
  1986. * @description Destroys the toolbar, all of it's elements and objects.
  1987. * @return {Boolean}
  1988. */
  1989. destroy: function() {
  1990. var len = this._configuredButtons.length, j, i;
  1991. for(b = 0; b < len; b++) {
  1992. this.destroyButton(this._configuredButtons[b]);
  1993. }
  1994. this._configuredButtons = null;
  1995. this.get('element').innerHTML = '';
  1996. this.get('element').className = '';
  1997. //Brutal Object Destroy
  1998. for (i in this) {
  1999. if (Lang.hasOwnProperty(this, i)) {
  2000. this[i] = null;
  2001. }
  2002. }
  2003. return true;
  2004. },
  2005. /**
  2006. * @method collapse
  2007. * @description Programatically collapse the toolbar.
  2008. * @param {Boolean} collapse True to collapse, false to expand.
  2009. */
  2010. collapse: function(collapse) {
  2011. var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
  2012. if (collapse === false) {
  2013. Dom.removeClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
  2014. if (el[0]) {
  2015. Dom.removeClass(el[0], 'collapsed');
  2016. el[0].title = this.STR_COLLAPSE;
  2017. }
  2018. this.fireEvent('toolbarExpanded', { type: 'toolbarExpanded', target: this });
  2019. } else {
  2020. if (el[0]) {
  2021. Dom.addClass(el[0], 'collapsed');
  2022. el[0].title = this.STR_EXPAND;
  2023. }
  2024. Dom.addClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
  2025. this.fireEvent('toolbarCollapsed', { type: 'toolbarCollapsed', target: this });
  2026. }
  2027. },
  2028. /**
  2029. * @method toString
  2030. * @description Returns a string representing the toolbar.
  2031. * @return {String}
  2032. */
  2033. toString: function() {
  2034. return 'Toolbar (#' + this.get('element').id + ') with ' + this._buttonList.length + ' buttons.';
  2035. }
  2036. });
  2037. /**
  2038. * @event buttonClick
  2039. * @param {Object} o The object passed to this handler is the button config used to create the button.
  2040. * @description Fires when any botton receives a click event. Passes back a single object representing the buttons config object. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  2041. * @type YAHOO.util.CustomEvent
  2042. */
  2043. /**
  2044. * @event valueClick
  2045. * @param {Object} o The object passed to this handler is the button config used to create the button.
  2046. * @description This is a special dynamic event that is created and dispatched based on the value property
  2047. * of the button config. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  2048. * Example:
  2049. * <code><pre>
  2050. * buttons : [
  2051. * { type: 'button', value: 'test', value: 'testButton' }
  2052. * ]</pre>
  2053. * </code>
  2054. * With the valueClick event you could subscribe to this buttons click event with this:
  2055. * tbar.in('testButtonClick', function() { alert('test button clicked'); })
  2056. * @type YAHOO.util.CustomEvent
  2057. */
  2058. /**
  2059. * @event toolbarExpanded
  2060. * @description Fires when the toolbar is expanded via the collapse button. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  2061. * @type YAHOO.util.CustomEvent
  2062. */
  2063. /**
  2064. * @event toolbarCollapsed
  2065. * @description Fires when the toolbar is collapsed via the collapse button. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  2066. * @type YAHOO.util.CustomEvent
  2067. */
  2068. })();
  2069. /**
  2070. * @module editor
  2071. * @description <p>The Rich Text Editor is a UI control that replaces a standard HTML textarea; it allows for the rich formatting of text content, including common structural treatments like lists, formatting treatments like bold and italic text, and drag-and-drop inclusion and sizing of images. The Rich Text Editor's toolbar is extensible via a plugin architecture so that advanced implementations can achieve a high degree of customization.</p>
  2072. * @namespace YAHOO.widget
  2073. * @requires yahoo, dom, element, event, toolbar
  2074. * @optional animation, container_core, resize, dragdrop
  2075. */
  2076. (function() {
  2077. var Dom = YAHOO.util.Dom,
  2078. Event = YAHOO.util.Event,
  2079. Lang = YAHOO.lang,
  2080. Toolbar = YAHOO.widget.Toolbar;
  2081. /**
  2082. * The Rich Text Editor is a UI control that replaces a standard HTML textarea; it allows for the rich formatting of text content, including common structural treatments like lists, formatting treatments like bold and italic text, and drag-and-drop inclusion and sizing of images. The Rich Text Editor's toolbar is extensible via a plugin architecture so that advanced implementations can achieve a high degree of customization.
  2083. * @constructor
  2084. * @class SimpleEditor
  2085. * @extends YAHOO.util.Element
  2086. * @param {String/HTMLElement} el The textarea element to turn into an editor.
  2087. * @param {Object} attrs Object liternal containing configuration parameters.
  2088. */
  2089. YAHOO.widget.SimpleEditor = function(el, attrs) {
  2090. YAHOO.log('SimpleEditor Initalizing', 'info', 'SimpleEditor');
  2091. var o = {};
  2092. if (Lang.isObject(el) && (!el.tagName) && !attrs) {
  2093. Lang.augmentObject(o, el); //Break the config reference
  2094. el = document.createElement('textarea');
  2095. this.DOMReady = true;
  2096. if (o.container) {
  2097. var c = Dom.get(o.container);
  2098. c.appendChild(el);
  2099. } else {
  2100. document.body.appendChild(el);
  2101. }
  2102. } else {
  2103. if (attrs) {
  2104. Lang.augmentObject(o, attrs); //Break the config reference
  2105. }
  2106. }
  2107. var oConfig = {
  2108. element: null,
  2109. attributes: o
  2110. }, id = null;
  2111. if (Lang.isString(el)) {
  2112. id = el;
  2113. } else {
  2114. if (oConfig.attributes.id) {
  2115. id = oConfig.attributes.id;
  2116. } else {
  2117. this.DOMReady = true;
  2118. id = Dom.generateId(el);
  2119. }
  2120. }
  2121. oConfig.element = el;
  2122. var element_cont = document.createElement('DIV');
  2123. oConfig.attributes.element_cont = new YAHOO.util.Element(element_cont, {
  2124. id: id + '_container'
  2125. });
  2126. var div = document.createElement('div');
  2127. Dom.addClass(div, 'first-child');
  2128. oConfig.attributes.element_cont.appendChild(div);
  2129. if (!oConfig.attributes.toolbar_cont) {
  2130. oConfig.attributes.toolbar_cont = document.createElement('DIV');
  2131. oConfig.attributes.toolbar_cont.id = id + '_toolbar';
  2132. div.appendChild(oConfig.attributes.toolbar_cont);
  2133. }
  2134. var editorWrapper = document.createElement('DIV');
  2135. div.appendChild(editorWrapper);
  2136. oConfig.attributes.editor_wrapper = editorWrapper;
  2137. YAHOO.widget.SimpleEditor.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
  2138. };
  2139. YAHOO.extend(YAHOO.widget.SimpleEditor, YAHOO.util.Element, {
  2140. /**
  2141. * @private
  2142. * @property _resizeConfig
  2143. * @description The default config for the Resize Utility
  2144. */
  2145. _resizeConfig: {
  2146. handles: ['br'],
  2147. autoRatio: true,
  2148. status: true,
  2149. proxy: true,
  2150. useShim: true,
  2151. setSize: false
  2152. },
  2153. /**
  2154. * @private
  2155. * @method _setupResize
  2156. * @description Creates the Resize instance and binds its events.
  2157. */
  2158. _setupResize: function() {
  2159. if (!YAHOO.util.DD || !YAHOO.util.Resize) { return false; }
  2160. if (this.get('resize')) {
  2161. var config = {};
  2162. Lang.augmentObject(config, this._resizeConfig); //Break the config reference
  2163. this.resize = new YAHOO.util.Resize(this.get('element_cont').get('element'), config);
  2164. this.resize.on('resize', function(args) {
  2165. var anim = this.get('animate');
  2166. this.set('animate', false);
  2167. this.set('width', args.width + 'px');
  2168. var h = args.height,
  2169. th = (this.toolbar.get('element').clientHeight + 2),
  2170. dh = 0;
  2171. if (this.dompath) {
  2172. dh = (this.dompath.clientHeight + 1); //It has a 1px top border..
  2173. }
  2174. var newH = (h - th - dh);
  2175. this.set('height', newH + 'px');
  2176. this.get('element_cont').setStyle('height', '');
  2177. this.set('animate', anim);
  2178. }, this, true);
  2179. }
  2180. },
  2181. /**
  2182. * @property resize
  2183. * @description A reference to the Resize object
  2184. * @type YAHOO.util.Resize
  2185. */
  2186. resize: null,
  2187. /**
  2188. * @private
  2189. * @method _setupDD
  2190. * @description Sets up the DD instance used from the 'drag' config option.
  2191. */
  2192. _setupDD: function() {
  2193. if (!YAHOO.util.DD) { return false; }
  2194. if (this.get('drag')) {
  2195. YAHOO.log('Attaching DD instance to Editor', 'info', 'SimpleEditor');
  2196. var d = this.get('drag'),
  2197. dd = YAHOO.util.DD;
  2198. if (d === 'proxy') {
  2199. dd = YAHOO.util.DDProxy;
  2200. }
  2201. this.dd = new dd(this.get('element_cont').get('element'));
  2202. this.toolbar.addClass('draggable');
  2203. this.dd.setHandleElId(this.toolbar._titlebar);
  2204. }
  2205. },
  2206. /**
  2207. * @property dd
  2208. * @description A reference to the DragDrop object.
  2209. * @type YAHOO.util.DD/YAHOO.util.DDProxy
  2210. */
  2211. dd: null,
  2212. /**
  2213. * @private
  2214. * @property _lastCommand
  2215. * @description A cache of the last execCommand (used for Undo/Redo so they don't mark an undo level)
  2216. * @type String
  2217. */
  2218. _lastCommand: null,
  2219. _undoNodeChange: function() {},
  2220. _storeUndo: function() {},
  2221. /**
  2222. * @private
  2223. * @method _checkKey
  2224. * @description Checks a keyMap entry against a key event
  2225. * @param {Object} k The _keyMap object
  2226. * @param {Event} e The Mouse Event
  2227. * @return {Boolean}
  2228. */
  2229. _checkKey: function(k, e) {
  2230. var ret = false;
  2231. if ((e.keyCode === k.key)) {
  2232. if (k.mods && (k.mods.length > 0)) {
  2233. var val = 0;
  2234. for (var i = 0; i < k.mods.length; i++) {
  2235. if (this.browser.mac) {
  2236. if (k.mods[i] == 'ctrl') {
  2237. k.mods[i] = 'meta';
  2238. }
  2239. }
  2240. if (e[k.mods[i] + 'Key'] === true) {
  2241. val++;
  2242. }
  2243. }
  2244. if (val === k.mods.length) {
  2245. ret = true;
  2246. }
  2247. } else {
  2248. ret = true;
  2249. }
  2250. }
  2251. //YAHOO.log('Shortcut Key Check: (' + k.key + ') return: ' + ret, 'info', 'SimpleEditor');
  2252. return ret;
  2253. },
  2254. /**
  2255. * @private
  2256. * @property _keyMap
  2257. * @description Named key maps for various actions in the Editor. Example: <code>CLOSE_WINDOW: { key: 87, mods: ['shift', 'ctrl'] }</code>.
  2258. * This entry shows that when key 87 (W) is found with the modifiers of shift and control, the window will close. You can customize this object to tweak keyboard shortcuts.
  2259. * @type {Object/Mixed}
  2260. */
  2261. _keyMap: {
  2262. SELECT_ALL: {
  2263. key: 65, //A key
  2264. mods: ['ctrl']
  2265. },
  2266. CLOSE_WINDOW: {
  2267. key: 87, //W key
  2268. mods: ['shift', 'ctrl']
  2269. },
  2270. FOCUS_TOOLBAR: {
  2271. key: 27,
  2272. mods: ['shift']
  2273. },
  2274. FOCUS_AFTER: {
  2275. key: 27
  2276. },
  2277. FONT_SIZE_UP: {
  2278. key: 38,
  2279. mods: ['shift', 'ctrl']
  2280. },
  2281. FONT_SIZE_DOWN: {
  2282. key: 40,
  2283. mods: ['shift', 'ctrl']
  2284. },
  2285. CREATE_LINK: {
  2286. key: 76,
  2287. mods: ['shift', 'ctrl']
  2288. },
  2289. BOLD: {
  2290. key: 66,
  2291. mods: ['shift', 'ctrl']
  2292. },
  2293. ITALIC: {
  2294. key: 73,
  2295. mods: ['shift', 'ctrl']
  2296. },
  2297. UNDERLINE: {
  2298. key: 85,
  2299. mods: ['shift', 'ctrl']
  2300. },
  2301. UNDO: {
  2302. key: 90,
  2303. mods: ['ctrl']
  2304. },
  2305. REDO: {
  2306. key: 90,
  2307. mods: ['shift', 'ctrl']
  2308. },
  2309. JUSTIFY_LEFT: {
  2310. key: 219,
  2311. mods: ['shift', 'ctrl']
  2312. },
  2313. JUSTIFY_CENTER: {
  2314. key: 220,
  2315. mods: ['shift', 'ctrl']
  2316. },
  2317. JUSTIFY_RIGHT: {
  2318. key: 221,
  2319. mods: ['shift', 'ctrl']
  2320. }
  2321. },
  2322. /**
  2323. * @private
  2324. * @method _cleanClassName
  2325. * @description Makes a useable classname from dynamic data, by dropping it to lowercase and replacing spaces with -'s.
  2326. * @param {String} str The classname to clean up
  2327. * @return {String}
  2328. */
  2329. _cleanClassName: function(str) {
  2330. return str.replace(/ /g, '-').toLowerCase();
  2331. },
  2332. /**
  2333. * @property _textarea
  2334. * @description Flag to determine if we are using a textarea or an HTML Node.
  2335. * @type Boolean
  2336. */
  2337. _textarea: null,
  2338. /**
  2339. * @property _docType
  2340. * @description The DOCTYPE to use in the editable container.
  2341. * @type String
  2342. */
  2343. _docType: '<!DOCTYPE HTML PUBLIC "-/'+'/W3C/'+'/DTD HTML 4.01/'+'/EN" "http:/'+'/www.w3.org/TR/html4/strict.dtd">',
  2344. /**
  2345. * @property editorDirty
  2346. * @description This flag will be set when certain things in the Editor happen. It is to be used by the developer to check to see if content has changed.
  2347. * @type Boolean
  2348. */
  2349. editorDirty: null,
  2350. /**
  2351. * @property _defaultCSS
  2352. * @description The default CSS used in the config for 'css'. This way you can add to the config like this: { css: YAHOO.widget.SimpleEditor.prototype._defaultCSS + 'ADD MYY CSS HERE' }
  2353. * @type String
  2354. */
  2355. _defaultCSS: 'html { height: 95%; } body { padding: 7px; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } .warning-localfile { border-bottom: 1px dashed red !important; } .yui-busy { cursor: wait !important; } img.selected { border: 2px dotted #808080; } img { cursor: pointer !important; border: none; } body.ptags.webkit div.yui-wk-p { margin: 11px 0; } body.ptags.webkit div.yui-wk-div { margin: 0; }',
  2356. /**
  2357. * @property _defaultToolbar
  2358. * @private
  2359. * @description Default toolbar config.
  2360. * @type Object
  2361. */
  2362. _defaultToolbar: null,
  2363. /**
  2364. * @property _lastButton
  2365. * @private
  2366. * @description The last button pressed, so we don't disable it.
  2367. * @type Object
  2368. */
  2369. _lastButton: null,
  2370. /**
  2371. * @property _baseHREF
  2372. * @private
  2373. * @description The base location of the editable page (this page) so that relative paths for image work.
  2374. * @type String
  2375. */
  2376. _baseHREF: function() {
  2377. var href = document.location.href;
  2378. if (href.indexOf('?') !== -1) { //Remove the query string
  2379. href = href.substring(0, href.indexOf('?'));
  2380. }
  2381. href = href.substring(0, href.lastIndexOf('/')) + '/';
  2382. return href;
  2383. }(),
  2384. /**
  2385. * @property _lastImage
  2386. * @private
  2387. * @description Safari reference for the last image selected (for styling as selected).
  2388. * @type HTMLElement
  2389. */
  2390. _lastImage: null,
  2391. /**
  2392. * @property _blankImageLoaded
  2393. * @private
  2394. * @description Don't load the blank image more than once..
  2395. * @type Boolean
  2396. */
  2397. _blankImageLoaded: null,
  2398. /**
  2399. * @property _fixNodesTimer
  2400. * @private
  2401. * @description Holder for the fixNodes timer
  2402. * @type Date
  2403. */
  2404. _fixNodesTimer: null,
  2405. /**
  2406. * @property _nodeChangeTimer
  2407. * @private
  2408. * @description Holds a reference to the nodeChange setTimeout call
  2409. * @type Number
  2410. */
  2411. _nodeChangeTimer: null,
  2412. /**
  2413. * @property _nodeChangeDelayTimer
  2414. * @private
  2415. * @description Holds a reference to the nodeChangeDelay setTimeout call
  2416. * @type Number
  2417. */
  2418. _nodeChangeDelayTimer: null,
  2419. /**
  2420. * @property _lastNodeChangeEvent
  2421. * @private
  2422. * @description Flag to determine the last event that fired a node change
  2423. * @type Event
  2424. */
  2425. _lastNodeChangeEvent: null,
  2426. /**
  2427. * @property _lastNodeChange
  2428. * @private
  2429. * @description Flag to determine when the last node change was fired
  2430. * @type Date
  2431. */
  2432. _lastNodeChange: 0,
  2433. /**
  2434. * @property _rendered
  2435. * @private
  2436. * @description Flag to determine if editor has been rendered or not
  2437. * @type Boolean
  2438. */
  2439. _rendered: null,
  2440. /**
  2441. * @property DOMReady
  2442. * @private
  2443. * @description Flag to determine if DOM is ready or not
  2444. * @type Boolean
  2445. */
  2446. DOMReady: null,
  2447. /**
  2448. * @property _selection
  2449. * @private
  2450. * @description Holder for caching iframe selections
  2451. * @type Object
  2452. */
  2453. _selection: null,
  2454. /**
  2455. * @property _mask
  2456. * @private
  2457. * @description DOM Element holder for the editor Mask when disabled
  2458. * @type Object
  2459. */
  2460. _mask: null,
  2461. /**
  2462. * @property _showingHiddenElements
  2463. * @private
  2464. * @description Status of the hidden elements button
  2465. * @type Boolean
  2466. */
  2467. _showingHiddenElements: null,
  2468. /**
  2469. * @property currentWindow
  2470. * @description A reference to the currently open EditorWindow
  2471. * @type Object
  2472. */
  2473. currentWindow: null,
  2474. /**
  2475. * @property currentEvent
  2476. * @description A reference to the current editor event
  2477. * @type Event
  2478. */
  2479. currentEvent: null,
  2480. /**
  2481. * @property operaEvent
  2482. * @private
  2483. * @description setTimeout holder for Opera and Image DoubleClick event..
  2484. * @type Object
  2485. */
  2486. operaEvent: null,
  2487. /**
  2488. * @property currentFont
  2489. * @description A reference to the last font selected from the Toolbar
  2490. * @type HTMLElement
  2491. */
  2492. currentFont: null,
  2493. /**
  2494. * @property currentElement
  2495. * @description A reference to the current working element in the editor
  2496. * @type Array
  2497. */
  2498. currentElement: null,
  2499. /**
  2500. * @property dompath
  2501. * @description A reference to the dompath container for writing the current working dom path to.
  2502. * @type HTMLElement
  2503. */
  2504. dompath: null,
  2505. /**
  2506. * @property beforeElement
  2507. * @description A reference to the H2 placed before the editor for Accessibilty.
  2508. * @type HTMLElement
  2509. */
  2510. beforeElement: null,
  2511. /**
  2512. * @property afterElement
  2513. * @description A reference to the H2 placed after the editor for Accessibilty.
  2514. * @type HTMLElement
  2515. */
  2516. afterElement: null,
  2517. /**
  2518. * @property invalidHTML
  2519. * @description Contains a list of HTML elements that are invalid inside the editor. They will be removed when they are found. If you set the value of a key to "{ keepContents: true }", then the element will be replaced with a yui-non span to be filtered out when cleanHTML is called. The only tag that is ignored here is the span tag as it will force the Editor into a loop and freeze the browser. However.. all of these tags will be removed in the cleanHTML routine.
  2520. * @type Object
  2521. */
  2522. invalidHTML: {
  2523. form: true,
  2524. input: true,
  2525. button: true,
  2526. select: true,
  2527. link: true,
  2528. html: true,
  2529. body: true,
  2530. iframe: true,
  2531. script: true,
  2532. style: true,
  2533. textarea: true
  2534. },
  2535. /**
  2536. * @property toolbar
  2537. * @description Local property containing the <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a> instance
  2538. * @type <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>
  2539. */
  2540. toolbar: null,
  2541. /**
  2542. * @private
  2543. * @property _contentTimer
  2544. * @description setTimeout holder for documentReady check
  2545. */
  2546. _contentTimer: null,
  2547. /**
  2548. * @private
  2549. * @property _contentTimerMax
  2550. * @description The number of times the loaded content should be checked before giving up. Default: 500
  2551. */
  2552. _contentTimerMax: 500,
  2553. /**
  2554. * @private
  2555. * @property _contentTimerCounter
  2556. * @description Counter to check the number of times the body is polled for before giving up
  2557. * @type Number
  2558. */
  2559. _contentTimerCounter: 0,
  2560. /**
  2561. * @private
  2562. * @property _disabled
  2563. * @description The Toolbar items that should be disabled if there is no selection present in the editor.
  2564. * @type Array
  2565. */
  2566. _disabled: [ 'createlink', 'fontname', 'fontsize', 'forecolor', 'backcolor' ],
  2567. /**
  2568. * @private
  2569. * @property _alwaysDisabled
  2570. * @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
  2571. * @type Object
  2572. */
  2573. _alwaysDisabled: { undo: true, redo: true },
  2574. /**
  2575. * @private
  2576. * @property _alwaysEnabled
  2577. * @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
  2578. * @type Object
  2579. */
  2580. _alwaysEnabled: { },
  2581. /**
  2582. * @private
  2583. * @property _semantic
  2584. * @description The Toolbar commands that we should attempt to make tags out of instead of using styles.
  2585. * @type Object
  2586. */
  2587. _semantic: { 'bold': true, 'italic' : true, 'underline' : true },
  2588. /**
  2589. * @private
  2590. * @property _tag2cmd
  2591. * @description A tag map of HTML tags to convert to the different types of commands so we can select the proper toolbar button.
  2592. * @type Object
  2593. */
  2594. _tag2cmd: {
  2595. 'b': 'bold',
  2596. 'strong': 'bold',
  2597. 'i': 'italic',
  2598. 'em': 'italic',
  2599. 'u': 'underline',
  2600. 'sup': 'superscript',
  2601. 'sub': 'subscript',
  2602. 'img': 'insertimage',
  2603. 'a' : 'createlink',
  2604. 'ul' : 'insertunorderedlist',
  2605. 'ol' : 'insertorderedlist'
  2606. },
  2607. /**
  2608. * @private _createIframe
  2609. * @description Creates the DOM and YUI Element for the iFrame editor area.
  2610. * @param {String} id The string ID to prefix the iframe with
  2611. * @return {Object} iFrame object
  2612. */
  2613. _createIframe: function() {
  2614. var ifrmDom = document.createElement('iframe');
  2615. ifrmDom.id = this.get('id') + '_editor';
  2616. var config = {
  2617. border: '0',
  2618. frameBorder: '0',
  2619. marginWidth: '0',
  2620. marginHeight: '0',
  2621. leftMargin: '0',
  2622. topMargin: '0',
  2623. allowTransparency: 'true',
  2624. width: '100%'
  2625. };
  2626. if (this.get('autoHeight')) {
  2627. config.scrolling = 'no';
  2628. }
  2629. for (var i in config) {
  2630. if (Lang.hasOwnProperty(config, i)) {
  2631. ifrmDom.setAttribute(i, config[i]);
  2632. }
  2633. }
  2634. var isrc = 'javascript:;';
  2635. if (this.browser.ie) {
  2636. //isrc = 'about:blank';
  2637. //TODO - Check this, I have changed it before..
  2638. isrc = 'javascript:false;';
  2639. }
  2640. ifrmDom.setAttribute('src', isrc);
  2641. var ifrm = new YAHOO.util.Element(ifrmDom);
  2642. ifrm.setStyle('visibility', 'hidden');
  2643. return ifrm;
  2644. },
  2645. /**
  2646. * @private _isElement
  2647. * @description Checks to see if an Element reference is a valid one and has a certain tag type
  2648. * @param {HTMLElement} el The element to check
  2649. * @param {String} tag The tag that the element needs to be
  2650. * @return {Boolean}
  2651. */
  2652. _isElement: function(el, tag) {
  2653. if (el && el.tagName && (el.tagName.toLowerCase() == tag)) {
  2654. return true;
  2655. }
  2656. if (el && el.getAttribute && (el.getAttribute('tag') == tag)) {
  2657. return true;
  2658. }
  2659. return false;
  2660. },
  2661. /**
  2662. * @private _hasParent
  2663. * @description Checks to see if an Element reference or one of it's parents is a valid one and has a certain tag type
  2664. * @param {HTMLElement} el The element to check
  2665. * @param {String} tag The tag that the element needs to be
  2666. * @return HTMLElement
  2667. */
  2668. _hasParent: function(el, tag) {
  2669. if (!el || !el.parentNode) {
  2670. return false;
  2671. }
  2672. while (el.parentNode) {
  2673. if (this._isElement(el, tag)) {
  2674. return el;
  2675. }
  2676. if (el.parentNode) {
  2677. el = el.parentNode;
  2678. } else {
  2679. return false;
  2680. }
  2681. }
  2682. return false;
  2683. },
  2684. /**
  2685. * @private
  2686. * @method _getDoc
  2687. * @description Get the Document of the IFRAME
  2688. * @return {Object}
  2689. */
  2690. _getDoc: function() {
  2691. var value = false;
  2692. try {
  2693. if (this.get('iframe').get('element').contentWindow.document) {
  2694. value = this.get('iframe').get('element').contentWindow.document;
  2695. return value;
  2696. }
  2697. } catch (e) {
  2698. return false;
  2699. }
  2700. },
  2701. /**
  2702. * @private
  2703. * @method _getWindow
  2704. * @description Get the Window of the IFRAME
  2705. * @return {Object}
  2706. */
  2707. _getWindow: function() {
  2708. return this.get('iframe').get('element').contentWindow;
  2709. },
  2710. /**
  2711. * @method focus
  2712. * @description Attempt to set the focus of the iframes window.
  2713. */
  2714. focus: function() {
  2715. this._getWindow().focus();
  2716. },
  2717. /**
  2718. * @private
  2719. * @depreciated - This should not be used, moved to this.focus();
  2720. * @method _focusWindow
  2721. * @description Attempt to set the focus of the iframes window.
  2722. */
  2723. _focusWindow: function() {
  2724. YAHOO.log('_focusWindow: depreciated in favor of this.focus()', 'warn', 'Editor');
  2725. this.focus();
  2726. },
  2727. /**
  2728. * @private
  2729. * @method _hasSelection
  2730. * @description Determines if there is a selection in the editor document.
  2731. * @return {Boolean}
  2732. */
  2733. _hasSelection: function() {
  2734. var sel = this._getSelection();
  2735. var range = this._getRange();
  2736. var hasSel = false;
  2737. if (!sel || !range) {
  2738. return hasSel;
  2739. }
  2740. //Internet Explorer
  2741. if (this.browser.ie || this.browser.opera) {
  2742. if (range.text) {
  2743. hasSel = true;
  2744. }
  2745. if (range.html) {
  2746. hasSel = true;
  2747. }
  2748. } else {
  2749. if (this.browser.webkit) {
  2750. if (sel+'' !== '') {
  2751. hasSel = true;
  2752. }
  2753. } else {
  2754. if (sel && (sel.toString() !== '') && (sel !== undefined)) {
  2755. hasSel = true;
  2756. }
  2757. }
  2758. }
  2759. return hasSel;
  2760. },
  2761. /**
  2762. * @private
  2763. * @method _getSelection
  2764. * @description Handles the different selection objects across the A-Grade list.
  2765. * @return {Object} Selection Object
  2766. */
  2767. _getSelection: function() {
  2768. var _sel = null;
  2769. if (this._getDoc() && this._getWindow()) {
  2770. if (this._getDoc().selection) {
  2771. _sel = this._getDoc().selection;
  2772. } else {
  2773. _sel = this._getWindow().getSelection();
  2774. }
  2775. //Handle Safari's lack of Selection Object
  2776. if (this.browser.webkit) {
  2777. if (_sel.baseNode) {
  2778. this._selection = {};
  2779. this._selection.baseNode = _sel.baseNode;
  2780. this._selection.baseOffset = _sel.baseOffset;
  2781. this._selection.extentNode = _sel.extentNode;
  2782. this._selection.extentOffset = _sel.extentOffset;
  2783. } else if (this._selection !== null) {
  2784. _sel = this._getWindow().getSelection();
  2785. _sel.setBaseAndExtent(
  2786. this._selection.baseNode,
  2787. this._selection.baseOffset,
  2788. this._selection.extentNode,
  2789. this._selection.extentOffset);
  2790. this._selection = null;
  2791. }
  2792. }
  2793. }
  2794. return _sel;
  2795. },
  2796. /**
  2797. * @private
  2798. * @method _selectNode
  2799. * @description Places the highlight around a given node
  2800. * @param {HTMLElement} node The node to select
  2801. */
  2802. _selectNode: function(node, collapse) {
  2803. if (!node) {
  2804. return false;
  2805. }
  2806. var sel = this._getSelection(),
  2807. range = null;
  2808. if (this.browser.ie) {
  2809. try { //IE freaks out here sometimes..
  2810. range = this._getDoc().body.createTextRange();
  2811. range.moveToElementText(node);
  2812. range.select();
  2813. } catch (e) {
  2814. YAHOO.log('IE failed to select element: ' + node.tagName, 'warn', 'SimpleEditor');
  2815. }
  2816. } else if (this.browser.webkit) {
  2817. if (collapse) {
  2818. sel.setBaseAndExtent(node, 1, node, node.innerText.length);
  2819. } else {
  2820. sel.setBaseAndExtent(node, 0, node, node.innerText.length);
  2821. }
  2822. } else if (this.browser.opera) {
  2823. sel = this._getWindow().getSelection();
  2824. range = this._getDoc().createRange();
  2825. range.selectNode(node);
  2826. sel.removeAllRanges();
  2827. sel.addRange(range);
  2828. } else {
  2829. range = this._getDoc().createRange();
  2830. range.selectNodeContents(node);
  2831. sel.removeAllRanges();
  2832. sel.addRange(range);
  2833. }
  2834. //TODO - Check Performance
  2835. this.nodeChange();
  2836. },
  2837. /**
  2838. * @private
  2839. * @method _getRange
  2840. * @description Handles the different range objects across the A-Grade list.
  2841. * @return {Object} Range Object
  2842. */
  2843. _getRange: function() {
  2844. var sel = this._getSelection();
  2845. if (sel === null) {
  2846. return null;
  2847. }
  2848. if (this.browser.webkit && !sel.getRangeAt) {
  2849. var _range = this._getDoc().createRange();
  2850. try {
  2851. _range.setStart(sel.anchorNode, sel.anchorOffset);
  2852. _range.setEnd(sel.focusNode, sel.focusOffset);
  2853. } catch (e) {
  2854. _range = this._getWindow().getSelection()+'';
  2855. }
  2856. return _range;
  2857. }
  2858. if (this.browser.ie || this.browser.opera) {
  2859. try {
  2860. return sel.createRange();
  2861. } catch (e2) {
  2862. return null;
  2863. }
  2864. }
  2865. if (sel.rangeCount > 0) {
  2866. return sel.getRangeAt(0);
  2867. }
  2868. return null;
  2869. },
  2870. /**
  2871. * @private
  2872. * @method _setDesignMode
  2873. * @description Sets the designMode property of the iFrame document's body.
  2874. * @param {String} state This should be either on or off
  2875. */
  2876. _setDesignMode: function(state) {
  2877. if (this.get('setDesignMode')) {
  2878. try {
  2879. this._getDoc().designMode = ((state.toLowerCase() == 'off') ? 'off' : 'on');
  2880. } catch(e) { }
  2881. }
  2882. },
  2883. /**
  2884. * @private
  2885. * @method _toggleDesignMode
  2886. * @description Toggles the designMode property of the iFrame document on and off.
  2887. * @return {String} The state that it was set to.
  2888. */
  2889. _toggleDesignMode: function() {
  2890. YAHOO.log('It is not recommended to use this method and it will be depreciated.', 'warn', 'SimpleEditor');
  2891. var _dMode = this._getDoc().designMode,
  2892. _state = ((_dMode.toLowerCase() == 'on') ? 'off' : 'on');
  2893. this._setDesignMode(_state);
  2894. return _state;
  2895. },
  2896. /**
  2897. * @private
  2898. * @property _focused
  2899. * @description Holder for trapping focus/blur state and prevent double events
  2900. * @type Boolean
  2901. */
  2902. _focused: null,
  2903. /**
  2904. * @private
  2905. * @method _handleFocus
  2906. * @description Handles the focus of the iframe. Note, this is window focus event, not an Editor focus event.
  2907. * @param {Event} e The DOM Event
  2908. */
  2909. _handleFocus: function(e) {
  2910. if (!this._focused) {
  2911. //YAHOO.log('Editor Window Focused', 'info', 'SimpleEditor');
  2912. this._focused = true;
  2913. this.fireEvent('editorWindowFocus', { type: 'editorWindowFocus', target: this });
  2914. }
  2915. },
  2916. /**
  2917. * @private
  2918. * @method _handleBlur
  2919. * @description Handles the blur of the iframe. Note, this is window blur event, not an Editor blur event.
  2920. * @param {Event} e The DOM Event
  2921. */
  2922. _handleBlur: function(e) {
  2923. if (this._focused) {
  2924. //YAHOO.log('Editor Window Blurred', 'info', 'SimpleEditor');
  2925. this._focused = false;
  2926. this.fireEvent('editorWindowBlur', { type: 'editorWindowBlur', target: this });
  2927. }
  2928. },
  2929. /**
  2930. * @private
  2931. * @method _initEditorEvents
  2932. * @description This method sets up the listeners on the Editors document.
  2933. */
  2934. _initEditorEvents: function() {
  2935. //Setup Listeners on iFrame
  2936. var doc = this._getDoc(),
  2937. win = this._getWindow();
  2938. Event.on(doc, 'mouseup', this._handleMouseUp, this, true);
  2939. Event.on(doc, 'mousedown', this._handleMouseDown, this, true);
  2940. Event.on(doc, 'click', this._handleClick, this, true);
  2941. Event.on(doc, 'dblclick', this._handleDoubleClick, this, true);
  2942. Event.on(doc, 'keypress', this._handleKeyPress, this, true);
  2943. Event.on(doc, 'keyup', this._handleKeyUp, this, true);
  2944. Event.on(doc, 'keydown', this._handleKeyDown, this, true);
  2945. /* TODO -- Everyone but Opera works here..
  2946. Event.on(doc, 'paste', function() {
  2947. YAHOO.log('PASTE', 'info', 'SimpleEditor');
  2948. }, this, true);
  2949. */
  2950. //Focus and blur..
  2951. Event.on(win, 'focus', this._handleFocus, this, true);
  2952. Event.on(win, 'blur', this._handleBlur, this, true);
  2953. },
  2954. /**
  2955. * @private
  2956. * @method _removeEditorEvents
  2957. * @description This method removes the listeners on the Editors document (for disabling).
  2958. */
  2959. _removeEditorEvents: function() {
  2960. //Remove Listeners on iFrame
  2961. var doc = this._getDoc(),
  2962. win = this._getWindow();
  2963. Event.removeListener(doc, 'mouseup', this._handleMouseUp, this, true);
  2964. Event.removeListener(doc, 'mousedown', this._handleMouseDown, this, true);
  2965. Event.removeListener(doc, 'click', this._handleClick, this, true);
  2966. Event.removeListener(doc, 'dblclick', this._handleDoubleClick, this, true);
  2967. Event.removeListener(doc, 'keypress', this._handleKeyPress, this, true);
  2968. Event.removeListener(doc, 'keyup', this._handleKeyUp, this, true);
  2969. Event.removeListener(doc, 'keydown', this._handleKeyDown, this, true);
  2970. //Focus and blur..
  2971. Event.removeListener(win, 'focus', this._handleFocus, this, true);
  2972. Event.removeListener(win, 'blur', this._handleBlur, this, true);
  2973. },
  2974. _fixWebkitDivs: function() {
  2975. if (this.browser.webkit) {
  2976. var divs = this._getDoc().body.getElementsByTagName('div');
  2977. Dom.addClass(divs, 'yui-wk-div');
  2978. }
  2979. },
  2980. /**
  2981. * @private
  2982. * @method _initEditor
  2983. * @param {Boolean} raw Don't add events.
  2984. * @description This method is fired from _checkLoaded when the document is ready. It turns on designMode and set's up the listeners.
  2985. */
  2986. _initEditor: function(raw) {
  2987. if (this._editorInit) {
  2988. return;
  2989. }
  2990. this._editorInit = true;
  2991. if (this.browser.ie) {
  2992. this._getDoc().body.style.margin = '0';
  2993. }
  2994. if (!this.get('disabled')) {
  2995. this._setDesignMode('on');
  2996. this._contentTimerCounter = 0;
  2997. }
  2998. if (!this._getDoc().body) {
  2999. YAHOO.log('Body is null, check again', 'error', 'SimpleEditor');
  3000. this._contentTimerCounter = 0;
  3001. this._editorInit = false;
  3002. this._checkLoaded();
  3003. return false;
  3004. }
  3005. YAHOO.log('editorLoaded', 'info', 'SimpleEditor');
  3006. if (!raw) {
  3007. this.toolbar.on('buttonClick', this._handleToolbarClick, this, true);
  3008. }
  3009. if (!this.get('disabled')) {
  3010. this._initEditorEvents();
  3011. this.toolbar.set('disabled', false);
  3012. }
  3013. if (raw) {
  3014. this.fireEvent('editorContentReloaded', { type: 'editorreloaded', target: this });
  3015. } else {
  3016. this.fireEvent('editorContentLoaded', { type: 'editorLoaded', target: this });
  3017. }
  3018. this._fixWebkitDivs();
  3019. if (this.get('dompath')) {
  3020. YAHOO.log('Delayed DomPath write', 'info', 'SimpleEditor');
  3021. var self = this;
  3022. setTimeout(function() {
  3023. self._writeDomPath.call(self);
  3024. self._setupResize.call(self);
  3025. }, 150);
  3026. }
  3027. var br = [];
  3028. for (var i in this.browser) {
  3029. if (this.browser[i]) {
  3030. br.push(i);
  3031. }
  3032. }
  3033. if (this.get('ptags')) {
  3034. br.push('ptags');
  3035. }
  3036. Dom.addClass(this._getDoc().body, br.join(' '));
  3037. this.nodeChange(true);
  3038. },
  3039. /**
  3040. * @private
  3041. * @method _checkLoaded
  3042. * @param {Boolean} raw Don't add events.
  3043. * @description Called from a setTimeout loop to check if the iframes body.onload event has fired, then it will init the editor.
  3044. */
  3045. _checkLoaded: function(raw) {
  3046. this._editorInit = false;
  3047. this._contentTimerCounter++;
  3048. if (this._contentTimer) {
  3049. clearTimeout(this._contentTimer);
  3050. }
  3051. if (this._contentTimerCounter > this._contentTimerMax) {
  3052. YAHOO.log('ERROR: Body Did Not load', 'error', 'SimpleEditor');
  3053. return false;
  3054. }
  3055. var init = false;
  3056. try {
  3057. if (this._getDoc() && this._getDoc().body) {
  3058. if (this.browser.ie) {
  3059. if (this._getDoc().body.readyState == 'complete') {
  3060. init = true;
  3061. }
  3062. } else {
  3063. if (this._getDoc().body._rteLoaded === true) {
  3064. init = true;
  3065. }
  3066. }
  3067. }
  3068. } catch (e) {
  3069. init = false;
  3070. YAHOO.log('checking body (e)' + e, 'error', 'SimpleEditor');
  3071. }
  3072. if (init === true) {
  3073. //The onload event has fired, clean up after ourselves and fire the _initEditor method
  3074. YAHOO.log('Firing _initEditor', 'info', 'SimpleEditor');
  3075. this._initEditor(raw);
  3076. } else {
  3077. var self = this;
  3078. this._contentTimer = setTimeout(function() {
  3079. self._checkLoaded.call(self, raw);
  3080. }, 20);
  3081. }
  3082. },
  3083. /**
  3084. * @private
  3085. * @method _setInitialContent
  3086. * @param {Boolean} raw Don't add events.
  3087. * @description This method will open the iframes content document and write the textareas value into it, then start the body.onload checking.
  3088. */
  3089. _setInitialContent: function(raw) {
  3090. YAHOO.log('Populating editor body with contents of the text area', 'info', 'SimpleEditor');
  3091. var value = ((this._textarea) ? this.get('element').value : this.get('element').innerHTML),
  3092. doc = null;
  3093. if (value === '') {
  3094. value = '<br>';
  3095. }
  3096. var html = Lang.substitute(this.get('html'), {
  3097. TITLE: this.STR_TITLE,
  3098. CONTENT: this._cleanIncomingHTML(value),
  3099. CSS: this.get('css'),
  3100. HIDDEN_CSS: ((this.get('hiddencss')) ? this.get('hiddencss') : '/* No Hidden CSS */'),
  3101. EXTRA_CSS: ((this.get('extracss')) ? this.get('extracss') : '/* No Extra CSS */')
  3102. }),
  3103. check = true;
  3104. html = html.replace(/RIGHT_BRACKET/gi, '{');
  3105. html = html.replace(/LEFT_BRACKET/gi, '}');
  3106. if (document.compatMode != 'BackCompat') {
  3107. YAHOO.log('Adding Doctype to editable area', 'info', 'SimpleEditor');
  3108. html = this._docType + "\n" + html;
  3109. } else {
  3110. YAHOO.log('DocType skipped because we are in BackCompat Mode.', 'warn', 'SimpleEditor');
  3111. }
  3112. if (this.browser.ie || this.browser.webkit || this.browser.opera || (navigator.userAgent.indexOf('Firefox/1.5') != -1)) {
  3113. //Firefox 1.5 doesn't like setting designMode on an document created with a data url
  3114. try {
  3115. //Adobe AIR Code
  3116. if (this.browser.air) {
  3117. doc = this._getDoc().implementation.createHTMLDocument();
  3118. var origDoc = this._getDoc();
  3119. origDoc.open();
  3120. origDoc.close();
  3121. doc.open();
  3122. doc.write(html);
  3123. doc.close();
  3124. var node = origDoc.importNode(doc.getElementsByTagName("html")[0], true);
  3125. origDoc.replaceChild(node, origDoc.getElementsByTagName("html")[0]);
  3126. origDoc.body._rteLoaded = true;
  3127. } else {
  3128. doc = this._getDoc();
  3129. doc.open();
  3130. doc.write(html);
  3131. doc.close();
  3132. }
  3133. } catch (e) {
  3134. YAHOO.log('Setting doc failed.. (_setInitialContent)', 'error', 'SimpleEditor');
  3135. //Safari will only be here if we are hidden
  3136. check = false;
  3137. }
  3138. } else {
  3139. //This keeps Firefox 2 from writing the iframe to history preserving the back buttons functionality
  3140. this.get('iframe').get('element').src = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
  3141. }
  3142. this.get('iframe').setStyle('visibility', '');
  3143. if (check) {
  3144. this._checkLoaded(raw);
  3145. }
  3146. },
  3147. /**
  3148. * @private
  3149. * @method _setMarkupType
  3150. * @param {String} action The action to take. Possible values are: css, default or semantic
  3151. * @description This method will turn on/off the useCSS execCommand.
  3152. */
  3153. _setMarkupType: function(action) {
  3154. switch (this.get('markup')) {
  3155. case 'css':
  3156. this._setEditorStyle(true);
  3157. break;
  3158. case 'default':
  3159. this._setEditorStyle(false);
  3160. break;
  3161. case 'semantic':
  3162. case 'xhtml':
  3163. if (this._semantic[action]) {
  3164. this._setEditorStyle(false);
  3165. } else {
  3166. this._setEditorStyle(true);
  3167. }
  3168. break;
  3169. }
  3170. },
  3171. /**
  3172. * Set the editor to use CSS instead of HTML
  3173. * @param {Booleen} stat True/False
  3174. */
  3175. _setEditorStyle: function(stat) {
  3176. try {
  3177. this._getDoc().execCommand('useCSS', false, !stat);
  3178. } catch (ex) {
  3179. }
  3180. },
  3181. /**
  3182. * @private
  3183. * @method _getSelectedElement
  3184. * @description This method will attempt to locate the element that was last interacted with, either via selection, location or event.
  3185. * @return {HTMLElement} The currently selected element.
  3186. */
  3187. _getSelectedElement: function() {
  3188. var doc = this._getDoc(),
  3189. range = null,
  3190. sel = null,
  3191. elm = null,
  3192. check = true;
  3193. if (this.browser.ie) {
  3194. this.currentEvent = this._getWindow().event; //Event utility assumes window.event, so we need to reset it to this._getWindow().event;
  3195. range = this._getRange();
  3196. if (range) {
  3197. elm = range.item ? range.item(0) : range.parentElement();
  3198. if (this._hasSelection()) {
  3199. //TODO
  3200. //WTF.. Why can't I get an element reference here?!??!
  3201. }
  3202. if (elm === doc.body) {
  3203. elm = null;
  3204. }
  3205. }
  3206. if ((this.currentEvent !== null) && (this.currentEvent.keyCode === 0)) {
  3207. elm = Event.getTarget(this.currentEvent);
  3208. }
  3209. } else {
  3210. sel = this._getSelection();
  3211. range = this._getRange();
  3212. if (!sel || !range) {
  3213. return null;
  3214. }
  3215. //TODO
  3216. if (!this._hasSelection() && this.browser.webkit3) {
  3217. //check = false;
  3218. }
  3219. if (this.browser.gecko) {
  3220. //Added in 2.6.0
  3221. if (range.startContainer) {
  3222. if (range.startContainer.nodeType === 3) {
  3223. elm = range.startContainer.parentNode;
  3224. } else if (range.startContainer.nodeType === 1) {
  3225. elm = range.startContainer;
  3226. }
  3227. //Added in 2.7.0
  3228. if (this.currentEvent) {
  3229. var tar = Event.getTarget(this.currentEvent);
  3230. if (!this._isElement(tar, 'html')) {
  3231. if (elm !== tar) {
  3232. elm = tar;
  3233. }
  3234. }
  3235. }
  3236. }
  3237. }
  3238. if (check) {
  3239. if (sel.anchorNode && (sel.anchorNode.nodeType == 3)) {
  3240. if (sel.anchorNode.parentNode) { //next check parentNode
  3241. elm = sel.anchorNode.parentNode;
  3242. }
  3243. if (sel.anchorNode.nextSibling != sel.focusNode.nextSibling) {
  3244. elm = sel.anchorNode.nextSibling;
  3245. }
  3246. }
  3247. if (this._isElement(elm, 'br')) {
  3248. elm = null;
  3249. }
  3250. if (!elm) {
  3251. elm = range.commonAncestorContainer;
  3252. if (!range.collapsed) {
  3253. if (range.startContainer == range.endContainer) {
  3254. if (range.startOffset - range.endOffset < 2) {
  3255. if (range.startContainer.hasChildNodes()) {
  3256. elm = range.startContainer.childNodes[range.startOffset];
  3257. }
  3258. }
  3259. }
  3260. }
  3261. }
  3262. }
  3263. }
  3264. if (this.currentEvent !== null) {
  3265. try {
  3266. switch (this.currentEvent.type) {
  3267. case 'click':
  3268. case 'mousedown':
  3269. case 'mouseup':
  3270. if (this.browser.webkit) {
  3271. elm = Event.getTarget(this.currentEvent);
  3272. }
  3273. break;
  3274. default:
  3275. //Do nothing
  3276. break;
  3277. }
  3278. } catch (e) {
  3279. YAHOO.log('Firefox 1.5 errors here: ' + e, 'error', 'SimpleEditor');
  3280. }
  3281. } else if ((this.currentElement && this.currentElement[0]) && (!this.browser.ie)) {
  3282. //TODO is this still needed?
  3283. //elm = this.currentElement[0];
  3284. }
  3285. if (this.browser.opera || this.browser.webkit) {
  3286. if (this.currentEvent && !elm) {
  3287. elm = YAHOO.util.Event.getTarget(this.currentEvent);
  3288. }
  3289. }
  3290. if (!elm || !elm.tagName) {
  3291. elm = doc.body;
  3292. }
  3293. if (this._isElement(elm, 'html')) {
  3294. //Safari sometimes gives us the HTML node back..
  3295. elm = doc.body;
  3296. }
  3297. if (this._isElement(elm, 'body')) {
  3298. //make sure that body means this body not the parent..
  3299. elm = doc.body;
  3300. }
  3301. if (elm && !elm.parentNode) { //Not in document
  3302. elm = doc.body;
  3303. }
  3304. if (elm === undefined) {
  3305. elm = null;
  3306. }
  3307. return elm;
  3308. },
  3309. /**
  3310. * @private
  3311. * @method _getDomPath
  3312. * @description This method will attempt to build the DOM path from the currently selected element.
  3313. * @param HTMLElement el The element to start with, if not provided _getSelectedElement is used
  3314. * @return {Array} An array of node references that will create the DOM Path.
  3315. */
  3316. _getDomPath: function(el) {
  3317. if (!el) {
  3318. el = this._getSelectedElement();
  3319. }
  3320. var domPath = [];
  3321. while (el !== null) {
  3322. if (el.ownerDocument != this._getDoc()) {
  3323. el = null;
  3324. break;
  3325. }
  3326. //Check to see if we get el.nodeName and nodeType
  3327. if (el.nodeName && el.nodeType && (el.nodeType == 1)) {
  3328. domPath[domPath.length] = el;
  3329. }
  3330. if (this._isElement(el, 'body')) {
  3331. break;
  3332. }
  3333. el = el.parentNode;
  3334. }
  3335. if (domPath.length === 0) {
  3336. if (this._getDoc() && this._getDoc().body) {
  3337. domPath[0] = this._getDoc().body;
  3338. }
  3339. }
  3340. return domPath.reverse();
  3341. },
  3342. /**
  3343. * @private
  3344. * @method _writeDomPath
  3345. * @description Write the current DOM path out to the dompath container below the editor.
  3346. */
  3347. _writeDomPath: function() {
  3348. var path = this._getDomPath(),
  3349. pathArr = [],
  3350. classPath = '',
  3351. pathStr = '';
  3352. for (var i = 0; i < path.length; i++) {
  3353. var tag = path[i].tagName.toLowerCase();
  3354. if ((tag == 'ol') && (path[i].type)) {
  3355. tag += ':' + path[i].type;
  3356. }
  3357. if (Dom.hasClass(path[i], 'yui-tag')) {
  3358. tag = path[i].getAttribute('tag');
  3359. }
  3360. if ((this.get('markup') == 'semantic') || (this.get('markup') == 'xhtml')) {
  3361. switch (tag) {
  3362. case 'b': tag = 'strong'; break;
  3363. case 'i': tag = 'em'; break;
  3364. }
  3365. }
  3366. if (!Dom.hasClass(path[i], 'yui-non')) {
  3367. if (Dom.hasClass(path[i], 'yui-tag')) {
  3368. pathStr = tag;
  3369. } else {
  3370. classPath = ((path[i].className !== '') ? '.' + path[i].className.replace(/ /g, '.') : '');
  3371. if ((classPath.indexOf('yui') != -1) || (classPath.toLowerCase().indexOf('apple-style-span') != -1)) {
  3372. classPath = '';
  3373. }
  3374. pathStr = tag + ((path[i].id) ? '#' + path[i].id : '') + classPath;
  3375. }
  3376. switch (tag) {
  3377. case 'body':
  3378. pathStr = 'body';
  3379. break;
  3380. case 'a':
  3381. if (path[i].getAttribute('href', 2)) {
  3382. pathStr += ':' + path[i].getAttribute('href', 2).replace('mailto:', '').replace('http:/'+'/', '').replace('https:/'+'/', ''); //May need to add others here ftp
  3383. }
  3384. break;
  3385. case 'img':
  3386. var h = path[i].height;
  3387. var w = path[i].width;
  3388. if (path[i].style.height) {
  3389. h = parseInt(path[i].style.height, 10);
  3390. }
  3391. if (path[i].style.width) {
  3392. w = parseInt(path[i].style.width, 10);
  3393. }
  3394. pathStr += '(' + w + 'x' + h + ')';
  3395. break;
  3396. }
  3397. if (pathStr.length > 10) {
  3398. pathStr = '<span title="' + pathStr + '">' + pathStr.substring(0, 10) + '...' + '</span>';
  3399. } else {
  3400. pathStr = '<span title="' + pathStr + '">' + pathStr + '</span>';
  3401. }
  3402. pathArr[pathArr.length] = pathStr;
  3403. }
  3404. }
  3405. var str = pathArr.join(' ' + this.SEP_DOMPATH + ' ');
  3406. //Prevent flickering
  3407. if (this.dompath.innerHTML != str) {
  3408. this.dompath.innerHTML = str;
  3409. }
  3410. },
  3411. /**
  3412. * @private
  3413. * @method _fixNodes
  3414. * @description Fix href and imgs as well as remove invalid HTML.
  3415. */
  3416. _fixNodes: function() {
  3417. try {
  3418. var doc = this._getDoc(),
  3419. els = [];
  3420. for (var v in this.invalidHTML) {
  3421. if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
  3422. if (v.toLowerCase() != 'span') {
  3423. var tags = doc.body.getElementsByTagName(v);
  3424. if (tags.length) {
  3425. for (var i = 0; i < tags.length; i++) {
  3426. els.push(tags[i]);
  3427. }
  3428. }
  3429. }
  3430. }
  3431. }
  3432. for (var h = 0; h < els.length; h++) {
  3433. if (els[h].parentNode) {
  3434. if (Lang.isObject(this.invalidHTML[els[h].tagName.toLowerCase()]) && this.invalidHTML[els[h].tagName.toLowerCase()].keepContents) {
  3435. this._swapEl(els[h], 'span', function(el) {
  3436. el.className = 'yui-non';
  3437. });
  3438. } else {
  3439. els[h].parentNode.removeChild(els[h]);
  3440. }
  3441. }
  3442. }
  3443. var imgs = this._getDoc().getElementsByTagName('img');
  3444. Dom.addClass(imgs, 'yui-img');
  3445. } catch(e) {}
  3446. },
  3447. /**
  3448. * @private
  3449. * @method _isNonEditable
  3450. * @param Event ev The Dom event being checked
  3451. * @description Method is called at the beginning of all event handlers to check if this element or a parent element has the class yui-noedit (this.CLASS_NOEDIT) applied.
  3452. * If it does, then this method will stop the event and return true. The event handlers will then return false and stop the nodeChange from occuring. This method will also
  3453. * disable and enable the Editor's toolbar based on the noedit state.
  3454. * @return Boolean
  3455. */
  3456. _isNonEditable: function(ev) {
  3457. if (this.get('allowNoEdit')) {
  3458. var el = Event.getTarget(ev);
  3459. if (this._isElement(el, 'html')) {
  3460. el = null;
  3461. }
  3462. var path = this._getDomPath(el);
  3463. for (var i = (path.length - 1); i > -1; i--) {
  3464. if (Dom.hasClass(path[i], this.CLASS_NOEDIT)) {
  3465. //if (this.toolbar.get('disabled') === false) {
  3466. // this.toolbar.set('disabled', true);
  3467. //}
  3468. try {
  3469. this._getDoc().execCommand('enableObjectResizing', false, 'false');
  3470. } catch (e) {}
  3471. this.nodeChange();
  3472. Event.stopEvent(ev);
  3473. YAHOO.log('CLASS_NOEDIT found in DOM Path, stopping event', 'info', 'SimpleEditor');
  3474. return true;
  3475. }
  3476. }
  3477. //if (this.toolbar.get('disabled') === true) {
  3478. //Should only happen once..
  3479. //this.toolbar.set('disabled', false);
  3480. try {
  3481. this._getDoc().execCommand('enableObjectResizing', false, 'true');
  3482. } catch (e2) {}
  3483. //}
  3484. }
  3485. return false;
  3486. },
  3487. /**
  3488. * @private
  3489. * @method _setCurrentEvent
  3490. * @param {Event} ev The event to cache
  3491. * @description Sets the current event property
  3492. */
  3493. _setCurrentEvent: function(ev) {
  3494. this.currentEvent = ev;
  3495. },
  3496. /**
  3497. * @private
  3498. * @method _handleClick
  3499. * @param {Event} ev The event we are working on.
  3500. * @description Handles all click events inside the iFrame document.
  3501. */
  3502. _handleClick: function(ev) {
  3503. var ret = this.fireEvent('beforeEditorClick', { type: 'beforeEditorClick', target: this, ev: ev });
  3504. if (ret === false) {
  3505. return false;
  3506. }
  3507. if (this._isNonEditable(ev)) {
  3508. return false;
  3509. }
  3510. this._setCurrentEvent(ev);
  3511. if (this.currentWindow) {
  3512. this.closeWindow();
  3513. }
  3514. if (this.currentWindow) {
  3515. this.closeWindow();
  3516. }
  3517. if (this.browser.webkit) {
  3518. var tar =Event.getTarget(ev);
  3519. if (this._isElement(tar, 'a') || this._isElement(tar.parentNode, 'a')) {
  3520. Event.stopEvent(ev);
  3521. this.nodeChange();
  3522. }
  3523. } else {
  3524. this.nodeChange();
  3525. }
  3526. this.fireEvent('editorClick', { type: 'editorClick', target: this, ev: ev });
  3527. },
  3528. /**
  3529. * @private
  3530. * @method _handleMouseUp
  3531. * @param {Event} ev The event we are working on.
  3532. * @description Handles all mouseup events inside the iFrame document.
  3533. */
  3534. _handleMouseUp: function(ev) {
  3535. var ret = this.fireEvent('beforeEditorMouseUp', { type: 'beforeEditorMouseUp', target: this, ev: ev });
  3536. if (ret === false) {
  3537. return false;
  3538. }
  3539. if (this._isNonEditable(ev)) {
  3540. return false;
  3541. }
  3542. //Don't set current event for mouseup.
  3543. //It get's fired after a menu is closed and gives up a bogus event to work with
  3544. //this._setCurrentEvent(ev);
  3545. var self = this;
  3546. if (this.browser.opera) {
  3547. /*
  3548. * @knownissue Opera appears to stop the MouseDown, Click and DoubleClick events on an image inside of a document with designMode on..
  3549. * @browser Opera
  3550. * @description This work around traps the MouseUp event and sets a timer to check if another MouseUp event fires in so many seconds. If another event is fired, they we internally fire the DoubleClick event.
  3551. */
  3552. var sel = Event.getTarget(ev);
  3553. if (this._isElement(sel, 'img')) {
  3554. this.nodeChange();
  3555. if (this.operaEvent) {
  3556. clearTimeout(this.operaEvent);
  3557. this.operaEvent = null;
  3558. this._handleDoubleClick(ev);
  3559. } else {
  3560. this.operaEvent = window.setTimeout(function() {
  3561. self.operaEvent = false;
  3562. }, 700);
  3563. }
  3564. }
  3565. }
  3566. //This will stop Safari from selecting the entire document if you select all the text in the editor
  3567. if (this.browser.webkit || this.browser.opera) {
  3568. if (this.browser.webkit) {
  3569. Event.stopEvent(ev);
  3570. }
  3571. }
  3572. this.nodeChange();
  3573. this.fireEvent('editorMouseUp', { type: 'editorMouseUp', target: this, ev: ev });
  3574. },
  3575. /**
  3576. * @private
  3577. * @method _handleMouseDown
  3578. * @param {Event} ev The event we are working on.
  3579. * @description Handles all mousedown events inside the iFrame document.
  3580. */
  3581. _handleMouseDown: function(ev) {
  3582. var ret = this.fireEvent('beforeEditorMouseDown', { type: 'beforeEditorMouseDown', target: this, ev: ev });
  3583. if (ret === false) {
  3584. return false;
  3585. }
  3586. if (this._isNonEditable(ev)) {
  3587. return false;
  3588. }
  3589. this._setCurrentEvent(ev);
  3590. var sel = Event.getTarget(ev);
  3591. if (this.browser.webkit && this._hasSelection()) {
  3592. var _sel = this._getSelection();
  3593. if (!this.browser.webkit3) {
  3594. _sel.collapse(true);
  3595. } else {
  3596. _sel.collapseToStart();
  3597. }
  3598. }
  3599. if (this.browser.webkit && this._lastImage) {
  3600. Dom.removeClass(this._lastImage, 'selected');
  3601. this._lastImage = null;
  3602. }
  3603. if (this._isElement(sel, 'img') || this._isElement(sel, 'a')) {
  3604. if (this.browser.webkit) {
  3605. Event.stopEvent(ev);
  3606. if (this._isElement(sel, 'img')) {
  3607. Dom.addClass(sel, 'selected');
  3608. this._lastImage = sel;
  3609. }
  3610. }
  3611. if (this.currentWindow) {
  3612. this.closeWindow();
  3613. }
  3614. this.nodeChange();
  3615. }
  3616. this.fireEvent('editorMouseDown', { type: 'editorMouseDown', target: this, ev: ev });
  3617. },
  3618. /**
  3619. * @private
  3620. * @method _handleDoubleClick
  3621. * @param {Event} ev The event we are working on.
  3622. * @description Handles all doubleclick events inside the iFrame document.
  3623. */
  3624. _handleDoubleClick: function(ev) {
  3625. var ret = this.fireEvent('beforeEditorDoubleClick', { type: 'beforeEditorDoubleClick', target: this, ev: ev });
  3626. if (ret === false) {
  3627. return false;
  3628. }
  3629. if (this._isNonEditable(ev)) {
  3630. return false;
  3631. }
  3632. this._setCurrentEvent(ev);
  3633. var sel = Event.getTarget(ev);
  3634. if (this._isElement(sel, 'img')) {
  3635. this.currentElement[0] = sel;
  3636. this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
  3637. this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
  3638. } else if (this._hasParent(sel, 'a')) { //Handle elements inside an a
  3639. this.currentElement[0] = this._hasParent(sel, 'a');
  3640. this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
  3641. this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
  3642. }
  3643. this.nodeChange();
  3644. this.fireEvent('editorDoubleClick', { type: 'editorDoubleClick', target: this, ev: ev });
  3645. },
  3646. /**
  3647. * @private
  3648. * @method _handleKeyUp
  3649. * @param {Event} ev The event we are working on.
  3650. * @description Handles all keyup events inside the iFrame document.
  3651. */
  3652. _handleKeyUp: function(ev) {
  3653. var ret = this.fireEvent('beforeEditorKeyUp', { type: 'beforeEditorKeyUp', target: this, ev: ev });
  3654. if (ret === false) {
  3655. return false;
  3656. }
  3657. if (this._isNonEditable(ev)) {
  3658. return false;
  3659. }
  3660. this._storeUndo();
  3661. this._setCurrentEvent(ev);
  3662. switch (ev.keyCode) {
  3663. case this._keyMap.SELECT_ALL.key:
  3664. if (this._checkKey(this._keyMap.SELECT_ALL, ev)) {
  3665. this.nodeChange();
  3666. }
  3667. break;
  3668. case 32: //Space Bar
  3669. case 35: //End
  3670. case 36: //Home
  3671. case 37: //Left Arrow
  3672. case 38: //Up Arrow
  3673. case 39: //Right Arrow
  3674. case 40: //Down Arrow
  3675. case 46: //Forward Delete
  3676. case 8: //Delete
  3677. case this._keyMap.CLOSE_WINDOW.key: //W key if window is open
  3678. if ((ev.keyCode == this._keyMap.CLOSE_WINDOW.key) && this.currentWindow) {
  3679. if (this._checkKey(this._keyMap.CLOSE_WINDOW, ev)) {
  3680. this.closeWindow();
  3681. }
  3682. } else {
  3683. if (!this.browser.ie) {
  3684. if (this._nodeChangeTimer) {
  3685. clearTimeout(this._nodeChangeTimer);
  3686. }
  3687. var self = this;
  3688. this._nodeChangeTimer = setTimeout(function() {
  3689. self._nodeChangeTimer = null;
  3690. self.nodeChange.call(self);
  3691. }, 100);
  3692. } else {
  3693. this.nodeChange();
  3694. }
  3695. this.editorDirty = true;
  3696. }
  3697. break;
  3698. }
  3699. this.fireEvent('editorKeyUp', { type: 'editorKeyUp', target: this, ev: ev });
  3700. },
  3701. /**
  3702. * @private
  3703. * @method _handleKeyPress
  3704. * @param {Event} ev The event we are working on.
  3705. * @description Handles all keypress events inside the iFrame document.
  3706. */
  3707. _handleKeyPress: function(ev) {
  3708. var ret = this.fireEvent('beforeEditorKeyPress', { type: 'beforeEditorKeyPress', target: this, ev: ev });
  3709. if (ret === false) {
  3710. return false;
  3711. }
  3712. if (this.get('allowNoEdit')) {
  3713. //if (ev && ev.keyCode && ((ev.keyCode == 46) || ev.keyCode == 63272)) {
  3714. if (ev && ev.keyCode && (ev.keyCode == 63272)) {
  3715. //Forward delete key
  3716. YAHOO.log('allowNoEdit is set, forward delete key has been disabled', 'warn', 'SimpleEditor');
  3717. Event.stopEvent(ev);
  3718. }
  3719. }
  3720. if (this._isNonEditable(ev)) {
  3721. return false;
  3722. }
  3723. this._setCurrentEvent(ev);
  3724. this._storeUndo();
  3725. if (this.browser.opera) {
  3726. if (ev.keyCode === 13) {
  3727. var tar = this._getSelectedElement();
  3728. if (!this._isElement(tar, 'li')) {
  3729. this.execCommand('inserthtml', '<br>');
  3730. Event.stopEvent(ev);
  3731. }
  3732. }
  3733. }
  3734. if (this.browser.webkit) {
  3735. if (!this.browser.webkit3) {
  3736. if (ev.keyCode && (ev.keyCode == 122) && (ev.metaKey)) {
  3737. //This is CMD + z (for undo)
  3738. if (this._hasParent(this._getSelectedElement(), 'li')) {
  3739. YAHOO.log('We are in an LI and we found CMD + z, stopping the event', 'warn', 'SimpleEditor');
  3740. Event.stopEvent(ev);
  3741. }
  3742. }
  3743. }
  3744. this._listFix(ev);
  3745. }
  3746. this._fixListDupIds();
  3747. this.fireEvent('editorKeyPress', { type: 'editorKeyPress', target: this, ev: ev });
  3748. },
  3749. /**
  3750. * @private
  3751. * @method _handleKeyDown
  3752. * @param {Event} ev The event we are working on.
  3753. * @description Handles all keydown events inside the iFrame document.
  3754. */
  3755. _handleKeyDown: function(ev) {
  3756. var ret = this.fireEvent('beforeEditorKeyDown', { type: 'beforeEditorKeyDown', target: this, ev: ev });
  3757. if (ret === false) {
  3758. return false;
  3759. }
  3760. var tar = null, _range = null;
  3761. if (this._isNonEditable(ev)) {
  3762. return false;
  3763. }
  3764. this._setCurrentEvent(ev);
  3765. if (this.currentWindow) {
  3766. this.closeWindow();
  3767. }
  3768. if (this.currentWindow) {
  3769. this.closeWindow();
  3770. }
  3771. var doExec = false,
  3772. action = null,
  3773. value = null,
  3774. exec = false;
  3775. //YAHOO.log('keyCode: ' + ev.keyCode, 'info', 'SimpleEditor');
  3776. switch (ev.keyCode) {
  3777. case this._keyMap.FOCUS_TOOLBAR.key:
  3778. if (this._checkKey(this._keyMap.FOCUS_TOOLBAR, ev)) {
  3779. var h = this.toolbar.getElementsByTagName('h2')[0];
  3780. if (h && h.firstChild) {
  3781. h.firstChild.focus();
  3782. }
  3783. } else if (this._checkKey(this._keyMap.FOCUS_AFTER, ev)) {
  3784. //Focus After Element - Esc
  3785. this.afterElement.focus();
  3786. }
  3787. Event.stopEvent(ev);
  3788. doExec = false;
  3789. break;
  3790. //case 76: //L
  3791. case this._keyMap.CREATE_LINK.key: //L
  3792. if (this._hasSelection()) {
  3793. if (this._checkKey(this._keyMap.CREATE_LINK, ev)) {
  3794. var makeLink = true;
  3795. if (this.get('limitCommands')) {
  3796. if (!this.toolbar.getButtonByValue('createlink')) {
  3797. YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'SimpleEditor');
  3798. makeLink = false;
  3799. }
  3800. }
  3801. if (makeLink) {
  3802. this.execCommand('createlink', '');
  3803. this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
  3804. this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
  3805. doExec = false;
  3806. }
  3807. }
  3808. }
  3809. break;
  3810. //case 90: //Z
  3811. case this._keyMap.UNDO.key:
  3812. case this._keyMap.REDO.key:
  3813. if (this._checkKey(this._keyMap.REDO, ev)) {
  3814. action = 'redo';
  3815. doExec = true;
  3816. } else if (this._checkKey(this._keyMap.UNDO, ev)) {
  3817. action = 'undo';
  3818. doExec = true;
  3819. }
  3820. break;
  3821. //case 66: //B
  3822. case this._keyMap.BOLD.key:
  3823. if (this._checkKey(this._keyMap.BOLD, ev)) {
  3824. action = 'bold';
  3825. doExec = true;
  3826. }
  3827. break;
  3828. case this._keyMap.FONT_SIZE_UP.key:
  3829. case this._keyMap.FONT_SIZE_DOWN.key:
  3830. var uk = false, dk = false;
  3831. if (this._checkKey(this._keyMap.FONT_SIZE_UP, ev)) {
  3832. uk = true;
  3833. }
  3834. if (this._checkKey(this._keyMap.FONT_SIZE_DOWN, ev)) {
  3835. dk = true;
  3836. }
  3837. if (uk || dk) {
  3838. var fs_button = this.toolbar.getButtonByValue('fontsize'),
  3839. label = parseInt(fs_button.get('label'), 10),
  3840. newValue = (label + 1);
  3841. if (dk) {
  3842. newValue = (label - 1);
  3843. }
  3844. action = 'fontsize';
  3845. value = newValue + 'px';
  3846. doExec = true;
  3847. }
  3848. break;
  3849. //case 73: //I
  3850. case this._keyMap.ITALIC.key:
  3851. if (this._checkKey(this._keyMap.ITALIC, ev)) {
  3852. action = 'italic';
  3853. doExec = true;
  3854. }
  3855. break;
  3856. //case 85: //U
  3857. case this._keyMap.UNDERLINE.key:
  3858. if (this._checkKey(this._keyMap.UNDERLINE, ev)) {
  3859. action = 'underline';
  3860. doExec = true;
  3861. }
  3862. break;
  3863. case 9:
  3864. if (this.browser.ie) {
  3865. //Insert a tab in Internet Explorer
  3866. _range = this._getRange();
  3867. tar = this._getSelectedElement();
  3868. if (!this._isElement(tar, 'li')) {
  3869. if (_range) {
  3870. _range.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
  3871. _range.collapse(false);
  3872. _range.select();
  3873. }
  3874. Event.stopEvent(ev);
  3875. }
  3876. }
  3877. //Firefox 3 code
  3878. if (this.browser.gecko > 1.8) {
  3879. tar = this._getSelectedElement();
  3880. if (this._isElement(tar, 'li')) {
  3881. if (ev.shiftKey) {
  3882. this._getDoc().execCommand('outdent', null, '');
  3883. } else {
  3884. this._getDoc().execCommand('indent', null, '');
  3885. }
  3886. } else if (!this._hasSelection()) {
  3887. this.execCommand('inserthtml', '&nbsp;&nbsp;&nbsp;&nbsp;');
  3888. }
  3889. Event.stopEvent(ev);
  3890. }
  3891. break;
  3892. case 13:
  3893. var p = null, i = 0;
  3894. if (this.get('ptags') && !ev.shiftKey) {
  3895. if (this.browser.gecko) {
  3896. tar = this._getSelectedElement();
  3897. if (!this._hasParent(tar, 'li')) {
  3898. if (this._hasParent(tar, 'p')) {
  3899. p = this._getDoc().createElement('p');
  3900. p.innerHTML = '&nbsp;';
  3901. Dom.insertAfter(p, tar);
  3902. this._selectNode(p.firstChild);
  3903. } else if (this._isElement(tar, 'body')) {
  3904. this.execCommand('insertparagraph', null);
  3905. var ps = this._getDoc().body.getElementsByTagName('p');
  3906. for (i = 0; i < ps.length; i++) {
  3907. if (ps[i].getAttribute('_moz_dirty') !== null) {
  3908. p = this._getDoc().createElement('p');
  3909. p.innerHTML = '&nbsp;';
  3910. Dom.insertAfter(p, ps[i]);
  3911. this._selectNode(p.firstChild);
  3912. ps[i].removeAttribute('_moz_dirty');
  3913. }
  3914. }
  3915. } else {
  3916. YAHOO.log('Something went wrong with paragraphs, please file a bug!!', 'error', 'SimpleEditor');
  3917. doExec = true;
  3918. action = 'insertparagraph';
  3919. }
  3920. Event.stopEvent(ev);
  3921. }
  3922. }
  3923. if (this.browser.webkit) {
  3924. tar = this._getSelectedElement();
  3925. if (!this._hasParent(tar, 'li')) {
  3926. this.execCommand('insertparagraph', null);
  3927. var divs = this._getDoc().body.getElementsByTagName('div');
  3928. for (i = 0; i < divs.length; i++) {
  3929. if (!Dom.hasClass(divs[i], 'yui-wk-div')) {
  3930. Dom.addClass(divs[i], 'yui-wk-p');
  3931. }
  3932. }
  3933. Event.stopEvent(ev);
  3934. }
  3935. }
  3936. } else {
  3937. if (this.browser.webkit) {
  3938. tar = this._getSelectedElement();
  3939. if (!this._hasParent(tar, 'li')) {
  3940. if (this.browser.webkit4) {
  3941. this.execCommand('insertlinebreak');
  3942. } else {
  3943. this.execCommand('inserthtml', '<var id="yui-br"></var>');
  3944. var holder = this._getDoc().getElementById('yui-br'),
  3945. br = this._getDoc().createElement('br'),
  3946. caret = this._getDoc().createElement('span');
  3947. holder.parentNode.replaceChild(br, holder);
  3948. caret.className = 'yui-non';
  3949. caret.innerHTML = '&nbsp;';
  3950. Dom.insertAfter(caret, br);
  3951. this._selectNode(caret);
  3952. }
  3953. Event.stopEvent(ev);
  3954. }
  3955. }
  3956. if (this.browser.ie) {
  3957. YAHOO.log('Stopping P tags', 'info', 'SimpleEditor');
  3958. //Insert a <br> instead of a <p></p> in Internet Explorer
  3959. _range = this._getRange();
  3960. tar = this._getSelectedElement();
  3961. if (!this._isElement(tar, 'li')) {
  3962. if (_range) {
  3963. _range.pasteHTML('<br>');
  3964. _range.collapse(false);
  3965. _range.select();
  3966. }
  3967. Event.stopEvent(ev);
  3968. }
  3969. }
  3970. }
  3971. break;
  3972. }
  3973. if (this.browser.ie) {
  3974. this._listFix(ev);
  3975. }
  3976. if (doExec && action) {
  3977. this.execCommand(action, value);
  3978. Event.stopEvent(ev);
  3979. this.nodeChange();
  3980. }
  3981. this._storeUndo();
  3982. this.fireEvent('editorKeyDown', { type: 'editorKeyDown', target: this, ev: ev });
  3983. },
  3984. /**
  3985. * @private
  3986. * @property _fixListRunning
  3987. * @type Boolean
  3988. * @description Keeps more than one _fixListDupIds from running at the same time.
  3989. */
  3990. _fixListRunning: null,
  3991. /**
  3992. * @private
  3993. * @method _fixListDupIds
  3994. * @description Some browsers will duplicate the id of an LI when created in designMode.
  3995. * This method will fix the duplicate id issue. However it will only preserve the first element
  3996. * in the document list with the unique id.
  3997. */
  3998. _fixListDupIds: function() {
  3999. if (this._fixListRunning) {
  4000. return false;
  4001. }
  4002. if (this._getDoc()) {
  4003. this._fixListRunning = true;
  4004. var lis = this._getDoc().body.getElementsByTagName('li'),
  4005. i = 0, ids = {};
  4006. for (i = 0; i < lis.length; i++) {
  4007. if (lis[i].id) {
  4008. if (ids[lis[i].id]) {
  4009. lis[i].id = '';
  4010. }
  4011. ids[lis[i].id] = true;
  4012. }
  4013. }
  4014. this._fixListRunning = false;
  4015. }
  4016. },
  4017. /**
  4018. * @private
  4019. * @method _listFix
  4020. * @param {Event} ev The event we are working on.
  4021. * @description Handles the Enter key, Tab Key and Shift + Tab keys for List Items.
  4022. */
  4023. _listFix: function(ev) {
  4024. //YAHOO.log('Lists Fix (' + ev.keyCode + ')', 'info', 'SimpleEditor');
  4025. var testLi = null, par = null, preContent = false, range = null;
  4026. //Enter Key
  4027. if (this.browser.webkit) {
  4028. if (ev.keyCode && (ev.keyCode == 13)) {
  4029. if (this._hasParent(this._getSelectedElement(), 'li')) {
  4030. var tar = this._hasParent(this._getSelectedElement(), 'li');
  4031. if (tar.previousSibling) {
  4032. if (tar.firstChild && (tar.firstChild.length == 1)) {
  4033. this._selectNode(tar);
  4034. }
  4035. }
  4036. }
  4037. }
  4038. }
  4039. //Shift + Tab Key
  4040. if (ev.keyCode && ((!this.browser.webkit3 && (ev.keyCode == 25)) || ((this.browser.webkit3 || !this.browser.webkit) && ((ev.keyCode == 9) && ev.shiftKey)))) {
  4041. testLi = this._getSelectedElement();
  4042. if (this._hasParent(testLi, 'li')) {
  4043. testLi = this._hasParent(testLi, 'li');
  4044. YAHOO.log('We have a SHIFT tab in an LI, reverse it..', 'info', 'SimpleEditor');
  4045. if (this._hasParent(testLi, 'ul') || this._hasParent(testLi, 'ol')) {
  4046. YAHOO.log('We have a double parent, move up a level', 'info', 'SimpleEditor');
  4047. par = this._hasParent(testLi, 'ul');
  4048. if (!par) {
  4049. par = this._hasParent(testLi, 'ol');
  4050. }
  4051. //YAHOO.log(par.previousSibling + ' :: ' + par.previousSibling.innerHTML);
  4052. if (this._isElement(par.previousSibling, 'li')) {
  4053. par.removeChild(testLi);
  4054. par.parentNode.insertBefore(testLi, par.nextSibling);
  4055. if (this.browser.ie) {
  4056. range = this._getDoc().body.createTextRange();
  4057. range.moveToElementText(testLi);
  4058. range.collapse(false);
  4059. range.select();
  4060. }
  4061. if (this.browser.webkit) {
  4062. this._selectNode(testLi.firstChild);
  4063. }
  4064. Event.stopEvent(ev);
  4065. }
  4066. }
  4067. }
  4068. }
  4069. //Tab Key
  4070. if (ev.keyCode && ((ev.keyCode == 9) && (!ev.shiftKey))) {
  4071. YAHOO.log('List Fix - Tab', 'info', 'SimpleEditor');
  4072. var preLi = this._getSelectedElement();
  4073. if (this._hasParent(preLi, 'li')) {
  4074. preContent = this._hasParent(preLi, 'li').innerHTML;
  4075. }
  4076. //YAHOO.log('preLI: ' + preLi.tagName + ' :: ' + preLi.innerHTML);
  4077. if (this.browser.webkit) {
  4078. this._getDoc().execCommand('inserttext', false, '\t');
  4079. }
  4080. testLi = this._getSelectedElement();
  4081. if (this._hasParent(testLi, 'li')) {
  4082. YAHOO.log('We have a tab in an LI', 'info', 'SimpleEditor');
  4083. par = this._hasParent(testLi, 'li');
  4084. YAHOO.log('parLI: ' + par.tagName + ' :: ' + par.innerHTML);
  4085. var newUl = this._getDoc().createElement(par.parentNode.tagName.toLowerCase());
  4086. if (this.browser.webkit) {
  4087. var span = Dom.getElementsByClassName('Apple-tab-span', 'span', par);
  4088. //Remove the span element that Safari puts in
  4089. if (span[0]) {
  4090. par.removeChild(span[0]);
  4091. par.innerHTML = Lang.trim(par.innerHTML);
  4092. //Put the HTML from the LI into this new LI
  4093. if (preContent) {
  4094. par.innerHTML = '<span class="yui-non">' + preContent + '</span>&nbsp;';
  4095. } else {
  4096. par.innerHTML = '<span class="yui-non">&nbsp;</span>&nbsp;';
  4097. }
  4098. }
  4099. } else {
  4100. if (preContent) {
  4101. par.innerHTML = preContent + '&nbsp;';
  4102. } else {
  4103. par.innerHTML = '&nbsp;';
  4104. }
  4105. }
  4106. par.parentNode.replaceChild(newUl, par);
  4107. newUl.appendChild(par);
  4108. if (this.browser.webkit) {
  4109. this._getSelection().setBaseAndExtent(par.firstChild, 1, par.firstChild, par.firstChild.innerText.length);
  4110. if (!this.browser.webkit3) {
  4111. par.parentNode.parentNode.style.display = 'list-item';
  4112. setTimeout(function() {
  4113. par.parentNode.parentNode.style.display = 'block';
  4114. }, 1);
  4115. }
  4116. } else if (this.browser.ie) {
  4117. range = this._getDoc().body.createTextRange();
  4118. range.moveToElementText(par);
  4119. range.collapse(false);
  4120. range.select();
  4121. } else {
  4122. this._selectNode(par);
  4123. }
  4124. Event.stopEvent(ev);
  4125. }
  4126. if (this.browser.webkit) {
  4127. Event.stopEvent(ev);
  4128. }
  4129. this.nodeChange();
  4130. }
  4131. },
  4132. /**
  4133. * @method nodeChange
  4134. * @param {Boolean} force Optional paramenter to skip the threshold counter
  4135. * @description Handles setting up the toolbar buttons, getting the Dom path, fixing nodes.
  4136. */
  4137. nodeChange: function(force) {
  4138. var NCself = this;
  4139. this._storeUndo();
  4140. if (this.get('nodeChangeDelay')) {
  4141. this._nodeChangeDelayTimer = window.setTimeout(function() {
  4142. NCself._nodeChangeDelayTimer = null;
  4143. NCself._nodeChange.apply(NCself, arguments);
  4144. }, 0);
  4145. } else {
  4146. this._nodeChange();
  4147. }
  4148. },
  4149. /**
  4150. * @private
  4151. * @method _nodeChange
  4152. * @param {Boolean} force Optional paramenter to skip the threshold counter
  4153. * @description Fired from nodeChange in a setTimeout.
  4154. */
  4155. _nodeChange: function(force) {
  4156. var threshold = parseInt(this.get('nodeChangeThreshold'), 10),
  4157. thisNodeChange = Math.round(new Date().getTime() / 1000),
  4158. self = this;
  4159. if (force === true) {
  4160. this._lastNodeChange = 0;
  4161. }
  4162. if ((this._lastNodeChange + threshold) < thisNodeChange) {
  4163. if (this._fixNodesTimer === null) {
  4164. this._fixNodesTimer = window.setTimeout(function() {
  4165. self._fixNodes.call(self);
  4166. self._fixNodesTimer = null;
  4167. }, 0);
  4168. }
  4169. }
  4170. this._lastNodeChange = thisNodeChange;
  4171. if (this.currentEvent) {
  4172. try {
  4173. this._lastNodeChangeEvent = this.currentEvent.type;
  4174. } catch (e) {}
  4175. }
  4176. var beforeNodeChange = this.fireEvent('beforeNodeChange', { type: 'beforeNodeChange', target: this });
  4177. if (beforeNodeChange === false) {
  4178. return false;
  4179. }
  4180. if (this.get('dompath')) {
  4181. window.setTimeout(function() {
  4182. self._writeDomPath.call(self);
  4183. }, 0);
  4184. }
  4185. //Check to see if we are disabled before continuing
  4186. if (!this.get('disabled')) {
  4187. if (this.STOP_NODE_CHANGE) {
  4188. //Reset this var for next action
  4189. this.STOP_NODE_CHANGE = false;
  4190. return false;
  4191. } else {
  4192. var sel = this._getSelection(),
  4193. range = this._getRange(),
  4194. el = this._getSelectedElement(),
  4195. fn_button = this.toolbar.getButtonByValue('fontname'),
  4196. fs_button = this.toolbar.getButtonByValue('fontsize'),
  4197. undo_button = this.toolbar.getButtonByValue('undo'),
  4198. redo_button = this.toolbar.getButtonByValue('redo');
  4199. //Handle updating the toolbar with active buttons
  4200. var _ex = {};
  4201. if (this._lastButton) {
  4202. _ex[this._lastButton.id] = true;
  4203. //this._lastButton = null;
  4204. }
  4205. if (!this._isElement(el, 'body')) {
  4206. if (fn_button) {
  4207. _ex[fn_button.get('id')] = true;
  4208. }
  4209. if (fs_button) {
  4210. _ex[fs_button.get('id')] = true;
  4211. }
  4212. }
  4213. if (redo_button) {
  4214. delete _ex[redo_button.get('id')];
  4215. }
  4216. this.toolbar.resetAllButtons(_ex);
  4217. //Handle disabled buttons
  4218. for (var d = 0; d < this._disabled.length; d++) {
  4219. var _button = this.toolbar.getButtonByValue(this._disabled[d]);
  4220. if (_button && _button.get) {
  4221. if (this._lastButton && (_button.get('id') === this._lastButton.id)) {
  4222. //Skip
  4223. } else {
  4224. if (!this._hasSelection() && !this.get('insert')) {
  4225. switch (this._disabled[d]) {
  4226. case 'fontname':
  4227. case 'fontsize':
  4228. break;
  4229. default:
  4230. //No Selection - disable
  4231. this.toolbar.disableButton(_button);
  4232. }
  4233. } else {
  4234. if (!this._alwaysDisabled[this._disabled[d]]) {
  4235. this.toolbar.enableButton(_button);
  4236. }
  4237. }
  4238. if (!this._alwaysEnabled[this._disabled[d]]) {
  4239. this.toolbar.deselectButton(_button);
  4240. }
  4241. }
  4242. }
  4243. }
  4244. var path = this._getDomPath();
  4245. var tag = null, cmd = null;
  4246. for (var i = 0; i < path.length; i++) {
  4247. tag = path[i].tagName.toLowerCase();
  4248. if (path[i].getAttribute('tag')) {
  4249. tag = path[i].getAttribute('tag').toLowerCase();
  4250. }
  4251. cmd = this._tag2cmd[tag];
  4252. if (cmd === undefined) {
  4253. cmd = [];
  4254. }
  4255. if (!Lang.isArray(cmd)) {
  4256. cmd = [cmd];
  4257. }
  4258. //Bold and Italic styles
  4259. if (path[i].style.fontWeight.toLowerCase() == 'bold') {
  4260. cmd[cmd.length] = 'bold';
  4261. }
  4262. if (path[i].style.fontStyle.toLowerCase() == 'italic') {
  4263. cmd[cmd.length] = 'italic';
  4264. }
  4265. if (path[i].style.textDecoration.toLowerCase() == 'underline') {
  4266. cmd[cmd.length] = 'underline';
  4267. }
  4268. if (path[i].style.textDecoration.toLowerCase() == 'line-through') {
  4269. cmd[cmd.length] = 'strikethrough';
  4270. }
  4271. if (cmd.length > 0) {
  4272. for (var j = 0; j < cmd.length; j++) {
  4273. this.toolbar.selectButton(cmd[j]);
  4274. this.toolbar.enableButton(cmd[j]);
  4275. }
  4276. }
  4277. //Handle Alignment
  4278. switch (path[i].style.textAlign.toLowerCase()) {
  4279. case 'left':
  4280. case 'right':
  4281. case 'center':
  4282. case 'justify':
  4283. var alignType = path[i].style.textAlign.toLowerCase();
  4284. if (path[i].style.textAlign.toLowerCase() == 'justify') {
  4285. alignType = 'full';
  4286. }
  4287. this.toolbar.selectButton('justify' + alignType);
  4288. this.toolbar.enableButton('justify' + alignType);
  4289. break;
  4290. }
  4291. }
  4292. //After for loop
  4293. //Reset Font Family and Size to the inital configs
  4294. if (fn_button) {
  4295. var family = fn_button._configs.label._initialConfig.value;
  4296. fn_button.set('label', '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>');
  4297. this._updateMenuChecked('fontname', family);
  4298. }
  4299. if (fs_button) {
  4300. fs_button.set('label', fs_button._configs.label._initialConfig.value);
  4301. }
  4302. var hd_button = this.toolbar.getButtonByValue('heading');
  4303. if (hd_button) {
  4304. hd_button.set('label', hd_button._configs.label._initialConfig.value);
  4305. this._updateMenuChecked('heading', 'none');
  4306. }
  4307. var img_button = this.toolbar.getButtonByValue('insertimage');
  4308. if (img_button && this.currentWindow && (this.currentWindow.name == 'insertimage')) {
  4309. this.toolbar.disableButton(img_button);
  4310. }
  4311. if (this._lastButton && this._lastButton.isSelected) {
  4312. this.toolbar.deselectButton(this._lastButton.id);
  4313. }
  4314. this._undoNodeChange();
  4315. }
  4316. }
  4317. this.fireEvent('afterNodeChange', { type: 'afterNodeChange', target: this });
  4318. },
  4319. /**
  4320. * @private
  4321. * @method _updateMenuChecked
  4322. * @param {Object} button The command identifier of the button you want to check
  4323. * @param {String} value The value of the menu item you want to check
  4324. * @param {<a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>} The Toolbar instance the button belongs to (defaults to this.toolbar)
  4325. * @description Gets the menu from a button instance, if the menu is not rendered it will render it. It will then search the menu for the specified value, unchecking all other items and checking the specified on.
  4326. */
  4327. _updateMenuChecked: function(button, value, tbar) {
  4328. if (!tbar) {
  4329. tbar = this.toolbar;
  4330. }
  4331. var _button = tbar.getButtonByValue(button);
  4332. _button.checkValue(value);
  4333. },
  4334. /**
  4335. * @private
  4336. * @method _handleToolbarClick
  4337. * @param {Event} ev The event that triggered the button click
  4338. * @description This is an event handler attached to the Toolbar's buttonClick event. It will fire execCommand with the command identifier from the Toolbar Button.
  4339. */
  4340. _handleToolbarClick: function(ev) {
  4341. var value = '';
  4342. var str = '';
  4343. var cmd = ev.button.value;
  4344. if (ev.button.menucmd) {
  4345. value = cmd;
  4346. cmd = ev.button.menucmd;
  4347. }
  4348. this._lastButton = ev.button;
  4349. if (this.STOP_EXEC_COMMAND) {
  4350. YAHOO.log('execCommand skipped because we found the STOP_EXEC_COMMAND flag set to true', 'warn', 'SimpleEditor');
  4351. YAHOO.log('NOEXEC::execCommand::(' + cmd + '), (' + value + ')', 'warn', 'SimpleEditor');
  4352. this.STOP_EXEC_COMMAND = false;
  4353. return false;
  4354. } else {
  4355. this.execCommand(cmd, value);
  4356. if (!this.browser.webkit) {
  4357. var Fself = this;
  4358. setTimeout(function() {
  4359. Fself.focus.call(Fself);
  4360. }, 5);
  4361. }
  4362. }
  4363. Event.stopEvent(ev);
  4364. },
  4365. /**
  4366. * @private
  4367. * @method _setupAfterElement
  4368. * @description Creates the accessibility h2 header and places it after the iframe in the Dom for navigation.
  4369. */
  4370. _setupAfterElement: function() {
  4371. if (!this.beforeElement) {
  4372. this.beforeElement = document.createElement('h2');
  4373. this.beforeElement.className = 'yui-editor-skipheader';
  4374. this.beforeElement.tabIndex = '-1';
  4375. this.beforeElement.innerHTML = this.STR_BEFORE_EDITOR;
  4376. this.get('element_cont').get('firstChild').insertBefore(this.beforeElement, this.toolbar.get('nextSibling'));
  4377. }
  4378. if (!this.afterElement) {
  4379. this.afterElement = document.createElement('h2');
  4380. this.afterElement.className = 'yui-editor-skipheader';
  4381. this.afterElement.tabIndex = '-1';
  4382. this.afterElement.innerHTML = this.STR_LEAVE_EDITOR;
  4383. this.get('element_cont').get('firstChild').appendChild(this.afterElement);
  4384. }
  4385. },
  4386. /**
  4387. * @private
  4388. * @method _disableEditor
  4389. * @param {Boolean} disabled Pass true to disable, false to enable
  4390. * @description Creates a mask to place over the Editor.
  4391. */
  4392. _disableEditor: function(disabled) {
  4393. var iframe, par, html, height;
  4394. if (!this.get('disabled_iframe')) {
  4395. iframe = this._createIframe();
  4396. iframe.set('id', 'disabled_' + this.get('iframe').get('id'));
  4397. iframe.setStyle('height', '100%');
  4398. iframe.setStyle('display', 'none');
  4399. iframe.setStyle('visibility', 'visible');
  4400. this.set('disabled_iframe', iframe);
  4401. par = this.get('iframe').get('parentNode');
  4402. par.appendChild(iframe.get('element'));
  4403. }
  4404. if (!iframe) {
  4405. iframe = this.get('disabled_iframe');
  4406. }
  4407. if (disabled) {
  4408. this._orgIframe = this.get('iframe');
  4409. if (this.toolbar) {
  4410. this.toolbar.set('disabled', true);
  4411. }
  4412. html = this.getEditorHTML();
  4413. height = this.get('iframe').get('offsetHeight');
  4414. iframe.setStyle('visibility', '');
  4415. iframe.setStyle('position', '');
  4416. iframe.setStyle('top', '');
  4417. iframe.setStyle('left', '');
  4418. this._orgIframe.setStyle('visibility', 'hidden');
  4419. this._orgIframe.setStyle('position', 'absolute');
  4420. this._orgIframe.setStyle('top', '-99999px');
  4421. this._orgIframe.setStyle('left', '-99999px');
  4422. this.set('iframe', iframe);
  4423. this._setInitialContent(true);
  4424. if (!this._mask) {
  4425. this._mask = document.createElement('DIV');
  4426. Dom.addClass(this._mask, 'yui-editor-masked');
  4427. if (this.browser.ie) {
  4428. this._mask.style.height = height + 'px';
  4429. }
  4430. this.get('iframe').get('parentNode').appendChild(this._mask);
  4431. }
  4432. this.on('editorContentReloaded', function() {
  4433. this._getDoc().body._rteLoaded = false;
  4434. this.setEditorHTML(html);
  4435. iframe.setStyle('display', 'block');
  4436. this.unsubscribeAll('editorContentReloaded');
  4437. });
  4438. } else {
  4439. if (this._mask) {
  4440. this._mask.parentNode.removeChild(this._mask);
  4441. this._mask = null;
  4442. if (this.toolbar) {
  4443. this.toolbar.set('disabled', false);
  4444. }
  4445. iframe.setStyle('visibility', 'hidden');
  4446. iframe.setStyle('position', 'absolute');
  4447. iframe.setStyle('top', '-99999px');
  4448. iframe.setStyle('left', '-99999px');
  4449. this._orgIframe.setStyle('visibility', '');
  4450. this._orgIframe.setStyle('position', '');
  4451. this._orgIframe.setStyle('top', '');
  4452. this._orgIframe.setStyle('left', '');
  4453. this.set('iframe', this._orgIframe);
  4454. this.focus();
  4455. var self = this;
  4456. window.setTimeout(function() {
  4457. self.nodeChange.call(self);
  4458. }, 100);
  4459. }
  4460. }
  4461. },
  4462. /**
  4463. * @property SEP_DOMPATH
  4464. * @description The value to place in between the Dom path items
  4465. * @type String
  4466. */
  4467. SEP_DOMPATH: '<',
  4468. /**
  4469. * @property STR_LEAVE_EDITOR
  4470. * @description The accessibility string for the element after the iFrame
  4471. * @type String
  4472. */
  4473. STR_LEAVE_EDITOR: 'You have left the Rich Text Editor.',
  4474. /**
  4475. * @property STR_BEFORE_EDITOR
  4476. * @description The accessibility string for the element before the iFrame
  4477. * @type String
  4478. */
  4479. STR_BEFORE_EDITOR: 'This text field can contain stylized text and graphics. To cycle through all formatting options, use the keyboard shortcut Shift + Escape to place focus on the toolbar and navigate between options with your arrow keys. To exit this text editor use the Escape key and continue tabbing. <h4>Common formatting keyboard shortcuts:</h4><ul><li>Control Shift B sets text to bold</li> <li>Control Shift I sets text to italic</li> <li>Control Shift U underlines text</li> <li>Control Shift L adds an HTML link</li></ul>',
  4480. /**
  4481. * @property STR_TITLE
  4482. * @description The Title of the HTML document that is created in the iFrame
  4483. * @type String
  4484. */
  4485. STR_TITLE: 'Rich Text Area.',
  4486. /**
  4487. * @property STR_IMAGE_HERE
  4488. * @description The text to place in the URL textbox when using the blankimage.
  4489. * @type String
  4490. */
  4491. STR_IMAGE_HERE: 'Image URL Here',
  4492. /**
  4493. * @property STR_IMAGE_URL
  4494. * @description The label string for Image URL
  4495. * @type String
  4496. */
  4497. STR_IMAGE_URL: 'Image URL',
  4498. /**
  4499. * @property STR_LINK_URL
  4500. * @description The label string for the Link URL.
  4501. * @type String
  4502. */
  4503. STR_LINK_URL: 'Link URL',
  4504. /**
  4505. * @protected
  4506. * @property STOP_EXEC_COMMAND
  4507. * @description Set to true when you want the default execCommand function to not process anything
  4508. * @type Boolean
  4509. */
  4510. STOP_EXEC_COMMAND: false,
  4511. /**
  4512. * @protected
  4513. * @property STOP_NODE_CHANGE
  4514. * @description Set to true when you want the default nodeChange function to not process anything
  4515. * @type Boolean
  4516. */
  4517. STOP_NODE_CHANGE: false,
  4518. /**
  4519. * @protected
  4520. * @property CLASS_NOEDIT
  4521. * @description CSS class applied to elements that are not editable.
  4522. * @type String
  4523. */
  4524. CLASS_NOEDIT: 'yui-noedit',
  4525. /**
  4526. * @protected
  4527. * @property CLASS_CONTAINER
  4528. * @description Default CSS class to apply to the editors container element
  4529. * @type String
  4530. */
  4531. CLASS_CONTAINER: 'yui-editor-container',
  4532. /**
  4533. * @protected
  4534. * @property CLASS_EDITABLE
  4535. * @description Default CSS class to apply to the editors iframe element
  4536. * @type String
  4537. */
  4538. CLASS_EDITABLE: 'yui-editor-editable',
  4539. /**
  4540. * @protected
  4541. * @property CLASS_EDITABLE_CONT
  4542. * @description Default CSS class to apply to the editors iframe's parent element
  4543. * @type String
  4544. */
  4545. CLASS_EDITABLE_CONT: 'yui-editor-editable-container',
  4546. /**
  4547. * @protected
  4548. * @property CLASS_PREFIX
  4549. * @description Default prefix for dynamically created class names
  4550. * @type String
  4551. */
  4552. CLASS_PREFIX: 'yui-editor',
  4553. /**
  4554. * @property browser
  4555. * @description Standard browser detection
  4556. * @type Object
  4557. */
  4558. browser: function() {
  4559. var br = YAHOO.env.ua;
  4560. //Check for webkit3
  4561. if (br.webkit >= 420) {
  4562. br.webkit3 = br.webkit;
  4563. } else {
  4564. br.webkit3 = 0;
  4565. }
  4566. if (br.webkit >= 530) {
  4567. br.webkit4 = br.webkit;
  4568. } else {
  4569. br.webkit4 = 0;
  4570. }
  4571. br.mac = false;
  4572. //Check for Mac
  4573. if (navigator.userAgent.indexOf('Macintosh') !== -1) {
  4574. br.mac = true;
  4575. }
  4576. return br;
  4577. }(),
  4578. /**
  4579. * @method init
  4580. * @description The Editor class' initialization method
  4581. */
  4582. init: function(p_oElement, p_oAttributes) {
  4583. YAHOO.log('init', 'info', 'SimpleEditor');
  4584. if (!this._defaultToolbar) {
  4585. this._defaultToolbar = {
  4586. collapse: true,
  4587. titlebar: 'Text Editing Tools',
  4588. draggable: false,
  4589. buttons: [
  4590. { group: 'fontstyle', label: 'Font Name and Size',
  4591. buttons: [
  4592. { type: 'select', label: 'Arial', value: 'fontname', disabled: true,
  4593. menu: [
  4594. { text: 'Arial', checked: true },
  4595. { text: 'Arial Black' },
  4596. { text: 'Comic Sans MS' },
  4597. { text: 'Courier New' },
  4598. { text: 'Lucida Console' },
  4599. { text: 'Tahoma' },
  4600. { text: 'Times New Roman' },
  4601. { text: 'Trebuchet MS' },
  4602. { text: 'Verdana' }
  4603. ]
  4604. },
  4605. { type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
  4606. ]
  4607. },
  4608. { type: 'separator' },
  4609. { group: 'textstyle', label: 'Font Style',
  4610. buttons: [
  4611. { type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
  4612. { type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
  4613. { type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
  4614. { type: 'push', label: 'Strike Through', value: 'strikethrough' },
  4615. { type: 'separator' },
  4616. { type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
  4617. { type: 'color', label: 'Background Color', value: 'backcolor', disabled: true }
  4618. ]
  4619. },
  4620. { type: 'separator' },
  4621. { group: 'indentlist', label: 'Lists',
  4622. buttons: [
  4623. { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
  4624. { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
  4625. ]
  4626. },
  4627. { type: 'separator' },
  4628. { group: 'insertitem', label: 'Insert Item',
  4629. buttons: [
  4630. { type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
  4631. { type: 'push', label: 'Insert Image', value: 'insertimage' }
  4632. ]
  4633. }
  4634. ]
  4635. };
  4636. }
  4637. YAHOO.widget.SimpleEditor.superclass.init.call(this, p_oElement, p_oAttributes);
  4638. YAHOO.widget.EditorInfo._instances[this.get('id')] = this;
  4639. this.currentElement = [];
  4640. this.on('contentReady', function() {
  4641. this.DOMReady = true;
  4642. this.fireQueue();
  4643. }, this, true);
  4644. },
  4645. /**
  4646. * @method initAttributes
  4647. * @description Initializes all of the configuration attributes used to create
  4648. * the editor.
  4649. * @param {Object} attr Object literal specifying a set of
  4650. * configuration attributes used to create the editor.
  4651. */
  4652. initAttributes: function(attr) {
  4653. YAHOO.widget.SimpleEditor.superclass.initAttributes.call(this, attr);
  4654. var self = this;
  4655. /**
  4656. * @config setDesignMode
  4657. * @description Should the Editor set designMode on the document. Default: true.
  4658. * @default true
  4659. * @type Boolean
  4660. */
  4661. this.setAttributeConfig('setDesignMode', {
  4662. value: ((attr.setDesignMode === false) ? false : true)
  4663. });
  4664. /**
  4665. * @config nodeChangeDelay
  4666. * @description Do we wrap the nodeChange method in a timeout for performance. Default: true.
  4667. * @default true
  4668. * @type Boolean
  4669. */
  4670. this.setAttributeConfig('nodeChangeDelay', {
  4671. value: ((attr.nodeChangeDelay === false) ? false : true)
  4672. });
  4673. /**
  4674. * @config maxUndo
  4675. * @description The max number of undo levels to store.
  4676. * @default 30
  4677. * @type Number
  4678. */
  4679. this.setAttributeConfig('maxUndo', {
  4680. writeOnce: true,
  4681. value: attr.maxUndo || 30
  4682. });
  4683. /**
  4684. * @config ptags
  4685. * @description If true, the editor uses &lt;P&gt; tags instead of &lt;br&gt; tags. (Use Shift + Enter to get a &lt;br&gt;)
  4686. * @default false
  4687. * @type Boolean
  4688. */
  4689. this.setAttributeConfig('ptags', {
  4690. writeOnce: true,
  4691. value: attr.ptags || false
  4692. });
  4693. /**
  4694. * @config insert
  4695. * @description If true, selection is not required for: fontname, fontsize, forecolor, backcolor.
  4696. * @default false
  4697. * @type Boolean
  4698. */
  4699. this.setAttributeConfig('insert', {
  4700. writeOnce: true,
  4701. value: attr.insert || false,
  4702. method: function(insert) {
  4703. if (insert) {
  4704. var buttons = {
  4705. fontname: true,
  4706. fontsize: true,
  4707. forecolor: true,
  4708. backcolor: true
  4709. };
  4710. var tmp = this._defaultToolbar.buttons;
  4711. for (var i = 0; i < tmp.length; i++) {
  4712. if (tmp[i].buttons) {
  4713. for (var a = 0; a < tmp[i].buttons.length; a++) {
  4714. if (tmp[i].buttons[a].value) {
  4715. if (buttons[tmp[i].buttons[a].value]) {
  4716. delete tmp[i].buttons[a].disabled;
  4717. }
  4718. }
  4719. }
  4720. }
  4721. }
  4722. }
  4723. }
  4724. });
  4725. /**
  4726. * @config container
  4727. * @description Used when dynamically creating the Editor from Javascript with no default textarea.
  4728. * We will create one and place it in this container. If no container is passed we will append to document.body.
  4729. * @default false
  4730. * @type HTMLElement
  4731. */
  4732. this.setAttributeConfig('container', {
  4733. writeOnce: true,
  4734. value: attr.container || false
  4735. });
  4736. /**
  4737. * @config plainText
  4738. * @description Process the inital textarea data as if it was plain text. Accounting for spaces, tabs and line feeds.
  4739. * @default false
  4740. * @type Boolean
  4741. */
  4742. this.setAttributeConfig('plainText', {
  4743. writeOnce: true,
  4744. value: attr.plainText || false
  4745. });
  4746. /**
  4747. * @private
  4748. * @config iframe
  4749. * @description Internal config for holding the iframe element.
  4750. * @default null
  4751. * @type HTMLElement
  4752. */
  4753. this.setAttributeConfig('iframe', {
  4754. value: null
  4755. });
  4756. /**
  4757. * @private
  4758. * @config disabled_iframe
  4759. * @description Internal config for holding the iframe element used when disabling the Editor.
  4760. * @default null
  4761. * @type HTMLElement
  4762. */
  4763. this.setAttributeConfig('disabled_iframe', {
  4764. value: null
  4765. });
  4766. /**
  4767. * @private
  4768. * @depreciated - No longer used, should use this.get('element')
  4769. * @config textarea
  4770. * @description Internal config for holding the textarea element (replaced with element).
  4771. * @default null
  4772. * @type HTMLElement
  4773. */
  4774. this.setAttributeConfig('textarea', {
  4775. value: null,
  4776. writeOnce: true
  4777. });
  4778. /**
  4779. * @config nodeChangeThreshold
  4780. * @description The number of seconds that need to be in between nodeChange processing
  4781. * @default 3
  4782. * @type Number
  4783. */
  4784. this.setAttributeConfig('nodeChangeThreshold', {
  4785. value: attr.nodeChangeThreshold || 3,
  4786. validator: YAHOO.lang.isNumber
  4787. });
  4788. /**
  4789. * @config allowNoEdit
  4790. * @description Should the editor check for non-edit fields. It should be noted that this technique is not perfect. If the user does the right things, they will still be able to make changes.
  4791. * Such as highlighting an element below and above the content and hitting a toolbar button or a shortcut key.
  4792. * @default false
  4793. * @type Boolean
  4794. */
  4795. this.setAttributeConfig('allowNoEdit', {
  4796. value: attr.allowNoEdit || false,
  4797. validator: YAHOO.lang.isBoolean
  4798. });
  4799. /**
  4800. * @config limitCommands
  4801. * @description Should the Editor limit the allowed execCommands to the ones available in the toolbar. If true, then execCommand and keyboard shortcuts will fail if they are not defined in the toolbar.
  4802. * @default false
  4803. * @type Boolean
  4804. */
  4805. this.setAttributeConfig('limitCommands', {
  4806. value: attr.limitCommands || false,
  4807. validator: YAHOO.lang.isBoolean
  4808. });
  4809. /**
  4810. * @config element_cont
  4811. * @description Internal config for the editors container
  4812. * @default false
  4813. * @type HTMLElement
  4814. */
  4815. this.setAttributeConfig('element_cont', {
  4816. value: attr.element_cont
  4817. });
  4818. /**
  4819. * @private
  4820. * @config editor_wrapper
  4821. * @description The outter wrapper for the entire editor.
  4822. * @default null
  4823. * @type HTMLElement
  4824. */
  4825. this.setAttributeConfig('editor_wrapper', {
  4826. value: attr.editor_wrapper || null,
  4827. writeOnce: true
  4828. });
  4829. /**
  4830. * @attribute height
  4831. * @description The height of the editor iframe container, not including the toolbar..
  4832. * @default Best guessed size of the textarea, for best results use CSS to style the height of the textarea or pass it in as an argument
  4833. * @type String
  4834. */
  4835. this.setAttributeConfig('height', {
  4836. value: attr.height || Dom.getStyle(self.get('element'), 'height'),
  4837. method: function(height) {
  4838. if (this._rendered) {
  4839. //We have been rendered, change the height
  4840. if (this.get('animate')) {
  4841. var anim = new YAHOO.util.Anim(this.get('iframe').get('parentNode'), {
  4842. height: {
  4843. to: parseInt(height, 10)
  4844. }
  4845. }, 0.5);
  4846. anim.animate();
  4847. } else {
  4848. Dom.setStyle(this.get('iframe').get('parentNode'), 'height', height);
  4849. }
  4850. }
  4851. }
  4852. });
  4853. /**
  4854. * @config autoHeight
  4855. * @description Remove the scrollbars from the edit area and resize it to fit the content. It will not go any lower than the current config height.
  4856. * @default false
  4857. * @type Boolean || Number
  4858. */
  4859. this.setAttributeConfig('autoHeight', {
  4860. value: attr.autoHeight || false,
  4861. method: function(a) {
  4862. if (a) {
  4863. if (this.get('iframe')) {
  4864. this.get('iframe').get('element').setAttribute('scrolling', 'no');
  4865. }
  4866. this.on('afterNodeChange', this._handleAutoHeight, this, true);
  4867. this.on('editorKeyDown', this._handleAutoHeight, this, true);
  4868. this.on('editorKeyPress', this._handleAutoHeight, this, true);
  4869. } else {
  4870. if (this.get('iframe')) {
  4871. this.get('iframe').get('element').setAttribute('scrolling', 'auto');
  4872. }
  4873. this.unsubscribe('afterNodeChange', this._handleAutoHeight);
  4874. this.unsubscribe('editorKeyDown', this._handleAutoHeight);
  4875. this.unsubscribe('editorKeyPress', this._handleAutoHeight);
  4876. }
  4877. }
  4878. });
  4879. /**
  4880. * @attribute width
  4881. * @description The width of the editor container.
  4882. * @default Best guessed size of the textarea, for best results use CSS to style the width of the textarea or pass it in as an argument
  4883. * @type String
  4884. */
  4885. this.setAttributeConfig('width', {
  4886. value: attr.width || Dom.getStyle(this.get('element'), 'width'),
  4887. method: function(width) {
  4888. if (this._rendered) {
  4889. //We have been rendered, change the width
  4890. if (this.get('animate')) {
  4891. var anim = new YAHOO.util.Anim(this.get('element_cont').get('element'), {
  4892. width: {
  4893. to: parseInt(width, 10)
  4894. }
  4895. }, 0.5);
  4896. anim.animate();
  4897. } else {
  4898. this.get('element_cont').setStyle('width', width);
  4899. }
  4900. }
  4901. }
  4902. });
  4903. /**
  4904. * @attribute blankimage
  4905. * @description The URL for the image placeholder to put in when inserting an image.
  4906. * @default The yahooapis.com address for the current release + 'assets/blankimage.png'
  4907. * @type String
  4908. */
  4909. this.setAttributeConfig('blankimage', {
  4910. value: attr.blankimage || this._getBlankImage()
  4911. });
  4912. /**
  4913. * @attribute css
  4914. * @description The Base CSS used to format the content of the editor
  4915. * @default <code><pre>html {
  4916. height: 95%;
  4917. }
  4918. body {
  4919. height: 100%;
  4920. padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;
  4921. }
  4922. a {
  4923. color: blue;
  4924. text-decoration: underline;
  4925. cursor: pointer;
  4926. }
  4927. .warning-localfile {
  4928. border-bottom: 1px dashed red !important;
  4929. }
  4930. .yui-busy {
  4931. cursor: wait !important;
  4932. }
  4933. img.selected { //Safari image selection
  4934. border: 2px dotted #808080;
  4935. }
  4936. img {
  4937. cursor: pointer !important;
  4938. border: none;
  4939. }
  4940. </pre></code>
  4941. * @type String
  4942. */
  4943. this.setAttributeConfig('css', {
  4944. value: attr.css || this._defaultCSS,
  4945. writeOnce: true
  4946. });
  4947. /**
  4948. * @attribute html
  4949. * @description The default HTML to be written to the iframe document before the contents are loaded (Note that the DOCTYPE attr will be added at render item)
  4950. * @default This HTML requires a few things if you are to override:
  4951. <p><code>{TITLE}, {CSS}, {HIDDEN_CSS}, {EXTRA_CSS}</code> and <code>{CONTENT}</code> need to be there, they are passed to YAHOO.lang.substitute to be replace with other strings.<p>
  4952. <p><code>onload="document.body._rteLoaded = true;"</code> : the onload statement must be there or the editor will not finish loading.</p>
  4953. <code>
  4954. <pre>
  4955. &lt;html&gt;
  4956. &lt;head&gt;
  4957. &lt;title&gt;{TITLE}&lt;/title&gt;
  4958. &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
  4959. &lt;style&gt;
  4960. {CSS}
  4961. &lt;/style&gt;
  4962. &lt;style&gt;
  4963. {HIDDEN_CSS}
  4964. &lt;/style&gt;
  4965. &lt;style&gt;
  4966. {EXTRA_CSS}
  4967. &lt;/style&gt;
  4968. &lt;/head&gt;
  4969. &lt;body onload="document.body._rteLoaded = true;"&gt;
  4970. {CONTENT}
  4971. &lt;/body&gt;
  4972. &lt;/html&gt;
  4973. </pre>
  4974. </code>
  4975. * @type String
  4976. */
  4977. this.setAttributeConfig('html', {
  4978. value: attr.html || '<html><head><title>{TITLE}</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><base href="' + this._baseHREF + '"><style>{CSS}</style><style>{HIDDEN_CSS}</style><style>{EXTRA_CSS}</style></head><body onload="document.body._rteLoaded = true;">{CONTENT}</body></html>',
  4979. writeOnce: true
  4980. });
  4981. /**
  4982. * @attribute extracss
  4983. * @description Extra user defined css to load after the default SimpleEditor CSS
  4984. * @default ''
  4985. * @type String
  4986. */
  4987. this.setAttributeConfig('extracss', {
  4988. value: attr.extracss || '',
  4989. writeOnce: true
  4990. });
  4991. /**
  4992. * @attribute handleSubmit
  4993. * @description Config handles if the editor will attach itself to the textareas parent form's submit handler.
  4994. If it is set to true, the editor will attempt to attach a submit listener to the textareas parent form.
  4995. Then it will trigger the editors save handler and place the new content back into the text area before the form is submitted.
  4996. * @default false
  4997. * @type Boolean
  4998. */
  4999. this.setAttributeConfig('handleSubmit', {
  5000. value: attr.handleSubmit || false,
  5001. method: function(exec) {
  5002. if (this.get('element').form) {
  5003. if (!this._formButtons) {
  5004. this._formButtons = [];
  5005. }
  5006. if (exec) {
  5007. Event.on(this.get('element').form, 'submit', this._handleFormSubmit, this, true);
  5008. var i = this.get('element').form.getElementsByTagName('input');
  5009. for (var s = 0; s < i.length; s++) {
  5010. var type = i[s].getAttribute('type');
  5011. if (type && (type.toLowerCase() == 'submit')) {
  5012. Event.on(i[s], 'click', this._handleFormButtonClick, this, true);
  5013. this._formButtons[this._formButtons.length] = i[s];
  5014. }
  5015. }
  5016. } else {
  5017. Event.removeListener(this.get('element').form, 'submit', this._handleFormSubmit);
  5018. if (this._formButtons) {
  5019. Event.removeListener(this._formButtons, 'click', this._handleFormButtonClick);
  5020. }
  5021. }
  5022. }
  5023. }
  5024. });
  5025. /**
  5026. * @attribute disabled
  5027. * @description This will toggle the editor's disabled state. When the editor is disabled, designMode is turned off and a mask is placed over the iframe so no interaction can take place.
  5028. All Toolbar buttons are also disabled so they cannot be used.
  5029. * @default false
  5030. * @type Boolean
  5031. */
  5032. this.setAttributeConfig('disabled', {
  5033. value: false,
  5034. method: function(disabled) {
  5035. if (this._rendered) {
  5036. this._disableEditor(disabled);
  5037. }
  5038. }
  5039. });
  5040. /**
  5041. * @config saveEl
  5042. * @description When save HTML is called, this element will be updated as well as the source of data.
  5043. * @default element
  5044. * @type HTMLElement
  5045. */
  5046. this.setAttributeConfig('saveEl', {
  5047. value: this.get('element')
  5048. });
  5049. /**
  5050. * @config toolbar_cont
  5051. * @description Internal config for the toolbars container
  5052. * @default false
  5053. * @type Boolean
  5054. */
  5055. this.setAttributeConfig('toolbar_cont', {
  5056. value: null,
  5057. writeOnce: true
  5058. });
  5059. /**
  5060. * @attribute toolbar
  5061. * @description The default toolbar config.
  5062. * @type Object
  5063. */
  5064. this.setAttributeConfig('toolbar', {
  5065. value: attr.toolbar || this._defaultToolbar,
  5066. writeOnce: true,
  5067. method: function(toolbar) {
  5068. if (!toolbar.buttonType) {
  5069. toolbar.buttonType = this._defaultToolbar.buttonType;
  5070. }
  5071. this._defaultToolbar = toolbar;
  5072. }
  5073. });
  5074. /**
  5075. * @attribute animate
  5076. * @description Should the editor animate window movements
  5077. * @default false unless Animation is found, then true
  5078. * @type Boolean
  5079. */
  5080. this.setAttributeConfig('animate', {
  5081. value: ((attr.animate) ? ((YAHOO.util.Anim) ? true : false) : false),
  5082. validator: function(value) {
  5083. var ret = true;
  5084. if (!YAHOO.util.Anim) {
  5085. ret = false;
  5086. }
  5087. return ret;
  5088. }
  5089. });
  5090. /**
  5091. * @config panel
  5092. * @description A reference to the panel we are using for windows.
  5093. * @default false
  5094. * @type Boolean
  5095. */
  5096. this.setAttributeConfig('panel', {
  5097. value: null,
  5098. writeOnce: true,
  5099. validator: function(value) {
  5100. var ret = true;
  5101. if (!YAHOO.widget.Overlay) {
  5102. ret = false;
  5103. }
  5104. return ret;
  5105. }
  5106. });
  5107. /**
  5108. * @attribute focusAtStart
  5109. * @description Should we focus the window when the content is ready?
  5110. * @default false
  5111. * @type Boolean
  5112. */
  5113. this.setAttributeConfig('focusAtStart', {
  5114. value: attr.focusAtStart || false,
  5115. writeOnce: true,
  5116. method: function(fs) {
  5117. if (fs) {
  5118. this.on('editorContentLoaded', function() {
  5119. var self = this;
  5120. setTimeout(function() {
  5121. self.focus.call(self);
  5122. self.editorDirty = false;
  5123. }, 400);
  5124. }, this, true);
  5125. }
  5126. }
  5127. });
  5128. /**
  5129. * @attribute dompath
  5130. * @description Toggle the display of the current Dom path below the editor
  5131. * @default false
  5132. * @type Boolean
  5133. */
  5134. this.setAttributeConfig('dompath', {
  5135. value: attr.dompath || false,
  5136. method: function(dompath) {
  5137. if (dompath && !this.dompath) {
  5138. this.dompath = document.createElement('DIV');
  5139. this.dompath.id = this.get('id') + '_dompath';
  5140. Dom.addClass(this.dompath, 'dompath');
  5141. this.get('element_cont').get('firstChild').appendChild(this.dompath);
  5142. if (this.get('iframe')) {
  5143. this._writeDomPath();
  5144. }
  5145. } else if (!dompath && this.dompath) {
  5146. this.dompath.parentNode.removeChild(this.dompath);
  5147. this.dompath = null;
  5148. }
  5149. }
  5150. });
  5151. /**
  5152. * @attribute markup
  5153. * @description Should we try to adjust the markup for the following types: semantic, css, default or xhtml
  5154. * @default "semantic"
  5155. * @type String
  5156. */
  5157. this.setAttributeConfig('markup', {
  5158. value: attr.markup || 'semantic',
  5159. validator: function(markup) {
  5160. switch (markup.toLowerCase()) {
  5161. case 'semantic':
  5162. case 'css':
  5163. case 'default':
  5164. case 'xhtml':
  5165. return true;
  5166. }
  5167. return false;
  5168. }
  5169. });
  5170. /**
  5171. * @attribute removeLineBreaks
  5172. * @description Should we remove linebreaks and extra spaces on cleanup
  5173. * @default false
  5174. * @type Boolean
  5175. */
  5176. this.setAttributeConfig('removeLineBreaks', {
  5177. value: attr.removeLineBreaks || false,
  5178. validator: YAHOO.lang.isBoolean
  5179. });
  5180. /**
  5181. * @config drag
  5182. * @description Set this config to make the Editor draggable, pass 'proxy' to make use YAHOO.util.DDProxy.
  5183. * @type {Boolean/String}
  5184. */
  5185. this.setAttributeConfig('drag', {
  5186. writeOnce: true,
  5187. value: attr.drag || false
  5188. });
  5189. /**
  5190. * @config resize
  5191. * @description Set this to true to make the Editor Resizable with YAHOO.util.Resize. The default config is available: myEditor._resizeConfig
  5192. * Animation will be ignored while performing this resize to allow for the dynamic change in size of the toolbar.
  5193. * @type Boolean
  5194. */
  5195. this.setAttributeConfig('resize', {
  5196. writeOnce: true,
  5197. value: attr.resize || false
  5198. });
  5199. /**
  5200. * @config filterWord
  5201. * @description Attempt to filter out MS Word HTML from the Editor's output.
  5202. * @type Boolean
  5203. */
  5204. this.setAttributeConfig('filterWord', {
  5205. value: attr.filterWord || false,
  5206. validator: YAHOO.lang.isBoolean
  5207. });
  5208. },
  5209. /**
  5210. * @private
  5211. * @method _getBlankImage
  5212. * @description Retrieves the full url of the image to use as the blank image.
  5213. * @return {String} The URL to the blank image
  5214. */
  5215. _getBlankImage: function() {
  5216. if (!this.DOMReady) {
  5217. this._queue[this._queue.length] = ['_getBlankImage', arguments];
  5218. return '';
  5219. }
  5220. var img = '';
  5221. if (!this._blankImageLoaded) {
  5222. if (YAHOO.widget.EditorInfo.blankImage) {
  5223. this.set('blankimage', YAHOO.widget.EditorInfo.blankImage);
  5224. this._blankImageLoaded = true;
  5225. } else {
  5226. var div = document.createElement('div');
  5227. div.style.position = 'absolute';
  5228. div.style.top = '-9999px';
  5229. div.style.left = '-9999px';
  5230. div.className = this.CLASS_PREFIX + '-blankimage';
  5231. document.body.appendChild(div);
  5232. img = YAHOO.util.Dom.getStyle(div, 'background-image');
  5233. img = img.replace('url(', '').replace(')', '').replace(/"/g, '');
  5234. //Adobe AIR Code
  5235. img = img.replace('app:/', '');
  5236. this.set('blankimage', img);
  5237. this._blankImageLoaded = true;
  5238. div.parentNode.removeChild(div);
  5239. YAHOO.widget.EditorInfo.blankImage = img;
  5240. }
  5241. } else {
  5242. img = this.get('blankimage');
  5243. }
  5244. return img;
  5245. },
  5246. /**
  5247. * @private
  5248. * @method _handleAutoHeight
  5249. * @description Handles resizing the editor's height based on the content
  5250. */
  5251. _handleAutoHeight: function() {
  5252. var doc = this._getDoc(),
  5253. body = doc.body,
  5254. docEl = doc.documentElement;
  5255. var height = parseInt(Dom.getStyle(this.get('editor_wrapper'), 'height'), 10);
  5256. var newHeight = body.scrollHeight;
  5257. if (this.browser.webkit) {
  5258. newHeight = docEl.scrollHeight;
  5259. }
  5260. if (newHeight < parseInt(this.get('height'), 10)) {
  5261. newHeight = parseInt(this.get('height'), 10);
  5262. }
  5263. if ((height != newHeight) && (newHeight >= parseInt(this.get('height'), 10))) {
  5264. var anim = this.get('animate');
  5265. this.set('animate', false);
  5266. this.set('height', newHeight + 'px');
  5267. this.set('animate', anim);
  5268. if (this.browser.ie) {
  5269. //Internet Explorer needs this
  5270. this.get('iframe').setStyle('height', '99%');
  5271. this.get('iframe').setStyle('zoom', '1');
  5272. var self = this;
  5273. window.setTimeout(function() {
  5274. self.get('iframe').setStyle('height', '100%');
  5275. }, 1);
  5276. }
  5277. }
  5278. },
  5279. /**
  5280. * @private
  5281. * @property _formButtons
  5282. * @description Array of buttons that are in the Editor's parent form (for handleSubmit)
  5283. * @type Array
  5284. */
  5285. _formButtons: null,
  5286. /**
  5287. * @private
  5288. * @property _formButtonClicked
  5289. * @description The form button that was clicked to submit the form.
  5290. * @type HTMLElement
  5291. */
  5292. _formButtonClicked: null,
  5293. /**
  5294. * @private
  5295. * @method _handleFormButtonClick
  5296. * @description The click listener assigned to each submit button in the Editor's parent form.
  5297. * @param {Event} ev The click event
  5298. */
  5299. _handleFormButtonClick: function(ev) {
  5300. var tar = Event.getTarget(ev);
  5301. this._formButtonClicked = tar;
  5302. },
  5303. /**
  5304. * @private
  5305. * @method _handleFormSubmit
  5306. * @description Handles the form submission.
  5307. * @param {Object} ev The Form Submit Event
  5308. */
  5309. _handleFormSubmit: function(ev) {
  5310. this.saveHTML();
  5311. var form = this.get('element').form,
  5312. tar = this._formButtonClicked || false;
  5313. Event.removeListener(form, 'submit', this._handleFormSubmit);
  5314. if (YAHOO.env.ua.ie) {
  5315. //form.fireEvent("onsubmit");
  5316. if (tar && !tar.disabled) {
  5317. tar.click();
  5318. }
  5319. } else { // Gecko, Opera, and Safari
  5320. if (tar && !tar.disabled) {
  5321. tar.click();
  5322. }
  5323. var oEvent = document.createEvent("HTMLEvents");
  5324. oEvent.initEvent("submit", true, true);
  5325. form.dispatchEvent(oEvent);
  5326. if (YAHOO.env.ua.webkit) {
  5327. if (YAHOO.lang.isFunction(form.submit)) {
  5328. form.submit();
  5329. }
  5330. }
  5331. }
  5332. //2.6.0
  5333. //Removed this, not need since removing Safari 2.x
  5334. //Event.stopEvent(ev);
  5335. },
  5336. /**
  5337. * @private
  5338. * @method _handleFontSize
  5339. * @description Handles the font size button in the toolbar.
  5340. * @param {Object} o Object returned from Toolbar's buttonClick Event
  5341. */
  5342. _handleFontSize: function(o) {
  5343. var button = this.toolbar.getButtonById(o.button.id);
  5344. var value = button.get('label') + 'px';
  5345. this.execCommand('fontsize', value);
  5346. return false;
  5347. },
  5348. /**
  5349. * @private
  5350. * @description Handles the colorpicker buttons in the toolbar.
  5351. * @param {Object} o Object returned from Toolbar's buttonClick Event
  5352. */
  5353. _handleColorPicker: function(o) {
  5354. var cmd = o.button;
  5355. var value = '#' + o.color;
  5356. if ((cmd == 'forecolor') || (cmd == 'backcolor')) {
  5357. this.execCommand(cmd, value);
  5358. }
  5359. },
  5360. /**
  5361. * @private
  5362. * @method _handleAlign
  5363. * @description Handles the alignment buttons in the toolbar.
  5364. * @param {Object} o Object returned from Toolbar's buttonClick Event
  5365. */
  5366. _handleAlign: function(o) {
  5367. var cmd = null;
  5368. for (var i = 0; i < o.button.menu.length; i++) {
  5369. if (o.button.menu[i].value == o.button.value) {
  5370. cmd = o.button.menu[i].value;
  5371. }
  5372. }
  5373. var value = this._getSelection();
  5374. this.execCommand(cmd, value);
  5375. return false;
  5376. },
  5377. /**
  5378. * @private
  5379. * @method _handleAfterNodeChange
  5380. * @description Fires after a nodeChange happens to setup the things that where reset on the node change (button state).
  5381. */
  5382. _handleAfterNodeChange: function() {
  5383. var path = this._getDomPath(),
  5384. elm = null,
  5385. family = null,
  5386. fontsize = null,
  5387. validFont = false,
  5388. fn_button = this.toolbar.getButtonByValue('fontname'),
  5389. fs_button = this.toolbar.getButtonByValue('fontsize'),
  5390. hd_button = this.toolbar.getButtonByValue('heading');
  5391. for (var i = 0; i < path.length; i++) {
  5392. elm = path[i];
  5393. var tag = elm.tagName.toLowerCase();
  5394. if (elm.getAttribute('tag')) {
  5395. tag = elm.getAttribute('tag');
  5396. }
  5397. family = elm.getAttribute('face');
  5398. if (Dom.getStyle(elm, 'font-family')) {
  5399. family = Dom.getStyle(elm, 'font-family');
  5400. //Adobe AIR Code
  5401. family = family.replace(/'/g, '');
  5402. }
  5403. if (tag.substring(0, 1) == 'h') {
  5404. if (hd_button) {
  5405. for (var h = 0; h < hd_button._configs.menu.value.length; h++) {
  5406. if (hd_button._configs.menu.value[h].value.toLowerCase() == tag) {
  5407. hd_button.set('label', hd_button._configs.menu.value[h].text);
  5408. }
  5409. }
  5410. this._updateMenuChecked('heading', tag);
  5411. }
  5412. }
  5413. }
  5414. if (fn_button) {
  5415. for (var b = 0; b < fn_button._configs.menu.value.length; b++) {
  5416. if (family && fn_button._configs.menu.value[b].text.toLowerCase() == family.toLowerCase()) {
  5417. validFont = true;
  5418. family = fn_button._configs.menu.value[b].text; //Put the proper menu name in the button
  5419. }
  5420. }
  5421. if (!validFont) {
  5422. family = fn_button._configs.label._initialConfig.value;
  5423. }
  5424. var familyLabel = '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>';
  5425. if (fn_button.get('label') != familyLabel) {
  5426. fn_button.set('label', familyLabel);
  5427. this._updateMenuChecked('fontname', family);
  5428. }
  5429. }
  5430. if (fs_button) {
  5431. fontsize = parseInt(Dom.getStyle(elm, 'fontSize'), 10);
  5432. if ((fontsize === null) || isNaN(fontsize)) {
  5433. fontsize = fs_button._configs.label._initialConfig.value;
  5434. }
  5435. fs_button.set('label', ''+fontsize);
  5436. }
  5437. if (!this._isElement(elm, 'body') && !this._isElement(elm, 'img')) {
  5438. this.toolbar.enableButton(fn_button);
  5439. this.toolbar.enableButton(fs_button);
  5440. this.toolbar.enableButton('forecolor');
  5441. this.toolbar.enableButton('backcolor');
  5442. }
  5443. if (this._isElement(elm, 'img')) {
  5444. if (YAHOO.widget.Overlay) {
  5445. this.toolbar.enableButton('createlink');
  5446. }
  5447. }
  5448. if (this._hasParent(elm, 'blockquote')) {
  5449. this.toolbar.selectButton('indent');
  5450. this.toolbar.disableButton('indent');
  5451. this.toolbar.enableButton('outdent');
  5452. }
  5453. if (this._hasParent(elm, 'ol') || this._hasParent(elm, 'ul')) {
  5454. this.toolbar.disableButton('indent');
  5455. }
  5456. this._lastButton = null;
  5457. },
  5458. /**
  5459. * @private
  5460. * @method _handleInsertImageClick
  5461. * @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
  5462. */
  5463. _handleInsertImageClick: function() {
  5464. if (this.get('limitCommands')) {
  5465. if (!this.toolbar.getButtonByValue('insertimage')) {
  5466. YAHOO.log('Toolbar Button for (insertimage) was not found, skipping exec.', 'info', 'SimpleEditor');
  5467. return false;
  5468. }
  5469. }
  5470. this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
  5471. var _handleAEC = function() {
  5472. var el = this.currentElement[0],
  5473. src = 'http://';
  5474. if (!el) {
  5475. el = this._getSelectedElement();
  5476. }
  5477. if (el) {
  5478. if (el.getAttribute('src')) {
  5479. src = el.getAttribute('src', 2);
  5480. if (src.indexOf(this.get('blankimage')) != -1) {
  5481. src = this.STR_IMAGE_HERE;
  5482. }
  5483. }
  5484. }
  5485. var str = prompt(this.STR_IMAGE_URL + ': ', src);
  5486. if ((str !== '') && (str !== null)) {
  5487. el.setAttribute('src', str);
  5488. } else if (str === '') {
  5489. el.parentNode.removeChild(el);
  5490. this.currentElement = [];
  5491. this.nodeChange();
  5492. } else if ((str === null)) {
  5493. src = el.getAttribute('src', 2);
  5494. if (src.indexOf(this.get('blankimage')) != -1) {
  5495. el.parentNode.removeChild(el);
  5496. this.currentElement = [];
  5497. this.nodeChange();
  5498. }
  5499. }
  5500. this.closeWindow();
  5501. this.toolbar.set('disabled', false);
  5502. this.unsubscribe('afterExecCommand', _handleAEC, this, true);
  5503. };
  5504. this.on('afterExecCommand', _handleAEC, this, true);
  5505. },
  5506. /**
  5507. * @private
  5508. * @method _handleInsertImageWindowClose
  5509. * @description Handles the closing of the Image Properties Window.
  5510. */
  5511. _handleInsertImageWindowClose: function() {
  5512. this.nodeChange();
  5513. },
  5514. /**
  5515. * @private
  5516. * @method _isLocalFile
  5517. * @param {String} url THe url/string to check
  5518. * @description Checks to see if a string (href or img src) is possibly a local file reference..
  5519. */
  5520. _isLocalFile: function(url) {
  5521. if ((url) && (url !== '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
  5522. return true;
  5523. }
  5524. return false;
  5525. },
  5526. /**
  5527. * @private
  5528. * @method _handleCreateLinkClick
  5529. * @description Handles the opening of the Link Properties Window when the Create Link button is clicked or an href is doubleclicked.
  5530. */
  5531. _handleCreateLinkClick: function() {
  5532. if (this.get('limitCommands')) {
  5533. if (!this.toolbar.getButtonByValue('createlink')) {
  5534. YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'SimpleEditor');
  5535. return false;
  5536. }
  5537. }
  5538. this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
  5539. var _handleAEC = function() {
  5540. var el = this.currentElement[0],
  5541. url = '';
  5542. if (el) {
  5543. if (el.getAttribute('href', 2) !== null) {
  5544. url = el.getAttribute('href', 2);
  5545. }
  5546. }
  5547. var str = prompt(this.STR_LINK_URL + ': ', url);
  5548. if ((str !== '') && (str !== null)) {
  5549. var urlValue = str;
  5550. if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
  5551. if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
  5552. //Found an @ sign, prefix with mailto:
  5553. urlValue = 'mailto:' + urlValue;
  5554. } else {
  5555. /* :// not found adding */
  5556. if (urlValue.substring(0, 1) != '#') {
  5557. //urlValue = 'http:/'+'/' + urlValue;
  5558. }
  5559. }
  5560. }
  5561. el.setAttribute('href', urlValue);
  5562. } else if (str !== null) {
  5563. var _span = this._getDoc().createElement('span');
  5564. _span.innerHTML = el.innerHTML;
  5565. Dom.addClass(_span, 'yui-non');
  5566. el.parentNode.replaceChild(_span, el);
  5567. }
  5568. this.closeWindow();
  5569. this.toolbar.set('disabled', false);
  5570. this.unsubscribe('afterExecCommand', _handleAEC, this, true);
  5571. };
  5572. this.on('afterExecCommand', _handleAEC, this);
  5573. },
  5574. /**
  5575. * @private
  5576. * @method _handleCreateLinkWindowClose
  5577. * @description Handles the closing of the Link Properties Window.
  5578. */
  5579. _handleCreateLinkWindowClose: function() {
  5580. this.nodeChange();
  5581. this.currentElement = [];
  5582. },
  5583. /**
  5584. * @method render
  5585. * @description Calls the private method _render in a setTimeout to allow for other things on the page to continue to load.
  5586. */
  5587. render: function() {
  5588. if (this._rendered) {
  5589. return false;
  5590. }
  5591. YAHOO.log('Render', 'info', 'SimpleEditor');
  5592. if (!this.DOMReady) {
  5593. YAHOO.log('!DOMReady', 'info', 'SimpleEditor');
  5594. this._queue[this._queue.length] = ['render', arguments];
  5595. return false;
  5596. }
  5597. if (this.get('element')) {
  5598. if (this.get('element').tagName) {
  5599. this._textarea = true;
  5600. if (this.get('element').tagName.toLowerCase() !== 'textarea') {
  5601. this._textarea = false;
  5602. }
  5603. } else {
  5604. YAHOO.log('No Valid Element', 'error', 'SimpleEditor');
  5605. return false;
  5606. }
  5607. } else {
  5608. YAHOO.log('No Element', 'error', 'SimpleEditor');
  5609. return false;
  5610. }
  5611. this._rendered = true;
  5612. var self = this;
  5613. window.setTimeout(function() {
  5614. self._render.call(self);
  5615. }, 4);
  5616. },
  5617. /**
  5618. * @private
  5619. * @method _render
  5620. * @description Causes the toolbar and the editor to render and replace the textarea.
  5621. */
  5622. _render: function() {
  5623. var self = this;
  5624. this.set('textarea', this.get('element'));
  5625. this.get('element_cont').setStyle('display', 'none');
  5626. this.get('element_cont').addClass(this.CLASS_CONTAINER);
  5627. this.set('iframe', this._createIframe());
  5628. window.setTimeout(function() {
  5629. self._setInitialContent.call(self);
  5630. }, 10);
  5631. this.get('editor_wrapper').appendChild(this.get('iframe').get('element'));
  5632. if (this.get('disabled')) {
  5633. this._disableEditor(true);
  5634. }
  5635. var tbarConf = this.get('toolbar');
  5636. //Create Toolbar instance
  5637. if (tbarConf instanceof Toolbar) {
  5638. this.toolbar = tbarConf;
  5639. //Set the toolbar to disabled until content is loaded
  5640. this.toolbar.set('disabled', true);
  5641. } else {
  5642. //Set the toolbar to disabled until content is loaded
  5643. tbarConf.disabled = true;
  5644. this.toolbar = new Toolbar(this.get('toolbar_cont'), tbarConf);
  5645. }
  5646. YAHOO.log('fireEvent::toolbarLoaded', 'info', 'SimpleEditor');
  5647. this.fireEvent('toolbarLoaded', { type: 'toolbarLoaded', target: this.toolbar });
  5648. this.toolbar.on('toolbarCollapsed', function() {
  5649. if (this.currentWindow) {
  5650. this.moveWindow();
  5651. }
  5652. }, this, true);
  5653. this.toolbar.on('toolbarExpanded', function() {
  5654. if (this.currentWindow) {
  5655. this.moveWindow();
  5656. }
  5657. }, this, true);
  5658. this.toolbar.on('fontsizeClick', this._handleFontSize, this, true);
  5659. this.toolbar.on('colorPickerClicked', function(o) {
  5660. this._handleColorPicker(o);
  5661. return false; //Stop the buttonClick event
  5662. }, this, true);
  5663. this.toolbar.on('alignClick', this._handleAlign, this, true);
  5664. this.on('afterNodeChange', this._handleAfterNodeChange, this, true);
  5665. this.toolbar.on('insertimageClick', this._handleInsertImageClick, this, true);
  5666. this.on('windowinsertimageClose', this._handleInsertImageWindowClose, this, true);
  5667. this.toolbar.on('createlinkClick', this._handleCreateLinkClick, this, true);
  5668. this.on('windowcreatelinkClose', this._handleCreateLinkWindowClose, this, true);
  5669. //Replace Textarea with editable area
  5670. this.get('parentNode').replaceChild(this.get('element_cont').get('element'), this.get('element'));
  5671. this.setStyle('visibility', 'hidden');
  5672. this.setStyle('position', 'absolute');
  5673. this.setStyle('top', '-9999px');
  5674. this.setStyle('left', '-9999px');
  5675. this.get('element_cont').appendChild(this.get('element'));
  5676. this.get('element_cont').setStyle('display', 'block');
  5677. Dom.addClass(this.get('iframe').get('parentNode'), this.CLASS_EDITABLE_CONT);
  5678. this.get('iframe').addClass(this.CLASS_EDITABLE);
  5679. //Set height and width of editor container
  5680. this.get('element_cont').setStyle('width', this.get('width'));
  5681. Dom.setStyle(this.get('iframe').get('parentNode'), 'height', this.get('height'));
  5682. this.get('iframe').setStyle('width', '100%'); //WIDTH
  5683. this.get('iframe').setStyle('height', '100%');
  5684. this._setupDD();
  5685. window.setTimeout(function() {
  5686. self._setupAfterElement.call(self);
  5687. }, 0);
  5688. this.fireEvent('afterRender', { type: 'afterRender', target: this });
  5689. },
  5690. /**
  5691. * @method execCommand
  5692. * @param {String} action The "execCommand" action to try to execute (Example: bold, insertimage, inserthtml)
  5693. * @param {String} value (optional) The value for a given action such as action: fontname value: 'Verdana'
  5694. * @description This method attempts to try and level the differences in the various browsers and their support for execCommand actions
  5695. */
  5696. execCommand: function(action, value) {
  5697. var beforeExec = this.fireEvent('beforeExecCommand', { type: 'beforeExecCommand', target: this, args: arguments });
  5698. if ((beforeExec === false) || (this.STOP_EXEC_COMMAND)) {
  5699. this.STOP_EXEC_COMMAND = false;
  5700. return false;
  5701. }
  5702. this._lastCommand = action;
  5703. this._setMarkupType(action);
  5704. if (this.browser.ie) {
  5705. this._getWindow().focus();
  5706. }
  5707. var exec = true;
  5708. if (this.get('limitCommands')) {
  5709. if (!this.toolbar.getButtonByValue(action)) {
  5710. YAHOO.log('Toolbar Button for (' + action + ') was not found, skipping exec.', 'info', 'SimpleEditor');
  5711. exec = false;
  5712. }
  5713. }
  5714. this.editorDirty = true;
  5715. if ((typeof this['cmd_' + action.toLowerCase()] == 'function') && exec) {
  5716. YAHOO.log('Found execCommand override method: (cmd_' + action.toLowerCase() + ')', 'info', 'SimpleEditor');
  5717. var retValue = this['cmd_' + action.toLowerCase()](value);
  5718. exec = retValue[0];
  5719. if (retValue[1]) {
  5720. action = retValue[1];
  5721. }
  5722. if (retValue[2]) {
  5723. value = retValue[2];
  5724. }
  5725. }
  5726. if (exec) {
  5727. YAHOO.log('execCommand::(' + action + '), (' + value + ')', 'info', 'SimpleEditor');
  5728. try {
  5729. this._getDoc().execCommand(action, false, value);
  5730. } catch(e) {
  5731. YAHOO.log('execCommand Failed', 'error', 'SimpleEditor');
  5732. }
  5733. } else {
  5734. YAHOO.log('OVERRIDE::execCommand::(' + action + '),(' + value + ') skipped', 'warn', 'SimpleEditor');
  5735. }
  5736. this.on('afterExecCommand', function() {
  5737. this.unsubscribeAll('afterExecCommand');
  5738. this.nodeChange();
  5739. }, this, true);
  5740. this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
  5741. },
  5742. /* {{{ Command Overrides */
  5743. /**
  5744. * @method cmd_bold
  5745. * @param value Value passed from the execCommand method
  5746. * @description This is an execCommand override method. It is called from execCommand when the execCommand('bold') is used.
  5747. */
  5748. cmd_bold: function(value) {
  5749. if (!this.browser.webkit) {
  5750. var el = this._getSelectedElement();
  5751. if (el && this._isElement(el, 'span') && this._hasSelection()) {
  5752. if (el.style.fontWeight == 'bold') {
  5753. el.style.fontWeight = '';
  5754. var b = this._getDoc().createElement('b'),
  5755. par = el.parentNode;
  5756. par.replaceChild(b, el);
  5757. b.appendChild(el);
  5758. }
  5759. }
  5760. }
  5761. return [true];
  5762. },
  5763. /**
  5764. * @method cmd_italic
  5765. * @param value Value passed from the execCommand method
  5766. * @description This is an execCommand override method. It is called from execCommand when the execCommand('italic') is used.
  5767. */
  5768. cmd_italic: function(value) {
  5769. if (!this.browser.webkit) {
  5770. var el = this._getSelectedElement();
  5771. if (el && this._isElement(el, 'span') && this._hasSelection()) {
  5772. if (el.style.fontStyle == 'italic') {
  5773. el.style.fontStyle = '';
  5774. var i = this._getDoc().createElement('i'),
  5775. par = el.parentNode;
  5776. par.replaceChild(i, el);
  5777. i.appendChild(el);
  5778. }
  5779. }
  5780. }
  5781. return [true];
  5782. },
  5783. /**
  5784. * @method cmd_underline
  5785. * @param value Value passed from the execCommand method
  5786. * @description This is an execCommand override method. It is called from execCommand when the execCommand('underline') is used.
  5787. */
  5788. cmd_underline: function(value) {
  5789. if (!this.browser.webkit) {
  5790. var el = this._getSelectedElement();
  5791. if (el && this._isElement(el, 'span')) {
  5792. if (el.style.textDecoration == 'underline') {
  5793. el.style.textDecoration = 'none';
  5794. } else {
  5795. el.style.textDecoration = 'underline';
  5796. }
  5797. return [false];
  5798. }
  5799. }
  5800. return [true];
  5801. },
  5802. /**
  5803. * @method cmd_backcolor
  5804. * @param value Value passed from the execCommand method
  5805. * @description This is an execCommand override method. It is called from execCommand when the execCommand('backcolor') is used.
  5806. */
  5807. cmd_backcolor: function(value) {
  5808. var exec = true,
  5809. el = this._getSelectedElement(),
  5810. action = 'backcolor';
  5811. if (this.browser.gecko || this.browser.opera) {
  5812. this._setEditorStyle(true);
  5813. action = 'hilitecolor';
  5814. }
  5815. if (!this._isElement(el, 'body') && !this._hasSelection()) {
  5816. el.style.backgroundColor = value;
  5817. this._selectNode(el);
  5818. exec = false;
  5819. } else {
  5820. if (this.get('insert')) {
  5821. el = this._createInsertElement({ backgroundColor: value });
  5822. } else {
  5823. this._createCurrentElement('span', { backgroundColor: value, color: el.style.color, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily });
  5824. this._selectNode(this.currentElement[0]);
  5825. }
  5826. exec = false;
  5827. }
  5828. return [exec, action];
  5829. },
  5830. /**
  5831. * @method cmd_forecolor
  5832. * @param value Value passed from the execCommand method
  5833. * @description This is an execCommand override method. It is called from execCommand when the execCommand('forecolor') is used.
  5834. */
  5835. cmd_forecolor: function(value) {
  5836. var exec = true,
  5837. el = this._getSelectedElement();
  5838. if (!this._isElement(el, 'body') && !this._hasSelection()) {
  5839. Dom.setStyle(el, 'color', value);
  5840. this._selectNode(el);
  5841. exec = false;
  5842. } else {
  5843. if (this.get('insert')) {
  5844. el = this._createInsertElement({ color: value });
  5845. } else {
  5846. this._createCurrentElement('span', { color: value, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily, backgroundColor: el.style.backgroundColor });
  5847. this._selectNode(this.currentElement[0]);
  5848. }
  5849. exec = false;
  5850. }
  5851. return [exec];
  5852. },
  5853. /**
  5854. * @method cmd_unlink
  5855. * @param value Value passed from the execCommand method
  5856. * @description This is an execCommand override method. It is called from execCommand when the execCommand('unlink') is used.
  5857. */
  5858. cmd_unlink: function(value) {
  5859. this._swapEl(this.currentElement[0], 'span', function(el) {
  5860. el.className = 'yui-non';
  5861. });
  5862. return [false];
  5863. },
  5864. /**
  5865. * @method cmd_createlink
  5866. * @param value Value passed from the execCommand method
  5867. * @description This is an execCommand override method. It is called from execCommand when the execCommand('createlink') is used.
  5868. */
  5869. cmd_createlink: function(value) {
  5870. var el = this._getSelectedElement(), _a = null;
  5871. if (this._hasParent(el, 'a')) {
  5872. this.currentElement[0] = this._hasParent(el, 'a');
  5873. } else if (this._isElement(el, 'li')) {
  5874. _a = this._getDoc().createElement('a');
  5875. _a.innerHTML = el.innerHTML;
  5876. el.innerHTML = '';
  5877. el.appendChild(_a);
  5878. this.currentElement[0] = _a;
  5879. } else if (!this._isElement(el, 'a')) {
  5880. this._createCurrentElement('a');
  5881. _a = this._swapEl(this.currentElement[0], 'a');
  5882. this.currentElement[0] = _a;
  5883. } else {
  5884. this.currentElement[0] = el;
  5885. }
  5886. return [false];
  5887. },
  5888. /**
  5889. * @method cmd_insertimage
  5890. * @param value Value passed from the execCommand method
  5891. * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertimage') is used.
  5892. */
  5893. cmd_insertimage: function(value) {
  5894. var exec = true, _img = null, action = 'insertimage',
  5895. el = this._getSelectedElement();
  5896. if (value === '') {
  5897. value = this.get('blankimage');
  5898. }
  5899. /*
  5900. * @knownissue Safari Cursor Position
  5901. * @browser Safari 2.x
  5902. * @description The issue here is that we have no way of knowing where the cursor position is
  5903. * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
  5904. */
  5905. YAHOO.log('InsertImage: ' + el.tagName, 'info', 'SimpleEditor');
  5906. if (this._isElement(el, 'img')) {
  5907. this.currentElement[0] = el;
  5908. exec = false;
  5909. } else {
  5910. if (this._getDoc().queryCommandEnabled(action)) {
  5911. this._getDoc().execCommand(action, false, value);
  5912. var imgs = this._getDoc().getElementsByTagName('img');
  5913. for (var i = 0; i < imgs.length; i++) {
  5914. if (!YAHOO.util.Dom.hasClass(imgs[i], 'yui-img')) {
  5915. YAHOO.util.Dom.addClass(imgs[i], 'yui-img');
  5916. this.currentElement[0] = imgs[i];
  5917. }
  5918. }
  5919. exec = false;
  5920. } else {
  5921. if (el == this._getDoc().body) {
  5922. _img = this._getDoc().createElement('img');
  5923. _img.setAttribute('src', value);
  5924. YAHOO.util.Dom.addClass(_img, 'yui-img');
  5925. this._getDoc().body.appendChild(_img);
  5926. } else {
  5927. this._createCurrentElement('img');
  5928. _img = this._getDoc().createElement('img');
  5929. _img.setAttribute('src', value);
  5930. YAHOO.util.Dom.addClass(_img, 'yui-img');
  5931. this.currentElement[0].parentNode.replaceChild(_img, this.currentElement[0]);
  5932. }
  5933. this.currentElement[0] = _img;
  5934. exec = false;
  5935. }
  5936. }
  5937. return [exec];
  5938. },
  5939. /**
  5940. * @method cmd_inserthtml
  5941. * @param value Value passed from the execCommand method
  5942. * @description This is an execCommand override method. It is called from execCommand when the execCommand('inserthtml') is used.
  5943. */
  5944. cmd_inserthtml: function(value) {
  5945. var exec = true, action = 'inserthtml', _span = null, _range = null;
  5946. /*
  5947. * @knownissue Safari cursor position
  5948. * @browser Safari 2.x
  5949. * @description The issue here is that we have no way of knowing where the cursor position is
  5950. * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
  5951. */
  5952. if (this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) {
  5953. YAHOO.log('More Safari DOM tricks (inserthtml)', 'info', 'EditorSafari');
  5954. this._createCurrentElement('img');
  5955. _span = this._getDoc().createElement('span');
  5956. _span.innerHTML = value;
  5957. this.currentElement[0].parentNode.replaceChild(_span, this.currentElement[0]);
  5958. exec = false;
  5959. } else if (this.browser.ie) {
  5960. _range = this._getRange();
  5961. if (_range.item) {
  5962. _range.item(0).outerHTML = value;
  5963. } else {
  5964. _range.pasteHTML(value);
  5965. }
  5966. exec = false;
  5967. }
  5968. return [exec];
  5969. },
  5970. /**
  5971. * @method cmd_list
  5972. * @param tag The tag of the list you want to create (eg, ul or ol)
  5973. * @description This is a combined execCommand override method. It is called from the cmd_insertorderedlist and cmd_insertunorderedlist methods.
  5974. */
  5975. cmd_list: function(tag) {
  5976. var exec = true, list = null, li = 0, el = null, str = '',
  5977. selEl = this._getSelectedElement(), action = 'insertorderedlist';
  5978. if (tag == 'ul') {
  5979. action = 'insertunorderedlist';
  5980. }
  5981. /*
  5982. * @knownissue Safari 2.+ doesn't support ordered and unordered lists
  5983. * @browser Safari 2.x
  5984. * The issue with this workaround is that when applied to a set of text
  5985. * that has BR's in it, Safari may or may not pick up the individual items as
  5986. * list items. This is fixed in WebKit (Safari 3)
  5987. * 2.6.0: Seems there are still some issues with List Creation and Safari 3, reverting to previously working Safari 2.x code
  5988. */
  5989. //if ((this.browser.webkit && !this._getDoc().queryCommandEnabled(action))) {
  5990. if ((this.browser.webkit && !this.browser.webkit4) || (this.browser.opera)) {
  5991. if (this._isElement(selEl, 'li') && this._isElement(selEl.parentNode, tag)) {
  5992. YAHOO.log('We already have a list, undo it', 'info', 'SimpleEditor');
  5993. el = selEl.parentNode;
  5994. list = this._getDoc().createElement('span');
  5995. YAHOO.util.Dom.addClass(list, 'yui-non');
  5996. str = '';
  5997. var lis = el.getElementsByTagName('li'), p_tag = ((this.browser.opera && this.get('ptags')) ? 'p' : 'div');
  5998. for (li = 0; li < lis.length; li++) {
  5999. str += '<' + p_tag + '>' + lis[li].innerHTML + '</' + p_tag + '>';
  6000. }
  6001. list.innerHTML = str;
  6002. this.currentElement[0] = el;
  6003. this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
  6004. } else {
  6005. YAHOO.log('Create list item', 'info', 'SimpleEditor');
  6006. this._createCurrentElement(tag.toLowerCase());
  6007. list = this._getDoc().createElement(tag);
  6008. for (li = 0; li < this.currentElement.length; li++) {
  6009. var newli = this._getDoc().createElement('li');
  6010. newli.innerHTML = this.currentElement[li].innerHTML + '<span class="yui-non">&nbsp;</span>&nbsp;';
  6011. list.appendChild(newli);
  6012. if (li > 0) {
  6013. this.currentElement[li].parentNode.removeChild(this.currentElement[li]);
  6014. }
  6015. }
  6016. var b_tag = ((this.browser.opera) ? '<BR>' : '<br>'),
  6017. items = list.firstChild.innerHTML.split(b_tag), i, item;
  6018. if (items.length > 0) {
  6019. list.innerHTML = '';
  6020. for (i = 0; i < items.length; i++) {
  6021. item = this._getDoc().createElement('li');
  6022. item.innerHTML = items[i];
  6023. list.appendChild(item);
  6024. }
  6025. }
  6026. this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
  6027. this.currentElement[0] = list;
  6028. var _h = this.currentElement[0].firstChild;
  6029. _h = Dom.getElementsByClassName('yui-non', 'span', _h)[0];
  6030. if (this.browser.webkit) {
  6031. this._getSelection().setBaseAndExtent(_h, 1, _h, _h.innerText.length);
  6032. }
  6033. }
  6034. exec = false;
  6035. } else {
  6036. el = this._getSelectedElement();
  6037. YAHOO.log(el.tagName);
  6038. if (this._isElement(el, 'li') && this._isElement(el.parentNode, tag) || (this.browser.ie && this._isElement(this._getRange().parentElement, 'li')) || (this.browser.ie && this._isElement(el, 'ul')) || (this.browser.ie && this._isElement(el, 'ol'))) { //we are in a list..
  6039. YAHOO.log('We already have a list, undo it', 'info', 'SimpleEditor');
  6040. if (this.browser.ie) {
  6041. if ((this.browser.ie && this._isElement(el, 'ul')) || (this.browser.ie && this._isElement(el, 'ol'))) {
  6042. el = el.getElementsByTagName('li')[0];
  6043. }
  6044. YAHOO.log('Undo IE', 'info', 'SimpleEditor');
  6045. str = '';
  6046. var lis2 = el.parentNode.getElementsByTagName('li');
  6047. for (var j = 0; j < lis2.length; j++) {
  6048. str += lis2[j].innerHTML + '<br>';
  6049. }
  6050. var newEl = this._getDoc().createElement('span');
  6051. newEl.innerHTML = str;
  6052. el.parentNode.parentNode.replaceChild(newEl, el.parentNode);
  6053. } else {
  6054. this.nodeChange();
  6055. this._getDoc().execCommand(action, '', el.parentNode);
  6056. this.nodeChange();
  6057. }
  6058. exec = false;
  6059. }
  6060. if (this.browser.opera) {
  6061. var self = this;
  6062. window.setTimeout(function() {
  6063. var liso = self._getDoc().getElementsByTagName('li');
  6064. for (var i = 0; i < liso.length; i++) {
  6065. if (liso[i].innerHTML.toLowerCase() == '<br>') {
  6066. liso[i].parentNode.parentNode.removeChild(liso[i].parentNode);
  6067. }
  6068. }
  6069. },30);
  6070. }
  6071. if (this.browser.ie && exec) {
  6072. var html = '';
  6073. if (this._getRange().html) {
  6074. html = '<li>' + this._getRange().html+ '</li>';
  6075. } else {
  6076. var t = this._getRange().text.split('\n');
  6077. if (t.length > 1) {
  6078. html = '';
  6079. for (var ie = 0; ie < t.length; ie++) {
  6080. html += '<li>' + t[ie] + '</li>';
  6081. }
  6082. } else {
  6083. var txt = this._getRange().text;
  6084. if (txt === '') {
  6085. html = '<li id="new_list_item">' + txt + '</li>';
  6086. } else {
  6087. html = '<li>' + txt + '</li>';
  6088. }
  6089. }
  6090. }
  6091. this._getRange().pasteHTML('<' + tag + '>' + html + '</' + tag + '>');
  6092. var new_item = this._getDoc().getElementById('new_list_item');
  6093. if (new_item) {
  6094. var range = this._getDoc().body.createTextRange();
  6095. range.moveToElementText(new_item);
  6096. range.collapse(false);
  6097. range.select();
  6098. new_item.id = '';
  6099. }
  6100. exec = false;
  6101. }
  6102. }
  6103. return exec;
  6104. },
  6105. /**
  6106. * @method cmd_insertorderedlist
  6107. * @param value Value passed from the execCommand method
  6108. * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertorderedlist ') is used.
  6109. */
  6110. cmd_insertorderedlist: function(value) {
  6111. return [this.cmd_list('ol')];
  6112. },
  6113. /**
  6114. * @method cmd_insertunorderedlist
  6115. * @param value Value passed from the execCommand method
  6116. * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertunorderedlist') is used.
  6117. */
  6118. cmd_insertunorderedlist: function(value) {
  6119. return [this.cmd_list('ul')];
  6120. },
  6121. /**
  6122. * @method cmd_fontname
  6123. * @param value Value passed from the execCommand method
  6124. * @description This is an execCommand override method. It is called from execCommand when the execCommand('fontname') is used.
  6125. */
  6126. cmd_fontname: function(value) {
  6127. var exec = true,
  6128. selEl = this._getSelectedElement();
  6129. this.currentFont = value;
  6130. if (selEl && selEl.tagName && !this._hasSelection() && !this._isElement(selEl, 'body') && !this.get('insert')) {
  6131. YAHOO.util.Dom.setStyle(selEl, 'font-family', value);
  6132. exec = false;
  6133. } else if (this.get('insert') && !this._hasSelection()) {
  6134. YAHOO.log('No selection and no selected element and we are in insert mode', 'info', 'SimpleEditor');
  6135. var el = this._createInsertElement({ fontFamily: value });
  6136. exec = false;
  6137. }
  6138. return [exec];
  6139. },
  6140. /**
  6141. * @method cmd_fontsize
  6142. * @param value Value passed from the execCommand method
  6143. * @description This is an execCommand override method. It is called from execCommand when the execCommand('fontsize') is used.
  6144. */
  6145. cmd_fontsize: function(value) {
  6146. var el = null, go = true;
  6147. el = this._getSelectedElement();
  6148. if (this.browser.webkit) {
  6149. if (this.currentElement[0]) {
  6150. if (el == this.currentElement[0]) {
  6151. go = false;
  6152. YAHOO.util.Dom.setStyle(el, 'fontSize', value);
  6153. this._selectNode(el);
  6154. this.currentElement[0] = el;
  6155. }
  6156. }
  6157. }
  6158. if (go) {
  6159. if (!this._isElement(this._getSelectedElement(), 'body') && (!this._hasSelection())) {
  6160. el = this._getSelectedElement();
  6161. YAHOO.util.Dom.setStyle(el, 'fontSize', value);
  6162. if (this.get('insert') && this.browser.ie) {
  6163. var r = this._getRange();
  6164. r.collapse(false);
  6165. r.select();
  6166. } else {
  6167. this._selectNode(el);
  6168. }
  6169. } else if (this.currentElement && (this.currentElement.length > 0) && (!this._hasSelection()) && (!this.get('insert'))) {
  6170. YAHOO.util.Dom.setStyle(this.currentElement, 'fontSize', value);
  6171. } else {
  6172. if (this.get('insert') && !this._hasSelection()) {
  6173. el = this._createInsertElement({ fontSize: value });
  6174. this.currentElement[0] = el;
  6175. this._selectNode(this.currentElement[0]);
  6176. } else {
  6177. this._createCurrentElement('span', {'fontSize': value, fontFamily: el.style.fontFamily, color: el.style.color, backgroundColor: el.style.backgroundColor });
  6178. this._selectNode(this.currentElement[0]);
  6179. }
  6180. }
  6181. }
  6182. return [false];
  6183. },
  6184. /* }}} */
  6185. /**
  6186. * @private
  6187. * @method _swapEl
  6188. * @param {HTMLElement} el The element to swap with
  6189. * @param {String} tagName The tagname of the element that you wish to create
  6190. * @param {Function} callback (optional) A function to run on the element after it is created, but before it is replaced. An element reference is passed to this function.
  6191. * @description This function will create a new element in the DOM and populate it with the contents of another element. Then it will assume it's place.
  6192. */
  6193. _swapEl: function(el, tagName, callback) {
  6194. var _el = this._getDoc().createElement(tagName);
  6195. if (el) {
  6196. _el.innerHTML = el.innerHTML;
  6197. }
  6198. if (typeof callback == 'function') {
  6199. callback.call(this, _el);
  6200. }
  6201. if (el) {
  6202. el.parentNode.replaceChild(_el, el);
  6203. }
  6204. return _el;
  6205. },
  6206. /**
  6207. * @private
  6208. * @method _createInsertElement
  6209. * @description Creates a new "currentElement" then adds some text (and other things) to make it selectable and stylable. Then the user can continue typing.
  6210. * @param {Object} css (optional) Object literal containing styles to apply to the new element.
  6211. * @return {HTMLElement}
  6212. */
  6213. _createInsertElement: function(css) {
  6214. this._createCurrentElement('span', css);
  6215. var el = this.currentElement[0];
  6216. if (this.browser.webkit) {
  6217. //Little Safari Hackery here..
  6218. el.innerHTML = '<span class="yui-non">&nbsp;</span>';
  6219. el = el.firstChild;
  6220. this._getSelection().setBaseAndExtent(el, 1, el, el.innerText.length);
  6221. } else if (this.browser.ie || this.browser.opera) {
  6222. el.innerHTML = '&nbsp;';
  6223. }
  6224. this.focus();
  6225. this._selectNode(el, true);
  6226. return el;
  6227. },
  6228. /**
  6229. * @private
  6230. * @method _createCurrentElement
  6231. * @param {String} tagName (optional defaults to a) The tagname of the element that you wish to create
  6232. * @param {Object} tagStyle (optional) Object literal containing styles to apply to the new element.
  6233. * @description This is a work around for the various browser issues with execCommand. This method will run <code>execCommand('fontname', false, 'yui-tmp')</code> on the given selection.
  6234. * It will then search the document for an element with the font-family set to <strong>yui-tmp</strong> and replace that with another span that has other information in it, then assign the new span to the
  6235. * <code>this.currentElement</code> array, so we now have element references to the elements that were just modified. At this point we can use standard DOM manipulation to change them as we see fit.
  6236. */
  6237. _createCurrentElement: function(tagName, tagStyle) {
  6238. tagName = ((tagName) ? tagName : 'a');
  6239. var tar = null,
  6240. el = [],
  6241. _doc = this._getDoc();
  6242. if (this.currentFont) {
  6243. if (!tagStyle) {
  6244. tagStyle = {};
  6245. }
  6246. tagStyle.fontFamily = this.currentFont;
  6247. this.currentFont = null;
  6248. }
  6249. this.currentElement = [];
  6250. var _elCreate = function(tagName, tagStyle) {
  6251. var el = null;
  6252. tagName = ((tagName) ? tagName : 'span');
  6253. tagName = tagName.toLowerCase();
  6254. switch (tagName) {
  6255. case 'h1':
  6256. case 'h2':
  6257. case 'h3':
  6258. case 'h4':
  6259. case 'h5':
  6260. case 'h6':
  6261. el = _doc.createElement(tagName);
  6262. break;
  6263. default:
  6264. el = _doc.createElement(tagName);
  6265. if (tagName === 'span') {
  6266. YAHOO.util.Dom.addClass(el, 'yui-tag-' + tagName);
  6267. YAHOO.util.Dom.addClass(el, 'yui-tag');
  6268. el.setAttribute('tag', tagName);
  6269. }
  6270. for (var k in tagStyle) {
  6271. if (YAHOO.lang.hasOwnProperty(tagStyle, k)) {
  6272. el.style[k] = tagStyle[k];
  6273. }
  6274. }
  6275. break;
  6276. }
  6277. return el;
  6278. };
  6279. if (!this._hasSelection()) {
  6280. if (this._getDoc().queryCommandEnabled('insertimage')) {
  6281. this._getDoc().execCommand('insertimage', false, 'yui-tmp-img');
  6282. var imgs = this._getDoc().getElementsByTagName('img');
  6283. for (var j = 0; j < imgs.length; j++) {
  6284. if (imgs[j].getAttribute('src', 2) == 'yui-tmp-img') {
  6285. el = _elCreate(tagName, tagStyle);
  6286. imgs[j].parentNode.replaceChild(el, imgs[j]);
  6287. this.currentElement[this.currentElement.length] = el;
  6288. }
  6289. }
  6290. } else {
  6291. if (this.currentEvent) {
  6292. tar = YAHOO.util.Event.getTarget(this.currentEvent);
  6293. } else {
  6294. //For Safari..
  6295. tar = this._getDoc().body;
  6296. }
  6297. }
  6298. if (tar) {
  6299. /*
  6300. * @knownissue Safari Cursor Position
  6301. * @browser Safari 2.x
  6302. * @description The issue here is that we have no way of knowing where the cursor position is
  6303. * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
  6304. */
  6305. el = _elCreate(tagName, tagStyle);
  6306. if (this._isElement(tar, 'body') || this._isElement(tar, 'html')) {
  6307. if (this._isElement(tar, 'html')) {
  6308. tar = this._getDoc().body;
  6309. }
  6310. tar.appendChild(el);
  6311. } else if (tar.nextSibling) {
  6312. tar.parentNode.insertBefore(el, tar.nextSibling);
  6313. } else {
  6314. tar.parentNode.appendChild(el);
  6315. }
  6316. //this.currentElement = el;
  6317. this.currentElement[this.currentElement.length] = el;
  6318. this.currentEvent = null;
  6319. if (this.browser.webkit) {
  6320. //Force Safari to focus the new element
  6321. this._getSelection().setBaseAndExtent(el, 0, el, 0);
  6322. if (this.browser.webkit3) {
  6323. this._getSelection().collapseToStart();
  6324. } else {
  6325. this._getSelection().collapse(true);
  6326. }
  6327. }
  6328. }
  6329. } else {
  6330. //Force CSS Styling for this action...
  6331. this._setEditorStyle(true);
  6332. this._getDoc().execCommand('fontname', false, 'yui-tmp');
  6333. var _tmp = [], __tmp, __els = ['font', 'span', 'i', 'b', 'u'];
  6334. if (!this._isElement(this._getSelectedElement(), 'body')) {
  6335. __els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().tagName);
  6336. __els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().parentNode.tagName);
  6337. }
  6338. for (var _els = 0; _els < __els.length; _els++) {
  6339. var _tmp1 = this._getDoc().getElementsByTagName(__els[_els]);
  6340. for (var e = 0; e < _tmp1.length; e++) {
  6341. _tmp[_tmp.length] = _tmp1[e];
  6342. }
  6343. }
  6344. for (var i = 0; i < _tmp.length; i++) {
  6345. if ((YAHOO.util.Dom.getStyle(_tmp[i], 'font-family') == 'yui-tmp') || (_tmp[i].face && (_tmp[i].face == 'yui-tmp'))) {
  6346. if (tagName !== 'span') {
  6347. el = _elCreate(tagName, tagStyle);
  6348. } else {
  6349. el = _elCreate(_tmp[i].tagName, tagStyle);
  6350. }
  6351. el.innerHTML = _tmp[i].innerHTML;
  6352. if (this._isElement(_tmp[i], 'ol') || (this._isElement(_tmp[i], 'ul'))) {
  6353. var fc = _tmp[i].getElementsByTagName('li')[0];
  6354. _tmp[i].style.fontFamily = 'inherit';
  6355. fc.style.fontFamily = 'inherit';
  6356. el.innerHTML = fc.innerHTML;
  6357. fc.innerHTML = '';
  6358. fc.appendChild(el);
  6359. this.currentElement[this.currentElement.length] = el;
  6360. } else if (this._isElement(_tmp[i], 'li')) {
  6361. _tmp[i].innerHTML = '';
  6362. _tmp[i].appendChild(el);
  6363. _tmp[i].style.fontFamily = 'inherit';
  6364. this.currentElement[this.currentElement.length] = el;
  6365. } else {
  6366. if (_tmp[i].parentNode) {
  6367. _tmp[i].parentNode.replaceChild(el, _tmp[i]);
  6368. this.currentElement[this.currentElement.length] = el;
  6369. this.currentEvent = null;
  6370. if (this.browser.webkit) {
  6371. //Force Safari to focus the new element
  6372. this._getSelection().setBaseAndExtent(el, 0, el, 0);
  6373. if (this.browser.webkit3) {
  6374. this._getSelection().collapseToStart();
  6375. } else {
  6376. this._getSelection().collapse(true);
  6377. }
  6378. }
  6379. if (this.browser.ie && tagStyle && tagStyle.fontSize) {
  6380. this._getSelection().empty();
  6381. }
  6382. if (this.browser.gecko) {
  6383. this._getSelection().collapseToStart();
  6384. }
  6385. }
  6386. }
  6387. }
  6388. }
  6389. var len = this.currentElement.length;
  6390. for (var o = 0; o < len; o++) {
  6391. if ((o + 1) != len) { //Skip the last one in the list
  6392. if (this.currentElement[o] && this.currentElement[o].nextSibling) {
  6393. if (this._isElement(this.currentElement[o], 'br')) {
  6394. this.currentElement[this.currentElement.length] = this.currentElement[o].nextSibling;
  6395. }
  6396. }
  6397. }
  6398. }
  6399. }
  6400. },
  6401. /**
  6402. * @method saveHTML
  6403. * @description Cleans the HTML with the cleanHTML method then places that string back into the textarea.
  6404. * @return String
  6405. */
  6406. saveHTML: function() {
  6407. var html = this.cleanHTML();
  6408. if (this._textarea) {
  6409. this.get('element').value = html;
  6410. } else {
  6411. this.get('element').innerHTML = html;
  6412. }
  6413. if (this.get('saveEl') !== this.get('element')) {
  6414. var out = this.get('saveEl');
  6415. if (Lang.isString(out)) {
  6416. out = Dom.get(out);
  6417. }
  6418. if (out) {
  6419. if (out.tagName.toLowerCase() === 'textarea') {
  6420. out.value = html;
  6421. } else {
  6422. out.innerHTML = html;
  6423. }
  6424. }
  6425. }
  6426. return html;
  6427. },
  6428. /**
  6429. * @method setEditorHTML
  6430. * @param {String} incomingHTML The html content to load into the editor
  6431. * @description Loads HTML into the editors body
  6432. */
  6433. setEditorHTML: function(incomingHTML) {
  6434. var html = this._cleanIncomingHTML(incomingHTML);
  6435. html = html.replace(/RIGHT_BRACKET/gi, '{');
  6436. html = html.replace(/LEFT_BRACKET/gi, '}');
  6437. this._getDoc().body.innerHTML = html;
  6438. this.nodeChange();
  6439. },
  6440. /**
  6441. * @method getEditorHTML
  6442. * @description Gets the unprocessed/unfiltered HTML from the editor
  6443. */
  6444. getEditorHTML: function() {
  6445. try {
  6446. var b = this._getDoc().body;
  6447. if (b === null) {
  6448. YAHOO.log('Body is null, returning null.', 'error', 'SimpleEditor');
  6449. return null;
  6450. }
  6451. return this._getDoc().body.innerHTML;
  6452. } catch (e) {
  6453. return '';
  6454. }
  6455. },
  6456. /**
  6457. * @method show
  6458. * @description This method needs to be called if the Editor was hidden (like in a TabView or Panel). It is used to reset the editor after being in a container that was set to display none.
  6459. */
  6460. show: function() {
  6461. if (this.browser.gecko) {
  6462. this._setDesignMode('on');
  6463. this.focus();
  6464. }
  6465. if (this.browser.webkit) {
  6466. var self = this;
  6467. window.setTimeout(function() {
  6468. self._setInitialContent.call(self);
  6469. }, 10);
  6470. }
  6471. //Adding this will close all other Editor window's when showing this one.
  6472. if (this.currentWindow) {
  6473. this.closeWindow();
  6474. }
  6475. //Put the iframe back in place
  6476. this.get('iframe').setStyle('position', 'static');
  6477. this.get('iframe').setStyle('left', '');
  6478. },
  6479. /**
  6480. * @method hide
  6481. * @description This method needs to be called if the Editor is to be hidden (like in a TabView or Panel). It should be called to clear timeouts and close open editor windows.
  6482. */
  6483. hide: function() {
  6484. //Adding this will close all other Editor window's.
  6485. if (this.currentWindow) {
  6486. this.closeWindow();
  6487. }
  6488. if (this._fixNodesTimer) {
  6489. clearTimeout(this._fixNodesTimer);
  6490. this._fixNodesTimer = null;
  6491. }
  6492. if (this._nodeChangeTimer) {
  6493. clearTimeout(this._nodeChangeTimer);
  6494. this._nodeChangeTimer = null;
  6495. }
  6496. this._lastNodeChange = 0;
  6497. //Move the iframe off of the screen, so that in containers with visiblity hidden, IE will not cover other elements.
  6498. this.get('iframe').setStyle('position', 'absolute');
  6499. this.get('iframe').setStyle('left', '-9999px');
  6500. },
  6501. /**
  6502. * @method _cleanIncomingHTML
  6503. * @param {String} html The unfiltered HTML
  6504. * @description Process the HTML with a few regexes to clean it up and stabilize the input
  6505. * @return {String} The filtered HTML
  6506. */
  6507. _cleanIncomingHTML: function(html) {
  6508. html = html.replace(/{/gi, 'RIGHT_BRACKET');
  6509. html = html.replace(/}/gi, 'LEFT_BRACKET');
  6510. html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
  6511. html = html.replace(/<\/strong>/gi, '</b>');
  6512. //replace embed before em check
  6513. html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
  6514. html = html.replace(/<\/embed>/gi, '</YUI_EMBED>');
  6515. html = html.replace(/<em([^>]*)>/gi, '<i$1>');
  6516. html = html.replace(/<\/em>/gi, '</i>');
  6517. html = html.replace(/_moz_dirty=""/gi, '');
  6518. //Put embed tags back in..
  6519. html = html.replace(/<YUI_EMBED([^>]*)>/gi, '<embed$1>');
  6520. html = html.replace(/<\/YUI_EMBED>/gi, '</embed>');
  6521. if (this.get('plainText')) {
  6522. YAHOO.log('Filtering as plain text', 'info', 'SimpleEditor');
  6523. html = html.replace(/\n/g, '<br>').replace(/\r/g, '<br>');
  6524. html = html.replace(/ /gi, '&nbsp;&nbsp;'); //Replace all double spaces
  6525. html = html.replace(/\t/gi, '&nbsp;&nbsp;&nbsp;&nbsp;'); //Replace all tabs
  6526. }
  6527. //Removing Script Tags from the Editor
  6528. html = html.replace(/<script([^>]*)>/gi, '<bad>');
  6529. html = html.replace(/<\/script([^>]*)>/gi, '</bad>');
  6530. html = html.replace(/&lt;script([^>]*)&gt;/gi, '<bad>');
  6531. html = html.replace(/&lt;\/script([^>]*)&gt;/gi, '</bad>');
  6532. //Replace the line feeds
  6533. html = html.replace(/\r\n/g, '<YUI_LF>').replace(/\n/g, '<YUI_LF>').replace(/\r/g, '<YUI_LF>');
  6534. //Remove Bad HTML elements (used to be script nodes)
  6535. html = html.replace(new RegExp('<bad([^>]*)>(.*?)<\/bad>', 'gi'), '');
  6536. //Replace the lines feeds
  6537. html = html.replace(/<YUI_LF>/g, '\n');
  6538. return html;
  6539. },
  6540. /**
  6541. * @method cleanHTML
  6542. * @param {String} html The unfiltered HTML
  6543. * @description Process the HTML with a few regexes to clean it up and stabilize the output
  6544. * @return {String} The filtered HTML
  6545. */
  6546. cleanHTML: function(html) {
  6547. //Start Filtering Output
  6548. //Begin RegExs..
  6549. if (!html) {
  6550. html = this.getEditorHTML();
  6551. }
  6552. var markup = this.get('markup');
  6553. //Make some backups...
  6554. html = this.pre_filter_linebreaks(html, markup);
  6555. //Filter MS Word
  6556. html = this.filter_msword(html);
  6557. html = html.replace(/<img([^>]*)\/>/gi, '<YUI_IMG$1>');
  6558. html = html.replace(/<img([^>]*)>/gi, '<YUI_IMG$1>');
  6559. html = html.replace(/<input([^>]*)\/>/gi, '<YUI_INPUT$1>');
  6560. html = html.replace(/<input([^>]*)>/gi, '<YUI_INPUT$1>');
  6561. html = html.replace(/<ul([^>]*)>/gi, '<YUI_UL$1>');
  6562. html = html.replace(/<\/ul>/gi, '<\/YUI_UL>');
  6563. html = html.replace(/<blockquote([^>]*)>/gi, '<YUI_BQ$1>');
  6564. html = html.replace(/<\/blockquote>/gi, '<\/YUI_BQ>');
  6565. html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
  6566. html = html.replace(/<\/embed>/gi, '<\/YUI_EMBED>');
  6567. //Convert b and i tags to strong and em tags
  6568. if ((markup == 'semantic') || (markup == 'xhtml')) {
  6569. html = html.replace(/<i(\s+[^>]*)?>/gi, '<em$1>');
  6570. html = html.replace(/<\/i>/gi, '</em>');
  6571. html = html.replace(/<b(\s+[^>]*)?>/gi, '<strong$1>');
  6572. html = html.replace(/<\/b>/gi, '</strong>');
  6573. }
  6574. html = html.replace(/_moz_dirty=""/gi, '');
  6575. //normalize strikethrough
  6576. html = html.replace(/<strike/gi, '<span style="text-decoration: line-through;"');
  6577. html = html.replace(/\/strike>/gi, '/span>');
  6578. //Case Changing
  6579. if (this.browser.ie) {
  6580. html = html.replace(/text-decoration/gi, 'text-decoration');
  6581. html = html.replace(/font-weight/gi, 'font-weight');
  6582. html = html.replace(/_width="([^>]*)"/gi, '');
  6583. html = html.replace(/_height="([^>]*)"/gi, '');
  6584. //Cleanup Image URL's
  6585. var url = this._baseHREF.replace(/\//gi, '\\/'),
  6586. re = new RegExp('src="' + url, 'gi');
  6587. html = html.replace(re, 'src="');
  6588. }
  6589. html = html.replace(/<font/gi, '<font');
  6590. html = html.replace(/<\/font>/gi, '</font>');
  6591. html = html.replace(/<span/gi, '<span');
  6592. html = html.replace(/<\/span>/gi, '</span>');
  6593. if ((markup == 'semantic') || (markup == 'xhtml') || (markup == 'css')) {
  6594. html = html.replace(new RegExp('<font([^>]*)face="([^>]*)">(.*?)<\/font>', 'gi'), '<span $1 style="font-family: $2;">$3</span>');
  6595. html = html.replace(/<u/gi, '<span style="text-decoration: underline;"');
  6596. if (this.browser.webkit) {
  6597. html = html.replace(new RegExp('<span class="Apple-style-span" style="font-weight: bold;">([^>]*)<\/span>', 'gi'), '<strong>$1</strong>');
  6598. html = html.replace(new RegExp('<span class="Apple-style-span" style="font-style: italic;">([^>]*)<\/span>', 'gi'), '<em>$1</em>');
  6599. }
  6600. html = html.replace(/\/u>/gi, '/span>');
  6601. if (markup == 'css') {
  6602. html = html.replace(/<em([^>]*)>/gi, '<i$1>');
  6603. html = html.replace(/<\/em>/gi, '</i>');
  6604. html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
  6605. html = html.replace(/<\/strong>/gi, '</b>');
  6606. html = html.replace(/<b/gi, '<span style="font-weight: bold;"');
  6607. html = html.replace(/\/b>/gi, '/span>');
  6608. html = html.replace(/<i/gi, '<span style="font-style: italic;"');
  6609. html = html.replace(/\/i>/gi, '/span>');
  6610. }
  6611. html = html.replace(/ /gi, ' '); //Replace all double spaces and replace with a single
  6612. } else {
  6613. html = html.replace(/<u/gi, '<u');
  6614. html = html.replace(/\/u>/gi, '/u>');
  6615. }
  6616. html = html.replace(/<ol([^>]*)>/gi, '<ol$1>');
  6617. html = html.replace(/\/ol>/gi, '/ol>');
  6618. html = html.replace(/<li/gi, '<li');
  6619. html = html.replace(/\/li>/gi, '/li>');
  6620. html = this.filter_safari(html);
  6621. html = this.filter_internals(html);
  6622. html = this.filter_all_rgb(html);
  6623. //Replace our backups with the real thing
  6624. html = this.post_filter_linebreaks(html, markup);
  6625. if (markup == 'xhtml') {
  6626. html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1 />');
  6627. html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1 />');
  6628. } else {
  6629. html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1>');
  6630. html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1>');
  6631. }
  6632. html = html.replace(/<YUI_UL([^>]*)>/g, '<ul$1>');
  6633. html = html.replace(/<\/YUI_UL>/g, '<\/ul>');
  6634. html = this.filter_invalid_lists(html);
  6635. html = html.replace(/<YUI_BQ([^>]*)>/g, '<blockquote$1>');
  6636. html = html.replace(/<\/YUI_BQ>/g, '<\/blockquote>');
  6637. html = html.replace(/<YUI_EMBED([^>]*)>/g, '<embed$1>');
  6638. html = html.replace(/<\/YUI_EMBED>/g, '<\/embed>');
  6639. //This should fix &amp;'s in URL's
  6640. html = html.replace(/ &amp; /gi, ' YUI_AMP ');
  6641. html = html.replace(/ &amp;/gi, ' YUI_AMP_F ');
  6642. html = html.replace(/&amp; /gi, ' YUI_AMP_R ');
  6643. html = html.replace(/&amp;/gi, '&');
  6644. html = html.replace(/ YUI_AMP /gi, ' &amp; ');
  6645. html = html.replace(/ YUI_AMP_F /gi, ' &amp;');
  6646. html = html.replace(/ YUI_AMP_R /gi, '&amp; ');
  6647. //Trim the output, removing whitespace from the beginning and end
  6648. html = YAHOO.lang.trim(html);
  6649. if (this.get('removeLineBreaks')) {
  6650. html = html.replace(/\n/g, '').replace(/\r/g, '');
  6651. html = html.replace(/ /gi, ' '); //Replace all double spaces and replace with a single
  6652. }
  6653. for (var v in this.invalidHTML) {
  6654. if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
  6655. if (Lang.isObject(v) && v.keepContents) {
  6656. html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '$1');
  6657. } else {
  6658. html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '');
  6659. }
  6660. }
  6661. }
  6662. /* LATER -- Add DOM manipulation
  6663. console.log(html);
  6664. var frag = document.createDocumentFragment();
  6665. frag.innerHTML = html;
  6666. var ps = frag.getElementsByTagName('p'),
  6667. len = ps.length;
  6668. for (var i = 0; i < len; i++) {
  6669. var ps2 = ps[i].getElementsByTagName('p');
  6670. if (ps2.length) {
  6671. }
  6672. }
  6673. html = frag.innerHTML;
  6674. console.log(html);
  6675. */
  6676. this.fireEvent('cleanHTML', { type: 'cleanHTML', target: this, html: html });
  6677. return html;
  6678. },
  6679. /**
  6680. * @method filter_msword
  6681. * @param String html The HTML string to filter
  6682. * @description Filters out msword html attributes and other junk. Activate with filterWord: true in config
  6683. */
  6684. filter_msword: function(html) {
  6685. if (!this.get('filterWord')) {
  6686. return html;
  6687. }
  6688. //Remove the ms o: tags
  6689. html = html.replace(/<o:p>\s*<\/o:p>/g, '');
  6690. html = html.replace(/<o:p>[\s\S]*?<\/o:p>/g, '&nbsp;');
  6691. //Remove the ms w: tags
  6692. html = html.replace( /<w:[^>]*>[\s\S]*?<\/w:[^>]*>/gi, '');
  6693. //Remove mso-? styles.
  6694. html = html.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '');
  6695. //Remove more bogus MS styles.
  6696. html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '');
  6697. html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"");
  6698. html = html.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '');
  6699. html = html.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"");
  6700. html = html.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");
  6701. html = html.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" );
  6702. html = html.replace( /\s*tab-stops:[^;"]*;?/gi, '');
  6703. html = html.replace( /\s*tab-stops:[^"]*/gi, '');
  6704. //Remove XML declarations
  6705. html = html.replace(/<\\?\?xml[^>]*>/gi, '');
  6706. //Remove lang
  6707. html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");
  6708. //Remove language tags
  6709. html = html.replace( /<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3");
  6710. //Remove onmouseover and onmouseout events (from MS Word comments effect)
  6711. html = html.replace( /<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3");
  6712. html = html.replace( /<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3");
  6713. return html;
  6714. },
  6715. /**
  6716. * @method filter_invalid_lists
  6717. * @param String html The HTML string to filter
  6718. * @description Filters invalid ol and ul list markup, converts this: <li></li><ol>..</ol> to this: <li></li><li><ol>..</ol></li>
  6719. */
  6720. filter_invalid_lists: function(html) {
  6721. html = html.replace(/<\/li>\n/gi, '</li>');
  6722. html = html.replace(/<\/li><ol>/gi, '</li><li><ol>');
  6723. html = html.replace(/<\/ol>/gi, '</ol></li>');
  6724. html = html.replace(/<\/ol><\/li>\n/gi, "</ol>");
  6725. html = html.replace(/<\/li><ul>/gi, '</li><li><ul>');
  6726. html = html.replace(/<\/ul>/gi, '</ul></li>');
  6727. html = html.replace(/<\/ul><\/li>\n?/gi, "</ul>");
  6728. html = html.replace(/<\/li>/gi, "</li>");
  6729. html = html.replace(/<\/ol>/gi, "</ol>");
  6730. html = html.replace(/<ol>/gi, "<ol>");
  6731. html = html.replace(/<ul>/gi, "<ul>");
  6732. return html;
  6733. },
  6734. /**
  6735. * @method filter_safari
  6736. * @param String html The HTML string to filter
  6737. * @description Filters strings specific to Safari
  6738. * @return String
  6739. */
  6740. filter_safari: function(html) {
  6741. if (this.browser.webkit) {
  6742. //<span class="Apple-tab-span" style="white-space:pre"> </span>
  6743. html = html.replace(/<span class="Apple-tab-span" style="white-space:pre">([^>])<\/span>/gi, '&nbsp;&nbsp;&nbsp;&nbsp;');
  6744. html = html.replace(/Apple-style-span/gi, '');
  6745. html = html.replace(/style="line-height: normal;"/gi, '');
  6746. html = html.replace(/yui-wk-div/gi, '');
  6747. html = html.replace(/yui-wk-p/gi, '');
  6748. //Remove bogus LI's
  6749. html = html.replace(/<li><\/li>/gi, '');
  6750. html = html.replace(/<li> <\/li>/gi, '');
  6751. html = html.replace(/<li> <\/li>/gi, '');
  6752. //Remove bogus DIV's - updated from just removing the div's to replacing /div with a break
  6753. if (this.get('ptags')) {
  6754. html = html.replace(/<div([^>]*)>/g, '<p$1>');
  6755. html = html.replace(/<\/div>/gi, '</p>');
  6756. } else {
  6757. //html = html.replace(/<div>/gi, '<br>');
  6758. html = html.replace(/<div([^>]*)>([ tnr]*)<\/div>/gi, '<br>');
  6759. html = html.replace(/<\/div>/gi, '');
  6760. }
  6761. }
  6762. return html;
  6763. },
  6764. /**
  6765. * @method filter_internals
  6766. * @param String html The HTML string to filter
  6767. * @description Filters internal RTE strings and bogus attrs we don't want
  6768. * @return String
  6769. */
  6770. filter_internals: function(html) {
  6771. html = html.replace(/\r/g, '');
  6772. //Fix stuff we don't want
  6773. html = html.replace(/<\/?(body|head|html)[^>]*>/gi, '');
  6774. //Fix last BR in LI
  6775. html = html.replace(/<YUI_BR><\/li>/gi, '</li>');
  6776. html = html.replace(/yui-tag-span/gi, '');
  6777. html = html.replace(/yui-tag/gi, '');
  6778. html = html.replace(/yui-non/gi, '');
  6779. html = html.replace(/yui-img/gi, '');
  6780. html = html.replace(/ tag="span"/gi, '');
  6781. html = html.replace(/ class=""/gi, '');
  6782. html = html.replace(/ style=""/gi, '');
  6783. html = html.replace(/ class=" "/gi, '');
  6784. html = html.replace(/ class=" "/gi, '');
  6785. html = html.replace(/ target=""/gi, '');
  6786. html = html.replace(/ title=""/gi, '');
  6787. if (this.browser.ie) {
  6788. html = html.replace(/ class= /gi, '');
  6789. html = html.replace(/ class= >/gi, '');
  6790. }
  6791. return html;
  6792. },
  6793. /**
  6794. * @method filter_all_rgb
  6795. * @param String str The HTML string to filter
  6796. * @description Converts all RGB color strings found in passed string to a hex color, example: style="color: rgb(0, 255, 0)" converts to style="color: #00ff00"
  6797. * @return String
  6798. */
  6799. filter_all_rgb: function(str) {
  6800. var exp = new RegExp("rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)", "gi");
  6801. var arr = str.match(exp);
  6802. if (Lang.isArray(arr)) {
  6803. for (var i = 0; i < arr.length; i++) {
  6804. var color = this.filter_rgb(arr[i]);
  6805. str = str.replace(arr[i].toString(), color);
  6806. }
  6807. }
  6808. return str;
  6809. },
  6810. /**
  6811. * @method filter_rgb
  6812. * @param String css The CSS string containing rgb(#,#,#);
  6813. * @description Converts an RGB color string to a hex color, example: rgb(0, 255, 0) converts to #00ff00
  6814. * @return String
  6815. */
  6816. filter_rgb: function(css) {
  6817. if (css.toLowerCase().indexOf('rgb') != -1) {
  6818. var exp = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi");
  6819. var rgb = css.replace(exp, "$1,$2,$3,$4,$5").split(',');
  6820. if (rgb.length == 5) {
  6821. var r = parseInt(rgb[1], 10).toString(16);
  6822. var g = parseInt(rgb[2], 10).toString(16);
  6823. var b = parseInt(rgb[3], 10).toString(16);
  6824. r = r.length == 1 ? '0' + r : r;
  6825. g = g.length == 1 ? '0' + g : g;
  6826. b = b.length == 1 ? '0' + b : b;
  6827. css = "#" + r + g + b;
  6828. }
  6829. }
  6830. return css;
  6831. },
  6832. /**
  6833. * @method pre_filter_linebreaks
  6834. * @param String html The HTML to filter
  6835. * @param String markup The markup type to filter to
  6836. * @description HTML Pre Filter
  6837. * @return String
  6838. */
  6839. pre_filter_linebreaks: function(html, markup) {
  6840. if (this.browser.webkit) {
  6841. html = html.replace(/<br class="khtml-block-placeholder">/gi, '<YUI_BR>');
  6842. html = html.replace(/<br class="webkit-block-placeholder">/gi, '<YUI_BR>');
  6843. }
  6844. html = html.replace(/<br>/gi, '<YUI_BR>');
  6845. html = html.replace(/<br (.*?)>/gi, '<YUI_BR>');
  6846. html = html.replace(/<br\/>/gi, '<YUI_BR>');
  6847. html = html.replace(/<br \/>/gi, '<YUI_BR>');
  6848. html = html.replace(/<div><YUI_BR><\/div>/gi, '<YUI_BR>');
  6849. html = html.replace(/<p>(&nbsp;|&#160;)<\/p>/g, '<YUI_BR>');
  6850. html = html.replace(/<p><br>&nbsp;<\/p>/gi, '<YUI_BR>');
  6851. html = html.replace(/<p>&nbsp;<\/p>/gi, '<YUI_BR>');
  6852. //Fix last BR
  6853. html = html.replace(/<YUI_BR>$/, '');
  6854. //Fix last BR in P
  6855. html = html.replace(/<YUI_BR><\/p>/g, '</p>');
  6856. if (this.browser.ie) {
  6857. html = html.replace(/&nbsp;&nbsp;&nbsp;&nbsp;/g, '\t');
  6858. }
  6859. return html;
  6860. },
  6861. /**
  6862. * @method post_filter_linebreaks
  6863. * @param String html The HTML to filter
  6864. * @param String markup The markup type to filter to
  6865. * @description HTML Pre Filter
  6866. * @return String
  6867. */
  6868. post_filter_linebreaks: function(html, markup) {
  6869. if (markup == 'xhtml') {
  6870. html = html.replace(/<YUI_BR>/g, '<br />');
  6871. } else {
  6872. html = html.replace(/<YUI_BR>/g, '<br>');
  6873. }
  6874. return html;
  6875. },
  6876. /**
  6877. * @method clearEditorDoc
  6878. * @description Clear the doc of the Editor
  6879. */
  6880. clearEditorDoc: function() {
  6881. this._getDoc().body.innerHTML = '&nbsp;';
  6882. },
  6883. /**
  6884. * @method openWindow
  6885. * @description Override Method for Advanced Editor
  6886. */
  6887. openWindow: function(win) {
  6888. },
  6889. /**
  6890. * @method moveWindow
  6891. * @description Override Method for Advanced Editor
  6892. */
  6893. moveWindow: function() {
  6894. },
  6895. /**
  6896. * @private
  6897. * @method _closeWindow
  6898. * @description Override Method for Advanced Editor
  6899. */
  6900. _closeWindow: function() {
  6901. },
  6902. /**
  6903. * @method closeWindow
  6904. * @description Override Method for Advanced Editor
  6905. */
  6906. closeWindow: function() {
  6907. //this.unsubscribeAll('afterExecCommand');
  6908. this.toolbar.resetAllButtons();
  6909. this.focus();
  6910. },
  6911. /**
  6912. * @method destroy
  6913. * @description Destroys the editor, all of it's elements and objects.
  6914. * @return {Boolean}
  6915. */
  6916. destroy: function() {
  6917. if (this._nodeChangeDelayTimer) {
  6918. clearTimeout(this._nodeChangeDelayTimer);
  6919. }
  6920. this.hide();
  6921. YAHOO.log('Destroying Editor', 'warn', 'SimpleEditor');
  6922. if (this.resize) {
  6923. YAHOO.log('Destroying Resize', 'warn', 'SimpleEditor');
  6924. this.resize.destroy();
  6925. }
  6926. if (this.dd) {
  6927. YAHOO.log('Unreg DragDrop Instance', 'warn', 'SimpleEditor');
  6928. this.dd.unreg();
  6929. }
  6930. if (this.get('panel')) {
  6931. YAHOO.log('Destroying Editor Panel', 'warn', 'SimpleEditor');
  6932. this.get('panel').destroy();
  6933. }
  6934. this.saveHTML();
  6935. this.toolbar.destroy();
  6936. YAHOO.log('Restoring TextArea', 'info', 'SimpleEditor');
  6937. this.setStyle('visibility', 'visible');
  6938. this.setStyle('position', 'static');
  6939. this.setStyle('top', '');
  6940. this.setStyle('left', '');
  6941. var textArea = this.get('element');
  6942. this.get('element_cont').get('parentNode').replaceChild(textArea, this.get('element_cont').get('element'));
  6943. this.get('element_cont').get('element').innerHTML = '';
  6944. this.set('handleSubmit', false); //Remove the submit handler
  6945. return true;
  6946. },
  6947. /**
  6948. * @method toString
  6949. * @description Returns a string representing the editor.
  6950. * @return {String}
  6951. */
  6952. toString: function() {
  6953. var str = 'SimpleEditor';
  6954. if (this.get && this.get('element_cont')) {
  6955. str = 'SimpleEditor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
  6956. }
  6957. return str;
  6958. }
  6959. });
  6960. /**
  6961. * @event toolbarLoaded
  6962. * @description Event is fired during the render process directly after the Toolbar is loaded. Allowing you to attach events to the toolbar. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6963. * @type YAHOO.util.CustomEvent
  6964. */
  6965. /**
  6966. * @event cleanHTML
  6967. * @description Event is fired after the cleanHTML method is called.
  6968. * @type YAHOO.util.CustomEvent
  6969. */
  6970. /**
  6971. * @event afterRender
  6972. * @description Event is fired after the render process finishes. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6973. * @type YAHOO.util.CustomEvent
  6974. */
  6975. /**
  6976. * @event editorContentLoaded
  6977. * @description Event is fired after the editor iframe's document fully loads and fires it's onload event. From here you can start injecting your own things into the document. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6978. * @type YAHOO.util.CustomEvent
  6979. */
  6980. /**
  6981. * @event beforeNodeChange
  6982. * @description Event fires at the beginning of the nodeChange process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6983. * @type YAHOO.util.CustomEvent
  6984. */
  6985. /**
  6986. * @event afterNodeChange
  6987. * @description Event fires at the end of the nodeChange process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6988. * @type YAHOO.util.CustomEvent
  6989. */
  6990. /**
  6991. * @event beforeExecCommand
  6992. * @description Event fires at the beginning of the execCommand process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6993. * @type YAHOO.util.CustomEvent
  6994. */
  6995. /**
  6996. * @event afterExecCommand
  6997. * @description Event fires at the end of the execCommand process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  6998. * @type YAHOO.util.CustomEvent
  6999. */
  7000. /**
  7001. * @event editorMouseUp
  7002. * @param {Event} ev The DOM Event that occured
  7003. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7004. * @type YAHOO.util.CustomEvent
  7005. */
  7006. /**
  7007. * @event editorMouseDown
  7008. * @param {Event} ev The DOM Event that occured
  7009. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7010. * @type YAHOO.util.CustomEvent
  7011. */
  7012. /**
  7013. * @event editorDoubleClick
  7014. * @param {Event} ev The DOM Event that occured
  7015. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7016. * @type YAHOO.util.CustomEvent
  7017. */
  7018. /**
  7019. * @event editorClick
  7020. * @param {Event} ev The DOM Event that occured
  7021. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7022. * @type YAHOO.util.CustomEvent
  7023. */
  7024. /**
  7025. * @event editorKeyUp
  7026. * @param {Event} ev The DOM Event that occured
  7027. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7028. * @type YAHOO.util.CustomEvent
  7029. */
  7030. /**
  7031. * @event editorKeyPress
  7032. * @param {Event} ev The DOM Event that occured
  7033. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7034. * @type YAHOO.util.CustomEvent
  7035. */
  7036. /**
  7037. * @event editorKeyDown
  7038. * @param {Event} ev The DOM Event that occured
  7039. * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
  7040. * @type YAHOO.util.CustomEvent
  7041. */
  7042. /**
  7043. * @event beforeEditorMouseUp
  7044. * @param {Event} ev The DOM Event that occured
  7045. * @description Fires before editor event, returning false will stop the internal processing.
  7046. * @type YAHOO.util.CustomEvent
  7047. */
  7048. /**
  7049. * @event beforeEditorMouseDown
  7050. * @param {Event} ev The DOM Event that occured
  7051. * @description Fires before editor event, returning false will stop the internal processing.
  7052. * @type YAHOO.util.CustomEvent
  7053. */
  7054. /**
  7055. * @event beforeEditorDoubleClick
  7056. * @param {Event} ev The DOM Event that occured
  7057. * @description Fires before editor event, returning false will stop the internal processing.
  7058. * @type YAHOO.util.CustomEvent
  7059. */
  7060. /**
  7061. * @event beforeEditorClick
  7062. * @param {Event} ev The DOM Event that occured
  7063. * @description Fires before editor event, returning false will stop the internal processing.
  7064. * @type YAHOO.util.CustomEvent
  7065. */
  7066. /**
  7067. * @event beforeEditorKeyUp
  7068. * @param {Event} ev The DOM Event that occured
  7069. * @description Fires before editor event, returning false will stop the internal processing.
  7070. * @type YAHOO.util.CustomEvent
  7071. */
  7072. /**
  7073. * @event beforeEditorKeyPress
  7074. * @param {Event} ev The DOM Event that occured
  7075. * @description Fires before editor event, returning false will stop the internal processing.
  7076. * @type YAHOO.util.CustomEvent
  7077. */
  7078. /**
  7079. * @event beforeEditorKeyDown
  7080. * @param {Event} ev The DOM Event that occured
  7081. * @description Fires before editor event, returning false will stop the internal processing.
  7082. * @type YAHOO.util.CustomEvent
  7083. */
  7084. /**
  7085. * @event editorWindowFocus
  7086. * @description Fires when the iframe is focused. Note, this is window focus event, not an Editor focus event.
  7087. * @type YAHOO.util.CustomEvent
  7088. */
  7089. /**
  7090. * @event editorWindowBlur
  7091. * @description Fires when the iframe is blurred. Note, this is window blur event, not an Editor blur event.
  7092. * @type YAHOO.util.CustomEvent
  7093. */
  7094. /**
  7095. * @description Singleton object used to track the open window objects and panels across the various open editors
  7096. * @class EditorInfo
  7097. * @static
  7098. */
  7099. YAHOO.widget.EditorInfo = {
  7100. /**
  7101. * @private
  7102. * @property _instances
  7103. * @description A reference to all editors on the page.
  7104. * @type Object
  7105. */
  7106. _instances: {},
  7107. /**
  7108. * @private
  7109. * @property blankImage
  7110. * @description A reference to the blankImage url
  7111. * @type String
  7112. */
  7113. blankImage: '',
  7114. /**
  7115. * @private
  7116. * @property window
  7117. * @description A reference to the currently open window object in any editor on the page.
  7118. * @type Object <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>
  7119. */
  7120. window: {},
  7121. /**
  7122. * @private
  7123. * @property panel
  7124. * @description A reference to the currently open panel in any editor on the page.
  7125. * @type Object <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>
  7126. */
  7127. panel: null,
  7128. /**
  7129. * @method getEditorById
  7130. * @description Returns a reference to the Editor object associated with the given textarea
  7131. * @param {String/HTMLElement} id The id or reference of the textarea to return the Editor instance of
  7132. * @return Object <a href="YAHOO.widget.Editor.html">YAHOO.widget.Editor</a>
  7133. */
  7134. getEditorById: function(id) {
  7135. if (!YAHOO.lang.isString(id)) {
  7136. //Not a string, assume a node Reference
  7137. id = id.id;
  7138. }
  7139. if (this._instances[id]) {
  7140. return this._instances[id];
  7141. }
  7142. return false;
  7143. },
  7144. /**
  7145. * @method saveAll
  7146. * @description Saves all Editor instances on the page. If a form reference is passed, only Editor's bound to this form will be saved.
  7147. * @param {HTMLElement} form The form to check if this Editor instance belongs to
  7148. */
  7149. saveAll: function(form) {
  7150. var i, e, items = YAHOO.widget.EditorInfo._instances;
  7151. if (form) {
  7152. for (i in items) {
  7153. if (Lang.hasOwnProperty(items, i)) {
  7154. e = items[i];
  7155. if (e.get('element').form && (e.get('element').form == form)) {
  7156. e.saveHTML();
  7157. }
  7158. }
  7159. }
  7160. } else {
  7161. for (i in items) {
  7162. if (Lang.hasOwnProperty(items, i)) {
  7163. items[i].saveHTML();
  7164. }
  7165. }
  7166. }
  7167. },
  7168. /**
  7169. * @method toString
  7170. * @description Returns a string representing the EditorInfo.
  7171. * @return {String}
  7172. */
  7173. toString: function() {
  7174. var len = 0;
  7175. for (var i in this._instances) {
  7176. if (Lang.hasOwnProperty(this._instances, i)) {
  7177. len++;
  7178. }
  7179. }
  7180. return 'Editor Info (' + len + ' registered intance' + ((len > 1) ? 's' : '') + ')';
  7181. }
  7182. };
  7183. })();
  7184. YAHOO.register("simpleeditor", YAHOO.widget.SimpleEditor, {version: "2.8.0r4", build: "2449"});