umeditor.js 379 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935
  1. /*!
  2. * UEditor Mini版本
  3. * version: 1.2.2
  4. * build: Wed Mar 19 2014 17:08:14 GMT+0800 (中国标准时间)
  5. */
  6. (function($){
  7. UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};
  8. window.UM = {
  9. plugins : {},
  10. commands : {},
  11. I18N : {},
  12. version : "1.2.2"
  13. };
  14. var dom = UM.dom = {};
  15. /**
  16. * 浏览器判断模块
  17. * @file
  18. * @module UE.browser
  19. * @since 1.2.6.1
  20. */
  21. /**
  22. * 提供浏览器检测的模块
  23. * @unfile
  24. * @module UE.browser
  25. */
  26. var browser = UM.browser = function(){
  27. var agent = navigator.userAgent.toLowerCase(),
  28. opera = window.opera,
  29. browser = {
  30. /**
  31. * @property {boolean} ie 检测当前浏览器是否为IE
  32. * @example
  33. * ```javascript
  34. * if ( UE.browser.ie ) {
  35. * console.log( '当前浏览器是IE' );
  36. * }
  37. * ```
  38. */
  39. ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  40. /**
  41. * @property {boolean} opera 检测当前浏览器是否为Opera
  42. * @example
  43. * ```javascript
  44. * if ( UE.browser.opera ) {
  45. * console.log( '当前浏览器是Opera' );
  46. * }
  47. * ```
  48. */
  49. opera : ( !!opera && opera.version ),
  50. /**
  51. * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
  52. * @example
  53. * ```javascript
  54. * if ( UE.browser.webkit ) {
  55. * console.log( '当前浏览器是webkit内核浏览器' );
  56. * }
  57. * ```
  58. */
  59. webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ),
  60. /**
  61. * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
  62. * @example
  63. * ```javascript
  64. * if ( UE.browser.mac ) {
  65. * console.log( '当前浏览器运行在mac平台下' );
  66. * }
  67. * ```
  68. */
  69. mac : ( agent.indexOf( 'macintosh' ) > -1 ),
  70. /**
  71. * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
  72. * @example
  73. * ```javascript
  74. * if ( UE.browser.quirks ) {
  75. * console.log( '当前浏览器运行处于“怪异模式”' );
  76. * }
  77. * ```
  78. */
  79. quirks : ( document.compatMode == 'BackCompat' )
  80. };
  81. /**
  82. * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
  83. * @example
  84. * ```javascript
  85. * if ( UE.browser.gecko ) {
  86. * console.log( '当前浏览器内核是gecko内核' );
  87. * }
  88. * ```
  89. */
  90. browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
  91. var version = 0;
  92. // Internet Explorer 6.0+
  93. if ( browser.ie ){
  94. var v1 = agent.match(/(?:msie\s([\w.]+))/);
  95. var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
  96. if(v1 && v2 && v1[1] && v2[1]){
  97. version = Math.max(v1[1]*1,v2[1]*1);
  98. }else if(v1 && v1[1]){
  99. version = v1[1]*1;
  100. }else if(v2 && v2[1]){
  101. version = v2[1]*1;
  102. }else{
  103. version = 0;
  104. }
  105. browser.ie11Compat = document.documentMode == 11;
  106. /**
  107. * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
  108. * @warning 如果浏览器不是IE, 则该值为undefined
  109. * @example
  110. * ```javascript
  111. * if ( UE.browser.ie9Compat ) {
  112. * console.log( '当前浏览器运行在IE9兼容模式下' );
  113. * }
  114. * ```
  115. */
  116. browser.ie9Compat = document.documentMode == 9;
  117. /**
  118. * @property { boolean } ie8 检测浏览器是否是IE8浏览器
  119. * @warning 如果浏览器不是IE, 则该值为undefined
  120. * @example
  121. * ```javascript
  122. * if ( UE.browser.ie8 ) {
  123. * console.log( '当前浏览器是IE8浏览器' );
  124. * }
  125. * ```
  126. */
  127. browser.ie8 = !!document.documentMode;
  128. /**
  129. * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
  130. * @warning 如果浏览器不是IE, 则该值为undefined
  131. * @example
  132. * ```javascript
  133. * if ( UE.browser.ie8Compat ) {
  134. * console.log( '当前浏览器运行在IE8兼容模式下' );
  135. * }
  136. * ```
  137. */
  138. browser.ie8Compat = document.documentMode == 8;
  139. /**
  140. * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
  141. * @warning 如果浏览器不是IE, 则该值为undefined
  142. * @example
  143. * ```javascript
  144. * if ( UE.browser.ie7Compat ) {
  145. * console.log( '当前浏览器运行在IE7兼容模式下' );
  146. * }
  147. * ```
  148. */
  149. browser.ie7Compat = ( ( version == 7 && !document.documentMode )
  150. || document.documentMode == 7 );
  151. /**
  152. * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
  153. * @warning 如果浏览器不是IE, 则该值为undefined
  154. * @example
  155. * ```javascript
  156. * if ( UE.browser.ie6Compat ) {
  157. * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
  158. * }
  159. * ```
  160. */
  161. browser.ie6Compat = ( version < 7 || browser.quirks );
  162. browser.ie9above = version > 8;
  163. browser.ie9below = version < 9;
  164. }
  165. // Gecko.
  166. if ( browser.gecko ){
  167. var geckoRelease = agent.match( /rv:([\d\.]+)/ );
  168. if ( geckoRelease )
  169. {
  170. geckoRelease = geckoRelease[1].split( '.' );
  171. version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
  172. }
  173. }
  174. /**
  175. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  176. * @warning 如果浏览器不是chrome, 则该值为undefined
  177. * @example
  178. * ```javascript
  179. * if ( UE.browser.chrome ) {
  180. * console.log( '当前浏览器是Chrome' );
  181. * }
  182. * ```
  183. */
  184. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  185. browser.chrome = + RegExp['\x241'];
  186. }
  187. /**
  188. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  189. * @warning 如果浏览器不是safari, 则该值为undefined
  190. * @example
  191. * ```javascript
  192. * if ( UE.browser.safari ) {
  193. * console.log( '当前浏览器是Safari' );
  194. * }
  195. * ```
  196. */
  197. if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
  198. browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
  199. }
  200. // Opera 9.50+
  201. if ( browser.opera )
  202. version = parseFloat( opera.version() );
  203. // WebKit 522+ (Safari 3+)
  204. if ( browser.webkit )
  205. version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
  206. /**
  207. * @property { Number } version 检测当前浏览器版本号
  208. * @remind
  209. * <ul>
  210. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  211. * <li>gecko系列会返回10900,158900等</li>
  212. * <li>webkit系列会返回其build号 (如 522等)</li>
  213. * </ul>
  214. * @example
  215. * ```javascript
  216. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  217. * ```
  218. */
  219. browser.version = version;
  220. /**
  221. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  222. * @example
  223. * ```javascript
  224. * if ( UE.browser.isCompatible ) {
  225. * console.log( '浏览器与UEditor能够良好兼容' );
  226. * }
  227. * ```
  228. */
  229. browser.isCompatible =
  230. !browser.mobile && (
  231. ( browser.ie && version >= 6 ) ||
  232. ( browser.gecko && version >= 10801 ) ||
  233. ( browser.opera && version >= 9.5 ) ||
  234. ( browser.air && version >= 1 ) ||
  235. ( browser.webkit && version >= 522 ) ||
  236. false );
  237. return browser;
  238. }();
  239. //快捷方式
  240. var ie = browser.ie,
  241. webkit = browser.webkit,
  242. gecko = browser.gecko,
  243. opera = browser.opera;
  244. /**
  245. * @file
  246. * @name UM.Utils
  247. * @short Utils
  248. * @desc UEditor封装使用的静态工具函数
  249. * @import editor.js
  250. */
  251. var utils = UM.utils = {
  252. /**
  253. * 遍历数组,对象,nodeList
  254. * @name each
  255. * @grammar UM.utils.each(obj,iterator,[context])
  256. * @since 1.2.4+
  257. * @desc
  258. * * obj 要遍历的对象
  259. * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
  260. * * context iterator的上下文
  261. * @example
  262. * UM.utils.each([1,2],function(v,i){
  263. * console.log(v)//值
  264. * console.log(i)//索引
  265. * })
  266. * UM.utils.each(document.getElementsByTagName('*'),function(n){
  267. * console.log(n.tagName)
  268. * })
  269. */
  270. each : function(obj, iterator, context) {
  271. if (obj == null) return;
  272. if (obj.length === +obj.length) {
  273. for (var i = 0, l = obj.length; i < l; i++) {
  274. if(iterator.call(context, obj[i], i, obj) === false)
  275. return false;
  276. }
  277. } else {
  278. for (var key in obj) {
  279. if (obj.hasOwnProperty(key)) {
  280. if(iterator.call(context, obj[key], key, obj) === false)
  281. return false;
  282. }
  283. }
  284. }
  285. },
  286. makeInstance:function (obj) {
  287. var noop = new Function();
  288. noop.prototype = obj;
  289. obj = new noop;
  290. noop.prototype = null;
  291. return obj;
  292. },
  293. /**
  294. * 将source对象中的属性扩展到target对象上
  295. * @name extend
  296. * @grammar UM.utils.extend(target,source) => Object //覆盖扩展
  297. * @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展
  298. */
  299. extend:function (t, s, b) {
  300. if (s) {
  301. for (var k in s) {
  302. if (!b || !t.hasOwnProperty(k)) {
  303. t[k] = s[k];
  304. }
  305. }
  306. }
  307. return t;
  308. },
  309. extend2:function (t) {
  310. var a = arguments;
  311. for (var i = 1; i < a.length; i++) {
  312. var x = a[i];
  313. for (var k in x) {
  314. if (!t.hasOwnProperty(k)) {
  315. t[k] = x[k];
  316. }
  317. }
  318. }
  319. return t;
  320. },
  321. /**
  322. * 模拟继承机制,subClass继承superClass
  323. * @name inherits
  324. * @grammar UM.utils.inherits(subClass,superClass) => subClass
  325. * @example
  326. * function SuperClass(){
  327. * this.name = "小李";
  328. * }
  329. * SuperClass.prototype = {
  330. * hello:function(str){
  331. * console.log(this.name + str);
  332. * }
  333. * }
  334. * function SubClass(){
  335. * this.name = "小张";
  336. * }
  337. * UM.utils.inherits(SubClass,SuperClass);
  338. * var sub = new SubClass();
  339. * sub.hello("早上好!"); ==> "小张早上好!"
  340. */
  341. inherits:function (subClass, superClass) {
  342. var oldP = subClass.prototype,
  343. newP = utils.makeInstance(superClass.prototype);
  344. utils.extend(newP, oldP, true);
  345. subClass.prototype = newP;
  346. return (newP.constructor = subClass);
  347. },
  348. /**
  349. * 用指定的context作为fn上下文,也就是this
  350. * @name bind
  351. * @grammar UM.utils.bind(fn,context) => fn
  352. */
  353. bind:function (fn, context) {
  354. return function () {
  355. return fn.apply(context, arguments);
  356. };
  357. },
  358. /**
  359. * 创建延迟delay执行的函数fn
  360. * @name defer
  361. * @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn
  362. * @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
  363. * @example
  364. * function test(){
  365. * console.log("延迟输出!");
  366. * }
  367. * //非互斥延迟执行
  368. * var testDefer = UM.utils.defer(test,1000);
  369. * testDefer(); => "延迟输出!";
  370. * testDefer(); => "延迟输出!";
  371. * //互斥延迟执行
  372. * var testDefer1 = UM.utils.defer(test,1000,true);
  373. * testDefer1(); => //本次不执行
  374. * testDefer1(); => "延迟输出!";
  375. */
  376. defer:function (fn, delay, exclusion) {
  377. var timerID;
  378. return function () {
  379. if (exclusion) {
  380. clearTimeout(timerID);
  381. }
  382. timerID = setTimeout(fn, delay);
  383. };
  384. },
  385. /**
  386. * 查找元素item在数组array中的索引, 若找不到返回-1
  387. * @name indexOf
  388. * @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索
  389. * @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置
  390. */
  391. indexOf:function (array, item, start) {
  392. var index = -1;
  393. start = this.isNumber(start) ? start : 0;
  394. this.each(array, function (v, i) {
  395. if (i >= start && v === item) {
  396. index = i;
  397. return false;
  398. }
  399. });
  400. return index;
  401. },
  402. /**
  403. * 移除数组array中的元素item
  404. * @name removeItem
  405. * @grammar UM.utils.removeItem(array,item)
  406. */
  407. removeItem:function (array, item) {
  408. for (var i = 0, l = array.length; i < l; i++) {
  409. if (array[i] === item) {
  410. array.splice(i, 1);
  411. i--;
  412. }
  413. }
  414. },
  415. /**
  416. * 删除字符串str的首尾空格
  417. * @name trim
  418. * @grammar UM.utils.trim(str) => String
  419. */
  420. trim:function (str) {
  421. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
  422. },
  423. /**
  424. * 将字符串list(以','分隔)或者数组list转成哈希对象
  425. * @name listToMap
  426. * @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1}
  427. */
  428. listToMap:function (list) {
  429. if (!list)return {};
  430. list = utils.isArray(list) ? list : list.split(',');
  431. for (var i = 0, ci, obj = {}; ci = list[i++];) {
  432. obj[ci.toUpperCase()] = obj[ci] = 1;
  433. }
  434. return obj;
  435. },
  436. /**
  437. * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符
  438. * @name unhtml
  439. * @grammar UM.utils.unhtml(str); => String
  440. * @grammar UM.utils.unhtml(str,reg) => String
  441. * @example
  442. * var html = '<body>You say:"你好!Baidu & UEditor!"</body>';
  443. * UM.utils.unhtml(html); ==> &lt;body&gt;You say:&quot;你好!Baidu &amp; UEditor!&quot;&lt;/body&gt;
  444. * UM.utils.unhtml(html,/[<>]/g) ==> &lt;body&gt;You say:"你好!Baidu & UEditor!"&lt;/body&gt;
  445. */
  446. unhtml:function (str, reg) {
  447. return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
  448. if (b) {
  449. return a;
  450. } else {
  451. return {
  452. '<':'&lt;',
  453. '&':'&amp;',
  454. '"':'&quot;',
  455. '>':'&gt;',
  456. "'":'&#39;'
  457. }[a]
  458. }
  459. }) : '';
  460. },
  461. /**
  462. * 将str中的转义字符还原成html字符
  463. * @name html
  464. * @grammar UM.utils.html(str) => String //详细参见<code><a href = '#unhtml'>unhtml</a></code>
  465. */
  466. html:function (str) {
  467. return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
  468. return {
  469. '&lt;':'<',
  470. '&amp;':'&',
  471. '&quot;':'"',
  472. '&gt;':'>',
  473. '&#39;':"'"
  474. }[m]
  475. }) : '';
  476. },
  477. /**
  478. * 将css样式转换为驼峰的形式。如font-size => fontSize
  479. * @name cssStyleToDomStyle
  480. * @grammar UM.utils.cssStyleToDomStyle(cssName) => String
  481. */
  482. cssStyleToDomStyle:function () {
  483. var test = document.createElement('div').style,
  484. cache = {
  485. 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
  486. };
  487. return function (cssName) {
  488. return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
  489. return match.charAt(1).toUpperCase();
  490. }));
  491. };
  492. }(),
  493. /**
  494. * 动态加载文件到doc中,并依据obj来设置属性,加载成功后执行回调函数fn
  495. * @name loadFile
  496. * @grammar UM.utils.loadFile(doc,obj)
  497. * @grammar UM.utils.loadFile(doc,obj,fn)
  498. * @example
  499. * //指定加载到当前document中一个script文件,加载成功后执行function
  500. * utils.loadFile( document, {
  501. * src:"test.js",
  502. * tag:"script",
  503. * type:"text/javascript",
  504. * defer:"defer"
  505. * }, function () {
  506. * console.log('加载成功!')
  507. * });
  508. */
  509. loadFile:function () {
  510. var tmpList = [];
  511. function getItem(doc, obj) {
  512. try {
  513. for (var i = 0, ci; ci = tmpList[i++];) {
  514. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  515. return ci;
  516. }
  517. }
  518. } catch (e) {
  519. return null;
  520. }
  521. }
  522. return function (doc, obj, fn) {
  523. var item = getItem(doc, obj);
  524. if (item) {
  525. if (item.ready) {
  526. fn && fn();
  527. } else {
  528. item.funs.push(fn)
  529. }
  530. return;
  531. }
  532. tmpList.push({
  533. doc:doc,
  534. url:obj.src || obj.href,
  535. funs:[fn]
  536. });
  537. if (!doc.body) {
  538. var html = [];
  539. for (var p in obj) {
  540. if (p == 'tag')continue;
  541. html.push(p + '="' + obj[p] + '"')
  542. }
  543. doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
  544. return;
  545. }
  546. if (obj.id && doc.getElementById(obj.id)) {
  547. return;
  548. }
  549. var element = doc.createElement(obj.tag);
  550. delete obj.tag;
  551. for (var p in obj) {
  552. element.setAttribute(p, obj[p]);
  553. }
  554. element.onload = element.onreadystatechange = function () {
  555. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  556. item = getItem(doc, obj);
  557. if (item.funs.length > 0) {
  558. item.ready = 1;
  559. for (var fi; fi = item.funs.pop();) {
  560. fi();
  561. }
  562. }
  563. element.onload = element.onreadystatechange = null;
  564. }
  565. };
  566. element.onerror = function () {
  567. throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file umeditor.config.js ')
  568. };
  569. doc.getElementsByTagName("head")[0].appendChild(element);
  570. }
  571. }(),
  572. /**
  573. * 判断obj对象是否为空
  574. * @name isEmptyObject
  575. * @grammar UM.utils.isEmptyObject(obj) => true|false
  576. * @example
  577. * UM.utils.isEmptyObject({}) ==>true
  578. * UM.utils.isEmptyObject([]) ==>true
  579. * UM.utils.isEmptyObject("") ==>true
  580. */
  581. isEmptyObject:function (obj) {
  582. if (obj == null) return true;
  583. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
  584. for (var key in obj) if (obj.hasOwnProperty(key)) return false;
  585. return true;
  586. },
  587. /**
  588. * 统一将颜色值使用16进制形式表示
  589. * @name fixColor
  590. * @grammar UM.utils.fixColor(name,value) => value
  591. * @example
  592. * rgb(255,255,255) => "#ffffff"
  593. */
  594. fixColor:function (name, value) {
  595. if (/color/i.test(name) && /rgba?/.test(value)) {
  596. var array = value.split(",");
  597. if (array.length > 3)
  598. return "";
  599. value = "#";
  600. for (var i = 0, color; color = array[i++];) {
  601. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
  602. value += color.length == 1 ? "0" + color : color;
  603. }
  604. value = value.toUpperCase();
  605. }
  606. return value;
  607. },
  608. /**
  609. * 深度克隆对象,从source到target
  610. * @name clone
  611. * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
  612. * @grammar UM.utils.clone(source,target) => target包含了source的所有内容,重名会覆盖
  613. */
  614. clone:function (source, target) {
  615. var tmp;
  616. target = target || {};
  617. for (var i in source) {
  618. if (source.hasOwnProperty(i)) {
  619. tmp = source[i];
  620. if (typeof tmp == 'object') {
  621. target[i] = utils.isArray(tmp) ? [] : {};
  622. utils.clone(source[i], target[i])
  623. } else {
  624. target[i] = tmp;
  625. }
  626. }
  627. }
  628. return target;
  629. },
  630. /**
  631. * 转换cm/pt到px
  632. * @name transUnitToPx
  633. * @grammar UM.utils.transUnitToPx('20pt') => '27px'
  634. * @grammar UM.utils.transUnitToPx('0pt') => '0'
  635. */
  636. transUnitToPx:function (val) {
  637. if (!/(pt|cm)/.test(val)) {
  638. return val
  639. }
  640. var unit;
  641. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  642. val = v;
  643. unit = u;
  644. });
  645. switch (unit) {
  646. case 'cm':
  647. val = parseFloat(val) * 25;
  648. break;
  649. case 'pt':
  650. val = Math.round(parseFloat(val) * 96 / 72);
  651. }
  652. return val + (val ? 'px' : '');
  653. },
  654. /**
  655. * 动态添加css样式
  656. * @name cssRule
  657. * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  658. * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  659. * @grammar UM.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  660. * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  661. */
  662. cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
  663. var indexList, index;
  664. doc = doc || document;
  665. if (doc.indexList) {
  666. indexList = doc.indexList;
  667. } else {
  668. indexList = doc.indexList = {};
  669. }
  670. var sheetStyle;
  671. if (!indexList[key]) {
  672. if (style === undefined) {
  673. return ''
  674. }
  675. sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
  676. indexList[key] = index;
  677. } else {
  678. sheetStyle = doc.styleSheets[indexList[key]];
  679. }
  680. if (style === undefined) {
  681. return sheetStyle.cssText
  682. }
  683. sheetStyle.cssText = style || ''
  684. } : function (key, style, doc) {
  685. doc = doc || document;
  686. var head = doc.getElementsByTagName('head')[0], node;
  687. if (!(node = doc.getElementById(key))) {
  688. if (style === undefined) {
  689. return ''
  690. }
  691. node = doc.createElement('style');
  692. node.id = key;
  693. head.appendChild(node)
  694. }
  695. if (style === undefined) {
  696. return node.innerHTML
  697. }
  698. if (style !== '') {
  699. node.innerHTML = style;
  700. } else {
  701. head.removeChild(node)
  702. }
  703. }
  704. };
  705. /**
  706. * 判断str是否为字符串
  707. * @name isString
  708. * @grammar UM.utils.isString(str) => true|false
  709. */
  710. /**
  711. * 判断array是否为数组
  712. * @name isArray
  713. * @grammar UM.utils.isArray(obj) => true|false
  714. */
  715. /**
  716. * 判断obj对象是否为方法
  717. * @name isFunction
  718. * @grammar UM.utils.isFunction(obj) => true|false
  719. */
  720. /**
  721. * 判断obj对象是否为数字
  722. * @name isNumber
  723. * @grammar UM.utils.isNumber(obj) => true|false
  724. */
  725. utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
  726. UM.utils['is' + v] = function (obj) {
  727. return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
  728. }
  729. });
  730. /**
  731. * @file
  732. * @name UM.EventBase
  733. * @short EventBase
  734. * @import editor.js,core/utils.js
  735. * @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  736. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  737. */
  738. var EventBase = UM.EventBase = function () {};
  739. EventBase.prototype = {
  740. /**
  741. * 注册事件监听器
  742. * @name addListener
  743. * @grammar editor.addListener(types,fn) //types为事件名称,多个可用空格分隔
  744. * @example
  745. * editor.addListener('selectionchange',function(){
  746. * console.log("选区已经变化!");
  747. * })
  748. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  749. * if(type == 'beforegetcontent'){
  750. * //do something
  751. * }else{
  752. * //do something
  753. * }
  754. * console.log(this.getContent) // this是注册的事件的编辑器实例
  755. * })
  756. */
  757. addListener:function (types, listener) {
  758. types = utils.trim(types).split(' ');
  759. for (var i = 0, ti; ti = types[i++];) {
  760. getListener(this, ti, true).push(listener);
  761. }
  762. },
  763. /**
  764. * 移除事件监听器
  765. * @name removeListener
  766. * @grammar editor.removeListener(types,fn) //types为事件名称,多个可用空格分隔
  767. * @example
  768. * //changeCallback为方法体
  769. * editor.removeListener("selectionchange",changeCallback);
  770. */
  771. removeListener:function (types, listener) {
  772. types = utils.trim(types).split(' ');
  773. for (var i = 0, ti; ti = types[i++];) {
  774. utils.removeItem(getListener(this, ti) || [], listener);
  775. }
  776. },
  777. /**
  778. * 触发事件
  779. * @name fireEvent
  780. * @grammar editor.fireEvent(types) //types为事件名称,多个可用空格分隔
  781. * @example
  782. * editor.fireEvent("selectionchange");
  783. */
  784. fireEvent:function () {
  785. var types = arguments[0];
  786. types = utils.trim(types).split(' ');
  787. for (var i = 0, ti; ti = types[i++];) {
  788. var listeners = getListener(this, ti),
  789. r, t, k;
  790. if (listeners) {
  791. k = listeners.length;
  792. while (k--) {
  793. if(!listeners[k])continue;
  794. t = listeners[k].apply(this, arguments);
  795. if(t === true){
  796. return t;
  797. }
  798. if (t !== undefined) {
  799. r = t;
  800. }
  801. }
  802. }
  803. if (t = this['on' + ti.toLowerCase()]) {
  804. r = t.apply(this, arguments);
  805. }
  806. }
  807. return r;
  808. }
  809. };
  810. /**
  811. * 获得对象所拥有监听类型的所有监听器
  812. * @public
  813. * @function
  814. * @param {Object} obj 查询监听器的对象
  815. * @param {String} type 事件类型
  816. * @param {Boolean} force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  817. * @returns {Array} 监听器数组
  818. */
  819. function getListener(obj, type, force) {
  820. var allListeners;
  821. type = type.toLowerCase();
  822. return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
  823. && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
  824. }
  825. ///import editor.js
  826. ///import core/dom/dom.js
  827. ///import core/utils.js
  828. /**
  829. * dtd html语义化的体现类
  830. * @constructor
  831. * @namespace dtd
  832. */
  833. var dtd = dom.dtd = (function() {
  834. function _( s ) {
  835. for (var k in s) {
  836. s[k.toUpperCase()] = s[k];
  837. }
  838. return s;
  839. }
  840. var X = utils.extend2;
  841. var A = _({isindex:1,fieldset:1}),
  842. B = _({input:1,button:1,select:1,textarea:1,label:1}),
  843. C = X( _({a:1}), B ),
  844. D = X( {iframe:1}, C ),
  845. E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
  846. F = _({ins:1,del:1,script:1,style:1}),
  847. G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
  848. H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
  849. I = X( _({p:1}), H ),
  850. J = X( _({iframe:1}), H, B ),
  851. K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
  852. L = X( _({a:0}), J ),//a不能被切开,所以把他
  853. M = _({tr:1}),
  854. N = _({'#':1}),
  855. O = X( _({param:1}), K ),
  856. P = X( _({form:1}), A, D, E, I ),
  857. Q = _({li:1,ol:1,ul:1}),
  858. R = _({style:1,script:1}),
  859. S = _({base:1,link:1,meta:1,title:1}),
  860. T = X( S, R ),
  861. U = _({head:1,body:1}),
  862. V = _({html:1});
  863. var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),
  864. empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
  865. return _({
  866. // $ 表示自定的属性
  867. // body外的元素列表.
  868. $nonBodyContent: X( V, U, S ),
  869. //块结构元素列表
  870. $block : block,
  871. //内联元素列表
  872. $inline : L,
  873. $inlineWithA : X(_({a:1}),L),
  874. $body : X( _({script:1,style:1}), block ),
  875. $cdata : _({script:1,style:1}),
  876. //自闭和元素
  877. $empty : empty,
  878. //不是自闭合,但不能让range选中里边
  879. $nonChild : _({iframe:1,textarea:1}),
  880. //列表元素列表
  881. $listItem : _({dd:1,dt:1,li:1}),
  882. //列表根元素列表
  883. $list: _({ul:1,ol:1,dl:1}),
  884. //不能认为是空的元素
  885. $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
  886. //如果没有子节点就可以删除的元素列表,像span,a
  887. $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
  888. $removeEmptyBlock : _({'p':1,'div':1}),
  889. //在table元素里的元素列表
  890. $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
  891. //不转换的标签
  892. $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
  893. html: U,
  894. head: T,
  895. style: N,
  896. script: N,
  897. body: P,
  898. base: {},
  899. link: {},
  900. meta: {},
  901. title: N,
  902. col : {},
  903. tr : _({td:1,th:1}),
  904. img : {},
  905. embed: {},
  906. colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
  907. noscript : P,
  908. td : P,
  909. br : {},
  910. th : P,
  911. center : P,
  912. kbd : L,
  913. button : X( I, E ),
  914. basefont : {},
  915. h5 : L,
  916. h4 : L,
  917. samp : L,
  918. h6 : L,
  919. ol : Q,
  920. h1 : L,
  921. h3 : L,
  922. option : N,
  923. h2 : L,
  924. form : X( A, D, E, I ),
  925. select : _({optgroup:1,option:1}),
  926. font : L,
  927. ins : L,
  928. menu : Q,
  929. abbr : L,
  930. label : L,
  931. table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
  932. code : L,
  933. tfoot : M,
  934. cite : L,
  935. li : P,
  936. input : {},
  937. iframe : P,
  938. strong : L,
  939. textarea : N,
  940. noframes : P,
  941. big : L,
  942. small : L,
  943. //trace:
  944. span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
  945. hr : L,
  946. dt : L,
  947. sub : L,
  948. optgroup : _({option:1}),
  949. param : {},
  950. bdo : L,
  951. 'var' : L,
  952. div : P,
  953. object : O,
  954. sup : L,
  955. dd : P,
  956. strike : L,
  957. area : {},
  958. dir : Q,
  959. map : X( _({area:1,form:1,p:1}), A, F, E ),
  960. applet : O,
  961. dl : _({dt:1,dd:1}),
  962. del : L,
  963. isindex : {},
  964. fieldset : X( _({legend:1}), K ),
  965. thead : M,
  966. ul : Q,
  967. acronym : L,
  968. b : L,
  969. a : X( _({a:1}), J ),
  970. blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
  971. caption : L,
  972. i : L,
  973. u : L,
  974. tbody : M,
  975. s : L,
  976. address : X( D, I ),
  977. tt : L,
  978. legend : L,
  979. q : L,
  980. pre : X( G, C ),
  981. p : X(_({'a':1}),L),
  982. em :L,
  983. dfn : L
  984. });
  985. })();
  986. /**
  987. * @file
  988. * @name UM.dom.domUtils
  989. * @short DomUtils
  990. * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js
  991. * @desc UEditor封装的底层dom操作库
  992. */
  993. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  994. var tmpNode = startFromChild && node[start],
  995. parent;
  996. !tmpNode && (tmpNode = node[ltr]);
  997. while (!tmpNode && (parent = (parent || node).parentNode)) {
  998. if (parent.tagName == 'BODY' || guard && !guard(parent)) {
  999. return null;
  1000. }
  1001. tmpNode = parent[ltr];
  1002. }
  1003. if (tmpNode && fn && !fn(tmpNode)) {
  1004. return getDomNode(tmpNode, start, ltr, false, fn);
  1005. }
  1006. return tmpNode;
  1007. }
  1008. var attrFix = ie && browser.version < 9 ? {
  1009. tabindex: "tabIndex",
  1010. readonly: "readOnly",
  1011. "for": "htmlFor",
  1012. "class": "className",
  1013. maxlength: "maxLength",
  1014. cellspacing: "cellSpacing",
  1015. cellpadding: "cellPadding",
  1016. rowspan: "rowSpan",
  1017. colspan: "colSpan",
  1018. usemap: "useMap",
  1019. frameborder: "frameBorder"
  1020. } : {
  1021. tabindex: "tabIndex",
  1022. readonly: "readOnly"
  1023. },
  1024. styleBlock = utils.listToMap([
  1025. '-webkit-box', '-moz-box', 'block' ,
  1026. 'list-item' , 'table' , 'table-row-group' ,
  1027. 'table-header-group', 'table-footer-group' ,
  1028. 'table-row' , 'table-column-group' , 'table-column' ,
  1029. 'table-cell' , 'table-caption'
  1030. ]);
  1031. var domUtils = dom.domUtils = {
  1032. //节点常量
  1033. NODE_ELEMENT: 1,
  1034. NODE_DOCUMENT: 9,
  1035. NODE_TEXT: 3,
  1036. NODE_COMMENT: 8,
  1037. NODE_DOCUMENT_FRAGMENT: 11,
  1038. //位置关系
  1039. POSITION_IDENTICAL: 0,
  1040. POSITION_DISCONNECTED: 1,
  1041. POSITION_FOLLOWING: 2,
  1042. POSITION_PRECEDING: 4,
  1043. POSITION_IS_CONTAINED: 8,
  1044. POSITION_CONTAINS: 16,
  1045. //ie6使用其他的会有一段空白出现
  1046. fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
  1047. //-------------------------Node部分--------------------------------
  1048. keys: {
  1049. /*Backspace*/ 8: 1, /*Delete*/ 46: 1,
  1050. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  1051. 37: 1, 38: 1, 39: 1, 40: 1,
  1052. 13: 1 /*enter*/
  1053. },
  1054. breakParent:function (node, parent) {
  1055. var tmpNode,
  1056. parentClone = node,
  1057. clone = node,
  1058. leftNodes,
  1059. rightNodes;
  1060. do {
  1061. parentClone = parentClone.parentNode;
  1062. if (leftNodes) {
  1063. tmpNode = parentClone.cloneNode(false);
  1064. tmpNode.appendChild(leftNodes);
  1065. leftNodes = tmpNode;
  1066. tmpNode = parentClone.cloneNode(false);
  1067. tmpNode.appendChild(rightNodes);
  1068. rightNodes = tmpNode;
  1069. } else {
  1070. leftNodes = parentClone.cloneNode(false);
  1071. rightNodes = leftNodes.cloneNode(false);
  1072. }
  1073. while (tmpNode = clone.previousSibling) {
  1074. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  1075. }
  1076. while (tmpNode = clone.nextSibling) {
  1077. rightNodes.appendChild(tmpNode);
  1078. }
  1079. clone = parentClone;
  1080. } while (parent !== parentClone);
  1081. tmpNode = parent.parentNode;
  1082. tmpNode.insertBefore(leftNodes, parent);
  1083. tmpNode.insertBefore(rightNodes, parent);
  1084. tmpNode.insertBefore(node, rightNodes);
  1085. domUtils.remove(parent);
  1086. return node;
  1087. },
  1088. trimWhiteTextNode:function (node) {
  1089. function remove(dir) {
  1090. var child;
  1091. while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
  1092. node.removeChild(child);
  1093. }
  1094. }
  1095. remove('firstChild');
  1096. remove('lastChild');
  1097. },
  1098. /**
  1099. * 获取节点A相对于节点B的位置关系
  1100. * @name getPosition
  1101. * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB) => Number
  1102. * @example
  1103. * switch (returnValue) {
  1104. * case 0: //相等,同一节点
  1105. * case 1: //无关,节点不相连
  1106. * case 2: //跟随,即节点A头部位于节点B头部的后面
  1107. * case 4: //前置,即节点A头部位于节点B头部的前面
  1108. * case 8: //被包含,即节点A被节点B包含
  1109. * case 10://组合类型,即节点A满足跟随节点B且被节点B包含。实际上,如果被包含,必定跟随,所以returnValue事实上不会存在8的情况。
  1110. * case 16://包含,即节点A包含节点B
  1111. * case 20://组合类型,即节点A满足前置节点A且包含节点B。同样,如果包含,必定前置,所以returnValue事实上也不会存在16的情况
  1112. * }
  1113. */
  1114. getPosition: function (nodeA, nodeB) {
  1115. // 如果两个节点是同一个节点
  1116. if (nodeA === nodeB) {
  1117. // domUtils.POSITION_IDENTICAL
  1118. return 0;
  1119. }
  1120. var node,
  1121. parentsA = [nodeA],
  1122. parentsB = [nodeB];
  1123. node = nodeA;
  1124. while (node = node.parentNode) {
  1125. // 如果nodeB是nodeA的祖先节点
  1126. if (node === nodeB) {
  1127. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  1128. return 10;
  1129. }
  1130. parentsA.push(node);
  1131. }
  1132. node = nodeB;
  1133. while (node = node.parentNode) {
  1134. // 如果nodeA是nodeB的祖先节点
  1135. if (node === nodeA) {
  1136. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  1137. return 20;
  1138. }
  1139. parentsB.push(node);
  1140. }
  1141. parentsA.reverse();
  1142. parentsB.reverse();
  1143. if (parentsA[0] !== parentsB[0]) {
  1144. // domUtils.POSITION_DISCONNECTED
  1145. return 1;
  1146. }
  1147. var i = -1;
  1148. while (i++, parentsA[i] === parentsB[i]) {
  1149. }
  1150. nodeA = parentsA[i];
  1151. nodeB = parentsB[i];
  1152. while (nodeA = nodeA.nextSibling) {
  1153. if (nodeA === nodeB) {
  1154. // domUtils.POSITION_PRECEDING
  1155. return 4
  1156. }
  1157. }
  1158. // domUtils.POSITION_FOLLOWING
  1159. return 2;
  1160. },
  1161. /**
  1162. * 返回节点node在父节点中的索引位置
  1163. * @name getNodeIndex
  1164. * @grammar UM.dom.domUtils.getNodeIndex(node) => Number //索引值从0开始
  1165. */
  1166. getNodeIndex: function (node, ignoreTextNode) {
  1167. var preNode = node,
  1168. i = 0;
  1169. while (preNode = preNode.previousSibling) {
  1170. if (ignoreTextNode && preNode.nodeType == 3) {
  1171. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  1172. i++;
  1173. }
  1174. continue;
  1175. }
  1176. i++;
  1177. }
  1178. return i;
  1179. },
  1180. /**
  1181. * 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含
  1182. * @name inDoc
  1183. * @grammar UM.dom.domUtils.inDoc(node,doc) => true|false
  1184. */
  1185. inDoc: function (node, doc) {
  1186. return domUtils.getPosition(node, doc) == 10;
  1187. },
  1188. /**
  1189. * 查找node节点的祖先节点
  1190. * @name findParent
  1191. * @grammar UM.dom.domUtils.findParent(node) => Element // 直接返回node节点的父节点
  1192. * @grammar UM.dom.domUtils.findParent(node,filterFn) => Element //filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回
  1193. * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf) => Element //includeSelf指定是否包含自身
  1194. */
  1195. findParent: function (node, filterFn, includeSelf) {
  1196. if (node && !domUtils.isBody(node)) {
  1197. node = includeSelf ? node : node.parentNode;
  1198. while (node) {
  1199. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  1200. return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
  1201. }
  1202. node = node.parentNode;
  1203. }
  1204. }
  1205. return null;
  1206. },
  1207. /**
  1208. * 通过tagName查找node节点的祖先节点
  1209. * @name findParentByTagName
  1210. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames) => Element //tagNames支持数组,区分大小写
  1211. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf) => Element //includeSelf指定是否包含自身
  1212. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn) => Element //excludeFn指定例外过滤条件,返回true时忽略该节点
  1213. */
  1214. findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
  1215. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  1216. return domUtils.findParent(node, function (node) {
  1217. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  1218. }, includeSelf);
  1219. },
  1220. /**
  1221. * 查找节点node的祖先节点集合
  1222. * @name findParents
  1223. * @grammar UM.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  1224. * @grammar UM.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  1225. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  1226. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  1227. */
  1228. findParents: function (node, includeSelf, filterFn, closerFirst) {
  1229. var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
  1230. while (node = domUtils.findParent(node, filterFn)) {
  1231. parents.push(node);
  1232. }
  1233. return closerFirst ? parents : parents.reverse();
  1234. },
  1235. /**
  1236. * 在节点node后面插入新节点newNode
  1237. * @name insertAfter
  1238. * @grammar UM.dom.domUtils.insertAfter(node,newNode) => newNode
  1239. */
  1240. insertAfter: function (node, newNode) {
  1241. return node.parentNode.insertBefore(newNode, node.nextSibling);
  1242. },
  1243. /**
  1244. * 删除节点node,并根据keepChildren指定是否保留子节点
  1245. * @name remove
  1246. * @grammar UM.dom.domUtils.remove(node) => node
  1247. * @grammar UM.dom.domUtils.remove(node,keepChildren) => node
  1248. */
  1249. remove: function (node, keepChildren) {
  1250. var parent = node.parentNode,
  1251. child;
  1252. if (parent) {
  1253. if (keepChildren && node.hasChildNodes()) {
  1254. while (child = node.firstChild) {
  1255. parent.insertBefore(child, node);
  1256. }
  1257. }
  1258. parent.removeChild(node);
  1259. }
  1260. return node;
  1261. },
  1262. /**
  1263. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  1264. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  1265. * @method getNextDomNode
  1266. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1267. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1268. * @example
  1269. * ```html
  1270. * <body>
  1271. * <div id="test">
  1272. * <span></span>
  1273. * </div>
  1274. * <i>xxx</i>
  1275. * </body>
  1276. * <script>
  1277. *
  1278. * //output: i节点
  1279. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1280. *
  1281. * </script>
  1282. * ```
  1283. * @example
  1284. * ```html
  1285. * <body>
  1286. * <div>
  1287. * <span></span>
  1288. * <i id="test">xxx</i>
  1289. * </div>
  1290. * <b>xxx</b>
  1291. * </body>
  1292. * <script>
  1293. *
  1294. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  1295. * //output: b节点
  1296. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1297. *
  1298. * </script>
  1299. * ```
  1300. */
  1301. /**
  1302. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  1303. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  1304. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  1305. * @method getNextDomNode
  1306. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1307. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  1308. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1309. * @see UE.dom.domUtils.getNextDomNode(Node)
  1310. */
  1311. getNextDomNode:function (node, startFromChild, filterFn, guard) {
  1312. return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
  1313. },
  1314. getPreDomNode:function (node, startFromChild, filterFn, guard) {
  1315. return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
  1316. },
  1317. /**
  1318. * 检测节点node是否属于bookmark节点
  1319. * @name isBookmarkNode
  1320. * @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false
  1321. */
  1322. isBookmarkNode: function (node) {
  1323. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  1324. },
  1325. /**
  1326. * 获取节点node所在的window对象
  1327. * @name getWindow
  1328. * @grammar UM.dom.domUtils.getWindow(node) => window对象
  1329. */
  1330. getWindow: function (node) {
  1331. var doc = node.ownerDocument || node;
  1332. return doc.defaultView || doc.parentWindow;
  1333. },
  1334. /**
  1335. * 获取离nodeA与nodeB最近的公共的祖先节点
  1336. * @method getCommonAncestor
  1337. * @param { Node } nodeA 第一个节点
  1338. * @param { Node } nodeB 第二个节点
  1339. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  1340. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  1341. * @example
  1342. * ```javascript
  1343. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  1344. * //output: true
  1345. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  1346. * ```
  1347. */
  1348. getCommonAncestor:function (nodeA, nodeB) {
  1349. if (nodeA === nodeB)
  1350. return nodeA;
  1351. var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
  1352. while (parent = parent.parentNode) {
  1353. if (parent === nodeB) {
  1354. return parent;
  1355. }
  1356. parentsA.push(parent);
  1357. }
  1358. parent = nodeB;
  1359. while (parent = parent.parentNode) {
  1360. if (parent === nodeA)
  1361. return parent;
  1362. parentsB.push(parent);
  1363. }
  1364. parentsA.reverse();
  1365. parentsB.reverse();
  1366. while (i++, parentsA[i] === parentsB[i]) {
  1367. }
  1368. return i == 0 ? null : parentsA[i - 1];
  1369. },
  1370. /**
  1371. * 清除node节点左右连续为空的兄弟inline节点
  1372. * @method clearEmptySibling
  1373. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1374. * 则这些兄弟节点将被删除
  1375. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  1376. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  1377. * @example
  1378. * ```html
  1379. * <body>
  1380. * <div></div>
  1381. * <span id="test"></span>
  1382. * <i></i>
  1383. * <b></b>
  1384. * <em>xxx</em>
  1385. * <span></span>
  1386. * </body>
  1387. * <script>
  1388. *
  1389. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  1390. *
  1391. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  1392. * console.log( document.body.innerHTML );
  1393. *
  1394. * </script>
  1395. * ```
  1396. */
  1397. /**
  1398. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1399. * 则忽略对右边兄弟节点的操作。
  1400. * @method clearEmptySibling
  1401. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1402. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1403. * 则这些兄弟节点将被删除
  1404. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1405. */
  1406. /**
  1407. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1408. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  1409. * @method clearEmptySibling
  1410. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1411. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1412. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  1413. * 则这些兄弟节点将被删除
  1414. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1415. */
  1416. clearEmptySibling:function (node, ignoreNext, ignorePre) {
  1417. function clear(next, dir) {
  1418. var tmpNode;
  1419. while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
  1420. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  1421. || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
  1422. tmpNode = next[dir];
  1423. domUtils.remove(next);
  1424. next = tmpNode;
  1425. }
  1426. }
  1427. !ignoreNext && clear(node.nextSibling, 'nextSibling');
  1428. !ignorePre && clear(node.previousSibling, 'previousSibling');
  1429. },
  1430. /**
  1431. * 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
  1432. * @name split
  1433. * @grammar UM.dom.domUtils.split(node,offset) => TextNode //返回从切分位置开始的后一个文本节点
  1434. */
  1435. split: function (node, offset) {
  1436. var doc = node.ownerDocument;
  1437. if (browser.ie && offset == node.nodeValue.length) {
  1438. var next = doc.createTextNode('');
  1439. return domUtils.insertAfter(node, next);
  1440. }
  1441. var retval = node.splitText(offset);
  1442. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  1443. if (browser.ie8) {
  1444. var tmpNode = doc.createTextNode('');
  1445. domUtils.insertAfter(retval, tmpNode);
  1446. domUtils.remove(tmpNode);
  1447. }
  1448. return retval;
  1449. },
  1450. /**
  1451. * 检测节点node是否为空节点(包括空格、换行、占位符等字符)
  1452. * @name isWhitespace
  1453. * @grammar UM.dom.domUtils.isWhitespace(node) => true|false
  1454. */
  1455. isWhitespace: function (node) {
  1456. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
  1457. },
  1458. /**
  1459. * 获取元素element相对于viewport的位置坐标
  1460. * @name getXY
  1461. * @grammar UM.dom.domUtils.getXY(element) => Object //返回坐标对象{x:left,y:top}
  1462. */
  1463. getXY: function (element) {
  1464. var x = 0, y = 0;
  1465. while (element.offsetParent) {
  1466. y += element.offsetTop;
  1467. x += element.offsetLeft;
  1468. element = element.offsetParent;
  1469. }
  1470. return { 'x': x, 'y': y};
  1471. },
  1472. /**
  1473. * 检查节点node是否是空inline节点
  1474. * @name isEmptyInlineElement
  1475. * @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0
  1476. * @example
  1477. * <b><i></i></b> => 1
  1478. * <b><i></i><u></u></b> => 1
  1479. * <b></b> => 1
  1480. * <b>xx<i></i></b> => 0
  1481. */
  1482. isEmptyInlineElement: function (node) {
  1483. if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
  1484. return 0;
  1485. }
  1486. node = node.firstChild;
  1487. while (node) {
  1488. //如果是创建的bookmark就跳过
  1489. if (domUtils.isBookmarkNode(node)) {
  1490. return 0;
  1491. }
  1492. if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
  1493. node.nodeType == 3 && !domUtils.isWhitespace(node)
  1494. ) {
  1495. return 0;
  1496. }
  1497. node = node.nextSibling;
  1498. }
  1499. return 1;
  1500. },
  1501. /**
  1502. * 检查节点node是否为块元素
  1503. * @name isBlockElm
  1504. * @grammar UM.dom.domUtils.isBlockElm(node) => true|false
  1505. */
  1506. isBlockElm: function (node) {
  1507. return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
  1508. },
  1509. /**
  1510. * 原生方法getElementsByTagName的封装
  1511. * @name getElementsByTagName
  1512. * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组
  1513. */
  1514. getElementsByTagName: function (node, name, filter) {
  1515. if (filter && utils.isString(filter)) {
  1516. var className = filter;
  1517. filter = function (node) {
  1518. var result = false;
  1519. $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
  1520. if ($(node).hasClass(v)) {
  1521. result = true;
  1522. return false;
  1523. }
  1524. })
  1525. return result;
  1526. }
  1527. }
  1528. name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
  1529. var arr = [];
  1530. for (var n = 0, ni; ni = name[n++];) {
  1531. var list = node.getElementsByTagName(ni);
  1532. for (var i = 0, ci; ci = list[i++];) {
  1533. if (!filter || filter(ci))
  1534. arr.push(ci);
  1535. }
  1536. }
  1537. return arr;
  1538. },
  1539. /**
  1540. * 设置节点node及其子节点不会被选中
  1541. * @name unSelectable
  1542. * @grammar UM.dom.domUtils.unSelectable(node)
  1543. */
  1544. unSelectable: ie && browser.ie9below || browser.opera ? function (node) {
  1545. //for ie9
  1546. node.onselectstart = function () {
  1547. return false;
  1548. };
  1549. node.onclick = node.onkeyup = node.onkeydown = function () {
  1550. return false;
  1551. };
  1552. node.unselectable = 'on';
  1553. node.setAttribute("unselectable", "on");
  1554. for (var i = 0, ci; ci = node.all[i++];) {
  1555. switch (ci.tagName.toLowerCase()) {
  1556. case 'iframe' :
  1557. case 'textarea' :
  1558. case 'input' :
  1559. case 'select' :
  1560. break;
  1561. default :
  1562. ci.unselectable = 'on';
  1563. node.setAttribute("unselectable", "on");
  1564. }
  1565. }
  1566. } : function (node) {
  1567. node.style.MozUserSelect =
  1568. node.style.webkitUserSelect =
  1569. node.style.msUserSelect =
  1570. node.style.KhtmlUserSelect = 'none';
  1571. },
  1572. /**
  1573. * 删除节点node上的属性attrNames,attrNames为属性名称数组
  1574. * @name removeAttributes
  1575. * @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
  1576. * @example
  1577. * //Before remove
  1578. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  1579. * //Remove
  1580. * UM.dom.domUtils.removeAttributes(node,["id","name"]);
  1581. * //After remove
  1582. * <span style="font-size:14px;">xxxxx</span>
  1583. */
  1584. removeAttributes: function (node, attrNames) {
  1585. attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
  1586. for (var i = 0, ci; ci = attrNames[i++];) {
  1587. ci = attrFix[ci] || ci;
  1588. switch (ci) {
  1589. case 'className':
  1590. node[ci] = '';
  1591. break;
  1592. case 'style':
  1593. node.style.cssText = '';
  1594. !browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))
  1595. }
  1596. node.removeAttribute(ci);
  1597. }
  1598. },
  1599. /**
  1600. * 在doc下创建一个标签名为tag,属性为attrs的元素
  1601. * @name createElement
  1602. * @grammar UM.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点
  1603. */
  1604. createElement: function (doc, tag, attrs) {
  1605. return domUtils.setAttributes(doc.createElement(tag), attrs)
  1606. },
  1607. /**
  1608. * 为节点node添加属性attrs,attrs为属性键值对
  1609. * @name setAttributes
  1610. * @grammar UM.dom.domUtils.setAttributes(node,attrs) => node
  1611. */
  1612. setAttributes: function (node, attrs) {
  1613. for (var attr in attrs) {
  1614. if (attrs.hasOwnProperty(attr)) {
  1615. var value = attrs[attr];
  1616. switch (attr) {
  1617. case 'class':
  1618. //ie下要这样赋值,setAttribute不起作用
  1619. node.className = value;
  1620. break;
  1621. case 'style' :
  1622. node.style.cssText = node.style.cssText + ";" + value;
  1623. break;
  1624. case 'innerHTML':
  1625. node[attr] = value;
  1626. break;
  1627. case 'value':
  1628. node.value = value;
  1629. break;
  1630. default:
  1631. node.setAttribute(attrFix[attr] || attr, value);
  1632. }
  1633. }
  1634. }
  1635. return node;
  1636. },
  1637. /**
  1638. * 获取元素element的计算样式
  1639. * @name getComputedStyle
  1640. * @grammar UM.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值
  1641. * @example
  1642. * getComputedStyle(document.body,"font-size") => "15px"
  1643. * getComputedStyle(form,"color") => "#ffccdd"
  1644. */
  1645. getComputedStyle: function (element, styleName) {
  1646. return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));
  1647. },
  1648. /**
  1649. * 阻止事件默认行为
  1650. * @param {Event} evt 需要组织的事件对象
  1651. */
  1652. preventDefault: function (evt) {
  1653. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  1654. },
  1655. /**
  1656. * 删除元素element指定的样式
  1657. * @method removeStyle
  1658. * @param { Element } element 需要删除样式的元素
  1659. * @param { String } styleName 需要删除的样式名
  1660. * @example
  1661. * ```html
  1662. * <span id="test" style="color: red; background: blue;"></span>
  1663. *
  1664. * <script>
  1665. *
  1666. * var testNode = document.getElementById("test");
  1667. *
  1668. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  1669. *
  1670. * //output: background: blue;
  1671. * console.log( testNode.style.cssText );
  1672. *
  1673. * </script>
  1674. * ```
  1675. */
  1676. removeStyle:function (element, name) {
  1677. if(browser.ie ){
  1678. //针对color先单独处理一下
  1679. if(name == 'color'){
  1680. name = '(^|;)' + name;
  1681. }
  1682. element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
  1683. }else{
  1684. if (element.style.removeProperty) {
  1685. element.style.removeProperty (name);
  1686. }else {
  1687. element.style.removeAttribute (utils.cssStyleToDomStyle(name));
  1688. }
  1689. }
  1690. if (!element.style.cssText) {
  1691. domUtils.removeAttributes(element, ['style']);
  1692. }
  1693. },
  1694. /**
  1695. * 获取元素element的某个样式值
  1696. * @name getStyle
  1697. * @grammar UM.dom.domUtils.getStyle(element,name) => String
  1698. */
  1699. getStyle: function (element, name) {
  1700. var value = element.style[ utils.cssStyleToDomStyle(name) ];
  1701. return utils.fixColor(name, value);
  1702. },
  1703. /**
  1704. * 为元素element设置样式属性值
  1705. * @name setStyle
  1706. * @grammar UM.dom.domUtils.setStyle(element,name,value)
  1707. */
  1708. setStyle: function (element, name, value) {
  1709. element.style[utils.cssStyleToDomStyle(name)] = value;
  1710. if (!utils.trim(element.style.cssText)) {
  1711. this.removeAttributes(element, 'style')
  1712. }
  1713. },
  1714. /**
  1715. * 删除_moz_dirty属性
  1716. * @function
  1717. */
  1718. removeDirtyAttr: function (node) {
  1719. for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
  1720. ci.removeAttribute('_moz_dirty');
  1721. }
  1722. node.removeAttribute('_moz_dirty');
  1723. },
  1724. /**
  1725. * 返回子节点的数量
  1726. * @function
  1727. * @param {Node} node 父节点
  1728. * @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量
  1729. * @return {Number} 符合条件子节点的数量
  1730. */
  1731. getChildCount: function (node, fn) {
  1732. var count = 0, first = node.firstChild;
  1733. fn = fn || function () {
  1734. return 1;
  1735. };
  1736. while (first) {
  1737. if (fn(first)) {
  1738. count++;
  1739. }
  1740. first = first.nextSibling;
  1741. }
  1742. return count;
  1743. },
  1744. /**
  1745. * 判断是否为空节点
  1746. * @function
  1747. * @param {Node} node 节点
  1748. * @return {Boolean} 是否为空节点
  1749. */
  1750. isEmptyNode: function (node) {
  1751. return !node.firstChild || domUtils.getChildCount(node, function (node) {
  1752. return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
  1753. }) == 0
  1754. },
  1755. /**
  1756. * 判断节点是否为br
  1757. * @function
  1758. * @param {Node} node 节点
  1759. */
  1760. isBr: function (node) {
  1761. return node.nodeType == 1 && node.tagName == 'BR';
  1762. },
  1763. isFillChar: function (node, isInStart) {
  1764. return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
  1765. },
  1766. isEmptyBlock: function (node, reg) {
  1767. if (node.nodeType != 1)
  1768. return 0;
  1769. reg = reg || new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g');
  1770. if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
  1771. return 0;
  1772. }
  1773. for (var n in dtd.$isNotEmpty) {
  1774. if (node.getElementsByTagName(n).length) {
  1775. return 0;
  1776. }
  1777. }
  1778. return 1;
  1779. },
  1780. //判断是否是编辑器自定义的参数
  1781. isCustomeNode: function (node) {
  1782. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
  1783. },
  1784. fillNode: function (doc, node) {
  1785. var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
  1786. node.innerHTML = '';
  1787. node.appendChild(tmpNode);
  1788. },
  1789. isBoundaryNode: function (node, dir) {
  1790. var tmp;
  1791. while (!domUtils.isBody(node)) {
  1792. tmp = node;
  1793. node = node.parentNode;
  1794. if (tmp !== node[dir]) {
  1795. return false;
  1796. }
  1797. }
  1798. return true;
  1799. },
  1800. isFillChar: function (node, isInStart) {
  1801. return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
  1802. },
  1803. isBody: function(node){
  1804. return $(node).hasClass('edui-body-container');
  1805. }
  1806. };
  1807. var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  1808. ///import editor.js
  1809. ///import core/utils.js
  1810. ///import core/browser.js
  1811. ///import core/dom/dom.js
  1812. ///import core/dom/dtd.js
  1813. ///import core/dom/domUtils.js
  1814. /**
  1815. * @file
  1816. * @name UM.dom.Range
  1817. * @anthor zhanyi
  1818. * @short Range
  1819. * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
  1820. * @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性
  1821. */
  1822. (function () {
  1823. var guid = 0,
  1824. fillChar = domUtils.fillChar,
  1825. fillData;
  1826. /**
  1827. * 更新range的collapse状态
  1828. * @param {Range} range range对象
  1829. */
  1830. function updateCollapse(range) {
  1831. range.collapsed =
  1832. range.startContainer && range.endContainer &&
  1833. range.startContainer === range.endContainer &&
  1834. range.startOffset == range.endOffset;
  1835. }
  1836. function selectOneNode(rng){
  1837. return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
  1838. }
  1839. function setEndPoint(toStart, node, offset, range) {
  1840. //如果node是自闭合标签要处理
  1841. if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
  1842. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
  1843. node = node.parentNode;
  1844. }
  1845. if (toStart) {
  1846. range.startContainer = node;
  1847. range.startOffset = offset;
  1848. if (!range.endContainer) {
  1849. range.collapse(true);
  1850. }
  1851. } else {
  1852. range.endContainer = node;
  1853. range.endOffset = offset;
  1854. if (!range.startContainer) {
  1855. range.collapse(false);
  1856. }
  1857. }
  1858. updateCollapse(range);
  1859. return range;
  1860. }
  1861. /**
  1862. * @name Range
  1863. * @grammar new UM.dom.Range(document) => Range 实例
  1864. * @desc 创建一个跟document绑定的空的Range实例
  1865. * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
  1866. * - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1867. * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
  1868. * - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1869. * - ***document*** 跟range关联的document对象
  1870. * - ***collapsed*** 是否是闭合状态
  1871. */
  1872. var Range = dom.Range = function (document,body) {
  1873. var me = this;
  1874. me.startContainer =
  1875. me.startOffset =
  1876. me.endContainer =
  1877. me.endOffset = null;
  1878. me.document = document;
  1879. me.collapsed = true;
  1880. me.body = body;
  1881. };
  1882. /**
  1883. * 删除fillData
  1884. * @param doc
  1885. * @param excludeNode
  1886. */
  1887. function removeFillData(doc, excludeNode) {
  1888. try {
  1889. if (fillData && domUtils.inDoc(fillData, doc)) {
  1890. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  1891. var tmpNode = fillData.parentNode;
  1892. domUtils.remove(fillData);
  1893. while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
  1894. //safari的contains有bug
  1895. (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
  1896. ) {
  1897. fillData = tmpNode.parentNode;
  1898. domUtils.remove(tmpNode);
  1899. tmpNode = fillData;
  1900. }
  1901. } else {
  1902. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
  1903. }
  1904. }
  1905. } catch (e) {
  1906. }
  1907. }
  1908. /**
  1909. *
  1910. * @param node
  1911. * @param dir
  1912. */
  1913. function mergeSibling(node, dir) {
  1914. var tmpNode;
  1915. node = node[dir];
  1916. while (node && domUtils.isFillChar(node)) {
  1917. tmpNode = node[dir];
  1918. domUtils.remove(node);
  1919. node = tmpNode;
  1920. }
  1921. }
  1922. function execContentsAction(range, action) {
  1923. //调整边界
  1924. //range.includeBookmark();
  1925. var start = range.startContainer,
  1926. end = range.endContainer,
  1927. startOffset = range.startOffset,
  1928. endOffset = range.endOffset,
  1929. doc = range.document,
  1930. frag = doc.createDocumentFragment(),
  1931. tmpStart, tmpEnd;
  1932. if (start.nodeType == 1) {
  1933. start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
  1934. }
  1935. if (end.nodeType == 1) {
  1936. end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
  1937. }
  1938. if (start === end && start.nodeType == 3) {
  1939. frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
  1940. //is not clone
  1941. if (action) {
  1942. start.deleteData(startOffset, endOffset - startOffset);
  1943. range.collapse(true);
  1944. }
  1945. return frag;
  1946. }
  1947. var current, currentLevel, clone = frag,
  1948. startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
  1949. for (var i = 0; startParents[i] == endParents[i];) {
  1950. i++;
  1951. }
  1952. for (var j = i, si; si = startParents[j]; j++) {
  1953. current = si.nextSibling;
  1954. if (si == start) {
  1955. if (!tmpStart) {
  1956. if (range.startContainer.nodeType == 3) {
  1957. clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
  1958. //is not clone
  1959. if (action) {
  1960. start.deleteData(startOffset, start.nodeValue.length - startOffset);
  1961. }
  1962. } else {
  1963. clone.appendChild(!action ? start.cloneNode(true) : start);
  1964. }
  1965. }
  1966. } else {
  1967. currentLevel = si.cloneNode(false);
  1968. clone.appendChild(currentLevel);
  1969. }
  1970. while (current) {
  1971. if (current === end || current === endParents[j]) {
  1972. break;
  1973. }
  1974. si = current.nextSibling;
  1975. clone.appendChild(!action ? current.cloneNode(true) : current);
  1976. current = si;
  1977. }
  1978. clone = currentLevel;
  1979. }
  1980. clone = frag;
  1981. if (!startParents[i]) {
  1982. clone.appendChild(startParents[i - 1].cloneNode(false));
  1983. clone = clone.firstChild;
  1984. }
  1985. for (var j = i, ei; ei = endParents[j]; j++) {
  1986. current = ei.previousSibling;
  1987. if (ei == end) {
  1988. if (!tmpEnd && range.endContainer.nodeType == 3) {
  1989. clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
  1990. //is not clone
  1991. if (action) {
  1992. end.deleteData(0, endOffset);
  1993. }
  1994. }
  1995. } else {
  1996. currentLevel = ei.cloneNode(false);
  1997. clone.appendChild(currentLevel);
  1998. }
  1999. //如果两端同级,右边第一次已经被开始做了
  2000. if (j != i || !startParents[i]) {
  2001. while (current) {
  2002. if (current === start) {
  2003. break;
  2004. }
  2005. ei = current.previousSibling;
  2006. clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
  2007. current = ei;
  2008. }
  2009. }
  2010. clone = currentLevel;
  2011. }
  2012. if (action) {
  2013. range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
  2014. }
  2015. tmpStart && domUtils.remove(tmpStart);
  2016. tmpEnd && domUtils.remove(tmpEnd);
  2017. return frag;
  2018. }
  2019. Range.prototype = {
  2020. /**
  2021. * @name deleteContents
  2022. * @grammar range.deleteContents() => Range
  2023. * @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态
  2024. * @example
  2025. * DOM Element :
  2026. * <b>x<i>x[x<i>xx]x</b>
  2027. * //执行方法后
  2028. * <b>x<i>x<i>|x</b>
  2029. * 注意range改变了
  2030. * range.startContainer => b
  2031. * range.startOffset => 2
  2032. * range.endContainer => b
  2033. * range.endOffset => 2
  2034. * range.collapsed => true
  2035. */
  2036. deleteContents:function () {
  2037. var txt;
  2038. if (!this.collapsed) {
  2039. execContentsAction(this, 1);
  2040. }
  2041. if (browser.webkit) {
  2042. txt = this.startContainer;
  2043. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  2044. this.setStartBefore(txt).collapse(true);
  2045. domUtils.remove(txt);
  2046. }
  2047. }
  2048. return this;
  2049. },
  2050. inFillChar : function(){
  2051. var start = this.startContainer;
  2052. if(this.collapsed && start.nodeType == 3
  2053. && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
  2054. ){
  2055. return true;
  2056. }
  2057. return false;
  2058. },
  2059. /**
  2060. * @name setStart
  2061. * @grammar range.setStart(node,offset) => Range
  2062. * @desc 设置range的开始位置位于node节点内,偏移量为offset
  2063. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2064. */
  2065. setStart:function (node, offset) {
  2066. return setEndPoint(true, node, offset, this);
  2067. },
  2068. /**
  2069. * 设置range的结束位置位于node节点,偏移量为offset
  2070. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2071. * @name setEnd
  2072. * @grammar range.setEnd(node,offset) => Range
  2073. */
  2074. setEnd:function (node, offset) {
  2075. return setEndPoint(false, node, offset, this);
  2076. },
  2077. /**
  2078. * 将Range开始位置设置到node节点之后
  2079. * @name setStartAfter
  2080. * @grammar range.setStartAfter(node) => Range
  2081. * @example
  2082. * <b>xx<i>x|x</i>x</b>
  2083. * 执行setStartAfter(i)后
  2084. * range.startContainer =>b
  2085. * range.startOffset =>2
  2086. */
  2087. setStartAfter:function (node) {
  2088. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2089. },
  2090. /**
  2091. * 将Range开始位置设置到node节点之前
  2092. * @name setStartBefore
  2093. * @grammar range.setStartBefore(node) => Range
  2094. * @example
  2095. * <b>xx<i>x|x</i>x</b>
  2096. * 执行setStartBefore(i)后
  2097. * range.startContainer =>b
  2098. * range.startOffset =>1
  2099. */
  2100. setStartBefore:function (node) {
  2101. return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
  2102. },
  2103. /**
  2104. * 将Range结束位置设置到node节点之后
  2105. * @name setEndAfter
  2106. * @grammar range.setEndAfter(node) => Range
  2107. * @example
  2108. * <b>xx<i>x|x</i>x</b>
  2109. * setEndAfter(i)后
  2110. * range.endContainer =>b
  2111. * range.endtOffset =>2
  2112. */
  2113. setEndAfter:function (node) {
  2114. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2115. },
  2116. /**
  2117. * 将Range结束位置设置到node节点之前
  2118. * @name setEndBefore
  2119. * @grammar range.setEndBefore(node) => Range
  2120. * @example
  2121. * <b>xx<i>x|x</i>x</b>
  2122. * 执行setEndBefore(i)后
  2123. * range.endContainer =>b
  2124. * range.endtOffset =>1
  2125. */
  2126. setEndBefore:function (node) {
  2127. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
  2128. },
  2129. /**
  2130. * 将Range开始位置设置到node节点内的开始位置
  2131. * @name setStartAtFirst
  2132. * @grammar range.setStartAtFirst(node) => Range
  2133. */
  2134. setStartAtFirst:function (node) {
  2135. return this.setStart(node, 0);
  2136. },
  2137. /**
  2138. * 将Range开始位置设置到node节点内的结束位置
  2139. * @name setStartAtLast
  2140. * @grammar range.setStartAtLast(node) => Range
  2141. */
  2142. setStartAtLast:function (node) {
  2143. return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2144. },
  2145. /**
  2146. * 将Range结束位置设置到node节点内的开始位置
  2147. * @name setEndAtFirst
  2148. * @grammar range.setEndAtFirst(node) => Range
  2149. */
  2150. setEndAtFirst:function (node) {
  2151. return this.setEnd(node, 0);
  2152. },
  2153. /**
  2154. * 将Range结束位置设置到node节点内的结束位置
  2155. * @name setEndAtLast
  2156. * @grammar range.setEndAtLast(node) => Range
  2157. */
  2158. setEndAtLast:function (node) {
  2159. return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2160. },
  2161. /**
  2162. * 选中完整的指定节点,并返回包含该节点的range
  2163. * @name selectNode
  2164. * @grammar range.selectNode(node) => Range
  2165. */
  2166. selectNode:function (node) {
  2167. return this.setStartBefore(node).setEndAfter(node);
  2168. },
  2169. /**
  2170. * 选中node内部的所有节点,并返回对应的range
  2171. * @name selectNodeContents
  2172. * @grammar range.selectNodeContents(node) => Range
  2173. * @example
  2174. * <b>xx[x<i>xxx</i>]xxx</b>
  2175. * 执行后
  2176. * <b>[xxx<i>xxx</i>xxx]</b>
  2177. * range.startContainer =>b
  2178. * range.startOffset =>0
  2179. * range.endContainer =>b
  2180. * range.endOffset =>3
  2181. */
  2182. selectNodeContents:function (node) {
  2183. return this.setStart(node, 0).setEndAtLast(node);
  2184. },
  2185. /**
  2186. * 克隆一个新的range对象
  2187. * @name cloneRange
  2188. * @grammar range.cloneRange() => Range
  2189. */
  2190. cloneRange:function () {
  2191. var me = this;
  2192. return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  2193. },
  2194. /**
  2195. * 让选区闭合到尾部,若toStart为真,则闭合到头部
  2196. * @name collapse
  2197. * @grammar range.collapse() => Range
  2198. * @grammar range.collapse(true) => Range //闭合选区到头部
  2199. */
  2200. collapse:function (toStart) {
  2201. var me = this;
  2202. if (toStart) {
  2203. me.endContainer = me.startContainer;
  2204. me.endOffset = me.startOffset;
  2205. } else {
  2206. me.startContainer = me.endContainer;
  2207. me.startOffset = me.endOffset;
  2208. }
  2209. me.collapsed = true;
  2210. return me;
  2211. },
  2212. /**
  2213. * 调整range的边界,使其"收缩"到最小的位置
  2214. * @name shrinkBoundary
  2215. * @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>
  2216. * @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
  2217. * @example
  2218. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2219. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>
  2220. * [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>
  2221. */
  2222. shrinkBoundary:function (ignoreEnd) {
  2223. var me = this, child,
  2224. collapsed = me.collapsed;
  2225. function check(node){
  2226. return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
  2227. }
  2228. while (me.startContainer.nodeType == 1 //是element
  2229. && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
  2230. && check(child)) {
  2231. me.setStart(child, 0);
  2232. }
  2233. if (collapsed) {
  2234. return me.collapse(true);
  2235. }
  2236. if (!ignoreEnd) {
  2237. while (me.endContainer.nodeType == 1//是element
  2238. && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  2239. && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
  2240. && check(child)) {
  2241. me.setEnd(child, child.childNodes.length);
  2242. }
  2243. }
  2244. return me;
  2245. },
  2246. /**
  2247. * 调整边界容器,如果是textNode,就调整到elementNode上
  2248. * @name trimBoundary
  2249. * @grammar range.trimBoundary([ignoreEnd]) => Range //true忽略结束边界
  2250. * @example
  2251. * DOM Element :
  2252. * <b>|xxx</b>
  2253. * startContainer = xxx; startOffset = 0
  2254. * //执行后本方法后
  2255. * startContainer = <b>; startOffset = 0
  2256. * @example
  2257. * Dom Element :
  2258. * <b>xx|x</b>
  2259. * startContainer = xxx; startOffset = 2
  2260. * //执行本方法后,xxx被实实在在地切分成两个TextNode
  2261. * startContainer = <b>; startOffset = 1
  2262. */
  2263. trimBoundary:function (ignoreEnd) {
  2264. this.txtToElmBoundary();
  2265. var start = this.startContainer,
  2266. offset = this.startOffset,
  2267. collapsed = this.collapsed,
  2268. end = this.endContainer;
  2269. if (start.nodeType == 3) {
  2270. if (offset == 0) {
  2271. this.setStartBefore(start);
  2272. } else {
  2273. if (offset >= start.nodeValue.length) {
  2274. this.setStartAfter(start);
  2275. } else {
  2276. var textNode = domUtils.split(start, offset);
  2277. //跟新结束边界
  2278. if (start === end) {
  2279. this.setEnd(textNode, this.endOffset - offset);
  2280. } else if (start.parentNode === end) {
  2281. this.endOffset += 1;
  2282. }
  2283. this.setStartBefore(textNode);
  2284. }
  2285. }
  2286. if (collapsed) {
  2287. return this.collapse(true);
  2288. }
  2289. }
  2290. if (!ignoreEnd) {
  2291. offset = this.endOffset;
  2292. end = this.endContainer;
  2293. if (end.nodeType == 3) {
  2294. if (offset == 0) {
  2295. this.setEndBefore(end);
  2296. } else {
  2297. offset < end.nodeValue.length && domUtils.split(end, offset);
  2298. this.setEndAfter(end);
  2299. }
  2300. }
  2301. }
  2302. return this;
  2303. },
  2304. /**
  2305. * 如果选区在文本的边界上,就扩展选区到文本的父节点上
  2306. * @name txtToElmBoundary
  2307. * @example
  2308. * Dom Element :
  2309. * <b> |xxx</b>
  2310. * startContainer = xxx; startOffset = 0
  2311. * //本方法执行后
  2312. * startContainer = <b>; startOffset = 0
  2313. * @example
  2314. * Dom Element :
  2315. * <b> xxx| </b>
  2316. * startContainer = xxx; startOffset = 3
  2317. * //本方法执行后
  2318. * startContainer = <b>; startOffset = 1
  2319. */
  2320. txtToElmBoundary:function (ignoreCollapsed) {
  2321. function adjust(r, c) {
  2322. var container = r[c + 'Container'],
  2323. offset = r[c + 'Offset'];
  2324. if (container.nodeType == 3) {
  2325. if (!offset) {
  2326. r['set' + c.replace(/(\w)/, function (a) {
  2327. return a.toUpperCase();
  2328. }) + 'Before'](container);
  2329. } else if (offset >= container.nodeValue.length) {
  2330. r['set' + c.replace(/(\w)/, function (a) {
  2331. return a.toUpperCase();
  2332. }) + 'After' ](container);
  2333. }
  2334. }
  2335. }
  2336. if (ignoreCollapsed || !this.collapsed) {
  2337. adjust(this, 'start');
  2338. adjust(this, 'end');
  2339. }
  2340. return this;
  2341. },
  2342. /**
  2343. * 在当前选区的开始位置前插入一个节点或者fragment,range的开始位置会在插入节点的前边
  2344. * @name insertNode
  2345. * @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment
  2346. * @example
  2347. * Range :
  2348. * xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2349. * 待插入Node :
  2350. * <p>ssss</p>
  2351. * 执行本方法后的Range :
  2352. * xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2353. */
  2354. insertNode:function (node) {
  2355. var first = node, length = 1;
  2356. if (node.nodeType == 11) {
  2357. first = node.firstChild;
  2358. length = node.childNodes.length;
  2359. }
  2360. this.trimBoundary(true);
  2361. var start = this.startContainer,
  2362. offset = this.startOffset;
  2363. var nextNode = start.childNodes[ offset ];
  2364. if (nextNode) {
  2365. start.insertBefore(node, nextNode);
  2366. } else {
  2367. start.appendChild(node);
  2368. }
  2369. if (first.parentNode === this.endContainer) {
  2370. this.endOffset = this.endOffset + length;
  2371. }
  2372. return this.setStartBefore(first);
  2373. },
  2374. /**
  2375. * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾
  2376. * @name setCursor
  2377. * @grammar range.setCursor([toEnd]) => Range //toEnd为true时,光标闭合到选区的末尾
  2378. */
  2379. setCursor:function (toEnd, noFillData) {
  2380. return this.collapse(!toEnd).select(noFillData);
  2381. },
  2382. /**
  2383. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  2384. * @name createBookmark
  2385. * @grammar range.createBookmark([serialize]) => Object //{start:开始标记,end:结束标记,id:serialize} serialize为真时,开始结束标记是插入节点的id,否则是插入节点的引用
  2386. */
  2387. createBookmark:function (serialize, same) {
  2388. var endNode,
  2389. startNode = this.document.createElement('span');
  2390. startNode.style.cssText = 'display:none;line-height:0px;';
  2391. startNode.appendChild(this.document.createTextNode('\u200D'));
  2392. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  2393. if (!this.collapsed) {
  2394. endNode = startNode.cloneNode(true);
  2395. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
  2396. }
  2397. this.insertNode(startNode);
  2398. if (endNode) {
  2399. this.collapse().insertNode(endNode).setEndBefore(endNode);
  2400. }
  2401. this.setStartAfter(startNode);
  2402. return {
  2403. start:serialize ? startNode.id : startNode,
  2404. end:endNode ? serialize ? endNode.id : endNode : null,
  2405. id:serialize
  2406. }
  2407. },
  2408. /**
  2409. * 移动边界到书签位置,并删除插入的书签节点
  2410. * @name moveToBookmark
  2411. * @grammar range.moveToBookmark(bookmark) => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的
  2412. */
  2413. moveToBookmark:function (bookmark) {
  2414. var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
  2415. end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
  2416. this.setStartBefore(start);
  2417. domUtils.remove(start);
  2418. if (end) {
  2419. this.setEndBefore(end);
  2420. domUtils.remove(end);
  2421. } else {
  2422. this.collapse(true);
  2423. }
  2424. return this;
  2425. },
  2426. /**
  2427. * 调整Range的边界,使其"缩小"到最合适的位置
  2428. * @name adjustmentBoundary
  2429. * @grammar range.adjustmentBoundary() => Range //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>
  2430. * @example
  2431. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2432. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>
  2433. */
  2434. adjustmentBoundary:function () {
  2435. if (!this.collapsed) {
  2436. while (!domUtils.isBody(this.startContainer) &&
  2437. this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
  2438. this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2439. ) {
  2440. this.setStartAfter(this.startContainer);
  2441. }
  2442. while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
  2443. this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2444. ) {
  2445. this.setEndBefore(this.endContainer);
  2446. }
  2447. }
  2448. return this;
  2449. },
  2450. /**
  2451. * 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点
  2452. * @name getClosedNode
  2453. * @grammar range.getClosedNode() => node|null
  2454. * @example
  2455. * <b>xxxx[<img />]xxx</b>
  2456. */
  2457. getClosedNode:function () {
  2458. var node;
  2459. if (!this.collapsed) {
  2460. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
  2461. if (selectOneNode(range)) {
  2462. var child = range.startContainer.childNodes[range.startOffset];
  2463. if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
  2464. node = child;
  2465. }
  2466. }
  2467. }
  2468. return node;
  2469. },
  2470. /**
  2471. * 根据当前range选中内容节点(在页面上表现为反白显示)
  2472. * @name select
  2473. * @grammar range.select(); => Range
  2474. */
  2475. select:browser.ie ? function (noFillData, textRange) {
  2476. var nativeRange;
  2477. if (!this.collapsed)
  2478. this.shrinkBoundary();
  2479. var node = this.getClosedNode();
  2480. if (node && !textRange) {
  2481. try {
  2482. nativeRange = this.document.body.createControlRange();
  2483. nativeRange.addElement(node);
  2484. nativeRange.select();
  2485. } catch (e) {}
  2486. return this;
  2487. }
  2488. var bookmark = this.createBookmark(),
  2489. start = bookmark.start,
  2490. end;
  2491. nativeRange = this.document.body.createTextRange();
  2492. nativeRange.moveToElementText(start);
  2493. nativeRange.moveStart('character', 1);
  2494. if (!this.collapsed) {
  2495. var nativeRangeEnd = this.document.body.createTextRange();
  2496. end = bookmark.end;
  2497. nativeRangeEnd.moveToElementText(end);
  2498. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
  2499. } else {
  2500. if (!noFillData && this.startContainer.nodeType != 3) {
  2501. //使用<span>|x<span>固定住光标
  2502. var tmpText = this.document.createTextNode(fillChar),
  2503. tmp = this.document.createElement('span');
  2504. tmp.appendChild(this.document.createTextNode(fillChar));
  2505. start.parentNode.insertBefore(tmp, start);
  2506. start.parentNode.insertBefore(tmpText, start);
  2507. //当点b,i,u时,不能清除i上边的b
  2508. removeFillData(this.document, tmpText);
  2509. fillData = tmpText;
  2510. mergeSibling(tmp, 'previousSibling');
  2511. mergeSibling(start, 'nextSibling');
  2512. nativeRange.moveStart('character', -1);
  2513. nativeRange.collapse(true);
  2514. }
  2515. }
  2516. this.moveToBookmark(bookmark);
  2517. tmp && domUtils.remove(tmp);
  2518. //IE在隐藏状态下不支持range操作,catch一下
  2519. try {
  2520. nativeRange.select();
  2521. } catch (e) {
  2522. }
  2523. return this;
  2524. } : function (notInsertFillData) {
  2525. function checkOffset(rng){
  2526. function check(node,offset,dir){
  2527. if(node.nodeType == 3 && node.nodeValue.length < offset){
  2528. rng[dir + 'Offset'] = node.nodeValue.length
  2529. }
  2530. }
  2531. check(rng.startContainer,rng.startOffset,'start');
  2532. check(rng.endContainer,rng.endOffset,'end');
  2533. }
  2534. var win = domUtils.getWindow(this.document),
  2535. sel = win.getSelection(),
  2536. txtNode;
  2537. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  2538. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  2539. browser.gecko ? this.body.focus() : win.focus();
  2540. if (sel) {
  2541. sel.removeAllRanges();
  2542. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  2543. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  2544. if (this.collapsed && !notInsertFillData) {
  2545. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  2546. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  2547. // var tmp = this.document.createTextNode('');
  2548. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  2549. // }
  2550. //
  2551. //处理光标落在文本节点的情况
  2552. //处理以下的情况
  2553. //<b>|xxxx</b>
  2554. //<b>xxxx</b>|xxxx
  2555. //xxxx<b>|</b>
  2556. var start = this.startContainer,child = start;
  2557. if(start.nodeType == 1){
  2558. child = start.childNodes[this.startOffset];
  2559. }
  2560. if( !(start.nodeType == 3 && this.startOffset) &&
  2561. (child ?
  2562. (!child.previousSibling || child.previousSibling.nodeType != 3)
  2563. :
  2564. (!start.lastChild || start.lastChild.nodeType != 3)
  2565. )
  2566. ){
  2567. txtNode = this.document.createTextNode(fillChar);
  2568. //跟着前边走
  2569. this.insertNode(txtNode);
  2570. removeFillData(this.document, txtNode);
  2571. mergeSibling(txtNode, 'previousSibling');
  2572. mergeSibling(txtNode, 'nextSibling');
  2573. fillData = txtNode;
  2574. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
  2575. }
  2576. }
  2577. var nativeRange = this.document.createRange();
  2578. if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
  2579. var child = this.startContainer.childNodes[this.startOffset];
  2580. if(!child){
  2581. //往前靠拢
  2582. child = this.startContainer.lastChild;
  2583. if( child && domUtils.isBr(child)){
  2584. this.setStartBefore(child).collapse(true);
  2585. }
  2586. }else{
  2587. //向后靠拢
  2588. while(child && domUtils.isBlockElm(child)){
  2589. if(child.nodeType == 1 && child.childNodes[0]){
  2590. child = child.childNodes[0]
  2591. }else{
  2592. break;
  2593. }
  2594. }
  2595. child && this.setStartBefore(child).collapse(true)
  2596. }
  2597. }
  2598. //是createAddress最后一位算的不准,现在这里进行微调
  2599. checkOffset(this);
  2600. nativeRange.setStart(this.startContainer, this.startOffset);
  2601. nativeRange.setEnd(this.endContainer, this.endOffset);
  2602. sel.addRange(nativeRange);
  2603. }
  2604. return this;
  2605. },
  2606. createAddress : function(ignoreEnd,ignoreTxt){
  2607. var addr = {},me = this;
  2608. function getAddress(isStart){
  2609. var node = isStart ? me.startContainer : me.endContainer;
  2610. var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
  2611. addrs = [];
  2612. for(var i = 0,ci;ci = parents[i++];){
  2613. addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
  2614. }
  2615. var firstIndex = 0;
  2616. if(ignoreTxt){
  2617. if(node.nodeType == 3){
  2618. var tmpNode = node.previousSibling;
  2619. while(tmpNode && tmpNode.nodeType == 3){
  2620. firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
  2621. tmpNode = tmpNode.previousSibling;
  2622. }
  2623. firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  2624. }else{
  2625. node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
  2626. if(node){
  2627. firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
  2628. }else{
  2629. node = isStart ? me.startContainer : me.endContainer;
  2630. var first = node.firstChild;
  2631. while(first){
  2632. if(domUtils.isFillChar(first)){
  2633. first = first.nextSibling;
  2634. continue;
  2635. }
  2636. firstIndex++;
  2637. if(first.nodeType == 3){
  2638. while( first && first.nodeType == 3){
  2639. first = first.nextSibling;
  2640. }
  2641. }else{
  2642. first = first.nextSibling;
  2643. }
  2644. }
  2645. }
  2646. }
  2647. }else{
  2648. firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
  2649. }
  2650. if(firstIndex < 0){
  2651. firstIndex = 0;
  2652. }
  2653. addrs.push(firstIndex);
  2654. return addrs;
  2655. }
  2656. addr.startAddress = getAddress(true);
  2657. if(!ignoreEnd){
  2658. addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
  2659. }
  2660. return addr;
  2661. },
  2662. moveToAddress : function(addr,ignoreEnd){
  2663. var me = this;
  2664. function getNode(address,isStart){
  2665. var tmpNode = me.body,
  2666. parentNode,offset;
  2667. for(var i= 0,ci,l=address.length;i<l;i++){
  2668. ci = address[i];
  2669. parentNode = tmpNode;
  2670. tmpNode = tmpNode.childNodes[ci];
  2671. if(!tmpNode){
  2672. offset = ci;
  2673. break;
  2674. }
  2675. }
  2676. if(isStart){
  2677. if(tmpNode){
  2678. me.setStartBefore(tmpNode)
  2679. }else{
  2680. me.setStart(parentNode,offset)
  2681. }
  2682. }else{
  2683. if(tmpNode){
  2684. me.setEndBefore(tmpNode)
  2685. }else{
  2686. me.setEnd(parentNode,offset)
  2687. }
  2688. }
  2689. }
  2690. getNode(addr.startAddress,true);
  2691. !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
  2692. return me;
  2693. },
  2694. equals : function(rng){
  2695. for(var p in this){
  2696. if(this.hasOwnProperty(p)){
  2697. if(this[p] !== rng[p])
  2698. return false
  2699. }
  2700. }
  2701. return true;
  2702. },
  2703. scrollIntoView : function(){
  2704. var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');
  2705. this.cloneRange().insertNode($span.get(0));
  2706. var winScrollTop = $(window).scrollTop(),
  2707. winHeight = $(window).height(),
  2708. spanTop = $span.offset().top;
  2709. if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){
  2710. if(spanTop > winScrollTop + winHeight){
  2711. window.scrollTo(0,spanTop - winHeight + $span.height())
  2712. }else{
  2713. window.scrollTo(0,winScrollTop - spanTop)
  2714. }
  2715. }
  2716. $span.remove();
  2717. },
  2718. getOffset : function(){
  2719. var bk = this.createBookmark();
  2720. var offset = $(bk.start).css('display','inline-block').offset();
  2721. this.moveToBookmark(bk);
  2722. return offset
  2723. }
  2724. };
  2725. })();
  2726. ///import editor.js
  2727. ///import core/browser.js
  2728. ///import core/dom/dom.js
  2729. ///import core/dom/dtd.js
  2730. ///import core/dom/domUtils.js
  2731. ///import core/dom/Range.js
  2732. /**
  2733. * @class UM.dom.Selection Selection类
  2734. */
  2735. (function () {
  2736. function getBoundaryInformation( range, start ) {
  2737. var getIndex = domUtils.getNodeIndex;
  2738. range = range.duplicate();
  2739. range.collapse( start );
  2740. var parent = range.parentElement();
  2741. //如果节点里没有子节点,直接退出
  2742. if ( !parent.hasChildNodes() ) {
  2743. return {container:parent, offset:0};
  2744. }
  2745. var siblings = parent.children,
  2746. child,
  2747. testRange = range.duplicate(),
  2748. startIndex = 0, endIndex = siblings.length - 1, index = -1,
  2749. distance;
  2750. while ( startIndex <= endIndex ) {
  2751. index = Math.floor( (startIndex + endIndex) / 2 );
  2752. child = siblings[index];
  2753. testRange.moveToElementText( child );
  2754. var position = testRange.compareEndPoints( 'StartToStart', range );
  2755. if ( position > 0 ) {
  2756. endIndex = index - 1;
  2757. } else if ( position < 0 ) {
  2758. startIndex = index + 1;
  2759. } else {
  2760. //trace:1043
  2761. return {container:parent, offset:getIndex( child )};
  2762. }
  2763. }
  2764. if ( index == -1 ) {
  2765. testRange.moveToElementText( parent );
  2766. testRange.setEndPoint( 'StartToStart', range );
  2767. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  2768. siblings = parent.childNodes;
  2769. if ( !distance ) {
  2770. child = siblings[siblings.length - 1];
  2771. return {container:child, offset:child.nodeValue.length};
  2772. }
  2773. var i = siblings.length;
  2774. while ( distance > 0 ){
  2775. distance -= siblings[ --i ].nodeValue.length;
  2776. }
  2777. return {container:siblings[i], offset:-distance};
  2778. }
  2779. testRange.collapse( position > 0 );
  2780. testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
  2781. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  2782. if ( !distance ) {
  2783. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
  2784. {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
  2785. {container:child, offset:position > 0 ? 0 : child.childNodes.length}
  2786. }
  2787. while ( distance > 0 ) {
  2788. try {
  2789. var pre = child;
  2790. child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
  2791. distance -= child.nodeValue.length;
  2792. } catch ( e ) {
  2793. return {container:parent, offset:getIndex( pre )};
  2794. }
  2795. }
  2796. return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
  2797. }
  2798. /**
  2799. * 将ieRange转换为Range对象
  2800. * @param {Range} ieRange ieRange对象
  2801. * @param {Range} range Range对象
  2802. * @return {Range} range 返回转换后的Range对象
  2803. */
  2804. function transformIERangeToRange( ieRange, range ) {
  2805. if ( ieRange.item ) {
  2806. range.selectNode( ieRange.item( 0 ) );
  2807. } else {
  2808. var bi = getBoundaryInformation( ieRange, true );
  2809. range.setStart( bi.container, bi.offset );
  2810. if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
  2811. bi = getBoundaryInformation( ieRange, false );
  2812. range.setEnd( bi.container, bi.offset );
  2813. }
  2814. }
  2815. return range;
  2816. }
  2817. /**
  2818. * 获得ieRange
  2819. * @param {Selection} sel Selection对象
  2820. * @return {ieRange} 得到ieRange
  2821. */
  2822. function _getIERange( sel,txtRange ) {
  2823. var ieRange;
  2824. //ie下有可能报错
  2825. try {
  2826. ieRange = sel.getNative(txtRange).createRange();
  2827. } catch ( e ) {
  2828. return null;
  2829. }
  2830. var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
  2831. if ( ( el.ownerDocument || el ) === sel.document ) {
  2832. return ieRange;
  2833. }
  2834. return null;
  2835. }
  2836. var Selection = dom.Selection = function ( doc,body ) {
  2837. var me = this;
  2838. me.document = doc;
  2839. me.body = body;
  2840. if ( browser.ie9below ) {
  2841. $( body).on('beforedeactivate', function () {
  2842. me._bakIERange = me.getIERange();
  2843. } ).on('activate', function () {
  2844. try {
  2845. var ieNativRng = _getIERange( me );
  2846. if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) {
  2847. me._bakIERange.select();
  2848. }
  2849. } catch ( ex ) {
  2850. }
  2851. me._bakIERange = null;
  2852. } );
  2853. }
  2854. };
  2855. Selection.prototype = {
  2856. hasNativeRange : function(){
  2857. var rng;
  2858. if(!browser.ie || browser.ie9above){
  2859. var nativeSel = this.getNative();
  2860. if(!nativeSel.rangeCount){
  2861. return false;
  2862. }
  2863. rng = nativeSel.getRangeAt(0);
  2864. }else{
  2865. rng = _getIERange(this);
  2866. }
  2867. return this.rangeInBody(rng);
  2868. },
  2869. /**
  2870. * 获取原生seleciton对象
  2871. * @public
  2872. * @function
  2873. * @name UM.dom.Selection.getNative
  2874. * @return {Selection} 获得selection对象
  2875. */
  2876. getNative:function (txtRange) {
  2877. var doc = this.document;
  2878. try {
  2879. return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection();
  2880. } catch ( e ) {
  2881. return null;
  2882. }
  2883. },
  2884. /**
  2885. * 获得ieRange
  2886. * @public
  2887. * @function
  2888. * @name UM.dom.Selection.getIERange
  2889. * @return {ieRange} 返回ie原生的Range
  2890. */
  2891. getIERange:function (txtRange) {
  2892. var ieRange = _getIERange( this,txtRange );
  2893. if ( !ieRange || !this.rangeInBody(ieRange,txtRange)) {
  2894. if ( this._bakIERange ) {
  2895. return this._bakIERange;
  2896. }
  2897. }
  2898. return ieRange;
  2899. },
  2900. rangeInBody : function(rng,txtRange){
  2901. var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  2902. return node === this.body || domUtils.inDoc(node,this.body);
  2903. },
  2904. /**
  2905. * 缓存当前选区的range和选区的开始节点
  2906. * @public
  2907. * @function
  2908. * @name UM.dom.Selection.cache
  2909. */
  2910. cache:function () {
  2911. this.clear();
  2912. this._cachedRange = this.getRange();
  2913. this._cachedStartElement = this.getStart();
  2914. this._cachedStartElementPath = this.getStartElementPath();
  2915. },
  2916. getStartElementPath:function () {
  2917. if ( this._cachedStartElementPath ) {
  2918. return this._cachedStartElementPath;
  2919. }
  2920. var start = this.getStart();
  2921. if ( start ) {
  2922. return domUtils.findParents( start, true, null, true )
  2923. }
  2924. return [];
  2925. },
  2926. /**
  2927. * 清空缓存
  2928. * @public
  2929. * @function
  2930. * @name UM.dom.Selection.clear
  2931. */
  2932. clear:function () {
  2933. this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
  2934. },
  2935. /**
  2936. * 编辑器是否得到了选区
  2937. */
  2938. isFocus:function () {
  2939. return this.hasNativeRange()
  2940. },
  2941. /**
  2942. * 获取选区对应的Range
  2943. * @public
  2944. * @function
  2945. * @name UM.dom.Selection.getRange
  2946. * @returns {UM.dom.Range} 得到Range对象
  2947. */
  2948. getRange:function () {
  2949. var me = this;
  2950. function optimze( range ) {
  2951. var child = me.body.firstChild,
  2952. collapsed = range.collapsed;
  2953. while ( child && child.firstChild ) {
  2954. range.setStart( child, 0 );
  2955. child = child.firstChild;
  2956. }
  2957. if ( !range.startContainer ) {
  2958. range.setStart( me.body, 0 )
  2959. }
  2960. if ( collapsed ) {
  2961. range.collapse( true );
  2962. }
  2963. }
  2964. if ( me._cachedRange != null ) {
  2965. return this._cachedRange;
  2966. }
  2967. var range = new dom.Range( me.document,me.body );
  2968. if ( browser.ie9below ) {
  2969. var nativeRange = me.getIERange();
  2970. if ( nativeRange && this.rangeInBody(nativeRange)) {
  2971. try{
  2972. transformIERangeToRange( nativeRange, range );
  2973. }catch(e){
  2974. optimze( range );
  2975. }
  2976. } else {
  2977. optimze( range );
  2978. }
  2979. } else {
  2980. var sel = me.getNative();
  2981. if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) {
  2982. var firstRange = sel.getRangeAt( 0 );
  2983. var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
  2984. range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
  2985. if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
  2986. optimze( range );
  2987. }
  2988. } else {
  2989. //trace:1734 有可能已经不在dom树上了,标识的节点
  2990. if ( this._bakRange && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){
  2991. return this._bakRange;
  2992. }
  2993. optimze( range );
  2994. }
  2995. }
  2996. return this._bakRange = range;
  2997. },
  2998. /**
  2999. * 获取开始元素,用于状态反射
  3000. * @public
  3001. * @function
  3002. * @name UM.dom.Selection.getStart
  3003. * @return {Element} 获得开始元素
  3004. */
  3005. getStart:function () {
  3006. if ( this._cachedStartElement ) {
  3007. return this._cachedStartElement;
  3008. }
  3009. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  3010. tmpRange,
  3011. start, tmp, parent;
  3012. if ( browser.ie9below ) {
  3013. if ( !range ) {
  3014. //todo 给第一个值可能会有问题
  3015. return this.document.body.firstChild;
  3016. }
  3017. //control元素
  3018. if ( range.item ){
  3019. return range.item( 0 );
  3020. }
  3021. tmpRange = range.duplicate();
  3022. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  3023. tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
  3024. tmpRange.collapse( 1 );
  3025. start = tmpRange.parentElement();
  3026. parent = tmp = range.parentElement();
  3027. while ( tmp = tmp.parentNode ) {
  3028. if ( tmp == start ) {
  3029. start = parent;
  3030. break;
  3031. }
  3032. }
  3033. } else {
  3034. start = range.startContainer;
  3035. if ( start.nodeType == 1 && start.hasChildNodes() ){
  3036. start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
  3037. }
  3038. if ( start.nodeType == 3 ){
  3039. return start.parentNode;
  3040. }
  3041. }
  3042. return start;
  3043. },
  3044. /**
  3045. * 得到选区中的文本
  3046. * @public
  3047. * @function
  3048. * @name UM.dom.Selection.getText
  3049. * @return {String} 选区中包含的文本
  3050. */
  3051. getText:function () {
  3052. var nativeSel, nativeRange;
  3053. if ( this.isFocus() && (nativeSel = this.getNative()) ) {
  3054. nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
  3055. return browser.ie9below ? nativeRange.text : nativeRange.toString();
  3056. }
  3057. return '';
  3058. }
  3059. };
  3060. })();
  3061. /**
  3062. * @file
  3063. * @name UM.Editor
  3064. * @short Editor
  3065. * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js
  3066. * @desc 编辑器主类,包含编辑器提供的大部分公用接口
  3067. */
  3068. (function () {
  3069. var uid = 0, _selectionChangeTimer;
  3070. /**
  3071. * @private
  3072. * @ignore
  3073. * @param form 编辑器所在的form元素
  3074. * @param editor 编辑器实例对象
  3075. */
  3076. function setValue(form, editor) {
  3077. var textarea;
  3078. if (editor.textarea) {
  3079. if (utils.isString(editor.textarea)) {
  3080. for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
  3081. if (ti.id == 'umeditor_textarea_' + editor.options.textarea) {
  3082. textarea = ti;
  3083. break;
  3084. }
  3085. }
  3086. } else {
  3087. textarea = editor.textarea;
  3088. }
  3089. }
  3090. if (!textarea) {
  3091. form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
  3092. 'name': editor.options.textarea,
  3093. 'id': 'umeditor_textarea_' + editor.options.textarea,
  3094. 'style': "display:none"
  3095. }));
  3096. //不要产生多个textarea
  3097. editor.textarea = textarea;
  3098. }
  3099. textarea.value = editor.hasContents() ?
  3100. (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
  3101. ''
  3102. }
  3103. function loadPlugins(me){
  3104. //初始化插件
  3105. for (var pi in UM.plugins) {
  3106. if(me.options.excludePlugins.indexOf(pi) == -1){
  3107. UM.plugins[pi].call(me);
  3108. me.plugins[pi] = 1;
  3109. }
  3110. }
  3111. me.langIsReady = true;
  3112. me.fireEvent("langReady");
  3113. }
  3114. function checkCurLang(I18N){
  3115. for(var lang in I18N){
  3116. return lang
  3117. }
  3118. }
  3119. /**
  3120. * UEditor编辑器类
  3121. * @name Editor
  3122. * @desc 创建一个跟编辑器实例
  3123. * - ***container*** 编辑器容器对象
  3124. * - ***iframe*** 编辑区域所在的iframe对象
  3125. * - ***window*** 编辑区域所在的window
  3126. * - ***document*** 编辑区域所在的document对象
  3127. * - ***body*** 编辑区域所在的body对象
  3128. * - ***selection*** 编辑区域的选区对象
  3129. */
  3130. var Editor = UM.Editor = function (options) {
  3131. var me = this;
  3132. me.uid = uid++;
  3133. EventBase.call(me);
  3134. me.commands = {};
  3135. me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);
  3136. me.shortcutkeys = {};
  3137. me.inputRules = [];
  3138. me.outputRules = [];
  3139. //设置默认的常用属性
  3140. me.setOpt({
  3141. isShow: true,
  3142. initialContent: '',
  3143. initialStyle:'',
  3144. autoClearinitialContent: false,
  3145. textarea: 'editorValue',
  3146. focus: false,
  3147. focusInEnd: true,
  3148. autoClearEmptyNode: true,
  3149. fullscreen: false,
  3150. readonly: false,
  3151. zIndex: 999,
  3152. enterTag: 'p',
  3153. lang: 'zh-cn',
  3154. langPath: me.options.UMEDITOR_HOME_URL + 'lang/',
  3155. theme: 'default',
  3156. themePath: me.options.UMEDITOR_HOME_URL + 'themes/',
  3157. allHtmlEnabled: false,
  3158. autoSyncData : true,
  3159. autoHeightEnabled : true,
  3160. excludePlugins:''
  3161. });
  3162. me.plugins = {};
  3163. if(!utils.isEmptyObject(UM.I18N)){
  3164. //修改默认的语言类型
  3165. me.options.lang = checkCurLang(UM.I18N);
  3166. loadPlugins(me)
  3167. }else{
  3168. utils.loadFile(document, {
  3169. src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
  3170. tag: "script",
  3171. type: "text/javascript",
  3172. defer: "defer"
  3173. }, function () {
  3174. loadPlugins(me)
  3175. });
  3176. }
  3177. };
  3178. Editor.prototype = {
  3179. /**
  3180. * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready,就马上执行fn,fn的中的this是编辑器实例。
  3181. * 大部分的实例接口都需要放在该方法内部执行,否则在IE下可能会报错。
  3182. * @name ready
  3183. * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function
  3184. * @example
  3185. * var editor = new UM.ui.Editor();
  3186. * editor.render("myEditor");
  3187. * editor.ready(function(){
  3188. * editor.setContent("欢迎使用UEditor!");
  3189. * })
  3190. */
  3191. ready: function (fn) {
  3192. var me = this;
  3193. if (fn) {
  3194. me.isReady ? fn.apply(me) : me.addListener('ready', fn);
  3195. }
  3196. },
  3197. /**
  3198. * 为编辑器设置默认参数值。若用户配置为空,则以默认配置为准
  3199. * @grammar editor.setOpt(key,value); //传入一个键、值对
  3200. * @grammar editor.setOpt({ key:value}); //传入一个json对象
  3201. */
  3202. setOpt: function (key, val) {
  3203. var obj = {};
  3204. if (utils.isString(key)) {
  3205. obj[key] = val
  3206. } else {
  3207. obj = key;
  3208. }
  3209. utils.extend(this.options, obj, true);
  3210. },
  3211. getOpt:function(key){
  3212. return this.options[key] || ''
  3213. },
  3214. /**
  3215. * 销毁编辑器实例对象
  3216. * @name destroy
  3217. * @grammar editor.destroy();
  3218. */
  3219. destroy: function () {
  3220. var me = this;
  3221. me.fireEvent('destroy');
  3222. var container = me.container.parentNode;
  3223. if(container === document.body){
  3224. container = me.container;
  3225. }
  3226. var textarea = me.textarea;
  3227. if (!textarea) {
  3228. textarea = document.createElement('textarea');
  3229. container.parentNode.insertBefore(textarea, container);
  3230. } else {
  3231. textarea.style.display = ''
  3232. }
  3233. textarea.style.width = me.body.offsetWidth + 'px';
  3234. textarea.style.height = me.body.offsetHeight + 'px';
  3235. textarea.value = me.getContent();
  3236. textarea.id = me.key;
  3237. if(container.contains(textarea)){
  3238. $(textarea).insertBefore(container);
  3239. }
  3240. container.innerHTML = '';
  3241. domUtils.remove(container);
  3242. UM.clearCache(me.id);
  3243. //trace:2004
  3244. for (var p in me) {
  3245. if (me.hasOwnProperty(p)) {
  3246. delete this[p];
  3247. }
  3248. }
  3249. },
  3250. initialCont : function(holder){
  3251. if(holder){
  3252. holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name'));
  3253. if (holder && /script|textarea/ig.test(holder.tagName)) {
  3254. var newDiv = document.createElement('div');
  3255. holder.parentNode.insertBefore(newDiv, holder);
  3256. this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml();
  3257. holder.className && (newDiv.className = holder.className);
  3258. holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  3259. if (/textarea/i.test(holder.tagName)) {
  3260. this.textarea = holder;
  3261. this.textarea.style.display = 'none';
  3262. } else {
  3263. holder.parentNode.removeChild(holder);
  3264. holder.id && (newDiv.id = holder.id);
  3265. }
  3266. holder = newDiv;
  3267. holder.innerHTML = '';
  3268. }
  3269. return holder;
  3270. }else{
  3271. return null;
  3272. }
  3273. },
  3274. /**
  3275. * 渲染编辑器的DOM到指定容器,必须且只能调用一次
  3276. * @name render
  3277. * @grammar editor.render(containerId); //可以指定一个容器ID
  3278. * @grammar editor.render(containerDom); //也可以直接指定容器对象
  3279. */
  3280. render: function (container) {
  3281. var me = this,
  3282. options = me.options,
  3283. getStyleValue=function(attr){
  3284. return parseInt($(container).css(attr));
  3285. };
  3286. if (utils.isString(container)) {
  3287. container = document.getElementById(container);
  3288. }
  3289. if (container) {
  3290. this.id = container.getAttribute('id');
  3291. UM.setEditor(this);
  3292. utils.cssRule('edui-style-body',me.options.initialStyle,document);
  3293. container = this.initialCont(container);
  3294. container.className += ' edui-body-container';
  3295. if(options.initialFrameWidth){
  3296. options.minFrameWidth = options.initialFrameWidth
  3297. }else{
  3298. //都没给值,先写死了
  3299. options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;
  3300. }
  3301. if(options.initialFrameHeight){
  3302. options.minFrameHeight = options.initialFrameHeight
  3303. }else{
  3304. options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;
  3305. }
  3306. container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth -
  3307. getStyleValue("padding-left")-
  3308. getStyleValue("padding-right") +'px';
  3309. var height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") );
  3310. if(this.options.autoHeightEnabled){
  3311. container.style.minHeight = height +'px';
  3312. container.style.height = '';
  3313. if(browser.ie && browser.version <= 6){
  3314. container.style.height = height ;
  3315. container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3316. }
  3317. }else{
  3318. $(container).height(height)
  3319. }
  3320. container.style.zIndex = options.zIndex;
  3321. this._setup(container);
  3322. }
  3323. },
  3324. /**
  3325. * 编辑器初始化
  3326. * @private
  3327. * @ignore
  3328. * @param {Element} doc 编辑器Iframe中的文档对象
  3329. */
  3330. _setup: function (cont) {
  3331. var me = this,
  3332. options = me.options;
  3333. cont.contentEditable = true;
  3334. document.body.spellcheck = false;
  3335. me.document = document;
  3336. me.window = document.defaultView || document.parentWindow;
  3337. me.body = cont;
  3338. me.$body = $(cont);
  3339. me.selection = new dom.Selection(document,me.body);
  3340. me._isEnabled = false;
  3341. //gecko初始化就能得到range,无法判断isFocus了
  3342. var geckoSel;
  3343. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3344. geckoSel.removeAllRanges();
  3345. }
  3346. this._initEvents();
  3347. //为form提交提供一个隐藏的textarea
  3348. for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {
  3349. if (form.tagName == 'FORM') {
  3350. me.form = form;
  3351. if(me.options.autoSyncData){
  3352. $(cont).on('blur',function(){
  3353. setValue(form,me);
  3354. })
  3355. }else{
  3356. $(form).on('submit', function () {
  3357. setValue(this, me);
  3358. })
  3359. }
  3360. break;
  3361. }
  3362. }
  3363. if (options.initialContent) {
  3364. if (options.autoClearinitialContent) {
  3365. var oldExecCommand = me.execCommand;
  3366. me.execCommand = function () {
  3367. me.fireEvent('firstBeforeExecCommand');
  3368. return oldExecCommand.apply(me, arguments);
  3369. };
  3370. this._setDefaultContent(options.initialContent);
  3371. } else
  3372. this.setContent(options.initialContent, false, true);
  3373. }
  3374. //编辑器不能为空内容
  3375. if (domUtils.isEmptyNode(me.body)) {
  3376. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  3377. }
  3378. //如果要求focus, 就把光标定位到内容开始
  3379. if (options.focus) {
  3380. setTimeout(function () {
  3381. me.focus(me.options.focusInEnd);
  3382. //如果自动清除开着,就不需要做selectionchange;
  3383. !me.options.autoClearinitialContent && me._selectionChange();
  3384. }, 0);
  3385. }
  3386. if (!me.container) {
  3387. me.container = cont.parentNode;
  3388. }
  3389. me._bindshortcutKeys();
  3390. me.isReady = 1;
  3391. me.fireEvent('ready');
  3392. options.onready && options.onready.call(me);
  3393. if(!browser.ie || browser.ie9above){
  3394. $(me.body).on( 'blur focus', function (e) {
  3395. var nSel = me.selection.getNative();
  3396. //chrome下会出现alt+tab切换时,导致选区位置不对
  3397. if (e.type == 'blur') {
  3398. if(nSel.rangeCount > 0 ){
  3399. me._bakRange = nSel.getRangeAt(0);
  3400. }
  3401. } else {
  3402. try {
  3403. me._bakRange && nSel.addRange(me._bakRange)
  3404. } catch (e) {
  3405. }
  3406. me._bakRange = null;
  3407. }
  3408. });
  3409. }
  3410. !options.isShow && me.setHide();
  3411. options.readonly && me.setDisabled();
  3412. },
  3413. /**
  3414. * 同步编辑器的数据,为提交数据做准备,主要用于你是手动提交的情况
  3415. * @name sync
  3416. * @grammar editor.sync(); //从编辑器的容器向上查找,如果找到就同步数据
  3417. * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  3418. * @desc
  3419. * 后台取得数据得键值使用你容器上得''name''属性,如果没有就使用参数传入的''textarea''
  3420. * @example
  3421. * editor.sync();
  3422. * form.sumbit(); //form变量已经指向了form元素
  3423. *
  3424. */
  3425. sync: function (formId) {
  3426. var me = this,
  3427. form = formId ? document.getElementById(formId) :
  3428. domUtils.findParent(me.body.parentNode, function (node) {
  3429. return node.tagName == 'FORM'
  3430. }, true);
  3431. form && setValue(form, me);
  3432. },
  3433. /**
  3434. * 设置编辑器高度
  3435. * @name setHeight
  3436. * @grammar editor.setHeight(number); //纯数值,不带单位
  3437. */
  3438. setHeight: function (height,notSetHeight) {
  3439. !notSetHeight && (this.options.initialFrameHeight = height);
  3440. if(this.options.autoHeightEnabled){
  3441. $(this.body).css({
  3442. 'min-height':height + 'px'
  3443. });
  3444. if(browser.ie && browser.version <= 6 && this.container){
  3445. this.container.style.height = height ;
  3446. this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3447. }
  3448. }else{
  3449. $(this.body).height(height)
  3450. }
  3451. this.fireEvent('resize');
  3452. },
  3453. /**
  3454. * 设置编辑器宽度
  3455. * @name setWidth
  3456. * @grammar editor.setWidth(number); //纯数值,不带单位
  3457. */
  3458. setWidth:function(width){
  3459. this.$container && this.$container.width(width);
  3460. $(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1);
  3461. this.fireEvent('resize');
  3462. },
  3463. addshortcutkey: function (cmd, keys) {
  3464. var obj = {};
  3465. if (keys) {
  3466. obj[cmd] = keys
  3467. } else {
  3468. obj = cmd;
  3469. }
  3470. utils.extend(this.shortcutkeys, obj)
  3471. },
  3472. _bindshortcutKeys: function () {
  3473. var me = this, shortcutkeys = this.shortcutkeys;
  3474. me.addListener('keydown', function (type, e) {
  3475. var keyCode = e.keyCode || e.which;
  3476. for (var i in shortcutkeys) {
  3477. var tmp = shortcutkeys[i].split(',');
  3478. for (var t = 0, ti; ti = tmp[t++];) {
  3479. ti = ti.split(':');
  3480. var key = ti[0], param = ti[1];
  3481. if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
  3482. if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
  3483. && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
  3484. && keyCode == RegExp.$3
  3485. ) ||
  3486. keyCode == RegExp.$1
  3487. ) {
  3488. if (me.queryCommandState(i,param) != -1)
  3489. me.execCommand(i, param);
  3490. domUtils.preventDefault(e);
  3491. }
  3492. }
  3493. }
  3494. }
  3495. });
  3496. },
  3497. /**
  3498. * 获取编辑器内容
  3499. * @name getContent
  3500. * @grammar editor.getContent() => String //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。
  3501. * @grammar editor.getContent(fn) => String
  3502. * @example
  3503. * getContent默认是会现调用hasContents来判断编辑器是否为空,如果是,就直接返回空字符串
  3504. * 你也可以传入一个fn来接替hasContents的工作,定制判断的规则
  3505. * editor.getContent(function(){
  3506. * return false //编辑器没有内容 ,getContent直接返回空
  3507. * })
  3508. */
  3509. getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
  3510. var me = this;
  3511. if (cmd && utils.isFunction(cmd)) {
  3512. fn = cmd;
  3513. cmd = '';
  3514. }
  3515. if (fn ? !fn() : !this.hasContents()) {
  3516. return '';
  3517. }
  3518. me.fireEvent('beforegetcontent');
  3519. var root = UM.htmlparser(me.body.innerHTML,ignoreBlank);
  3520. me.filterOutputRule(root);
  3521. me.fireEvent('aftergetcontent',root);
  3522. return root.toHtml(formatter);
  3523. },
  3524. /**
  3525. * 取得完整的html代码,可以直接显示成完整的html文档
  3526. * @name getAllHtml
  3527. * @grammar editor.getAllHtml() => String
  3528. */
  3529. getAllHtml: function () {
  3530. var me = this,
  3531. headHtml = [],
  3532. html = '';
  3533. me.fireEvent('getAllHtml', headHtml);
  3534. if (browser.ie && browser.version > 8) {
  3535. var headHtmlForIE9 = '';
  3536. utils.each(me.document.styleSheets, function (si) {
  3537. headHtmlForIE9 += ( si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
  3538. });
  3539. utils.each(me.document.getElementsByTagName('script'), function (si) {
  3540. headHtmlForIE9 += si.outerHTML;
  3541. });
  3542. }
  3543. return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
  3544. + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
  3545. + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
  3546. },
  3547. /**
  3548. * 得到编辑器的纯文本内容,但会保留段落格式
  3549. * @name getPlainTxt
  3550. * @grammar editor.getPlainTxt() => String
  3551. */
  3552. getPlainTxt: function () {
  3553. var reg = new RegExp(domUtils.fillChar, 'g'),
  3554. html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
  3555. html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  3556. .replace(/<br\/?>/gi, '\n')
  3557. .replace(/<[^>/]+>/g, '')
  3558. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  3559. return dtd.$block[c] ? '\n' : b ? b : '';
  3560. });
  3561. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3562. return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
  3563. },
  3564. /**
  3565. * 获取编辑器中的纯文本内容,没有段落格式
  3566. * @name getContentTxt
  3567. * @grammar editor.getContentTxt() => String
  3568. */
  3569. getContentTxt: function () {
  3570. var reg = new RegExp(domUtils.fillChar, 'g');
  3571. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3572. return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
  3573. },
  3574. /**
  3575. * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值,则必须放在ready方法内部执行
  3576. * @name setContent
  3577. * @grammar editor.setContent(html)
  3578. * @example
  3579. * var editor = new UM.ui.Editor()
  3580. * editor.ready(function(){
  3581. * //需要ready后执行,否则可能报错
  3582. * editor.setContent("欢迎使用UEditor!");
  3583. * })
  3584. */
  3585. setContent: function (html, isAppendTo, notFireSelectionchange) {
  3586. var me = this;
  3587. me.fireEvent('beforesetcontent', html);
  3588. var root = UM.htmlparser(html);
  3589. me.filterInputRule(root);
  3590. html = root.toHtml();
  3591. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  3592. function isCdataDiv(node){
  3593. return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
  3594. }
  3595. //给文本或者inline节点套p标签
  3596. if (me.options.enterTag == 'p') {
  3597. var child = this.body.firstChild, tmpNode;
  3598. if (!child || child.nodeType == 1 &&
  3599. (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
  3600. domUtils.isCustomeNode(child)
  3601. )
  3602. && child === this.body.lastChild) {
  3603. this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  3604. } else {
  3605. var p = me.document.createElement('p');
  3606. while (child) {
  3607. while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
  3608. tmpNode = child.nextSibling;
  3609. p.appendChild(child);
  3610. child = tmpNode;
  3611. }
  3612. if (p.firstChild) {
  3613. if (!child) {
  3614. me.body.appendChild(p);
  3615. break;
  3616. } else {
  3617. child.parentNode.insertBefore(p, child);
  3618. p = me.document.createElement('p');
  3619. }
  3620. }
  3621. child = child.nextSibling;
  3622. }
  3623. }
  3624. }
  3625. me.fireEvent('aftersetcontent');
  3626. me.fireEvent('contentchange');
  3627. !notFireSelectionchange && me._selectionChange();
  3628. //清除保存的选区
  3629. me._bakRange = me._bakIERange = me._bakNativeRange = null;
  3630. //trace:1742 setContent后gecko能得到焦点问题
  3631. var geckoSel;
  3632. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3633. geckoSel.removeAllRanges();
  3634. }
  3635. if(me.options.autoSyncData){
  3636. me.form && setValue(me.form,me);
  3637. }
  3638. },
  3639. /**
  3640. * 让编辑器获得焦点,toEnd确定focus位置
  3641. * @name focus
  3642. * @grammar editor.focus([toEnd]) //默认focus到编辑器头部,toEnd为true时focus到内容尾部
  3643. */
  3644. focus: function (toEnd) {
  3645. try {
  3646. var me = this,
  3647. rng = me.selection.getRange();
  3648. if (toEnd) {
  3649. rng.setStartAtLast(me.body.lastChild).setCursor(false, true);
  3650. } else {
  3651. rng.select(true);
  3652. }
  3653. this.fireEvent('focus');
  3654. } catch (e) {
  3655. }
  3656. },
  3657. /**
  3658. * 使编辑区域失去焦点
  3659. */
  3660. blur:function(){
  3661. var sel = this.selection.getNative();
  3662. sel.empty ? sel.empty() : sel.removeAllRanges();
  3663. this.fireEvent('blur')
  3664. },
  3665. /**
  3666. * 判断编辑器当前是否获得了焦点
  3667. */
  3668. isFocus : function(){
  3669. if(this.fireEvent('isfocus')===true){
  3670. return true;
  3671. }
  3672. return this.selection.isFocus();
  3673. },
  3674. /**
  3675. * 初始化UE事件及部分事件代理
  3676. * @private
  3677. * @ignore
  3678. */
  3679. _initEvents: function () {
  3680. var me = this,
  3681. cont = me.body,
  3682. _proxyDomEvent = function(){
  3683. me._proxyDomEvent.apply(me, arguments);
  3684. };
  3685. $(cont)
  3686. .on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)
  3687. .on( 'focus blur', _proxyDomEvent)
  3688. .on('mouseup keydown', function (evt) {
  3689. //特殊键不触发selectionchange
  3690. if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
  3691. return;
  3692. }
  3693. if (evt.button == 2)return;
  3694. me._selectionChange(250, evt);
  3695. });
  3696. },
  3697. /**
  3698. * 触发事件代理
  3699. * @private
  3700. * @ignore
  3701. */
  3702. _proxyDomEvent: function (evt) {
  3703. return this.fireEvent(evt.type.replace(/^on/, ''), evt);
  3704. },
  3705. /**
  3706. * 变化选区
  3707. * @private
  3708. * @ignore
  3709. */
  3710. _selectionChange: function (delay, evt) {
  3711. var me = this;
  3712. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  3713. // if ( !me.selection.isFocus() ){
  3714. // return;
  3715. // }
  3716. var hackForMouseUp = false;
  3717. var mouseX, mouseY;
  3718. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  3719. var range = this.selection.getRange();
  3720. if (!range.collapsed) {
  3721. hackForMouseUp = true;
  3722. mouseX = evt.clientX;
  3723. mouseY = evt.clientY;
  3724. }
  3725. }
  3726. clearTimeout(_selectionChangeTimer);
  3727. _selectionChangeTimer = setTimeout(function () {
  3728. if (!me.selection.getNative()) {
  3729. return;
  3730. }
  3731. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  3732. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  3733. var ieRange;
  3734. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  3735. ieRange = me.document.body.createTextRange();
  3736. try {
  3737. ieRange.moveToPoint(mouseX, mouseY);
  3738. } catch (ex) {
  3739. ieRange = null;
  3740. }
  3741. }
  3742. var bakGetIERange;
  3743. if (ieRange) {
  3744. bakGetIERange = me.selection.getIERange;
  3745. me.selection.getIERange = function () {
  3746. return ieRange;
  3747. };
  3748. }
  3749. me.selection.cache();
  3750. if (bakGetIERange) {
  3751. me.selection.getIERange = bakGetIERange;
  3752. }
  3753. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  3754. me.fireEvent('beforeselectionchange');
  3755. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  3756. me.fireEvent('selectionchange', !!evt);
  3757. me.fireEvent('afterselectionchange');
  3758. me.selection.clear();
  3759. }
  3760. }, delay || 50);
  3761. },
  3762. _callCmdFn: function (fnName, args) {
  3763. args = Array.prototype.slice.call(args,0);
  3764. var cmdName = args.shift().toLowerCase(),
  3765. cmd, cmdFn;
  3766. cmd = this.commands[cmdName] || UM.commands[cmdName];
  3767. cmdFn = cmd && cmd[fnName];
  3768. //没有querycommandstate或者没有command的都默认返回0
  3769. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  3770. return 0;
  3771. } else if (cmdFn) {
  3772. return cmdFn.apply(this, [cmdName].concat(args));
  3773. }
  3774. },
  3775. /**
  3776. * 执行编辑命令cmdName,完成富文本编辑效果
  3777. * @name execCommand
  3778. * @grammar editor.execCommand(cmdName) => {*}
  3779. */
  3780. execCommand: function (cmdName) {
  3781. if(!this.isFocus()){
  3782. var bakRange = this.selection._bakRange;
  3783. if(bakRange){
  3784. bakRange.select()
  3785. }else{
  3786. this.focus(true)
  3787. }
  3788. }
  3789. cmdName = cmdName.toLowerCase();
  3790. var me = this,
  3791. result,
  3792. cmd = me.commands[cmdName] || UM.commands[cmdName];
  3793. if (!cmd || !cmd.execCommand) {
  3794. return null;
  3795. }
  3796. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  3797. me.__hasEnterExecCommand = true;
  3798. if (me.queryCommandState.apply(me,arguments) != -1) {
  3799. me.fireEvent('saveScene');
  3800. me.fireEvent('beforeexeccommand', cmdName);
  3801. result = this._callCmdFn('execCommand', arguments);
  3802. (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  3803. me.fireEvent('afterexeccommand', cmdName);
  3804. me.fireEvent('saveScene');
  3805. }
  3806. me.__hasEnterExecCommand = false;
  3807. } else {
  3808. result = this._callCmdFn('execCommand', arguments);
  3809. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
  3810. }
  3811. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
  3812. return result;
  3813. },
  3814. /**
  3815. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  3816. * @name queryCommandState
  3817. * @grammar editor.queryCommandState(cmdName) => (-1|0|1)
  3818. * @desc
  3819. * * ''-1'' 当前命令不可用
  3820. * * ''0'' 当前命令可用
  3821. * * ''1'' 当前命令已经执行过了
  3822. */
  3823. queryCommandState: function (cmdName) {
  3824. try{
  3825. return this._callCmdFn('queryCommandState', arguments);
  3826. }catch(e){
  3827. return 0
  3828. }
  3829. },
  3830. /**
  3831. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  3832. * @name queryCommandValue
  3833. * @grammar editor.queryCommandValue(cmdName) => {*}
  3834. */
  3835. queryCommandValue: function (cmdName) {
  3836. try{
  3837. return this._callCmdFn('queryCommandValue', arguments);
  3838. }catch(e){
  3839. return null
  3840. }
  3841. },
  3842. /**
  3843. * 检查编辑区域中是否有内容,若包含tags中的节点类型,直接返回true
  3844. * @name hasContents
  3845. * @desc
  3846. * 默认有文本内容,或者有以下节点都不认为是空
  3847. * <code>{table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1}</code>
  3848. * @grammar editor.hasContents() => (true|false)
  3849. * @grammar editor.hasContents(tags) => (true|false) //若文档中包含tags数组里对应的tag,直接返回true
  3850. * @example
  3851. * editor.hasContents(['span']) //如果编辑器里有这些,不认为是空
  3852. */
  3853. hasContents: function (tags) {
  3854. if (tags) {
  3855. for (var i = 0, ci; ci = tags[i++];) {
  3856. if (this.body.getElementsByTagName(ci).length > 0) {
  3857. return true;
  3858. }
  3859. }
  3860. }
  3861. if (!domUtils.isEmptyBlock(this.body)) {
  3862. return true
  3863. }
  3864. //随时添加,定义的特殊标签如果存在,不能认为是空
  3865. tags = ['div'];
  3866. for (i = 0; ci = tags[i++];) {
  3867. var nodes = domUtils.getElementsByTagName(this.body, ci);
  3868. for (var n = 0, cn; cn = nodes[n++];) {
  3869. if (domUtils.isCustomeNode(cn)) {
  3870. return true;
  3871. }
  3872. }
  3873. }
  3874. return false;
  3875. },
  3876. /**
  3877. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  3878. * @name reset
  3879. * @desc
  3880. * * 清空编辑器内容
  3881. * * 清空回退列表
  3882. * @grammar editor.reset()
  3883. */
  3884. reset: function () {
  3885. this.fireEvent('reset');
  3886. },
  3887. isEnabled: function(){
  3888. return this._isEnabled != true;
  3889. },
  3890. setEnabled: function () {
  3891. var me = this, range;
  3892. me.body.contentEditable = true;
  3893. /* 恢复选区 */
  3894. if (me.lastBk) {
  3895. range = me.selection.getRange();
  3896. try {
  3897. range.moveToBookmark(me.lastBk);
  3898. delete me.lastBk
  3899. } catch (e) {
  3900. range.setStartAtFirst(me.body).collapse(true)
  3901. }
  3902. range.select(true);
  3903. }
  3904. /* 恢复query函数 */
  3905. if (me.bkqueryCommandState) {
  3906. me.queryCommandState = me.bkqueryCommandState;
  3907. delete me.bkqueryCommandState;
  3908. }
  3909. /* 恢复原生事件 */
  3910. if (me._bkproxyDomEvent) {
  3911. me._proxyDomEvent = me._bkproxyDomEvent;
  3912. delete me._bkproxyDomEvent;
  3913. }
  3914. /* 触发事件 */
  3915. me.fireEvent('setEnabled');
  3916. },
  3917. /**
  3918. * 设置当前编辑区域可以编辑
  3919. * @name enable
  3920. * @grammar editor.enable()
  3921. */
  3922. enable: function () {
  3923. return this.setEnabled();
  3924. },
  3925. setDisabled: function (except, keepDomEvent) {
  3926. var me = this;
  3927. me.body.contentEditable = false;
  3928. me._except = except ? utils.isArray(except) ? except : [except] : [];
  3929. /* 备份最后的选区 */
  3930. if (!me.lastBk) {
  3931. me.lastBk = me.selection.getRange().createBookmark(true);
  3932. }
  3933. /* 备份并重置query函数 */
  3934. if(!me.bkqueryCommandState) {
  3935. me.bkqueryCommandState = me.queryCommandState;
  3936. me.queryCommandState = function (type) {
  3937. if (utils.indexOf(me._except, type) != -1) {
  3938. return me.bkqueryCommandState.apply(me, arguments);
  3939. }
  3940. return -1;
  3941. };
  3942. }
  3943. /* 备份并墙原生事件 */
  3944. if(!keepDomEvent && !me._bkproxyDomEvent) {
  3945. me._bkproxyDomEvent = me._proxyDomEvent;
  3946. me._proxyDomEvent = function () {
  3947. return false;
  3948. };
  3949. }
  3950. /* 触发事件 */
  3951. me.fireEvent('selectionchange');
  3952. me.fireEvent('setDisabled', me._except);
  3953. },
  3954. /** 设置当前编辑区域不可编辑,except中的命令除外
  3955. * @name disable
  3956. * @grammar editor.disable()
  3957. * @grammar editor.disable(except) //例外的命令,也即即使设置了disable,此处配置的命令仍然可以执行
  3958. * @example
  3959. * //禁用工具栏中除加粗和插入图片之外的所有功能
  3960. * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array
  3961. */
  3962. disable: function (except) {
  3963. return this.setDisabled(except);
  3964. },
  3965. /**
  3966. * 设置默认内容
  3967. * @ignore
  3968. * @private
  3969. * @param {String} cont 要存入的内容
  3970. */
  3971. _setDefaultContent: function () {
  3972. function clear() {
  3973. var me = this;
  3974. if (me.document.getElementById('initContent')) {
  3975. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
  3976. me.removeListener('firstBeforeExecCommand focus', clear);
  3977. setTimeout(function () {
  3978. me.focus();
  3979. me._selectionChange();
  3980. }, 0)
  3981. }
  3982. }
  3983. return function (cont) {
  3984. var me = this;
  3985. me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  3986. me.addListener('firstBeforeExecCommand focus', clear);
  3987. }
  3988. }(),
  3989. /**
  3990. * show方法的兼容版本
  3991. * @private
  3992. * @ignore
  3993. */
  3994. setShow: function () {
  3995. var me = this, range = me.selection.getRange();
  3996. if (me.container.style.display == 'none') {
  3997. //有可能内容丢失了
  3998. try {
  3999. range.moveToBookmark(me.lastBk);
  4000. delete me.lastBk
  4001. } catch (e) {
  4002. range.setStartAtFirst(me.body).collapse(true)
  4003. }
  4004. //ie下focus实效,所以做了个延迟
  4005. setTimeout(function () {
  4006. range.select(true);
  4007. }, 100);
  4008. me.container.style.display = '';
  4009. }
  4010. },
  4011. /**
  4012. * 显示编辑器
  4013. * @name show
  4014. * @grammar editor.show()
  4015. */
  4016. show: function () {
  4017. return this.setShow();
  4018. },
  4019. /**
  4020. * hide方法的兼容版本
  4021. * @private
  4022. * @ignore
  4023. */
  4024. setHide: function () {
  4025. var me = this;
  4026. if (!me.lastBk) {
  4027. me.lastBk = me.selection.getRange().createBookmark(true);
  4028. }
  4029. me.container.style.display = 'none'
  4030. },
  4031. /**
  4032. * 隐藏编辑器
  4033. * @name hide
  4034. * @grammar editor.hide()
  4035. */
  4036. hide: function () {
  4037. return this.setHide();
  4038. },
  4039. /**
  4040. * 根据制定的路径,获取对应的语言资源
  4041. * @name getLang
  4042. * @grammar editor.getLang(path) => (JSON|String) 路径根据的是lang目录下的语言文件的路径结构
  4043. * @example
  4044. * editor.getLang('contextMenu.delete') //如果当前是中文,那返回是的是删除
  4045. */
  4046. getLang: function (path) {
  4047. var lang = UM.I18N[this.options.lang];
  4048. if (!lang) {
  4049. throw Error("not import language file");
  4050. }
  4051. path = (path || "").split(".");
  4052. for (var i = 0, ci; ci = path[i++];) {
  4053. lang = lang[ci];
  4054. if (!lang)break;
  4055. }
  4056. return lang;
  4057. },
  4058. /**
  4059. * 计算编辑器当前内容的长度
  4060. * @name getContentLength
  4061. * @grammar editor.getContentLength(ingoneHtml,tagNames) =>
  4062. * @example
  4063. * editor.getLang(true)
  4064. */
  4065. getContentLength: function (ingoneHtml, tagNames) {
  4066. var count = this.getContent(false,false,true).length;
  4067. if (ingoneHtml) {
  4068. tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
  4069. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
  4070. for (var i = 0, ci; ci = tagNames[i++];) {
  4071. count += this.body.getElementsByTagName(ci).length;
  4072. }
  4073. }
  4074. return count;
  4075. },
  4076. addInputRule: function (rule,ignoreUndo) {
  4077. rule.ignoreUndo = ignoreUndo;
  4078. this.inputRules.push(rule);
  4079. },
  4080. filterInputRule: function (root,isUndoLoad) {
  4081. for (var i = 0, ci; ci = this.inputRules[i++];) {
  4082. if(isUndoLoad && ci.ignoreUndo){
  4083. continue;
  4084. }
  4085. ci.call(this, root)
  4086. }
  4087. },
  4088. addOutputRule: function (rule,ignoreUndo) {
  4089. rule.ignoreUndo = ignoreUndo;
  4090. this.outputRules.push(rule)
  4091. },
  4092. filterOutputRule: function (root,isUndoLoad) {
  4093. for (var i = 0, ci; ci = this.outputRules[i++];) {
  4094. if(isUndoLoad && ci.ignoreUndo){
  4095. continue;
  4096. }
  4097. ci.call(this, root)
  4098. }
  4099. }
  4100. };
  4101. utils.inherits(Editor, EventBase);
  4102. })();
  4103. /**
  4104. * @file
  4105. * @name UM.filterWord
  4106. * @short filterWord
  4107. * @desc 用来过滤word粘贴过来的字符串
  4108. * @import editor.js,core/utils.js
  4109. * @anthor zhanyi
  4110. */
  4111. var filterWord = UM.filterWord = function () {
  4112. //是否是word过来的内容
  4113. function isWordDocument( str ) {
  4114. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
  4115. }
  4116. //去掉小数
  4117. function transUnit( v ) {
  4118. v = v.replace( /[\d.]+\w+/g, function ( m ) {
  4119. return utils.transUnitToPx(m);
  4120. } );
  4121. return v;
  4122. }
  4123. function filterPasteWord( str ) {
  4124. return str.replace(/[\t\r\n]+/g,' ')
  4125. .replace( /<!--[\s\S]*?-->/ig, "" )
  4126. //转换图片
  4127. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
  4128. //opera能自己解析出image所这里直接返回空
  4129. if(browser.opera){
  4130. return '';
  4131. }
  4132. try{
  4133. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  4134. if(/Bitmap/i.test(str)){
  4135. return '';
  4136. }
  4137. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  4138. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  4139. src = str.match(/src=\s*"([^"]*)"/i)[1];
  4140. return '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
  4141. } catch(e){
  4142. return '';
  4143. }
  4144. })
  4145. //针对wps添加的多余标签处理
  4146. .replace(/<\/?div[^>]*>/g,'')
  4147. //去掉多余的属性
  4148. .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
  4149. .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
  4150. .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
  4151. //去掉多余的属性
  4152. .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
  4153. //保留list的标示
  4154. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  4155. })
  4156. //清除多余的font/span不能匹配&nbsp;有可能是空格
  4157. .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
  4158. return c.replace(/[\t\r\n ]+/g,' ')
  4159. })
  4160. //处理style的问题
  4161. .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
  4162. var n = [],
  4163. s = style.replace( /^\s+|\s+$/, '' )
  4164. .replace(/&#39;/g,'\'')
  4165. .replace( /&quot;/gi, "'" )
  4166. .split( /;\s*/g );
  4167. for ( var i = 0,v; v = s[i];i++ ) {
  4168. var name, value,
  4169. parts = v.split( ":" );
  4170. if ( parts.length == 2 ) {
  4171. name = parts[0].toLowerCase();
  4172. value = parts[1].toLowerCase();
  4173. if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
  4174. ||
  4175. /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
  4176. ){
  4177. continue;
  4178. }
  4179. switch ( name ) {
  4180. case "mso-padding-alt":
  4181. case "mso-padding-top-alt":
  4182. case "mso-padding-right-alt":
  4183. case "mso-padding-bottom-alt":
  4184. case "mso-padding-left-alt":
  4185. case "mso-margin-alt":
  4186. case "mso-margin-top-alt":
  4187. case "mso-margin-right-alt":
  4188. case "mso-margin-bottom-alt":
  4189. case "mso-margin-left-alt":
  4190. //ie下会出现挤到一起的情况
  4191. //case "mso-table-layout-alt":
  4192. case "mso-height":
  4193. case "mso-width":
  4194. case "mso-vertical-align-alt":
  4195. //trace:1819 ff下会解析出padding在table上
  4196. if(!/<table/.test(tag))
  4197. n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
  4198. continue;
  4199. case "horiz-align":
  4200. n[i] = "text-align:" + value;
  4201. continue;
  4202. case "vert-align":
  4203. n[i] = "vertical-align:" + value;
  4204. continue;
  4205. case "font-color":
  4206. case "mso-foreground":
  4207. n[i] = "color:" + value;
  4208. continue;
  4209. case "mso-background":
  4210. case "mso-highlight":
  4211. n[i] = "background:" + value;
  4212. continue;
  4213. case "mso-default-height":
  4214. n[i] = "min-height:" + transUnit( value );
  4215. continue;
  4216. case "mso-default-width":
  4217. n[i] = "min-width:" + transUnit( value );
  4218. continue;
  4219. case "mso-padding-between-alt":
  4220. n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
  4221. continue;
  4222. case "text-line-through":
  4223. if ( (value == "single") || (value == "double") ) {
  4224. n[i] = "text-decoration:line-through";
  4225. }
  4226. continue;
  4227. case "mso-zero-height":
  4228. if ( value == "yes" ) {
  4229. n[i] = "display:none";
  4230. }
  4231. continue;
  4232. // case 'background':
  4233. // break;
  4234. case 'margin':
  4235. if ( !/[1-9]/.test( value ) ) {
  4236. continue;
  4237. }
  4238. }
  4239. if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name )
  4240. ||
  4241. /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
  4242. ) {
  4243. continue;
  4244. }
  4245. n[i] = name + ":" + parts[1];
  4246. }
  4247. }
  4248. return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
  4249. })
  4250. .replace(/[\d.]+(cm|pt)/g,function(str){
  4251. return utils.transUnitToPx(str)
  4252. })
  4253. }
  4254. return function ( html ) {
  4255. return (isWordDocument( html ) ? filterPasteWord( html ) : html);
  4256. };
  4257. }();
  4258. ///import editor.js
  4259. ///import core/utils.js
  4260. ///import core/dom/dom.js
  4261. ///import core/dom/dtd.js
  4262. ///import core/htmlparser.js
  4263. //模拟的节点类
  4264. //by zhanyi
  4265. (function () {
  4266. var uNode = UM.uNode = function (obj) {
  4267. this.type = obj.type;
  4268. this.data = obj.data;
  4269. this.tagName = obj.tagName;
  4270. this.parentNode = obj.parentNode;
  4271. this.attrs = obj.attrs || {};
  4272. this.children = obj.children;
  4273. };
  4274. var notTransAttrs = {
  4275. 'href':1,
  4276. 'src':1,
  4277. '_src':1,
  4278. '_href':1,
  4279. 'cdata_data':1
  4280. };
  4281. var notTransTagName = {
  4282. style:1,
  4283. script:1
  4284. };
  4285. var indentChar = ' ',
  4286. breakChar = '\n';
  4287. function insertLine(arr, current, begin) {
  4288. arr.push(breakChar);
  4289. return current + (begin ? 1 : -1);
  4290. }
  4291. function insertIndent(arr, current) {
  4292. //插入缩进
  4293. for (var i = 0; i < current; i++) {
  4294. arr.push(indentChar);
  4295. }
  4296. }
  4297. //创建uNode的静态方法
  4298. //支持标签和html
  4299. uNode.createElement = function (html) {
  4300. if (/[<>]/.test(html)) {
  4301. return UM.htmlparser(html).children[0]
  4302. } else {
  4303. return new uNode({
  4304. type:'element',
  4305. children:[],
  4306. tagName:html
  4307. })
  4308. }
  4309. };
  4310. uNode.createText = function (data,noTrans) {
  4311. return new UM.uNode({
  4312. type:'text',
  4313. 'data':noTrans ? data : utils.unhtml(data || '')
  4314. })
  4315. };
  4316. function nodeToHtml(node, arr, formatter, current) {
  4317. switch (node.type) {
  4318. case 'root':
  4319. for (var i = 0, ci; ci = node.children[i++];) {
  4320. //插入新行
  4321. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4322. insertLine(arr, current, true);
  4323. insertIndent(arr, current)
  4324. }
  4325. nodeToHtml(ci, arr, formatter, current)
  4326. }
  4327. break;
  4328. case 'text':
  4329. isText(node, arr);
  4330. break;
  4331. case 'element':
  4332. isElement(node, arr, formatter, current);
  4333. break;
  4334. case 'comment':
  4335. isComment(node, arr, formatter);
  4336. }
  4337. return arr;
  4338. }
  4339. function isText(node, arr) {
  4340. if(node.parentNode.tagName == 'pre'){
  4341. //源码模式下输入html标签,不能做转换处理,直接输出
  4342. arr.push(node.data)
  4343. }else{
  4344. arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' &nbsp;'))
  4345. }
  4346. }
  4347. function isElement(node, arr, formatter, current) {
  4348. var attrhtml = '';
  4349. if (node.attrs) {
  4350. attrhtml = [];
  4351. var attrs = node.attrs;
  4352. for (var a in attrs) {
  4353. //这里就针对
  4354. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  4355. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  4356. //有可能做的不够
  4357. attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  4358. return '&quot;'
  4359. }) : utils.unhtml(attrs[a])) + '"' : ''))
  4360. }
  4361. attrhtml = attrhtml.join(' ');
  4362. }
  4363. arr.push('<' + node.tagName +
  4364. (attrhtml ? ' ' + attrhtml : '') +
  4365. (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
  4366. );
  4367. //插入新行
  4368. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4369. if(node.children && node.children.length){
  4370. current = insertLine(arr, current, true);
  4371. insertIndent(arr, current)
  4372. }
  4373. }
  4374. if (node.children && node.children.length) {
  4375. for (var i = 0, ci; ci = node.children[i++];) {
  4376. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4377. insertLine(arr, current);
  4378. insertIndent(arr, current)
  4379. }
  4380. nodeToHtml(ci, arr, formatter, current)
  4381. }
  4382. }
  4383. if (!dtd.$empty[node.tagName]) {
  4384. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4385. if(node.children && node.children.length){
  4386. current = insertLine(arr, current);
  4387. insertIndent(arr, current)
  4388. }
  4389. }
  4390. arr.push('<\/' + node.tagName + '>');
  4391. }
  4392. }
  4393. function isComment(node, arr) {
  4394. arr.push('<!--' + node.data + '-->');
  4395. }
  4396. function getNodeById(root, id) {
  4397. var node;
  4398. if (root.type == 'element' && root.getAttr('id') == id) {
  4399. return root;
  4400. }
  4401. if (root.children && root.children.length) {
  4402. for (var i = 0, ci; ci = root.children[i++];) {
  4403. if (node = getNodeById(ci, id)) {
  4404. return node;
  4405. }
  4406. }
  4407. }
  4408. }
  4409. function getNodesByTagName(node, tagName, arr) {
  4410. if (node.type == 'element' && node.tagName == tagName) {
  4411. arr.push(node);
  4412. }
  4413. if (node.children && node.children.length) {
  4414. for (var i = 0, ci; ci = node.children[i++];) {
  4415. getNodesByTagName(ci, tagName, arr)
  4416. }
  4417. }
  4418. }
  4419. function nodeTraversal(root,fn){
  4420. if(root.children && root.children.length){
  4421. for(var i= 0,ci;ci=root.children[i];){
  4422. nodeTraversal(ci,fn);
  4423. //ci被替换的情况,这里就不再走 fn了
  4424. if(ci.parentNode ){
  4425. if(ci.children && ci.children.length){
  4426. fn(ci)
  4427. }
  4428. if(ci.parentNode) i++
  4429. }
  4430. }
  4431. }else{
  4432. fn(root)
  4433. }
  4434. }
  4435. uNode.prototype = {
  4436. /**
  4437. * 当前节点对象,转换成html文本
  4438. * @method toHtml
  4439. * @return { String } 返回转换后的html字符串
  4440. * @example
  4441. * ```javascript
  4442. * node.toHtml();
  4443. * ```
  4444. */
  4445. /**
  4446. * 当前节点对象,转换成html文本
  4447. * @method toHtml
  4448. * @param { Boolean } formatter 是否格式化返回值
  4449. * @return { String } 返回转换后的html字符串
  4450. * @example
  4451. * ```javascript
  4452. * node.toHtml( true );
  4453. * ```
  4454. */
  4455. toHtml:function (formatter) {
  4456. var arr = [];
  4457. nodeToHtml(this, arr, formatter, 0);
  4458. return arr.join('')
  4459. },
  4460. /**
  4461. * 获取节点的html内容
  4462. * @method innerHTML
  4463. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4464. * @return { String } 返回节点的html内容
  4465. * @example
  4466. * ```javascript
  4467. * var htmlstr = node.innerHTML();
  4468. * ```
  4469. */
  4470. /**
  4471. * 设置节点的html内容
  4472. * @method innerHTML
  4473. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4474. * @param { String } htmlstr 传入要设置的html内容
  4475. * @return { UM.uNode } 返回节点本身
  4476. * @example
  4477. * ```javascript
  4478. * node.innerHTML('<span>text</span>');
  4479. * ```
  4480. */
  4481. innerHTML:function (htmlstr) {
  4482. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4483. return this;
  4484. }
  4485. if (utils.isString(htmlstr)) {
  4486. if(this.children){
  4487. for (var i = 0, ci; ci = this.children[i++];) {
  4488. ci.parentNode = null;
  4489. }
  4490. }
  4491. this.children = [];
  4492. var tmpRoot = UM.htmlparser(htmlstr);
  4493. for (var i = 0, ci; ci = tmpRoot.children[i++];) {
  4494. this.children.push(ci);
  4495. ci.parentNode = this;
  4496. }
  4497. return this;
  4498. } else {
  4499. var tmpRoot = new UM.uNode({
  4500. type:'root',
  4501. children:this.children
  4502. });
  4503. return tmpRoot.toHtml();
  4504. }
  4505. },
  4506. /**
  4507. * 获取节点的纯文本内容
  4508. * @method innerText
  4509. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4510. * @return { String } 返回节点的存文本内容
  4511. * @example
  4512. * ```javascript
  4513. * var textStr = node.innerText();
  4514. * ```
  4515. */
  4516. /**
  4517. * 设置节点的纯文本内容
  4518. * @method innerText
  4519. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4520. * @param { String } textStr 传入要设置的文本内容
  4521. * @return { UM.uNode } 返回节点本身
  4522. * @example
  4523. * ```javascript
  4524. * node.innerText('<span>text</span>');
  4525. * ```
  4526. */
  4527. innerText:function (textStr,noTrans) {
  4528. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4529. return this;
  4530. }
  4531. if (textStr) {
  4532. if(this.children){
  4533. for (var i = 0, ci; ci = this.children[i++];) {
  4534. ci.parentNode = null;
  4535. }
  4536. }
  4537. this.children = [];
  4538. this.appendChild(uNode.createText(textStr,noTrans));
  4539. return this;
  4540. } else {
  4541. return this.toHtml().replace(/<[^>]+>/g, '');
  4542. }
  4543. },
  4544. /**
  4545. * 获取当前对象的data属性
  4546. * @method getData
  4547. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  4548. * @example
  4549. * ```javascript
  4550. * node.getData();
  4551. * ```
  4552. */
  4553. getData:function () {
  4554. if (this.type == 'element')
  4555. return '';
  4556. return this.data
  4557. },
  4558. /**
  4559. * 获取当前节点下的第一个子节点
  4560. * @method firstChild
  4561. * @return { UM.uNode } 返回第一个子节点
  4562. * @example
  4563. * ```javascript
  4564. * node.firstChild(); //返回第一个子节点
  4565. * ```
  4566. */
  4567. firstChild:function () {
  4568. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4569. // return this;
  4570. // }
  4571. return this.children ? this.children[0] : null;
  4572. },
  4573. /**
  4574. * 获取当前节点下的最后一个子节点
  4575. * @method lastChild
  4576. * @return { UM.uNode } 返回最后一个子节点
  4577. * @example
  4578. * ```javascript
  4579. * node.lastChild(); //返回最后一个子节点
  4580. * ```
  4581. */
  4582. lastChild:function () {
  4583. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  4584. // return this;
  4585. // }
  4586. return this.children ? this.children[this.children.length - 1] : null;
  4587. },
  4588. /**
  4589. * 获取和当前节点有相同父亲节点的前一个节点
  4590. * @method previousSibling
  4591. * @return { UM.uNode } 返回前一个节点
  4592. * @example
  4593. * ```javascript
  4594. * node.children[2].previousSibling(); //返回子节点node.children[1]
  4595. * ```
  4596. */
  4597. previousSibling : function(){
  4598. var parent = this.parentNode;
  4599. for (var i = 0, ci; ci = parent.children[i]; i++) {
  4600. if (ci === this) {
  4601. return i == 0 ? null : parent.children[i-1];
  4602. }
  4603. }
  4604. },
  4605. /**
  4606. * 获取和当前节点有相同父亲节点的后一个节点
  4607. * @method nextSibling
  4608. * @return { UM.uNode } 返回后一个节点,找不到返回null
  4609. * @example
  4610. * ```javascript
  4611. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  4612. * ```
  4613. */
  4614. nextSibling : function(){
  4615. var parent = this.parentNode;
  4616. for (var i = 0, ci; ci = parent.children[i++];) {
  4617. if (ci === this) {
  4618. return parent.children[i];
  4619. }
  4620. }
  4621. },
  4622. /**
  4623. * 用新的节点替换当前节点
  4624. * @method replaceChild
  4625. * @param { UM.uNode } target 要替换成该节点参数
  4626. * @param { UM.uNode } source 要被替换掉的节点
  4627. * @return { UM.uNode } 返回替换之后的节点对象
  4628. * @example
  4629. * ```javascript
  4630. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  4631. * ```
  4632. */
  4633. replaceChild:function (target, source) {
  4634. if (this.children) {
  4635. if(target.parentNode){
  4636. target.parentNode.removeChild(target);
  4637. }
  4638. for (var i = 0, ci; ci = this.children[i]; i++) {
  4639. if (ci === source) {
  4640. this.children.splice(i, 1, target);
  4641. source.parentNode = null;
  4642. target.parentNode = this;
  4643. return target;
  4644. }
  4645. }
  4646. }
  4647. },
  4648. /**
  4649. * 在节点的子节点列表最后位置插入一个节点
  4650. * @method appendChild
  4651. * @param { UM.uNode } node 要插入的节点
  4652. * @return { UM.uNode } 返回刚插入的子节点
  4653. * @example
  4654. * ```javascript
  4655. * node.appendChild( newNode ); //在node内插入子节点newNode
  4656. * ```
  4657. */
  4658. appendChild:function (node) {
  4659. if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
  4660. if (!this.children) {
  4661. this.children = []
  4662. }
  4663. if(node.parentNode){
  4664. node.parentNode.removeChild(node);
  4665. }
  4666. for (var i = 0, ci; ci = this.children[i]; i++) {
  4667. if (ci === node) {
  4668. this.children.splice(i, 1);
  4669. break;
  4670. }
  4671. }
  4672. this.children.push(node);
  4673. node.parentNode = this;
  4674. return node;
  4675. }
  4676. },
  4677. /**
  4678. * 在传入节点的前面插入一个节点
  4679. * @method insertBefore
  4680. * @param { UM.uNode } target 要插入的节点
  4681. * @param { UM.uNode } source 在该参数节点前面插入
  4682. * @return { UM.uNode } 返回刚插入的子节点
  4683. * @example
  4684. * ```javascript
  4685. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  4686. * ```
  4687. */
  4688. insertBefore:function (target, source) {
  4689. if (this.children) {
  4690. if(target.parentNode){
  4691. target.parentNode.removeChild(target);
  4692. }
  4693. for (var i = 0, ci; ci = this.children[i]; i++) {
  4694. if (ci === source) {
  4695. this.children.splice(i, 0, target);
  4696. target.parentNode = this;
  4697. return target;
  4698. }
  4699. }
  4700. }
  4701. },
  4702. /**
  4703. * 在传入节点的后面插入一个节点
  4704. * @method insertAfter
  4705. * @param { UM.uNode } target 要插入的节点
  4706. * @param { UM.uNode } source 在该参数节点后面插入
  4707. * @return { UM.uNode } 返回刚插入的子节点
  4708. * @example
  4709. * ```javascript
  4710. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  4711. * ```
  4712. */
  4713. insertAfter:function (target, source) {
  4714. if (this.children) {
  4715. if(target.parentNode){
  4716. target.parentNode.removeChild(target);
  4717. }
  4718. for (var i = 0, ci; ci = this.children[i]; i++) {
  4719. if (ci === source) {
  4720. this.children.splice(i + 1, 0, target);
  4721. target.parentNode = this;
  4722. return target;
  4723. }
  4724. }
  4725. }
  4726. },
  4727. /**
  4728. * 从当前节点的子节点列表中,移除节点
  4729. * @method removeChild
  4730. * @param { UM.uNode } node 要移除的节点引用
  4731. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  4732. * @return { * } 返回刚移除的子节点
  4733. * @example
  4734. * ```javascript
  4735. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  4736. * ```
  4737. */
  4738. removeChild:function (node,keepChildren) {
  4739. if (this.children) {
  4740. for (var i = 0, ci; ci = this.children[i]; i++) {
  4741. if (ci === node) {
  4742. this.children.splice(i, 1);
  4743. ci.parentNode = null;
  4744. if(keepChildren && ci.children && ci.children.length){
  4745. for(var j= 0,cj;cj=ci.children[j];j++){
  4746. this.children.splice(i+j,0,cj);
  4747. cj.parentNode = this;
  4748. }
  4749. }
  4750. return ci;
  4751. }
  4752. }
  4753. }
  4754. },
  4755. /**
  4756. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  4757. * @method getAttr
  4758. * @param { String } attrName 要获取的属性名称
  4759. * @return { * } 返回attrs对象下的属性值
  4760. * @example
  4761. * ```javascript
  4762. * node.getAttr('title');
  4763. * ```
  4764. */
  4765. getAttr:function (attrName) {
  4766. return this.attrs && this.attrs[attrName.toLowerCase()]
  4767. },
  4768. /**
  4769. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  4770. * @method setAttr
  4771. * @param { String } attrName 要设置的属性名称
  4772. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  4773. * @return { * } 返回attrs对象下的属性值
  4774. * @example
  4775. * ```javascript
  4776. * node.setAttr('title','标题');
  4777. * ```
  4778. */
  4779. setAttr:function (attrName, attrVal) {
  4780. if (!attrName) {
  4781. delete this.attrs;
  4782. return;
  4783. }
  4784. if(!this.attrs){
  4785. this.attrs = {};
  4786. }
  4787. if (utils.isObject(attrName)) {
  4788. for (var a in attrName) {
  4789. if (!attrName[a]) {
  4790. delete this.attrs[a]
  4791. } else {
  4792. this.attrs[a.toLowerCase()] = attrName[a];
  4793. }
  4794. }
  4795. } else {
  4796. if (!attrVal) {
  4797. delete this.attrs[attrName]
  4798. } else {
  4799. this.attrs[attrName.toLowerCase()] = attrVal;
  4800. }
  4801. }
  4802. },
  4803. hasAttr: function( attrName ){
  4804. var attrVal = this.getAttr( attrName );
  4805. return ( attrVal !== null ) && ( attrVal !== undefined );
  4806. },
  4807. /**
  4808. * 获取当前节点在父节点下的位置索引
  4809. * @method getIndex
  4810. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  4811. * @example
  4812. * ```javascript
  4813. * node.getIndex();
  4814. * ```
  4815. */
  4816. getIndex:function(){
  4817. var parent = this.parentNode;
  4818. for(var i= 0,ci;ci=parent.children[i];i++){
  4819. if(ci === this){
  4820. return i;
  4821. }
  4822. }
  4823. return -1;
  4824. },
  4825. /**
  4826. * 在当前节点下,根据id查找节点
  4827. * @method getNodeById
  4828. * @param { String } id 要查找的id
  4829. * @return { UM.uNode } 返回找到的节点
  4830. * @example
  4831. * ```javascript
  4832. * node.getNodeById('textId');
  4833. * ```
  4834. */
  4835. getNodeById:function (id) {
  4836. var node;
  4837. if (this.children && this.children.length) {
  4838. for (var i = 0, ci; ci = this.children[i++];) {
  4839. if (node = getNodeById(ci, id)) {
  4840. return node;
  4841. }
  4842. }
  4843. }
  4844. },
  4845. /**
  4846. * 在当前节点下,根据元素名称查找节点列表
  4847. * @method getNodesByTagName
  4848. * @param { String } tagNames 要查找的元素名称
  4849. * @return { Array } 返回找到的节点列表
  4850. * @example
  4851. * ```javascript
  4852. * node.getNodesByTagName('span');
  4853. * ```
  4854. */
  4855. getNodesByTagName:function (tagNames) {
  4856. tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
  4857. var arr = [], me = this;
  4858. utils.each(tagNames, function (tagName) {
  4859. if (me.children && me.children.length) {
  4860. for (var i = 0, ci; ci = me.children[i++];) {
  4861. getNodesByTagName(ci, tagName, arr)
  4862. }
  4863. }
  4864. });
  4865. return arr;
  4866. },
  4867. /**
  4868. * 根据样式名称,获取节点的样式值
  4869. * @method getStyle
  4870. * @param { String } name 要获取的样式名称
  4871. * @return { String } 返回样式值
  4872. * @example
  4873. * ```javascript
  4874. * node.getStyle('font-size');
  4875. * ```
  4876. */
  4877. getStyle:function (name) {
  4878. var cssStyle = this.getAttr('style');
  4879. if (!cssStyle) {
  4880. return ''
  4881. }
  4882. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
  4883. var match = cssStyle.match(reg);
  4884. if (match && match[0]) {
  4885. return match[2]
  4886. }
  4887. return '';
  4888. },
  4889. /**
  4890. * 给节点设置样式
  4891. * @method setStyle
  4892. * @param { String } name 要设置的的样式名称
  4893. * @param { String } val 要设置的的样值
  4894. * @example
  4895. * ```javascript
  4896. * node.setStyle('font-size', '12px');
  4897. * ```
  4898. */
  4899. setStyle:function (name, val) {
  4900. function exec(name, val) {
  4901. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
  4902. cssStyle = cssStyle.replace(reg, '$1');
  4903. if (val) {
  4904. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  4905. }
  4906. }
  4907. var cssStyle = this.getAttr('style');
  4908. if (!cssStyle) {
  4909. cssStyle = '';
  4910. }
  4911. if (utils.isObject(name)) {
  4912. for (var a in name) {
  4913. exec(a, name[a])
  4914. }
  4915. } else {
  4916. exec(name, val)
  4917. }
  4918. this.setAttr('style', utils.trim(cssStyle))
  4919. },
  4920. hasClass: function( className ){
  4921. if( this.hasAttr('class') ) {
  4922. var classNames = this.getAttr('class').split(/\s+/),
  4923. hasClass = false;
  4924. $.each(classNames, function(key, item){
  4925. if( item === className ) {
  4926. hasClass = true;
  4927. }
  4928. });
  4929. return hasClass;
  4930. } else {
  4931. return false;
  4932. }
  4933. },
  4934. addClass: function( className ){
  4935. var classes = null,
  4936. hasClass = false;
  4937. if( this.hasAttr('class') ) {
  4938. classes = this.getAttr('class');
  4939. classes = classes.split(/\s+/);
  4940. classes.forEach( function( item ){
  4941. if( item===className ) {
  4942. hasClass = true;
  4943. return;
  4944. }
  4945. } );
  4946. !hasClass && classes.push( className );
  4947. this.setAttr('class', classes.join(" "));
  4948. } else {
  4949. this.setAttr('class', className);
  4950. }
  4951. },
  4952. removeClass: function( className ){
  4953. if( this.hasAttr('class') ) {
  4954. var cl = this.getAttr('class');
  4955. cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),'');
  4956. this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' '));
  4957. }
  4958. },
  4959. /**
  4960. * 传入一个函数,递归遍历当前节点下的所有节点
  4961. * @method traversal
  4962. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  4963. * @example
  4964. * ```javascript
  4965. * traversal(node, function(){
  4966. * console.log(node.type);
  4967. * });
  4968. * ```
  4969. */
  4970. traversal:function(fn){
  4971. if(this.children && this.children.length){
  4972. nodeTraversal(this,fn);
  4973. }
  4974. return this;
  4975. }
  4976. }
  4977. })();
  4978. //html字符串转换成uNode节点
  4979. //by zhanyi
  4980. var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) {
  4981. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  4982. //先去掉了,加上的原因忘了,这里先记录
  4983. var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  4984. re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  4985. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  4986. var allowEmptyTags = {
  4987. b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
  4988. sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
  4989. };
  4990. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
  4991. if(!ignoreBlank){
  4992. htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
  4993. //br暂时单独处理
  4994. if(b && allowEmptyTags[b.toLowerCase()]){
  4995. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
  4996. }
  4997. return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
  4998. });
  4999. }
  5000. var notTransAttrs = {
  5001. 'href':1,
  5002. 'src':1
  5003. };
  5004. var uNode = UM.uNode,
  5005. needParentNode = {
  5006. 'td':'tr',
  5007. 'tr':['tbody','thead','tfoot'],
  5008. 'tbody':'table',
  5009. 'th':'tr',
  5010. 'thead':'table',
  5011. 'tfoot':'table',
  5012. 'caption':'table',
  5013. 'li':['ul', 'ol'],
  5014. 'dt':'dl',
  5015. 'dd':'dl',
  5016. 'option':'select'
  5017. },
  5018. needChild = {
  5019. 'ol':'li',
  5020. 'ul':'li'
  5021. };
  5022. function text(parent, data) {
  5023. if(needChild[parent.tagName]){
  5024. var tmpNode = uNode.createElement(needChild[parent.tagName]);
  5025. parent.appendChild(tmpNode);
  5026. tmpNode.appendChild(uNode.createText(data));
  5027. parent = tmpNode;
  5028. }else{
  5029. parent.appendChild(uNode.createText(data));
  5030. }
  5031. }
  5032. function element(parent, tagName, htmlattr) {
  5033. var needParentTag;
  5034. if (needParentTag = needParentNode[tagName]) {
  5035. var tmpParent = parent,hasParent;
  5036. while(tmpParent.type != 'root'){
  5037. if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
  5038. parent = tmpParent;
  5039. hasParent = true;
  5040. break;
  5041. }
  5042. tmpParent = tmpParent.parentNode;
  5043. }
  5044. if(!hasParent){
  5045. parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
  5046. }
  5047. }
  5048. //按dtd处理嵌套
  5049. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  5050. // parent = parent.parentNode;
  5051. var elm = new uNode({
  5052. parentNode:parent,
  5053. type:'element',
  5054. tagName:tagName.toLowerCase(),
  5055. //是自闭合的处理一下
  5056. children:dtd.$empty[tagName] ? null : []
  5057. });
  5058. //如果属性存在,处理属性
  5059. if (htmlattr) {
  5060. var attrs = {}, match;
  5061. while (match = re_attr.exec(htmlattr)) {
  5062. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
  5063. }
  5064. elm.attrs = attrs;
  5065. }
  5066. parent.children.push(elm);
  5067. //如果是自闭合节点返回父亲节点
  5068. return dtd.$empty[tagName] ? parent : elm
  5069. }
  5070. function comment(parent, data) {
  5071. parent.children.push(new uNode({
  5072. type:'comment',
  5073. data:data,
  5074. parentNode:parent
  5075. }));
  5076. }
  5077. var match, currentIndex = 0, nextIndex = 0;
  5078. //设置根节点
  5079. var root = new uNode({
  5080. type:'root',
  5081. children:[]
  5082. });
  5083. var currentParent = root;
  5084. while (match = re_tag.exec(htmlstr)) {
  5085. currentIndex = match.index;
  5086. try{
  5087. if (currentIndex > nextIndex) {
  5088. //text node
  5089. text(currentParent, htmlstr.slice(nextIndex, currentIndex));
  5090. }
  5091. if (match[3]) {
  5092. if(dtd.$cdata[currentParent.tagName]){
  5093. text(currentParent, match[0]);
  5094. }else{
  5095. //start tag
  5096. currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
  5097. }
  5098. } else if (match[1]) {
  5099. if(currentParent.type != 'root'){
  5100. if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
  5101. text(currentParent, match[0]);
  5102. }else{
  5103. var tmpParent = currentParent;
  5104. while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
  5105. currentParent = currentParent.parentNode;
  5106. if(currentParent.type == 'root'){
  5107. currentParent = tmpParent;
  5108. throw 'break'
  5109. }
  5110. }
  5111. //end tag
  5112. currentParent = currentParent.parentNode;
  5113. }
  5114. }
  5115. } else if (match[2]) {
  5116. //comment
  5117. comment(currentParent, match[2])
  5118. }
  5119. }catch(e){}
  5120. nextIndex = re_tag.lastIndex;
  5121. }
  5122. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  5123. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  5124. if (nextIndex < htmlstr.length) {
  5125. text(currentParent, htmlstr.slice(nextIndex));
  5126. }
  5127. return root;
  5128. };
  5129. /**
  5130. * @file
  5131. * @name UM.filterNode
  5132. * @short filterNode
  5133. * @desc 根据给定的规则过滤节点
  5134. * @import editor.js,core/utils.js
  5135. * @anthor zhanyi
  5136. */
  5137. var filterNode = UM.filterNode = function () {
  5138. function filterNode(node,rules){
  5139. switch (node.type) {
  5140. case 'text':
  5141. break;
  5142. case 'element':
  5143. var val;
  5144. if(val = rules[node.tagName]){
  5145. if(val === '-'){
  5146. node.parentNode.removeChild(node)
  5147. }else if(utils.isFunction(val)){
  5148. var parentNode = node.parentNode,
  5149. index = node.getIndex();
  5150. val(node);
  5151. if(node.parentNode){
  5152. if(node.children){
  5153. for(var i = 0,ci;ci=node.children[i];){
  5154. filterNode(ci,rules);
  5155. if(ci.parentNode){
  5156. i++;
  5157. }
  5158. }
  5159. }
  5160. }else{
  5161. for(var i = index,ci;ci=parentNode.children[i];){
  5162. filterNode(ci,rules);
  5163. if(ci.parentNode){
  5164. i++;
  5165. }
  5166. }
  5167. }
  5168. }else{
  5169. var attrs = val['$'];
  5170. if(attrs && node.attrs){
  5171. var tmpAttrs = {},tmpVal;
  5172. for(var a in attrs){
  5173. tmpVal = node.getAttr(a);
  5174. //todo 只先对style单独处理
  5175. if(a == 'style' && utils.isArray(attrs[a])){
  5176. var tmpCssStyle = [];
  5177. utils.each(attrs[a],function(v){
  5178. var tmp;
  5179. if(tmp = node.getStyle(v)){
  5180. tmpCssStyle.push(v + ':' + tmp);
  5181. }
  5182. });
  5183. tmpVal = tmpCssStyle.join(';')
  5184. }
  5185. if(tmpVal){
  5186. tmpAttrs[a] = tmpVal;
  5187. }
  5188. }
  5189. node.attrs = tmpAttrs;
  5190. }
  5191. if(node.children){
  5192. for(var i = 0,ci;ci=node.children[i];){
  5193. filterNode(ci,rules);
  5194. if(ci.parentNode){
  5195. i++;
  5196. }
  5197. }
  5198. }
  5199. }
  5200. }else{
  5201. //如果不在名单里扣出子节点并删除该节点,cdata除外
  5202. if(dtd.$cdata[node.tagName]){
  5203. node.parentNode.removeChild(node)
  5204. }else{
  5205. var parentNode = node.parentNode,
  5206. index = node.getIndex();
  5207. node.parentNode.removeChild(node,true);
  5208. for(var i = index,ci;ci=parentNode.children[i];){
  5209. filterNode(ci,rules);
  5210. if(ci.parentNode){
  5211. i++;
  5212. }
  5213. }
  5214. }
  5215. }
  5216. break;
  5217. case 'comment':
  5218. node.parentNode.removeChild(node)
  5219. }
  5220. }
  5221. return function(root,rules){
  5222. if(utils.isEmptyObject(rules)){
  5223. return root;
  5224. }
  5225. var val;
  5226. if(val = rules['-']){
  5227. utils.each(val.split(' '),function(k){
  5228. rules[k] = '-'
  5229. })
  5230. }
  5231. for(var i= 0,ci;ci=root.children[i];){
  5232. filterNode(ci,rules);
  5233. if(ci.parentNode){
  5234. i++;
  5235. }
  5236. }
  5237. return root;
  5238. }
  5239. }();
  5240. ///import core
  5241. /**
  5242. * @description 插入内容
  5243. * @name baidu.editor.execCommand
  5244. * @param {String} cmdName inserthtml插入内容的命令
  5245. * @param {String} html 要插入的内容
  5246. * @author zhanyi
  5247. */
  5248. UM.commands['inserthtml'] = {
  5249. execCommand: function (command,html,notNeedFilter){
  5250. var me = this,
  5251. range,
  5252. div;
  5253. if(!html){
  5254. return;
  5255. }
  5256. if(me.fireEvent('beforeinserthtml',html) === true){
  5257. return;
  5258. }
  5259. range = me.selection.getRange();
  5260. div = range.document.createElement( 'div' );
  5261. div.style.display = 'inline';
  5262. if (!notNeedFilter) {
  5263. var root = UM.htmlparser(html);
  5264. //如果给了过滤规则就先进行过滤
  5265. if(me.options.filterRules){
  5266. UM.filterNode(root,me.options.filterRules);
  5267. }
  5268. //执行默认的处理
  5269. me.filterInputRule(root);
  5270. html = root.toHtml()
  5271. }
  5272. div.innerHTML = utils.trim( html );
  5273. if ( !range.collapsed ) {
  5274. var tmpNode = range.startContainer;
  5275. if(domUtils.isFillChar(tmpNode)){
  5276. range.setStartBefore(tmpNode)
  5277. }
  5278. tmpNode = range.endContainer;
  5279. if(domUtils.isFillChar(tmpNode)){
  5280. range.setEndAfter(tmpNode)
  5281. }
  5282. range.txtToElmBoundary();
  5283. //结束边界可能放到了br的前边,要把br包含进来
  5284. // x[xxx]<br/>
  5285. if(range.endContainer && range.endContainer.nodeType == 1){
  5286. tmpNode = range.endContainer.childNodes[range.endOffset];
  5287. if(tmpNode && domUtils.isBr(tmpNode)){
  5288. range.setEndAfter(tmpNode);
  5289. }
  5290. }
  5291. if(range.startOffset == 0){
  5292. tmpNode = range.startContainer;
  5293. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  5294. tmpNode = range.endContainer;
  5295. if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  5296. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  5297. range.setStart(me.body.firstChild,0).collapse(true)
  5298. }
  5299. }
  5300. }
  5301. !range.collapsed && range.deleteContents();
  5302. if(range.startContainer.nodeType == 1){
  5303. var child = range.startContainer.childNodes[range.startOffset],pre;
  5304. if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
  5305. range.setEnd(pre,pre.childNodes.length).collapse();
  5306. while(child.firstChild){
  5307. pre.appendChild(child.firstChild);
  5308. }
  5309. domUtils.remove(child);
  5310. }
  5311. }
  5312. }
  5313. var child,parent,pre,tmp,hadBreak = 0, nextNode;
  5314. //如果当前位置选中了fillchar要干掉,要不会产生空行
  5315. if(range.inFillChar()){
  5316. child = range.startContainer;
  5317. if(domUtils.isFillChar(child)){
  5318. range.setStartBefore(child).collapse(true);
  5319. domUtils.remove(child);
  5320. }else if(domUtils.isFillChar(child,true)){
  5321. child.nodeValue = child.nodeValue.replace(fillCharReg,'');
  5322. range.startOffset--;
  5323. range.collapsed && range.collapse(true)
  5324. }
  5325. }
  5326. while ( child = div.firstChild ) {
  5327. if(hadBreak){
  5328. var p = me.document.createElement('p');
  5329. while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
  5330. nextNode = child.nextSibling;
  5331. p.appendChild(child);
  5332. child = nextNode;
  5333. }
  5334. if(p.firstChild){
  5335. child = p
  5336. }
  5337. }
  5338. range.insertNode( child );
  5339. nextNode = child.nextSibling;
  5340. if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
  5341. parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
  5342. if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
  5343. if(!dtd[parent.tagName][child.nodeName]){
  5344. pre = parent;
  5345. }else{
  5346. tmp = child.parentNode;
  5347. while (tmp !== parent){
  5348. pre = tmp;
  5349. tmp = tmp.parentNode;
  5350. }
  5351. }
  5352. domUtils.breakParent( child, pre || tmp );
  5353. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  5354. var pre = child.previousSibling;
  5355. domUtils.trimWhiteTextNode(pre);
  5356. if(!pre.childNodes.length){
  5357. domUtils.remove(pre);
  5358. }
  5359. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  5360. if(!browser.ie &&
  5361. (next = child.nextSibling) &&
  5362. domUtils.isBlockElm(next) &&
  5363. next.lastChild &&
  5364. !domUtils.isBr(next.lastChild)){
  5365. next.appendChild(me.document.createElement('br'));
  5366. }
  5367. hadBreak = 1;
  5368. }
  5369. }
  5370. var next = child.nextSibling;
  5371. if(!div.firstChild && next && domUtils.isBlockElm(next)){
  5372. range.setStart(next,0).collapse(true);
  5373. break;
  5374. }
  5375. range.setEndAfter( child ).collapse();
  5376. }
  5377. child = range.startContainer;
  5378. if(nextNode && domUtils.isBr(nextNode)){
  5379. domUtils.remove(nextNode)
  5380. }
  5381. //用chrome可能有空白展位符
  5382. if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
  5383. if(nextNode = child.nextSibling){
  5384. domUtils.remove(child);
  5385. if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
  5386. range.setStart(nextNode,0).collapse(true).shrinkBoundary()
  5387. }
  5388. }else{
  5389. try{
  5390. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  5391. }catch(e){
  5392. range.setStartBefore(child);
  5393. domUtils.remove(child)
  5394. }
  5395. }
  5396. }
  5397. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  5398. try{
  5399. if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){
  5400. var start = range.startContainer,pre = start.childNodes[range.startOffset-1];
  5401. if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){
  5402. var txt = this.document.createTextNode(domUtils.fillChar);
  5403. range.insertNode(txt).setStart(txt,0).collapse(true);
  5404. }
  5405. }
  5406. setTimeout(function(){
  5407. range.select(true);
  5408. })
  5409. }catch(e){}
  5410. setTimeout(function(){
  5411. range = me.selection.getRange();
  5412. range.scrollIntoView();
  5413. me.fireEvent('afterinserthtml');
  5414. },200);
  5415. }
  5416. };
  5417. ///import core
  5418. ///import plugins\inserthtml.js
  5419. ///commands 插入图片,操作图片的对齐方式
  5420. ///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter
  5421. ///commandsTitle 图片,默认,居左,居右,居中
  5422. ///commandsDialog dialogs\image
  5423. /**
  5424. * Created by .
  5425. * User: zhanyi
  5426. * for image
  5427. */
  5428. UM.commands['insertimage'] = {
  5429. execCommand:function (cmd, opt) {
  5430. opt = utils.isArray(opt) ? opt : [opt];
  5431. if (!opt.length) {
  5432. return;
  5433. }
  5434. var me = this;
  5435. var html = [], str = '', ci;
  5436. ci = opt[0];
  5437. if (opt.length == 1) {
  5438. str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5439. (ci.width ? 'width="' + ci.width + '" ' : '') +
  5440. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5441. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
  5442. (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
  5443. (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
  5444. (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
  5445. (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
  5446. (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
  5447. if (ci['floatStyle'] == 'center') {
  5448. str = '<p style="text-align: center">' + str + '</p>';
  5449. }
  5450. html.push(str);
  5451. } else {
  5452. for (var i = 0; ci = opt[i++];) {
  5453. str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
  5454. (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5455. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5456. ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
  5457. (ci.border || '') + '" ' +
  5458. (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
  5459. html.push(str);
  5460. }
  5461. }
  5462. me.execCommand('insertHtml', html.join(''), true);
  5463. }
  5464. };
  5465. ///import core
  5466. ///commands 段落格式,居左,居右,居中,两端对齐
  5467. ///commandsName JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify
  5468. ///commandsTitle 居左对齐,居中对齐,居右对齐,两端对齐
  5469. /**
  5470. * @description 居左右中
  5471. * @name UM.execCommand
  5472. * @param {String} cmdName justify执行对齐方式的命令
  5473. * @param {String} align 对齐方式:left居左,right居右,center居中,justify两端对齐
  5474. * @author zhanyi
  5475. */
  5476. UM.plugins['justify']=function(){
  5477. var me = this;
  5478. $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){
  5479. me.commands[cmdName] = {
  5480. execCommand:function (cmdName) {
  5481. return this.document.execCommand(cmdName);
  5482. },
  5483. queryCommandValue: function (cmdName) {
  5484. var val = this.document.queryCommandValue(cmdName);
  5485. return val === true || val === 'true' ? cmdName.replace(/justify/,'') : '';
  5486. },
  5487. queryCommandState: function (cmdName) {
  5488. return this.document.queryCommandState(cmdName) ? 1 : 0
  5489. }
  5490. };
  5491. })
  5492. };
  5493. ///import core
  5494. ///import plugins\removeformat.js
  5495. ///commands 字体颜色,背景色,字号,字体,下划线,删除线
  5496. ///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough
  5497. ///commandsTitle 字体颜色,背景色,字号,字体,下划线,删除线
  5498. /**
  5499. * @description 字体
  5500. * @name UM.execCommand
  5501. * @param {String} cmdName 执行的功能名称
  5502. * @param {String} value 传入的值
  5503. */
  5504. UM.plugins['font'] = function () {
  5505. var me = this,
  5506. fonts = {
  5507. 'forecolor': 'forecolor',
  5508. 'backcolor': 'backcolor',
  5509. 'fontsize': 'fontsize',
  5510. 'fontfamily': 'fontname'
  5511. },
  5512. cmdNameToStyle = {
  5513. 'forecolor': 'color',
  5514. 'backcolor': 'background-color',
  5515. 'fontsize': 'font-size',
  5516. 'fontfamily': 'font-family'
  5517. },
  5518. cmdNameToAttr = {
  5519. 'forecolor': 'color',
  5520. 'fontsize': 'size',
  5521. 'fontfamily': 'face'
  5522. };
  5523. me.setOpt({
  5524. 'fontfamily': [
  5525. { name: 'songti', val: '宋体,SimSun'},
  5526. { name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
  5527. { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
  5528. { name: 'heiti', val: '黑体, SimHei'},
  5529. { name: 'lishu', val: '隶书, SimLi'},
  5530. { name: 'andaleMono', val: 'andale mono'},
  5531. { name: 'arial', val: 'arial, helvetica,sans-serif'},
  5532. { name: 'arialBlack', val: 'arial black,avant garde'},
  5533. { name: 'comicSansMs', val: 'comic sans ms'},
  5534. { name: 'impact', val: 'impact,chicago'},
  5535. { name: 'timesNewRoman', val: 'times new roman'},
  5536. { name: 'sans-serif',val:'sans-serif'}
  5537. ],
  5538. 'fontsize': [10, 12, 16, 18,24, 32,48]
  5539. });
  5540. me.addOutputRule(function (root) {
  5541. utils.each(root.getNodesByTagName('font'), function (node) {
  5542. if (node.tagName == 'font') {
  5543. var cssStyle = [];
  5544. for (var p in node.attrs) {
  5545. switch (p) {
  5546. case 'size':
  5547. var val = node.attrs[p];
  5548. $.each({
  5549. '10':'1',
  5550. '12':'2',
  5551. '16':'3',
  5552. '18':'4',
  5553. '24':'5',
  5554. '32':'6',
  5555. '48':'7'
  5556. },function(k,v){
  5557. if(v == val){
  5558. val = k;
  5559. return false;
  5560. }
  5561. });
  5562. cssStyle.push('font-size:' + val + 'px');
  5563. break;
  5564. case 'color':
  5565. cssStyle.push('color:' + node.attrs[p]);
  5566. break;
  5567. case 'face':
  5568. cssStyle.push('font-family:' + node.attrs[p]);
  5569. break;
  5570. case 'style':
  5571. cssStyle.push(node.attrs[p]);
  5572. }
  5573. }
  5574. node.attrs = {
  5575. 'style': cssStyle.join(';')
  5576. };
  5577. }
  5578. node.tagName = 'span';
  5579. if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){
  5580. $.each(node.attrs,function(k,v){
  5581. node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;
  5582. })
  5583. node.parentNode.removeChild(node,true);
  5584. }
  5585. });
  5586. });
  5587. for(var p in fonts){
  5588. (function (cmd) {
  5589. me.commands[cmd] = {
  5590. execCommand: function (cmdName,value) {
  5591. if(value == 'transparent'){
  5592. return;
  5593. }
  5594. var rng = this.selection.getRange();
  5595. if(rng.collapsed){
  5596. var span = $('<span></span>').css(cmdNameToStyle[cmdName],value)[0];
  5597. rng.insertNode(span).setStart(span,0).setCursor();
  5598. }else{
  5599. if(cmdName == 'fontsize'){
  5600. value = {
  5601. '10':'1',
  5602. '12':'2',
  5603. '16':'3',
  5604. '18':'4',
  5605. '24':'5',
  5606. '32':'6',
  5607. '48':'7'
  5608. }[(value+"").replace(/px/,'')]
  5609. }
  5610. this.document.execCommand(fonts[cmdName],false, value);
  5611. if(browser.gecko){
  5612. $.each(this.$body.find('a'),function(i,a){
  5613. var parent = a.parentNode;
  5614. if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){
  5615. var cloneNode = parent.cloneNode(false);
  5616. cloneNode.innerHTML = a.innerHTML;
  5617. $(a).html('').append(cloneNode).insertBefore(parent);
  5618. $(parent).remove();
  5619. }
  5620. });
  5621. }
  5622. if(!browser.ie){
  5623. var nativeRange = this.selection.getNative().getRangeAt(0);
  5624. var common = nativeRange.commonAncestorContainer;
  5625. var rng = this.selection.getRange(),
  5626. bk = rng.createBookmark(true);
  5627. $(common).find('a').each(function(i,n){
  5628. var parent = n.parentNode;
  5629. if(parent.nodeName == 'FONT'){
  5630. var font = parent.cloneNode(false);
  5631. font.innerHTML = n.innerHTML;
  5632. $(n).html('').append(font);
  5633. }
  5634. });
  5635. rng.moveToBookmark(bk).select()
  5636. }
  5637. return true
  5638. }
  5639. },
  5640. queryCommandValue: function (cmdName) {
  5641. var start = me.selection.getStart();
  5642. var val = $(start).css(cmdNameToStyle[cmdName]);
  5643. if(val === undefined){
  5644. val = $(start).attr(cmdNameToAttr[cmdName])
  5645. }
  5646. return val ? utils.fixColor(cmdName,val).replace(/px/,'') : '';
  5647. },
  5648. queryCommandState: function (cmdName) {
  5649. return this.queryCommandValue(cmdName)
  5650. }
  5651. };
  5652. })(p);
  5653. }
  5654. };
  5655. ///import core
  5656. ///commands 超链接,取消链接
  5657. ///commandsName Link,Unlink
  5658. ///commandsTitle 超链接,取消链接
  5659. ///commandsDialog dialogs\link
  5660. /**
  5661. * 超链接
  5662. * @function
  5663. * @name UM.execCommand
  5664. * @param {String} cmdName link插入超链接
  5665. * @param {Object} options url地址,title标题,target是否打开新页
  5666. * @author zhanyi
  5667. */
  5668. /**
  5669. * 取消链接
  5670. * @function
  5671. * @name UM.execCommand
  5672. * @param {String} cmdName unlink取消链接
  5673. * @author zhanyi
  5674. */
  5675. UM.plugins['link'] = function(){
  5676. var me = this;
  5677. me.setOpt('autourldetectinie',false);
  5678. //在ie下禁用autolink
  5679. if(browser.ie && this.options.autourldetectinie === false){
  5680. this.addListener('keyup',function(cmd,evt){
  5681. var me = this,keyCode = evt.keyCode;
  5682. if(keyCode == 13 || keyCode == 32){
  5683. var rng = me.selection.getRange();
  5684. var start = rng.startContainer;
  5685. if(keyCode == 13){
  5686. if(start.nodeName == 'P'){
  5687. var pre = start.previousSibling;
  5688. if(pre && pre.nodeType == 1){
  5689. var pre = pre.lastChild;
  5690. if(pre && pre.nodeName == 'A' && !pre.getAttribute('_href')){
  5691. domUtils.remove(pre,true);
  5692. }
  5693. }
  5694. }
  5695. }else if(keyCode == 32){
  5696. if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){
  5697. start = start.previousSibling;
  5698. if(start && start.nodeName == 'A' && !start.getAttribute('_href')){
  5699. domUtils.remove(start,true);
  5700. }
  5701. }
  5702. }
  5703. }
  5704. });
  5705. }
  5706. this.addOutputRule(function(root){
  5707. $.each(root.getNodesByTagName('a'),function(i,a){
  5708. var _href = utils.html(a.getAttr('_href'));
  5709. if(!/^(ftp|https?|\/|file)/.test(_href)){
  5710. _href = 'http://' + _href;
  5711. }
  5712. a.setAttr('href', _href);
  5713. a.setAttr('_href')
  5714. if(a.getAttr('title')==''){
  5715. a.setAttr('title')
  5716. }
  5717. })
  5718. });
  5719. this.addInputRule(function(root){
  5720. $.each(root.getNodesByTagName('a'),function(i,a){
  5721. a.setAttr('_href', utils.html(a.getAttr('href')));
  5722. })
  5723. });
  5724. me.commands['link'] = {
  5725. execCommand : function( cmdName, opt ) {
  5726. var me = this;
  5727. var rng = me.selection.getRange();
  5728. if(rng.collapsed){
  5729. var start = rng.startContainer;
  5730. if(start = domUtils.findParentByTagName(start,'a',true)){
  5731. $(start).attr(opt);
  5732. rng.selectNode(start).select()
  5733. }else{
  5734. rng.insertNode($('<a>' +opt.href+'</a>').attr(opt)[0]).select()
  5735. }
  5736. }else{
  5737. me.document.execCommand('createlink',false,'_umeditor_link');
  5738. utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){
  5739. return n.getAttribute('href') == '_umeditor_link'
  5740. }),function(l){
  5741. if($(l).text() == '_umeditor_link'){
  5742. $(l).text(opt.href);
  5743. }
  5744. domUtils.setAttributes(l,opt);
  5745. rng.selectNode(l).select()
  5746. })
  5747. }
  5748. },
  5749. queryCommandState:function(){
  5750. return this.queryCommandValue('link') ? 1 : 0;
  5751. },
  5752. queryCommandValue:function(){
  5753. var path = this.selection.getStartElementPath();
  5754. var result;
  5755. $.each(path,function(i,n){
  5756. if(n.nodeName == "A"){
  5757. result = n;
  5758. return false;
  5759. }
  5760. })
  5761. return result;
  5762. }
  5763. };
  5764. me.commands['unlink'] = {
  5765. execCommand : function() {
  5766. this.document.execCommand('unlink');
  5767. }
  5768. };
  5769. };
  5770. ///import core
  5771. ///commands 打印
  5772. ///commandsName Print
  5773. ///commandsTitle 打印
  5774. /**
  5775. * @description 打印
  5776. * @name baidu.editor.execCommand
  5777. * @param {String} cmdName print打印编辑器内容
  5778. * @author zhanyi
  5779. */
  5780. UM.commands['print'] = {
  5781. execCommand : function(){
  5782. var me = this,
  5783. id = 'editor_print_' + +new Date();
  5784. $('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').attr('id', id)
  5785. .css({
  5786. width:'0px',
  5787. height:'0px',
  5788. 'overflow':'hidden',
  5789. 'float':'left',
  5790. 'position':'absolute',
  5791. top:'-10000px',
  5792. left:'-10000px'
  5793. })
  5794. .appendTo(me.$container.find('.edui-dialog-container'));
  5795. var w = window.open('', id, ''),
  5796. d = w.document;
  5797. d.open();
  5798. d.write('<html><head></head><body><div>'+this.getContent(null,null,true)+'</div><script>' +
  5799. "setTimeout(function(){" +
  5800. "window.print();" +
  5801. "setTimeout(function(){" +
  5802. "window.parent.$('#" + id + "').remove();" +
  5803. "},100);" +
  5804. "},200);" +
  5805. '</script></body></html>');
  5806. d.close();
  5807. },
  5808. notNeedUndo : 1
  5809. };
  5810. ///import core
  5811. ///commands 格式
  5812. ///commandsName Paragraph
  5813. ///commandsTitle 段落格式
  5814. /**
  5815. * 段落样式
  5816. * @function
  5817. * @name UM.execCommand
  5818. * @param {String} cmdName paragraph插入段落执行命令
  5819. * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
  5820. * @param {String} attrs 标签的属性
  5821. * @author zhanyi
  5822. */
  5823. UM.plugins['paragraph'] = function() {
  5824. var me = this;
  5825. me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});
  5826. me.commands['paragraph'] = {
  5827. execCommand : function( cmdName, style ) {
  5828. return this.document.execCommand('formatBlock',false,'<' + style + '>');
  5829. },
  5830. queryCommandValue : function() {
  5831. try{
  5832. var val = this.document.queryCommandValue('formatBlock')
  5833. }catch(e){
  5834. }
  5835. return val ;
  5836. }
  5837. };
  5838. };
  5839. ///import core
  5840. ///import plugins\inserthtml.js
  5841. ///commands 分割线
  5842. ///commandsName Horizontal
  5843. ///commandsTitle 分隔线
  5844. /**
  5845. * 分割线
  5846. * @function
  5847. * @name UM.execCommand
  5848. * @param {String} cmdName horizontal插入分割线
  5849. */
  5850. UM.plugins['horizontal'] = function(){
  5851. var me = this;
  5852. me.commands['horizontal'] = {
  5853. execCommand : function( ) {
  5854. this.document.execCommand('insertHorizontalRule');
  5855. var rng = me.selection.getRange().txtToElmBoundary(true),
  5856. start = rng.startContainer;
  5857. if(domUtils.isBody(rng.startContainer)){
  5858. var next = rng.startContainer.childNodes[rng.startOffset];
  5859. if(!next){
  5860. next = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[0]
  5861. }
  5862. rng.setStart(next,0).setCursor()
  5863. }else{
  5864. while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){
  5865. var parent = start.parentNode;
  5866. parent.appendChild(start.firstChild);
  5867. parent.removeChild(start);
  5868. start = parent;
  5869. }
  5870. while(dtd.$inline[start.tagName]){
  5871. start = start.parentNode;
  5872. }
  5873. if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){
  5874. var hr = start.lastChild;
  5875. $(hr).insertBefore(start);
  5876. rng.setStart(start,0).setCursor();
  5877. }else{
  5878. hr = $('hr',start)[0];
  5879. domUtils.breakParent(hr,start);
  5880. var pre = hr.previousSibling;
  5881. if(pre && domUtils.isEmptyBlock(pre)){
  5882. $(pre).remove()
  5883. }
  5884. rng.setStart(hr.nextSibling,0).setCursor();
  5885. }
  5886. }
  5887. }
  5888. };
  5889. };
  5890. ///import core
  5891. ///commands 清空文档
  5892. ///commandsName ClearDoc
  5893. ///commandsTitle 清空文档
  5894. /**
  5895. *
  5896. * 清空文档
  5897. * @function
  5898. * @name UM.execCommand
  5899. * @param {String} cmdName cleardoc清空文档
  5900. */
  5901. UM.commands['cleardoc'] = {
  5902. execCommand : function() {
  5903. var me = this,
  5904. range = me.selection.getRange();
  5905. me.body.innerHTML = "<p>"+(ie ? "" : "<br/>")+"</p>";
  5906. range.setStart(me.body.firstChild,0).setCursor(false,true);
  5907. setTimeout(function(){
  5908. me.fireEvent("clearDoc");
  5909. },0);
  5910. }
  5911. };
  5912. ///import core
  5913. ///commands 撤销和重做
  5914. ///commandsName Undo,Redo
  5915. ///commandsTitle 撤销,重做
  5916. /**
  5917. * @description 回退
  5918. * @author zhanyi
  5919. */
  5920. UM.plugins['undo'] = function () {
  5921. var saveSceneTimer;
  5922. var me = this,
  5923. maxUndoCount = me.options.maxUndoCount || 20,
  5924. maxInputCount = me.options.maxInputCount || 20,
  5925. fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
  5926. var noNeedFillCharTags = {
  5927. ol:1,ul:1,table:1,tbody:1,tr:1,body:1
  5928. };
  5929. var orgState = me.options.autoClearEmptyNode;
  5930. function compareAddr(indexA, indexB) {
  5931. if (indexA.length != indexB.length)
  5932. return 0;
  5933. for (var i = 0, l = indexA.length; i < l; i++) {
  5934. if (indexA[i] != indexB[i])
  5935. return 0
  5936. }
  5937. return 1;
  5938. }
  5939. function compareRangeAddress(rngAddrA, rngAddrB) {
  5940. if (rngAddrA.collapsed != rngAddrB.collapsed) {
  5941. return 0;
  5942. }
  5943. if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
  5944. return 0;
  5945. }
  5946. return 1;
  5947. }
  5948. function UndoManager() {
  5949. this.list = [];
  5950. this.index = 0;
  5951. this.hasUndo = false;
  5952. this.hasRedo = false;
  5953. this.undo = function () {
  5954. if (this.hasUndo) {
  5955. if (!this.list[this.index - 1] && this.list.length == 1) {
  5956. this.reset();
  5957. return;
  5958. }
  5959. while (this.list[this.index].content == this.list[this.index - 1].content) {
  5960. this.index--;
  5961. if (this.index == 0) {
  5962. return this.restore(0);
  5963. }
  5964. }
  5965. this.restore(--this.index);
  5966. }
  5967. };
  5968. this.redo = function () {
  5969. if (this.hasRedo) {
  5970. while (this.list[this.index].content == this.list[this.index + 1].content) {
  5971. this.index++;
  5972. if (this.index == this.list.length - 1) {
  5973. return this.restore(this.index);
  5974. }
  5975. }
  5976. this.restore(++this.index);
  5977. }
  5978. };
  5979. this.restore = function () {
  5980. var me = this.editor;
  5981. var scene = this.list[this.index];
  5982. var root = UM.htmlparser(scene.content.replace(fillchar, ''));
  5983. me.options.autoClearEmptyNode = false;
  5984. me.filterInputRule(root,true);
  5985. me.options.autoClearEmptyNode = orgState;
  5986. //trace:873
  5987. //去掉展位符
  5988. me.body.innerHTML = root.toHtml();
  5989. me.fireEvent('afterscencerestore');
  5990. //处理undo后空格不展位的问题
  5991. if (browser.ie) {
  5992. utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
  5993. if(domUtils.isEmptyNode(node)){
  5994. domUtils.fillNode(me.document, node);
  5995. }
  5996. })
  5997. }
  5998. try{
  5999. var rng = new dom.Range(me.document,me.body).moveToAddress(scene.address);
  6000. if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){
  6001. var tmpNode = rng.startContainer.childNodes[rng.startOffset];
  6002. if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){
  6003. rng.insertNode(me.document.createTextNode(' ')).collapse(true);
  6004. }
  6005. }
  6006. rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
  6007. }catch(e){}
  6008. this.update();
  6009. this.clearKey();
  6010. //不能把自己reset了
  6011. me.fireEvent('reset', true);
  6012. };
  6013. this.getScene = function () {
  6014. var me = this.editor;
  6015. var rng = me.selection.getRange(),
  6016. rngAddress = rng.createAddress(false,true);
  6017. me.fireEvent('beforegetscene');
  6018. var root = UM.htmlparser(me.body.innerHTML,true);
  6019. me.options.autoClearEmptyNode = false;
  6020. me.filterOutputRule(root,true);
  6021. me.options.autoClearEmptyNode = orgState;
  6022. var cont = root.toHtml();
  6023. browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
  6024. me.fireEvent('aftergetscene');
  6025. return {
  6026. address:rngAddress,
  6027. content:cont
  6028. }
  6029. };
  6030. this.save = function (notCompareRange,notSetCursor) {
  6031. clearTimeout(saveSceneTimer);
  6032. var currentScene = this.getScene(notSetCursor),
  6033. lastScene = this.list[this.index];
  6034. //内容相同位置相同不存
  6035. if (lastScene && lastScene.content == currentScene.content &&
  6036. ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
  6037. ) {
  6038. return;
  6039. }
  6040. this.list = this.list.slice(0, this.index + 1);
  6041. this.list.push(currentScene);
  6042. //如果大于最大数量了,就把最前的剔除
  6043. if (this.list.length > maxUndoCount) {
  6044. this.list.shift();
  6045. }
  6046. this.index = this.list.length - 1;
  6047. this.clearKey();
  6048. //跟新undo/redo状态
  6049. this.update();
  6050. };
  6051. this.update = function () {
  6052. this.hasRedo = !!this.list[this.index + 1];
  6053. this.hasUndo = !!this.list[this.index - 1];
  6054. };
  6055. this.reset = function () {
  6056. this.list = [];
  6057. this.index = 0;
  6058. this.hasUndo = false;
  6059. this.hasRedo = false;
  6060. this.clearKey();
  6061. };
  6062. this.clearKey = function () {
  6063. keycont = 0;
  6064. lastKeyCode = null;
  6065. };
  6066. }
  6067. me.undoManger = new UndoManager();
  6068. me.undoManger.editor = me;
  6069. function saveScene() {
  6070. this.undoManger.save();
  6071. }
  6072. me.addListener('saveScene', function () {
  6073. var args = Array.prototype.splice.call(arguments,1);
  6074. this.undoManger.save.apply(this.undoManger,args);
  6075. });
  6076. me.addListener('beforeexeccommand', saveScene);
  6077. me.addListener('afterexeccommand', saveScene);
  6078. me.addListener('reset', function (type, exclude) {
  6079. if (!exclude) {
  6080. this.undoManger.reset();
  6081. }
  6082. });
  6083. me.commands['redo'] = me.commands['undo'] = {
  6084. execCommand:function (cmdName) {
  6085. this.undoManger[cmdName]();
  6086. },
  6087. queryCommandState:function (cmdName) {
  6088. return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
  6089. },
  6090. notNeedUndo:1
  6091. };
  6092. var keys = {
  6093. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  6094. /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
  6095. 37:1, 38:1, 39:1, 40:1
  6096. },
  6097. keycont = 0,
  6098. lastKeyCode;
  6099. //输入法状态下不计算字符数
  6100. var inputType = false;
  6101. me.addListener('ready', function () {
  6102. $(this.body).on('compositionstart', function () {
  6103. inputType = true;
  6104. }).on('compositionend', function () {
  6105. inputType = false;
  6106. })
  6107. });
  6108. //快捷键
  6109. me.addshortcutkey({
  6110. "Undo":"ctrl+90", //undo
  6111. "Redo":"ctrl+89,shift+ctrl+z" //redo
  6112. });
  6113. var isCollapsed = true;
  6114. me.addListener('keydown', function (type, evt) {
  6115. var me = this;
  6116. var keyCode = evt.keyCode || evt.which;
  6117. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6118. if (inputType)
  6119. return;
  6120. if(!me.selection.getRange().collapsed){
  6121. me.undoManger.save(false,true);
  6122. isCollapsed = false;
  6123. return;
  6124. }
  6125. if (me.undoManger.list.length == 0) {
  6126. me.undoManger.save(true);
  6127. }
  6128. clearTimeout(saveSceneTimer);
  6129. function save(cont){
  6130. if (cont.selection.getRange().collapsed)
  6131. cont.fireEvent('contentchange');
  6132. cont.undoManger.save(false,true);
  6133. cont.fireEvent('selectionchange');
  6134. }
  6135. saveSceneTimer = setTimeout(function(){
  6136. if(inputType){
  6137. var interalTimer = setInterval(function(){
  6138. if(!inputType){
  6139. save(me);
  6140. clearInterval(interalTimer)
  6141. }
  6142. },300)
  6143. return;
  6144. }
  6145. save(me);
  6146. },200);
  6147. lastKeyCode = keyCode;
  6148. keycont++;
  6149. if (keycont >= maxInputCount ) {
  6150. save(me)
  6151. }
  6152. }
  6153. });
  6154. me.addListener('keyup', function (type, evt) {
  6155. var keyCode = evt.keyCode || evt.which;
  6156. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6157. if (inputType)
  6158. return;
  6159. if(!isCollapsed){
  6160. this.undoManger.save(false,true);
  6161. isCollapsed = true;
  6162. }
  6163. }
  6164. });
  6165. };
  6166. ///import core
  6167. ///import plugins/inserthtml.js
  6168. ///import plugins/undo.js
  6169. ///import plugins/serialize.js
  6170. ///commands 粘贴
  6171. ///commandsName PastePlain
  6172. ///commandsTitle 纯文本粘贴模式
  6173. /**
  6174. * @description 粘贴
  6175. * @author zhanyi
  6176. */
  6177. UM.plugins['paste'] = function () {
  6178. function getClipboardData(callback) {
  6179. var doc = this.document;
  6180. if (doc.getElementById('baidu_pastebin')) {
  6181. return;
  6182. }
  6183. var range = this.selection.getRange(),
  6184. bk = range.createBookmark(),
  6185. //创建剪贴的容器div
  6186. pastebin = doc.createElement('div');
  6187. pastebin.id = 'baidu_pastebin';
  6188. // Safari 要求div必须有内容,才能粘贴内容进来
  6189. browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
  6190. this.body.appendChild(pastebin);
  6191. //trace:717 隐藏的span不能得到top
  6192. //bk.start.innerHTML = '&nbsp;';
  6193. bk.start.style.display = '';
  6194. pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
  6195. //要在现在光标平行的位置加入,否则会出现跳动的问题
  6196. $(bk.start).position().top + 'px';
  6197. range.selectNodeContents(pastebin).select(true);
  6198. setTimeout(function () {
  6199. if (browser.webkit) {
  6200. for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
  6201. if (domUtils.isEmptyNode(pi)) {
  6202. domUtils.remove(pi);
  6203. } else {
  6204. pastebin = pi;
  6205. break;
  6206. }
  6207. }
  6208. }
  6209. try {
  6210. pastebin.parentNode.removeChild(pastebin);
  6211. } catch (e) {
  6212. }
  6213. range.moveToBookmark(bk).select(true);
  6214. callback(pastebin);
  6215. }, 0);
  6216. }
  6217. var me = this;
  6218. function filter(div) {
  6219. var html;
  6220. if (div.firstChild) {
  6221. //去掉cut中添加的边界值
  6222. var nodes = domUtils.getElementsByTagName(div, 'span');
  6223. for (var i = 0, ni; ni = nodes[i++];) {
  6224. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  6225. domUtils.remove(ni);
  6226. }
  6227. }
  6228. if (browser.webkit) {
  6229. var brs = div.querySelectorAll('div br');
  6230. for (var i = 0, bi; bi = brs[i++];) {
  6231. var pN = bi.parentNode;
  6232. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  6233. pN.innerHTML = '<p><br/></p>';
  6234. domUtils.remove(pN);
  6235. }
  6236. }
  6237. var divs = div.querySelectorAll('#baidu_pastebin');
  6238. for (var i = 0, di; di = divs[i++];) {
  6239. var tmpP = me.document.createElement('p');
  6240. di.parentNode.insertBefore(tmpP, di);
  6241. while (di.firstChild) {
  6242. tmpP.appendChild(di.firstChild);
  6243. }
  6244. domUtils.remove(di);
  6245. }
  6246. var metas = div.querySelectorAll('meta');
  6247. for (var i = 0, ci; ci = metas[i++];) {
  6248. domUtils.remove(ci);
  6249. }
  6250. var brs = div.querySelectorAll('br');
  6251. for (i = 0; ci = brs[i++];) {
  6252. if (/^apple-/i.test(ci.className)) {
  6253. domUtils.remove(ci);
  6254. }
  6255. }
  6256. }
  6257. if (browser.gecko) {
  6258. var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
  6259. for (i = 0; ci = dirtyNodes[i++];) {
  6260. ci.removeAttribute('_moz_dirty');
  6261. }
  6262. }
  6263. if (!browser.ie) {
  6264. var spans = div.querySelectorAll('span.Apple-style-span');
  6265. for (var i = 0, ci; ci = spans[i++];) {
  6266. domUtils.remove(ci, true);
  6267. }
  6268. }
  6269. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  6270. html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  6271. //过滤word粘贴过来的冗余属性
  6272. html = UM.filterWord(html);
  6273. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  6274. var root = UM.htmlparser(html);
  6275. //如果给了过滤规则就先进行过滤
  6276. if (me.options.filterRules) {
  6277. UM.filterNode(root, me.options.filterRules);
  6278. }
  6279. //执行默认的处理
  6280. me.filterInputRule(root);
  6281. //针对chrome的处理
  6282. if (browser.webkit) {
  6283. var br = root.lastChild();
  6284. if (br && br.type == 'element' && br.tagName == 'br') {
  6285. root.removeChild(br)
  6286. }
  6287. utils.each(me.body.querySelectorAll('div'), function (node) {
  6288. if (domUtils.isEmptyBlock(node)) {
  6289. domUtils.remove(node)
  6290. }
  6291. })
  6292. }
  6293. html = {'html': root.toHtml()};
  6294. me.fireEvent('beforepaste', html, root);
  6295. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  6296. if(!html.html){
  6297. return;
  6298. }
  6299. me.execCommand('insertHtml', html.html, true);
  6300. me.fireEvent("afterpaste", html);
  6301. }
  6302. }
  6303. me.addListener('ready', function () {
  6304. $(me.body).on( 'cut', function () {
  6305. var range = me.selection.getRange();
  6306. if (!range.collapsed && me.undoManger) {
  6307. me.undoManger.save();
  6308. }
  6309. }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
  6310. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  6311. if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
  6312. return;
  6313. }
  6314. getClipboardData.call(me, function (div) {
  6315. filter(div);
  6316. });
  6317. });
  6318. });
  6319. };
  6320. ///import core
  6321. ///commands 有序列表,无序列表
  6322. ///commandsName InsertOrderedList,InsertUnorderedList
  6323. ///commandsTitle 有序列表,无序列表
  6324. /**
  6325. * 有序列表
  6326. * @function
  6327. * @name UM.execCommand
  6328. * @param {String} cmdName insertorderlist插入有序列表
  6329. * @param {String} style 值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman
  6330. * @author zhanyi
  6331. */
  6332. /**
  6333. * 无序链接
  6334. * @function
  6335. * @name UM.execCommand
  6336. * @param {String} cmdName insertunorderlist插入无序列表
  6337. * * @param {String} style 值为:circle,disc,square
  6338. * @author zhanyi
  6339. */
  6340. UM.plugins['list'] = function () {
  6341. var me = this;
  6342. me.setOpt( {
  6343. 'insertorderedlist':{
  6344. 'decimal':'',
  6345. 'lower-alpha':'',
  6346. 'lower-roman':'',
  6347. 'upper-alpha':'',
  6348. 'upper-roman':''
  6349. },
  6350. 'insertunorderedlist':{
  6351. 'circle':'',
  6352. 'disc':'',
  6353. 'square':''
  6354. }
  6355. } );
  6356. this.addInputRule(function(root){
  6357. utils.each(root.getNodesByTagName('li'), function (node) {
  6358. if(node.children.length == 0){
  6359. node.parentNode.removeChild(node);
  6360. }
  6361. })
  6362. });
  6363. me.commands['insertorderedlist'] =
  6364. me.commands['insertunorderedlist'] = {
  6365. execCommand:function (cmdName) {
  6366. this.document.execCommand(cmdName);
  6367. var rng = this.selection.getRange(),
  6368. bk = rng.createBookmark(true);
  6369. this.$body.find('ol,ul').each(function(i,n){
  6370. var parent = n.parentNode;
  6371. if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){
  6372. $(n).children().each(function(j,li){
  6373. var p = parent.cloneNode(false);
  6374. $(p).append(li.innerHTML);
  6375. $(li).html('').append(p);
  6376. });
  6377. $(n).insertBefore(parent);
  6378. $(parent).remove();
  6379. }
  6380. if(dtd.$inline[parent.tagName]){
  6381. if(parent.tagName == 'SPAN'){
  6382. $(n).children().each(function(k,li){
  6383. var span = parent.cloneNode(false);
  6384. if(li.firstChild.nodeName != 'P'){
  6385. while(li.firstChild){
  6386. span.appendChild(li.firstChild)
  6387. };
  6388. $('<p></p>').appendTo(li).append(span);
  6389. }else{
  6390. while(li.firstChild){
  6391. span.appendChild(li.firstChild)
  6392. };
  6393. $(li.firstChild).append(span);
  6394. }
  6395. })
  6396. }
  6397. domUtils.remove(parent,true)
  6398. }
  6399. });
  6400. rng.moveToBookmark(bk).select();
  6401. return true;
  6402. },
  6403. queryCommandState:function (cmdName) {
  6404. return this.document.queryCommandState(cmdName);
  6405. }
  6406. };
  6407. };
  6408. ///import core
  6409. ///import plugins/serialize.js
  6410. ///import plugins/undo.js
  6411. ///commands 查看源码
  6412. ///commandsName Source
  6413. ///commandsTitle 查看源码
  6414. (function (){
  6415. var sourceEditors = {
  6416. textarea: function (editor, holder){
  6417. var textarea = holder.ownerDocument.createElement('textarea');
  6418. textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';
  6419. // todo: IE下只有onresize属性可用... 很纠结
  6420. if (browser.ie && browser.version < 8) {
  6421. textarea.style.width = holder.offsetWidth + 'px';
  6422. textarea.style.height = holder.offsetHeight + 'px';
  6423. holder.onresize = function (){
  6424. textarea.style.width = holder.offsetWidth + 'px';
  6425. textarea.style.height = holder.offsetHeight + 'px';
  6426. };
  6427. }
  6428. holder.appendChild(textarea);
  6429. return {
  6430. container : textarea,
  6431. setContent: function (content){
  6432. textarea.value = content;
  6433. },
  6434. getContent: function (){
  6435. return textarea.value;
  6436. },
  6437. select: function (){
  6438. var range;
  6439. if (browser.ie) {
  6440. range = textarea.createTextRange();
  6441. range.collapse(true);
  6442. range.select();
  6443. } else {
  6444. //todo: chrome下无法设置焦点
  6445. textarea.setSelectionRange(0, 0);
  6446. textarea.focus();
  6447. }
  6448. },
  6449. dispose: function (){
  6450. holder.removeChild(textarea);
  6451. // todo
  6452. holder.onresize = null;
  6453. textarea = null;
  6454. holder = null;
  6455. }
  6456. };
  6457. }
  6458. };
  6459. UM.plugins['source'] = function (){
  6460. var me = this;
  6461. var opt = this.options;
  6462. var sourceMode = false;
  6463. var sourceEditor;
  6464. opt.sourceEditor = 'textarea';
  6465. me.setOpt({
  6466. sourceEditorFirst:false
  6467. });
  6468. function createSourceEditor(holder){
  6469. return sourceEditors.textarea(me, holder);
  6470. }
  6471. var bakCssText;
  6472. //解决在源码模式下getContent不能得到最新的内容问题
  6473. var oldGetContent = me.getContent,
  6474. bakAddress;
  6475. me.commands['source'] = {
  6476. execCommand: function (){
  6477. sourceMode = !sourceMode;
  6478. if (sourceMode) {
  6479. bakAddress = me.selection.getRange().createAddress(false,true);
  6480. me.undoManger && me.undoManger.save(true);
  6481. if(browser.gecko){
  6482. me.body.contentEditable = false;
  6483. }
  6484. // bakCssText = me.body.style.cssText;
  6485. me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';
  6486. me.fireEvent('beforegetcontent');
  6487. var root = UM.htmlparser(me.body.innerHTML);
  6488. me.filterOutputRule(root);
  6489. root.traversal(function (node) {
  6490. if (node.type == 'element') {
  6491. switch (node.tagName) {
  6492. case 'td':
  6493. case 'th':
  6494. case 'caption':
  6495. if(node.children && node.children.length == 1){
  6496. if(node.firstChild().tagName == 'br' ){
  6497. node.removeChild(node.firstChild())
  6498. }
  6499. };
  6500. break;
  6501. case 'pre':
  6502. node.innerText(node.innerText().replace(/&nbsp;/g,' '))
  6503. }
  6504. }
  6505. });
  6506. me.fireEvent('aftergetcontent');
  6507. var content = root.toHtml(true);
  6508. sourceEditor = createSourceEditor(me.body.parentNode);
  6509. sourceEditor.setContent(content);
  6510. var getStyleValue=function(attr){
  6511. return parseInt($(me.body).css(attr));
  6512. };
  6513. $(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right"))
  6514. .height($(me.body).height());
  6515. setTimeout(function (){
  6516. sourceEditor.select();
  6517. });
  6518. //重置getContent,源码模式下取值也能是最新的数据
  6519. me.getContent = function (){
  6520. return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  6521. };
  6522. } else {
  6523. me.$body.css({
  6524. 'position':'',
  6525. 'left':'',
  6526. 'top':''
  6527. });
  6528. // me.body.style.cssText = bakCssText;
  6529. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  6530. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  6531. cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
  6532. if(b && !dtd.$inlineWithA[b.toLowerCase()]){
  6533. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
  6534. }
  6535. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
  6536. });
  6537. me.setContent(cont);
  6538. sourceEditor.dispose();
  6539. sourceEditor = null;
  6540. //还原getContent方法
  6541. me.getContent = oldGetContent;
  6542. var first = me.body.firstChild;
  6543. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  6544. if(!first){
  6545. me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
  6546. }
  6547. //要在ifm为显示时ff才能取到selection,否则报错
  6548. //这里不能比较位置了
  6549. me.undoManger && me.undoManger.save(true);
  6550. if(browser.gecko){
  6551. me.body.contentEditable = true;
  6552. }
  6553. try{
  6554. me.selection.getRange().moveToAddress(bakAddress).select();
  6555. }catch(e){}
  6556. }
  6557. this.fireEvent('sourcemodechanged', sourceMode);
  6558. },
  6559. queryCommandState: function (){
  6560. return sourceMode|0;
  6561. },
  6562. notNeedUndo : 1
  6563. };
  6564. var oldQueryCommandState = me.queryCommandState;
  6565. me.queryCommandState = function (cmdName){
  6566. cmdName = cmdName.toLowerCase();
  6567. if (sourceMode) {
  6568. //源码模式下可以开启的命令
  6569. return cmdName in {
  6570. 'source' : 1,
  6571. 'fullscreen' : 1
  6572. } ? oldQueryCommandState.apply(this, arguments) : -1
  6573. }
  6574. return oldQueryCommandState.apply(this, arguments);
  6575. };
  6576. };
  6577. })();
  6578. ///import core
  6579. ///import plugins/undo.js
  6580. ///commands 设置回车标签p或br
  6581. ///commandsName EnterKey
  6582. ///commandsTitle 设置回车标签p或br
  6583. /**
  6584. * @description 处理回车
  6585. * @author zhanyi
  6586. */
  6587. UM.plugins['enterkey'] = function() {
  6588. var hTag,
  6589. me = this,
  6590. tag = me.options.enterTag;
  6591. me.addListener('keyup', function(type, evt) {
  6592. var keyCode = evt.keyCode || evt.which;
  6593. if (keyCode == 13) {
  6594. var range = me.selection.getRange(),
  6595. start = range.startContainer,
  6596. doSave;
  6597. //修正在h1-h6里边回车后不能嵌套p的问题
  6598. if (!browser.ie) {
  6599. if (/h\d/i.test(hTag)) {
  6600. if (browser.gecko) {
  6601. var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
  6602. if (!h) {
  6603. me.document.execCommand('formatBlock', false, '<p>');
  6604. doSave = 1;
  6605. }
  6606. } else {
  6607. //chrome remove div
  6608. if (start.nodeType == 1) {
  6609. var tmp = me.document.createTextNode(''),div;
  6610. range.insertNode(tmp);
  6611. div = domUtils.findParentByTagName(tmp, 'div', true);
  6612. if (div) {
  6613. var p = me.document.createElement('p');
  6614. while (div.firstChild) {
  6615. p.appendChild(div.firstChild);
  6616. }
  6617. div.parentNode.insertBefore(p, div);
  6618. domUtils.remove(div);
  6619. range.setStartBefore(tmp).setCursor();
  6620. doSave = 1;
  6621. }
  6622. domUtils.remove(tmp);
  6623. }
  6624. }
  6625. if (me.undoManger && doSave) {
  6626. me.undoManger.save();
  6627. }
  6628. }
  6629. //没有站位符,会出现多行的问题
  6630. browser.opera && range.select();
  6631. }else{
  6632. me.fireEvent('saveScene',true,true)
  6633. }
  6634. }
  6635. });
  6636. me.addListener('keydown', function(type, evt) {
  6637. var keyCode = evt.keyCode || evt.which;
  6638. if (keyCode == 13) {//回车
  6639. if(me.fireEvent('beforeenterkeydown')){
  6640. domUtils.preventDefault(evt);
  6641. return;
  6642. }
  6643. me.fireEvent('saveScene',true,true);
  6644. hTag = '';
  6645. var range = me.selection.getRange();
  6646. if (!range.collapsed) {
  6647. //跨td不能删
  6648. var start = range.startContainer,
  6649. end = range.endContainer,
  6650. startTd = domUtils.findParentByTagName(start, 'td', true),
  6651. endTd = domUtils.findParentByTagName(end, 'td', true);
  6652. if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
  6653. evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
  6654. return;
  6655. }
  6656. }
  6657. if (tag == 'p') {
  6658. if (!browser.ie) {
  6659. start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
  6660. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  6661. //trace:2431
  6662. if (!start && !browser.opera) {
  6663. me.document.execCommand('formatBlock', false, '<p>');
  6664. if (browser.gecko) {
  6665. range = me.selection.getRange();
  6666. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  6667. start && domUtils.removeDirtyAttr(start);
  6668. }
  6669. } else {
  6670. hTag = start.tagName;
  6671. start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
  6672. }
  6673. }
  6674. }
  6675. }
  6676. });
  6677. browser.ie && me.addListener('setDisabled',function(){
  6678. $(me.body).find('p').each(function(i,p){
  6679. if(domUtils.isEmptyBlock(p)){
  6680. p.innerHTML = '&nbsp;'
  6681. }
  6682. })
  6683. })
  6684. };
  6685. ///import core
  6686. ///commands 预览
  6687. ///commandsName Preview
  6688. ///commandsTitle 预览
  6689. /**
  6690. * 预览
  6691. * @function
  6692. * @name UM.execCommand
  6693. * @param {String} cmdName preview预览编辑器内容
  6694. */
  6695. UM.commands['preview'] = {
  6696. execCommand : function(){
  6697. var w = window.open('', '_blank', ''),
  6698. d = w.document,
  6699. c = this.getContent(null,null,true),
  6700. path = this.getOpt('UMEDITOR_HOME_URL'),
  6701. formula = c.indexOf('mathquill-embedded-latex')!=-1 ?
  6702. '<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +
  6703. '<script src="' + path + 'third-party/jquery.min.js"></script>' +
  6704. '<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>':'';
  6705. d.open();
  6706. d.write('<html><head>' + formula + '</head><body><div>'+c+'</div></body></html>');
  6707. d.close();
  6708. },
  6709. notNeedUndo : 1
  6710. };
  6711. ///import core
  6712. ///commands 加粗,斜体,上标,下标
  6713. ///commandsName Bold,Italic,Subscript,Superscript
  6714. ///commandsTitle 加粗,加斜,下标,上标
  6715. /**
  6716. * b u i等基础功能实现
  6717. * @function
  6718. * @name UM.execCommands
  6719. * @param {String} cmdName bold加粗。italic斜体。subscript上标。superscript下标。
  6720. */
  6721. UM.plugins['basestyle'] = function(){
  6722. var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'],
  6723. me = this;
  6724. //添加快捷键
  6725. me.addshortcutkey({
  6726. "Bold" : "ctrl+66",//^B
  6727. "Italic" : "ctrl+73", //^I
  6728. "Underline" : "ctrl+shift+85",//^U
  6729. "strikeThrough" : 'ctrl+shift+83' //^s
  6730. });
  6731. //过滤最后的产出数据
  6732. me.addOutputRule(function(root){
  6733. $.each(root.getNodesByTagName('b i u strike s'),function(i,node){
  6734. switch (node.tagName){
  6735. case 'b':
  6736. node.tagName = 'strong';
  6737. break;
  6738. case 'i':
  6739. node.tagName = 'em';
  6740. break;
  6741. case 'u':
  6742. node.tagName = 'span';
  6743. node.setStyle('text-decoration','underline');
  6744. break;
  6745. case 's':
  6746. case 'strike':
  6747. node.tagName = 'span';
  6748. node.setStyle('text-decoration','line-through')
  6749. }
  6750. });
  6751. });
  6752. $.each(basestyles,function(i,cmd){
  6753. me.commands[cmd] = {
  6754. execCommand : function( cmdName ) {
  6755. var rng = this.selection.getRange();
  6756. if(rng.collapsed && this.queryCommandState(cmdName) != 1){
  6757. var node = this.document.createElement({
  6758. 'bold':'strong',
  6759. 'underline':'u',
  6760. 'superscript':'sup',
  6761. 'subscript':'sub',
  6762. 'italic':'em',
  6763. 'strikethrough':'strike'
  6764. }[cmdName]);
  6765. rng.insertNode(node).setStart(node,0).setCursor(false);
  6766. return true;
  6767. }else{
  6768. return this.document.execCommand(cmdName)
  6769. }
  6770. },
  6771. queryCommandState : function(cmdName) {
  6772. if(browser.gecko){
  6773. return this.document.queryCommandState(cmdName)
  6774. }
  6775. var path = this.selection.getStartElementPath(),result = false;
  6776. $.each(path,function(i,n){
  6777. switch (cmdName){
  6778. case 'bold':
  6779. if(n.nodeName == 'STRONG' || n.nodeName == 'B'){
  6780. result = 1;
  6781. return false;
  6782. }
  6783. break;
  6784. case 'underline':
  6785. if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){
  6786. result = 1;
  6787. return false;
  6788. }
  6789. break;
  6790. case 'superscript':
  6791. if(n.nodeName == 'SUP'){
  6792. result = 1;
  6793. return false;
  6794. }
  6795. break;
  6796. case 'subscript':
  6797. if(n.nodeName == 'SUB'){
  6798. result = 1;
  6799. return false;
  6800. }
  6801. break;
  6802. case 'italic':
  6803. if(n.nodeName == 'EM' || n.nodeName == 'I'){
  6804. result = 1;
  6805. return false;
  6806. }
  6807. break;
  6808. case 'strikethrough':
  6809. if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){
  6810. result = 1;
  6811. return false;
  6812. }
  6813. break;
  6814. }
  6815. })
  6816. return result
  6817. }
  6818. };
  6819. })
  6820. };
  6821. ///import core
  6822. ///import plugins/inserthtml.js
  6823. ///commands 视频
  6824. ///commandsName InsertVideo
  6825. ///commandsTitle 插入视频
  6826. ///commandsDialog dialogs\video
  6827. UM.plugins['video'] = function (){
  6828. var me =this,
  6829. div;
  6830. /**
  6831. * 创建插入视频字符窜
  6832. * @param url 视频地址
  6833. * @param width 视频宽度
  6834. * @param height 视频高度
  6835. * @param align 视频对齐
  6836. * @param toEmbed 是否以flash代替显示
  6837. * @param addParagraph 是否需要添加P 标签
  6838. */
  6839. function creatInsertStr(url,width,height,id,align,toEmbed){
  6840. return !toEmbed ?
  6841. '<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-video"' +
  6842. ' src="' + me.options.UMEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UMEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'
  6843. :
  6844. '<embed type="application/x-shockwave-flash" class="edui-faked-video" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
  6845. ' src="' + url + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"': '') +
  6846. ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
  6847. }
  6848. function switchImgAndEmbed(root,img2embed){
  6849. utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){
  6850. if(node.getAttr('class') == 'edui-faked-video'){
  6851. var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed);
  6852. node.parentNode.replaceChild(UM.uNode.createElement(html),node)
  6853. }
  6854. })
  6855. }
  6856. me.addOutputRule(function(root){
  6857. switchImgAndEmbed(root,true)
  6858. });
  6859. me.addInputRule(function(root){
  6860. switchImgAndEmbed(root)
  6861. });
  6862. me.commands["insertvideo"] = {
  6863. execCommand: function (cmd, videoObjs){
  6864. videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
  6865. var html = [],id = 'tmpVedio';
  6866. for(var i=0,vi,len = videoObjs.length;i<len;i++){
  6867. vi = videoObjs[i];
  6868. html.push(creatInsertStr( vi.url, vi.width || 420, vi.height || 280, id + i,vi.align,false));
  6869. }
  6870. me.execCommand("inserthtml",html.join(""),true);
  6871. },
  6872. queryCommandState : function(){
  6873. var img = me.selection.getRange().getClosedNode(),
  6874. flag = img && (img.className == "edui-faked-video");
  6875. return flag ? 1 : 0;
  6876. }
  6877. };
  6878. };
  6879. ///import core
  6880. ///commands 全选
  6881. ///commandsName SelectAll
  6882. ///commandsTitle 全选
  6883. /**
  6884. * 选中所有
  6885. * @function
  6886. * @name UM.execCommand
  6887. * @param {String} cmdName selectall选中编辑器里的所有内容
  6888. * @author zhanyi
  6889. */
  6890. UM.plugins['selectall'] = function(){
  6891. var me = this;
  6892. me.commands['selectall'] = {
  6893. execCommand : function(){
  6894. //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
  6895. var me = this,body = me.body,
  6896. range = me.selection.getRange();
  6897. range.selectNodeContents(body);
  6898. if(domUtils.isEmptyBlock(body)){
  6899. //opera不能自动合并到元素的里边,要手动处理一下
  6900. if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
  6901. range.setStartAtFirst(body.firstChild);
  6902. }
  6903. range.collapse(true);
  6904. }
  6905. range.select(true);
  6906. },
  6907. notNeedUndo : 1
  6908. };
  6909. //快捷键
  6910. me.addshortcutkey({
  6911. "selectAll" : "ctrl+65"
  6912. });
  6913. };
  6914. //UM.plugins['removeformat'] = function () {
  6915. // var me = this;
  6916. // me.commands['removeformat'] = {
  6917. // execCommand: function () {
  6918. // me.document.execCommand('removeformat');
  6919. //
  6920. // /* 处理ie8和firefox选区有链接时,清除格式的bug */
  6921. // if (browser.gecko || browser.ie8 || browser.webkit) {
  6922. // var nativeRange = this.selection.getNative().getRangeAt(0),
  6923. // common = nativeRange.commonAncestorContainer,
  6924. // rng = me.selection.getRange(),
  6925. // bk = rng.createBookmark();
  6926. //
  6927. // function isEleInBookmark(node, bk){
  6928. // if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&
  6929. // (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {
  6930. // return true;
  6931. // } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||
  6932. // (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {
  6933. // return true;
  6934. // }
  6935. // return false;
  6936. // }
  6937. //
  6938. // $(common).find('a').each(function (k, a) {
  6939. // if ( isEleInBookmark(a, bk) ) {
  6940. // a.removeAttribute('style');
  6941. // }
  6942. // });
  6943. //
  6944. // }
  6945. // }
  6946. // };
  6947. //
  6948. //};
  6949. //
  6950. UM.plugins['removeformat'] = function(){
  6951. var me = this;
  6952. me.setOpt({
  6953. 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
  6954. 'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
  6955. });
  6956. me.commands['removeformat'] = {
  6957. execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
  6958. var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
  6959. removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
  6960. range = new dom.Range( this.document ),
  6961. bookmark,node,parent,
  6962. filter = function( node ) {
  6963. return node.nodeType == 1;
  6964. };
  6965. function isRedundantSpan (node) {
  6966. if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
  6967. return 0;
  6968. }
  6969. if (browser.ie) {
  6970. //ie 下判断实效,所以只能简单用style来判断
  6971. //return node.style.cssText == '' ? 1 : 0;
  6972. var attrs = node.attributes;
  6973. if ( attrs.length ) {
  6974. for ( var i = 0,l = attrs.length; i<l; i++ ) {
  6975. if ( attrs[i].specified ) {
  6976. return 0;
  6977. }
  6978. }
  6979. return 1;
  6980. }
  6981. }
  6982. return !node.attributes.length;
  6983. }
  6984. function doRemove( range ) {
  6985. var bookmark1 = range.createBookmark();
  6986. if ( range.collapsed ) {
  6987. range.enlarge( true );
  6988. }
  6989. //不能把a标签切了
  6990. if(!notIncludeA){
  6991. var aNode = domUtils.findParentByTagName(range.startContainer,'a',true);
  6992. if(aNode){
  6993. range.setStartBefore(aNode);
  6994. }
  6995. aNode = domUtils.findParentByTagName(range.endContainer,'a',true);
  6996. if(aNode){
  6997. range.setEndAfter(aNode);
  6998. }
  6999. }
  7000. bookmark = range.createBookmark();
  7001. node = bookmark.start;
  7002. //切开始
  7003. while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
  7004. domUtils.breakParent( node, parent );
  7005. domUtils.clearEmptySibling( node );
  7006. }
  7007. if ( bookmark.end ) {
  7008. //切结束
  7009. node = bookmark.end;
  7010. while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
  7011. domUtils.breakParent( node, parent );
  7012. domUtils.clearEmptySibling( node );
  7013. }
  7014. //开始去除样式
  7015. var current = domUtils.getNextDomNode( bookmark.start, false, filter ),
  7016. next;
  7017. while ( current ) {
  7018. if ( current == bookmark.end ) {
  7019. break;
  7020. }
  7021. next = domUtils.getNextDomNode( current, true, filter );
  7022. if ( !dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode( current ) ) {
  7023. if ( tagReg.test( current.tagName ) ) {
  7024. if ( style ) {
  7025. domUtils.removeStyle( current, style );
  7026. if ( isRedundantSpan( current ) && style != 'text-decoration'){
  7027. domUtils.remove( current, true );
  7028. }
  7029. } else {
  7030. domUtils.remove( current, true );
  7031. }
  7032. } else {
  7033. //trace:939 不能把list上的样式去掉
  7034. if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){
  7035. domUtils.removeAttributes( current, removeFormatAttributes );
  7036. if ( isRedundantSpan( current ) ){
  7037. domUtils.remove( current, true );
  7038. }
  7039. }
  7040. }
  7041. }
  7042. current = next;
  7043. }
  7044. }
  7045. //trace:1035
  7046. //trace:1096 不能把td上的样式去掉,比如边框
  7047. var pN = bookmark.start.parentNode;
  7048. if(domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]){
  7049. domUtils.removeAttributes( pN,removeFormatAttributes );
  7050. }
  7051. pN = bookmark.end.parentNode;
  7052. if(bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName]&& !dtd.$list[pN.tagName]){
  7053. domUtils.removeAttributes( pN,removeFormatAttributes );
  7054. }
  7055. range.moveToBookmark( bookmark ).moveToBookmark(bookmark1);
  7056. //清除冗余的代码 <b><bookmark></b>
  7057. var node = range.startContainer,
  7058. tmp,
  7059. collapsed = range.collapsed;
  7060. while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
  7061. tmp = node.parentNode;
  7062. range.setStartBefore(node);
  7063. //trace:937
  7064. //更新结束边界
  7065. if(range.startContainer === range.endContainer){
  7066. range.endOffset--;
  7067. }
  7068. domUtils.remove(node);
  7069. node = tmp;
  7070. }
  7071. if(!collapsed){
  7072. node = range.endContainer;
  7073. while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
  7074. tmp = node.parentNode;
  7075. range.setEndBefore(node);
  7076. domUtils.remove(node);
  7077. node = tmp;
  7078. }
  7079. }
  7080. }
  7081. range = this.selection.getRange();
  7082. if(!range.collapsed) {
  7083. doRemove( range );
  7084. range.select();
  7085. }
  7086. }
  7087. };
  7088. };
  7089. /*
  7090. * 处理特殊键的兼容性问题
  7091. */
  7092. UM.plugins['keystrokes'] = function() {
  7093. var me = this;
  7094. var collapsed = true;
  7095. me.addListener('keydown', function(type, evt) {
  7096. var keyCode = evt.keyCode || evt.which,
  7097. rng = me.selection.getRange();
  7098. //处理全选的情况
  7099. if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
  7100. || keyCode >= 48 && keyCode <= 57 ||
  7101. keyCode >= 96 && keyCode <= 111 || {
  7102. 13:1,
  7103. 8:1,
  7104. 46:1
  7105. }[keyCode])
  7106. ){
  7107. var tmpNode = rng.startContainer;
  7108. if(domUtils.isFillChar(tmpNode)){
  7109. rng.setStartBefore(tmpNode)
  7110. }
  7111. tmpNode = rng.endContainer;
  7112. if(domUtils.isFillChar(tmpNode)){
  7113. rng.setEndAfter(tmpNode)
  7114. }
  7115. rng.txtToElmBoundary();
  7116. //结束边界可能放到了br的前边,要把br包含进来
  7117. // x[xxx]<br/>
  7118. if(rng.endContainer && rng.endContainer.nodeType == 1){
  7119. tmpNode = rng.endContainer.childNodes[rng.endOffset];
  7120. if(tmpNode && domUtils.isBr(tmpNode)){
  7121. rng.setEndAfter(tmpNode);
  7122. }
  7123. }
  7124. if(rng.startOffset == 0){
  7125. tmpNode = rng.startContainer;
  7126. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  7127. tmpNode = rng.endContainer;
  7128. if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  7129. me.fireEvent('saveScene');
  7130. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  7131. rng.setStart(me.body.firstChild,0).setCursor(false,true);
  7132. me._selectionChange();
  7133. return;
  7134. }
  7135. }
  7136. }
  7137. }
  7138. //处理backspace
  7139. if (keyCode == 8) {
  7140. rng = me.selection.getRange();
  7141. collapsed = rng.collapsed;
  7142. if(me.fireEvent('delkeydown',evt)){
  7143. return;
  7144. }
  7145. var start,end;
  7146. //避免按两次删除才能生效的问题
  7147. if(rng.collapsed && rng.inFillChar()){
  7148. start = rng.startContainer;
  7149. if(domUtils.isFillChar(start)){
  7150. rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
  7151. domUtils.remove(start)
  7152. }else{
  7153. start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
  7154. rng.startOffset--;
  7155. rng.collapse(true).select(true)
  7156. }
  7157. }
  7158. //解决选中control元素不能删除的问题
  7159. if (start = rng.getClosedNode()) {
  7160. me.fireEvent('saveScene');
  7161. rng.setStartBefore(start);
  7162. domUtils.remove(start);
  7163. rng.setCursor();
  7164. me.fireEvent('saveScene');
  7165. domUtils.preventDefault(evt);
  7166. return;
  7167. }
  7168. //阻止在table上的删除
  7169. if (!browser.ie) {
  7170. start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
  7171. end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
  7172. if (start && !end || !start && end || start !== end) {
  7173. evt.preventDefault();
  7174. return;
  7175. }
  7176. }
  7177. start = rng.startContainer;
  7178. if(rng.collapsed && start.nodeType == 1){
  7179. var currentNode = start.childNodes[rng.startOffset-1];
  7180. if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){
  7181. me.fireEvent('saveScene');
  7182. rng.setStartBefore(currentNode).collapse(true);
  7183. domUtils.remove(currentNode);
  7184. rng.select();
  7185. me.fireEvent('saveScene');
  7186. }
  7187. }
  7188. //trace:3613
  7189. if(browser.chrome){
  7190. if(rng.collapsed){
  7191. while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){
  7192. rng.setStartBefore(rng.startContainer)
  7193. }
  7194. var pre = rng.startContainer.childNodes[rng.startOffset-1];
  7195. if(pre && pre.nodeName == 'BR'){
  7196. rng.setStartBefore(pre);
  7197. me.fireEvent('saveScene');
  7198. $(pre).remove();
  7199. rng.setCursor();
  7200. me.fireEvent('saveScene');
  7201. }
  7202. }
  7203. }
  7204. }
  7205. //trace:1634
  7206. //ff的del键在容器空的时候,也会删除
  7207. if(browser.gecko && keyCode == 46){
  7208. var range = me.selection.getRange();
  7209. if(range.collapsed){
  7210. start = range.startContainer;
  7211. if(domUtils.isEmptyBlock(start)){
  7212. var parent = start.parentNode;
  7213. while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
  7214. start = parent;
  7215. parent = parent.parentNode;
  7216. }
  7217. if(start === parent.lastChild)
  7218. evt.preventDefault();
  7219. return;
  7220. }
  7221. }
  7222. }
  7223. });
  7224. me.addListener('keyup', function(type, evt) {
  7225. var keyCode = evt.keyCode || evt.which,
  7226. rng,me = this;
  7227. if(keyCode == 8){
  7228. if(me.fireEvent('delkeyup')){
  7229. return;
  7230. }
  7231. rng = me.selection.getRange();
  7232. if(rng.collapsed){
  7233. var tmpNode,
  7234. autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
  7235. if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
  7236. if(domUtils.isEmptyBlock(tmpNode)){
  7237. var pre = tmpNode.previousSibling;
  7238. if(pre && pre.nodeName != 'TABLE'){
  7239. domUtils.remove(tmpNode);
  7240. rng.setStartAtLast(pre).setCursor(false,true);
  7241. return;
  7242. }else{
  7243. var next = tmpNode.nextSibling;
  7244. if(next && next.nodeName != 'TABLE'){
  7245. domUtils.remove(tmpNode);
  7246. rng.setStartAtFirst(next).setCursor(false,true);
  7247. return;
  7248. }
  7249. }
  7250. }
  7251. }
  7252. //处理当删除到body时,要重新给p标签展位
  7253. if(domUtils.isBody(rng.startContainer)){
  7254. var tmpNode = domUtils.createElement(me.document,'p',{
  7255. 'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
  7256. });
  7257. rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
  7258. }
  7259. }
  7260. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  7261. if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
  7262. if(browser.ie){
  7263. var span = rng.document.createElement('span');
  7264. rng.insertNode(span).setStartBefore(span).collapse(true);
  7265. rng.select();
  7266. domUtils.remove(span)
  7267. }else{
  7268. rng.select()
  7269. }
  7270. }
  7271. }
  7272. })
  7273. };
  7274. /**
  7275. * 自动保存草稿
  7276. */
  7277. UM.plugins['autosave'] = function() {
  7278. var me = this,
  7279. //无限循环保护
  7280. lastSaveTime = new Date(),
  7281. //最小保存间隔时间
  7282. MIN_TIME = 20,
  7283. //auto save key
  7284. saveKey = null;
  7285. //默认间隔时间
  7286. me.setOpt('saveInterval', 500);
  7287. //存储媒介封装
  7288. var LocalStorage = UM.LocalStorage = ( function () {
  7289. var storage = window.localStorage || getUserData() || null,
  7290. LOCAL_FILE = "localStorage";
  7291. return {
  7292. saveLocalData: function ( key, data ) {
  7293. if ( storage && data) {
  7294. storage.setItem( key, data );
  7295. return true;
  7296. }
  7297. return false;
  7298. },
  7299. getLocalData: function ( key ) {
  7300. if ( storage ) {
  7301. return storage.getItem( key );
  7302. }
  7303. return null;
  7304. },
  7305. removeItem: function ( key ) {
  7306. storage && storage.removeItem( key );
  7307. }
  7308. };
  7309. function getUserData () {
  7310. var container = document.createElement( "div" );
  7311. container.style.display = "none";
  7312. if( !container.addBehavior ) {
  7313. return null;
  7314. }
  7315. container.addBehavior("#default#userdata");
  7316. return {
  7317. getItem: function ( key ) {
  7318. var result = null;
  7319. try {
  7320. document.body.appendChild( container );
  7321. container.load( LOCAL_FILE );
  7322. result = container.getAttribute( key );
  7323. document.body.removeChild( container );
  7324. } catch ( e ) {
  7325. }
  7326. return result;
  7327. },
  7328. setItem: function ( key, value ) {
  7329. document.body.appendChild( container );
  7330. container.setAttribute( key, value );
  7331. container.save( LOCAL_FILE );
  7332. document.body.removeChild( container );
  7333. },
  7334. // 暂时没有用到
  7335. // clear: function () {
  7336. //
  7337. // var expiresTime = new Date();
  7338. // expiresTime.setFullYear( expiresTime.getFullYear() - 1 );
  7339. // document.body.appendChild( container );
  7340. // container.expires = expiresTime.toUTCString();
  7341. // container.save( LOCAL_FILE );
  7342. // document.body.removeChild( container );
  7343. //
  7344. // },
  7345. removeItem: function ( key ) {
  7346. document.body.appendChild( container );
  7347. container.removeAttribute( key );
  7348. container.save( LOCAL_FILE );
  7349. document.body.removeChild( container );
  7350. }
  7351. };
  7352. }
  7353. } )();
  7354. function save ( editor ) {
  7355. var saveData = null;
  7356. if ( new Date() - lastSaveTime < MIN_TIME ) {
  7357. return;
  7358. }
  7359. if ( !editor.hasContents() ) {
  7360. //这里不能调用命令来删除, 会造成事件死循环
  7361. saveKey && LocalStorage.removeItem( saveKey );
  7362. return;
  7363. }
  7364. lastSaveTime = new Date();
  7365. editor._saveFlag = null;
  7366. saveData = me.body.innerHTML;
  7367. if ( editor.fireEvent( "beforeautosave", {
  7368. content: saveData
  7369. } ) === false ) {
  7370. return;
  7371. }
  7372. LocalStorage.saveLocalData( saveKey, saveData );
  7373. editor.fireEvent( "afterautosave", {
  7374. content: saveData
  7375. } );
  7376. }
  7377. me.addListener('ready', function(){
  7378. var _suffix = "-drafts-data",
  7379. key = null;
  7380. if ( me.key ) {
  7381. key = me.key + _suffix;
  7382. } else {
  7383. key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;
  7384. }
  7385. //页面地址+编辑器ID 保持唯一
  7386. saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;
  7387. });
  7388. me.addListener('contentchange', function(){
  7389. if ( !saveKey ) {
  7390. return;
  7391. }
  7392. if ( me._saveFlag ) {
  7393. window.clearTimeout( me._saveFlag );
  7394. }
  7395. if ( me.options.saveInterval > 0 ) {
  7396. me._saveFlag = window.setTimeout( function () {
  7397. save( me );
  7398. }, me.options.saveInterval );
  7399. } else {
  7400. save(me);
  7401. }
  7402. })
  7403. me.commands['clearlocaldata'] = {
  7404. execCommand:function (cmd, name) {
  7405. if ( saveKey && LocalStorage.getLocalData( saveKey ) ) {
  7406. LocalStorage.removeItem( saveKey )
  7407. }
  7408. },
  7409. notNeedUndo: true,
  7410. ignoreContentChange:true
  7411. };
  7412. me.commands['getlocaldata'] = {
  7413. execCommand:function (cmd, name) {
  7414. return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : '';
  7415. },
  7416. notNeedUndo: true,
  7417. ignoreContentChange:true
  7418. };
  7419. me.commands['drafts'] = {
  7420. execCommand:function (cmd, name) {
  7421. if ( saveKey ) {
  7422. me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '<p>'+(browser.ie ? '&nbsp;' : '<br/>')+'</p>';
  7423. me.focus(true);
  7424. }
  7425. },
  7426. queryCommandState: function () {
  7427. return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1;
  7428. },
  7429. notNeedUndo: true,
  7430. ignoreContentChange:true
  7431. }
  7432. };
  7433. /**
  7434. * @description
  7435. * 1.拖放文件到编辑区域,自动上传并插入到选区
  7436. * 2.插入粘贴板的图片,自动上传并插入到选区
  7437. * @author Jinqn
  7438. * @date 2013-10-14
  7439. */
  7440. UM.plugins['autoupload'] = function () {
  7441. var me = this;
  7442. me.setOpt('pasteImageEnabled', true);
  7443. me.setOpt('dropFileEnabled', true);
  7444. var insertBase64Image = function (file, editor) {
  7445. var reader = new FileReader();
  7446. reader.onload = function (e) {
  7447. editor.execCommand('insertimage', {
  7448. src: this.result,
  7449. _src: this.result
  7450. });
  7451. };
  7452. reader.readAsDataURL(file);
  7453. };
  7454. var sendAndInsertImage = function (file, editor) {
  7455. //模拟数据
  7456. var fd = new FormData();
  7457. fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));
  7458. fd.append('type', 'ajax');
  7459. var xhr = new XMLHttpRequest();
  7460. xhr.open("post", me.options.imageUrl, true);
  7461. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  7462. xhr.addEventListener('load', function (e) {
  7463. try {
  7464. var json = eval('('+e.target.response+')'),
  7465. link = json.url,
  7466. picLink = me.options.imagePath + link;
  7467. editor.execCommand('insertimage', {
  7468. src: picLink,
  7469. _src: picLink
  7470. });
  7471. } catch (er) {
  7472. }
  7473. });
  7474. xhr.send(fd);
  7475. };
  7476. function getPasteImage(e) {
  7477. return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
  7478. }
  7479. function getDropImage(e) {
  7480. return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
  7481. }
  7482. me.addListener('ready', function () {
  7483. if (window.FormData && window.FileReader) {
  7484. var autoUploadHandler = function (e) {
  7485. var hasImg = false,
  7486. items;
  7487. //获取粘贴板文件列表或者拖放文件列表
  7488. items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);
  7489. if (items) {
  7490. var len = items.length,
  7491. file;
  7492. while (len--) {
  7493. file = items[len];
  7494. if (file.getAsFile) file = file.getAsFile();
  7495. if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {
  7496. me.getOpt('convertImageToBase64Enable') ? insertBase64Image(file, me) : sendAndInsertImage(file, me);
  7497. hasImg = true;
  7498. }
  7499. }
  7500. if (hasImg) return false;
  7501. }
  7502. };
  7503. me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);
  7504. me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);
  7505. //取消拖放图片时出现的文字光标位置提示
  7506. me.$body.on('dragover', function (e) {
  7507. if (e.originalEvent.dataTransfer.types[0] == 'Files') {
  7508. return false;
  7509. }
  7510. });
  7511. }
  7512. });
  7513. };
  7514. /**
  7515. * 公式插件
  7516. */
  7517. UM.plugins['formula'] = function () {
  7518. var me = this;
  7519. function getActiveIframe() {
  7520. return me.$body.find('iframe.edui-formula-active')[0] || null;
  7521. }
  7522. function blurActiveIframe(){
  7523. var iframe = getActiveIframe();
  7524. iframe && iframe.contentWindow.formula.blur();
  7525. }
  7526. me.addInputRule(function (root) {
  7527. $.each(root.getNodesByTagName('span'), function (i, node) {
  7528. if (node.hasClass('mathquill-embedded-latex')) {
  7529. var firstChild, latex = '';
  7530. while(firstChild = node.firstChild()){
  7531. latex += firstChild.data;
  7532. node.removeChild(firstChild);
  7533. }
  7534. node.tagName = 'iframe';
  7535. node.setAttr({
  7536. 'frameborder': '0',
  7537. 'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',
  7538. 'data-latex': utils.unhtml(latex)
  7539. });
  7540. }
  7541. });
  7542. });
  7543. me.addOutputRule(function (root) {
  7544. $.each(root.getNodesByTagName('iframe'), function (i, node) {
  7545. if (node.hasClass('mathquill-embedded-latex')) {
  7546. node.tagName = 'span';
  7547. node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));
  7548. node.setAttr({
  7549. 'frameborder': '',
  7550. 'src': '',
  7551. 'data-latex': ''
  7552. });
  7553. }
  7554. });
  7555. });
  7556. me.addListener('click', function(){
  7557. blurActiveIframe();
  7558. });
  7559. me.addListener('afterexeccommand', function(type, cmd){
  7560. if(cmd != 'formula') {
  7561. blurActiveIframe();
  7562. }
  7563. });
  7564. me.commands['formula'] = {
  7565. execCommand: function (cmd, latex) {
  7566. var iframe = getActiveIframe();
  7567. if (iframe) {
  7568. iframe.contentWindow.formula.insertLatex(latex);
  7569. } else {
  7570. me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');
  7571. browser.ie && browser.ie9below && setTimeout(function(){
  7572. var rng = me.selection.getRange(),
  7573. startContainer = rng.startContainer;
  7574. if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){
  7575. rng.insertNode(me.document.createTextNode(' '));
  7576. rng.setCursor()
  7577. }
  7578. },100)
  7579. }
  7580. },
  7581. queryCommandState: function (cmd) {
  7582. return 0;
  7583. },
  7584. queryCommandValue: function (cmd) {
  7585. var iframe = getActiveIframe();
  7586. return iframe && iframe.contentWindow.formula.getLatex();
  7587. }
  7588. }
  7589. };
  7590. (function ($) {
  7591. //对jquery的扩展
  7592. $.parseTmpl = function parse(str, data) {
  7593. var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) {
  7594. return "'," + code.replace(/\\'/g, "'") + ",'";
  7595. }).replace(/<%([\s\S]+?)%>/g,function (match, code) {
  7596. return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
  7597. }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
  7598. var func = new Function('obj', tmpl);
  7599. return data ? func(data) : func;
  7600. };
  7601. $.extend2 = function (t, s) {
  7602. var a = arguments,
  7603. notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,
  7604. len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;
  7605. for (var i = 1; i < len; i++) {
  7606. var x = a[i];
  7607. for (var k in x) {
  7608. if (!notCover || !t.hasOwnProperty(k)) {
  7609. t[k] = x[k];
  7610. }
  7611. }
  7612. }
  7613. return t;
  7614. };
  7615. $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;
  7616. //所有ui的基类
  7617. var _eventHandler = [];
  7618. var _widget = function () {
  7619. };
  7620. var _prefix = 'edui';
  7621. _widget.prototype = {
  7622. on: function (ev, cb) {
  7623. this.root().on(ev, $.proxy(cb, this));
  7624. return this;
  7625. },
  7626. off: function (ev, cb) {
  7627. this.root().off(ev, $.proxy(cb, this));
  7628. return this;
  7629. },
  7630. trigger: function (ev, data) {
  7631. return this.root().trigger(ev, data) === false ? false : this;
  7632. },
  7633. root: function ($el) {
  7634. return this._$el || (this._$el = $el);
  7635. },
  7636. destroy: function () {
  7637. },
  7638. data: function (key, val) {
  7639. if (val !== undefined) {
  7640. this.root().data(_prefix + key, val);
  7641. return this;
  7642. } else {
  7643. return this.root().data(_prefix + key)
  7644. }
  7645. },
  7646. register: function (eventName, $el, fn) {
  7647. _eventHandler.push({
  7648. 'evtname': eventName,
  7649. '$els': $.isArray($el) ? $el : [$el],
  7650. handler: $.proxy(fn, $el)
  7651. })
  7652. }
  7653. };
  7654. //从jq实例上拿到绑定的widget实例
  7655. $.fn.edui = function (obj) {
  7656. return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');
  7657. };
  7658. function _createClass(ClassObj, properties, supperClass) {
  7659. ClassObj.prototype = $.extend2(
  7660. $.extend({}, properties),
  7661. (UM.ui[supperClass] || _widget).prototype,
  7662. true
  7663. );
  7664. ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;
  7665. //父class的defaultOpt 合并
  7666. if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) {
  7667. var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,
  7668. subDefaultOptions = ClassObj.prototype.defaultOpt;
  7669. ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} );
  7670. }
  7671. return ClassObj
  7672. }
  7673. var _guid = 1;
  7674. function mergeToJQ(ClassObj, className) {
  7675. $[_prefix + className] = ClassObj;
  7676. $.fn[_prefix + className] = function (opt) {
  7677. var result, args = Array.prototype.slice.call(arguments, 1);
  7678. this.each(function (i, el) {
  7679. var $this = $(el);
  7680. var obj = $this.edui();
  7681. if (!obj) {
  7682. ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);
  7683. $this.edui(obj)
  7684. }
  7685. if ($.type(opt) == 'string') {
  7686. if (opt == 'this') {
  7687. result = obj;
  7688. } else {
  7689. result = obj[opt].apply(obj, args);
  7690. if (result !== obj && result !== undefined) {
  7691. return false;
  7692. }
  7693. result = null;
  7694. }
  7695. }
  7696. });
  7697. return result !== null ? result : this;
  7698. }
  7699. }
  7700. UM.ui = {
  7701. define: function (className, properties, supperClass) {
  7702. var ClassObj = UM.ui[className] = _createClass(function (options, $el) {
  7703. var _obj = function () {
  7704. };
  7705. $.extend(_obj.prototype, ClassObj.prototype, {
  7706. guid: className + _guid++,
  7707. widgetName: className
  7708. }
  7709. );
  7710. var obj = new _obj;
  7711. if ($.type(options) == 'string') {
  7712. obj.init && obj.init({});
  7713. obj.root().edui(obj);
  7714. obj.root().find('a').click(function (evt) {
  7715. evt.preventDefault()
  7716. });
  7717. return obj.root()[_prefix + className].apply(obj.root(), arguments)
  7718. } else {
  7719. $el && obj.root($el);
  7720. obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);
  7721. try{
  7722. obj.root().find('a').click(function (evt) {
  7723. evt.preventDefault()
  7724. });
  7725. }catch(e){
  7726. }
  7727. return obj.root().edui(obj);
  7728. }
  7729. },properties, supperClass);
  7730. mergeToJQ(ClassObj, className);
  7731. }
  7732. };
  7733. $(function () {
  7734. $(document).on('click mouseup mousedown dblclick mouseover', function (evt) {
  7735. $.each(_eventHandler, function (i, obj) {
  7736. if (obj.evtname == evt.type) {
  7737. $.each(obj.$els, function (i, $el) {
  7738. if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {
  7739. obj.handler(evt);
  7740. }
  7741. })
  7742. }
  7743. })
  7744. })
  7745. })
  7746. })(jQuery);
  7747. //button 类
  7748. UM.ui.define('button', {
  7749. tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%><% if(title) {%> data-original-title="<%=title%>" <%};%>> ' +
  7750. '<% if(icon) {%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><% }; %><%if(text) {%><span unselectable="on" onmousedown="return false" class="edui-button-label"><%=text%></span><%}%>' +
  7751. '<%if(caret && text){%><span class="edui-button-spacing"></span><%}%>' +
  7752. '<% if(caret) {%><span unselectable="on" onmousedown="return false" class="edui-caret"></span><% };%></<%if(!texttype){%>div<%}else{%>a<%}%>>',
  7753. defaultOpt: {
  7754. text: '',
  7755. title: '',
  7756. icon: '',
  7757. width: '',
  7758. caret: false,
  7759. texttype: false,
  7760. click: function () {
  7761. }
  7762. },
  7763. init: function (options) {
  7764. var me = this;
  7765. me.root($($.parseTmpl(me.tpl, options)))
  7766. .click(function (evt) {
  7767. me.wrapclick(options.click, evt)
  7768. });
  7769. me.root().hover(function () {
  7770. if(!me.root().hasClass("edui-disabled")){
  7771. me.root().toggleClass('edui-hover')
  7772. }
  7773. })
  7774. return me;
  7775. },
  7776. wrapclick: function (fn, evt) {
  7777. if (!this.disabled()) {
  7778. this.root().trigger('wrapclick');
  7779. $.proxy(fn, this, evt)()
  7780. }
  7781. return this;
  7782. },
  7783. label: function (text) {
  7784. if (text === undefined) {
  7785. return this.root().find('.edui-button-label').text();
  7786. } else {
  7787. this.root().find('.edui-button-label').text(text);
  7788. return this;
  7789. }
  7790. },
  7791. disabled: function (state) {
  7792. if (state === undefined) {
  7793. return this.root().hasClass('edui-disabled')
  7794. }
  7795. this.root().toggleClass('edui-disabled', state);
  7796. if(this.root().hasClass('edui-disabled')){
  7797. this.root().removeClass('edui-hover')
  7798. }
  7799. return this;
  7800. },
  7801. active: function (state) {
  7802. if (state === undefined) {
  7803. return this.root().hasClass('edui-active')
  7804. }
  7805. this.root().toggleClass('edui-active', state)
  7806. return this;
  7807. },
  7808. mergeWith: function ($obj) {
  7809. var me = this;
  7810. me.data('$mergeObj', $obj);
  7811. $obj.edui().data('$mergeObj', me.root());
  7812. if (!$.contains(document.body, $obj[0])) {
  7813. $obj.appendTo(me.root());
  7814. }
  7815. me.on('click',function () {
  7816. me.wrapclick(function () {
  7817. $obj.edui().show();
  7818. })
  7819. }).register('click', me.root(), function (evt) {
  7820. $obj.hide()
  7821. });
  7822. }
  7823. });
  7824. //toolbar 类
  7825. (function () {
  7826. UM.ui.define('toolbar', {
  7827. tpl: '<div class="edui-toolbar" ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false" ></div></div>'
  7828. ,
  7829. init: function () {
  7830. var $root = this.root($(this.tpl));
  7831. this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))
  7832. },
  7833. appendToBtnmenu : function(data){
  7834. var $cont = this.data('$btnToolbar');
  7835. data = $.isArray(data) ? data : [data];
  7836. $.each(data,function(i,$item){
  7837. $cont.append($item)
  7838. })
  7839. }
  7840. });
  7841. })();
  7842. //menu 类
  7843. UM.ui.define('menu',{
  7844. show : function($obj,dir,fnname,topOffset,leftOffset){
  7845. fnname = fnname || 'position';
  7846. if(this.trigger('beforeshow') === false){
  7847. return;
  7848. }else{
  7849. this.root().css($.extend({display:'block'},$obj ? {
  7850. top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),
  7851. left : $obj[fnname]().left + (dir == 'right' ? $obj.outerWidth() : 0) - (leftOffset || 0)
  7852. }:{}))
  7853. this.trigger('aftershow');
  7854. }
  7855. },
  7856. hide : function(all){
  7857. var $parentmenu;
  7858. if(this.trigger('beforehide') === false){
  7859. return;
  7860. } else {
  7861. if($parentmenu = this.root().data('parentmenu')){
  7862. if($parentmenu.data('parentmenu')|| all)
  7863. $parentmenu.edui().hide();
  7864. }
  7865. this.root().css('display','none');
  7866. this.trigger('afterhide');
  7867. }
  7868. },
  7869. attachTo : function($obj){
  7870. var me = this;
  7871. if(!$obj.data('$mergeObj')){
  7872. $obj.data('$mergeObj',me.root());
  7873. $obj.on('wrapclick',function(evt){
  7874. me.show()
  7875. });
  7876. me.register('click',$obj,function(evt){
  7877. me.hide()
  7878. });
  7879. me.data('$mergeObj',$obj)
  7880. }
  7881. }
  7882. });
  7883. //dropmenu 类
  7884. UM.ui.define('dropmenu', {
  7885. tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +
  7886. '<%for(var i=0,ci;ci=data[i++];){%>' +
  7887. '<%if(ci.divider){%><li class="edui-divider"></li><%}else{%>' +
  7888. '<li <%if(ci.active||ci.disabled){%>class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' +
  7889. '<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em><%= ci.label%></a>' +
  7890. '</li><%}%>' +
  7891. '<%}%>' +
  7892. '</ul>',
  7893. defaultOpt: {
  7894. data: [],
  7895. click: function () {
  7896. }
  7897. },
  7898. init: function (options) {
  7899. var me = this;
  7900. var eventName = {
  7901. click: 1,
  7902. mouseover: 1,
  7903. mouseout: 1
  7904. };
  7905. this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) {
  7906. $.proxy(options.click, me, evt, $(this).data('value'), $(this))()
  7907. }).find('li').each(function (i, el) {
  7908. var $this = $(this);
  7909. if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {
  7910. var data = options.data[i];
  7911. $.each(eventName, function (k) {
  7912. data[k] && $this[k](function (evt) {
  7913. $.proxy(data[k], el)(evt, data, me.root)
  7914. })
  7915. })
  7916. }
  7917. })
  7918. },
  7919. disabled: function (cb) {
  7920. $('li[class!=edui-divider]', this.root()).each(function () {
  7921. var $el = $(this);
  7922. if (cb === true) {
  7923. $el.addClass('edui-disabled')
  7924. } else if ($.isFunction(cb)) {
  7925. $el.toggleClass('edui-disabled', cb(li))
  7926. } else {
  7927. $el.removeClass('edui-disabled')
  7928. }
  7929. });
  7930. },
  7931. val: function (val) {
  7932. var currentVal;
  7933. $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {
  7934. var $el = $(this);
  7935. if (val === undefined) {
  7936. if ($el.find('em.edui-dropmenu-checked').length) {
  7937. currentVal = $el.data('value');
  7938. return false
  7939. }
  7940. } else {
  7941. $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)
  7942. }
  7943. });
  7944. if (val === undefined) {
  7945. return currentVal
  7946. }
  7947. },
  7948. addSubmenu: function (label, menu, index) {
  7949. index = index || 0;
  7950. var $list = $('li[class!=edui-divider]', this.root());
  7951. var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);
  7952. if (index >= 0 && index < $list.length) {
  7953. $node.insertBefore($list[index]);
  7954. } else if (index < 0) {
  7955. $node.insertBefore($list[0]);
  7956. } else if (index >= $list.length) {
  7957. $node.appendTo($list);
  7958. }
  7959. }
  7960. }, 'menu');
  7961. //splitbutton 类
  7962. ///import button
  7963. UM.ui.define('splitbutton',{
  7964. tpl :'<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><%if(text){%><%=text%><%}%></div>'+
  7965. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
  7966. '<div unselectable="on" class="edui-caret"><\/div>'+
  7967. '</div>'+
  7968. '</div>',
  7969. defaultOpt:{
  7970. text:'',
  7971. title:'',
  7972. click:function(){}
  7973. },
  7974. init : function(options){
  7975. var me = this;
  7976. me.root( $($.parseTmpl(me.tpl,options)));
  7977. me.root().find('.edui-btn:first').click(function(evt){
  7978. if(!me.disabled()){
  7979. $.proxy(options.click,me)();
  7980. }
  7981. });
  7982. me.root().find('.edui-dropdown-toggle').click(function(){
  7983. if(!me.disabled()){
  7984. me.trigger('arrowclick')
  7985. }
  7986. });
  7987. me.root().hover(function () {
  7988. if(!me.root().hasClass("edui-disabled")){
  7989. me.root().toggleClass('edui-hover')
  7990. }
  7991. });
  7992. return me;
  7993. },
  7994. wrapclick:function(fn,evt){
  7995. if(!this.disabled()){
  7996. $.proxy(fn,this,evt)()
  7997. }
  7998. return this;
  7999. },
  8000. disabled : function(state){
  8001. if(state === undefined){
  8002. return this.root().hasClass('edui-disabled')
  8003. }
  8004. this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state);
  8005. return this;
  8006. },
  8007. active:function(state){
  8008. if(state === undefined){
  8009. return this.root().hasClass('edui-active')
  8010. }
  8011. this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state);
  8012. return this;
  8013. },
  8014. mergeWith:function($obj){
  8015. var me = this;
  8016. me.data('$mergeObj',$obj);
  8017. $obj.edui().data('$mergeObj',me.root());
  8018. if(!$.contains(document.body,$obj[0])){
  8019. $obj.appendTo(me.root());
  8020. }
  8021. me.root().delegate('.edui-dropdown-toggle','click',function(){
  8022. me.wrapclick(function(){
  8023. $obj.edui().show();
  8024. })
  8025. });
  8026. me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){
  8027. $obj.hide()
  8028. });
  8029. }
  8030. });
  8031. /**
  8032. * Created with JetBrains PhpStorm.
  8033. * User: hn
  8034. * Date: 13-7-10
  8035. * Time: 下午3:07
  8036. * To change this template use File | Settings | File Templates.
  8037. */
  8038. UM.ui.define('colorsplitbutton',{
  8039. tpl : '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><div class="edui-splitbutton-color-label" <%if (color) {%>style="background: <%=color%>"<%}%>></div><%if(text){%><%=text%><%}%></div>'+
  8040. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
  8041. '<div unselectable="on" class="edui-caret"><\/div>'+
  8042. '</div>'+
  8043. '</div>',
  8044. defaultOpt: {
  8045. color: ''
  8046. },
  8047. init: function( options ){
  8048. var me = this;
  8049. me.supper.init.call( me, options );
  8050. },
  8051. colorLabel: function(){
  8052. return this.root().find('.edui-splitbutton-color-label');
  8053. }
  8054. }, 'splitbutton');
  8055. //popup 类
  8056. UM.ui.define('popup', {
  8057. tpl: '<div class="edui-dropdown-menu edui-popup"'+
  8058. '<%if(!<%=stopprop%>){%>onmousedown="return false"<%}%>'+
  8059. '><div class="edui-popup-body" unselectable="on" onmousedown="return false"><%=subtpl%></div>' +
  8060. '<div class="edui-popup-caret"></div>' +
  8061. '</div>',
  8062. defaultOpt: {
  8063. stopprop:false,
  8064. subtpl: '',
  8065. width: '',
  8066. height: ''
  8067. },
  8068. init: function (options) {
  8069. this.root($($.parseTmpl(this.tpl, options)));
  8070. return this;
  8071. },
  8072. mergeTpl: function (data) {
  8073. return $.parseTmpl(this.tpl, {subtpl: data});
  8074. },
  8075. show: function ($obj, posObj) {
  8076. if (!posObj) posObj = {};
  8077. var fnname = posObj.fnname || 'position';
  8078. if (this.trigger('beforeshow') === false) {
  8079. return;
  8080. } else {
  8081. this.root().css($.extend({display: 'block'}, $obj ? {
  8082. top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),
  8083. left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),
  8084. position: 'absolute'
  8085. } : {}));
  8086. this.root().find('.edui-popup-caret').css({
  8087. top: posObj.caretTop || 0,
  8088. left: posObj.caretLeft || 0,
  8089. position: 'absolute'
  8090. }).addClass(posObj.caretDir || "up")
  8091. }
  8092. this.trigger("aftershow");
  8093. },
  8094. hide: function () {
  8095. this.root().css('display', 'none');
  8096. this.trigger('afterhide')
  8097. },
  8098. attachTo: function ($obj, posObj) {
  8099. var me = this
  8100. if (!$obj.data('$mergeObj')) {
  8101. $obj.data('$mergeObj', me.root());
  8102. $obj.on('wrapclick', function (evt) {
  8103. me.show($obj, posObj)
  8104. });
  8105. me.register('click', $obj, function (evt) {
  8106. me.hide()
  8107. });
  8108. me.data('$mergeObj', $obj)
  8109. }
  8110. },
  8111. getBodyContainer: function () {
  8112. return this.root().find(".edui-popup-body");
  8113. }
  8114. });
  8115. //scale 类
  8116. UM.ui.define('scale', {
  8117. tpl: '<div class="edui-scale" unselectable="on">' +
  8118. '<span class="edui-scale-hand0"></span>' +
  8119. '<span class="edui-scale-hand1"></span>' +
  8120. '<span class="edui-scale-hand2"></span>' +
  8121. '<span class="edui-scale-hand3"></span>' +
  8122. '<span class="edui-scale-hand4"></span>' +
  8123. '<span class="edui-scale-hand5"></span>' +
  8124. '<span class="edui-scale-hand6"></span>' +
  8125. '<span class="edui-scale-hand7"></span>' +
  8126. '</div>',
  8127. defaultOpt: {
  8128. $doc: $(document),
  8129. $wrap: $(document)
  8130. },
  8131. init: function (options) {
  8132. if(options.$doc) this.defaultOpt.$doc = options.$doc;
  8133. if(options.$wrap) this.defaultOpt.$wrap = options.$wrap;
  8134. this.root($($.parseTmpl(this.tpl, options)));
  8135. this.initStyle();
  8136. this.startPos = this.prePos = {x: 0, y: 0};
  8137. this.dragId = -1;
  8138. return this;
  8139. },
  8140. initStyle: function () {
  8141. utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +
  8142. '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
  8143. + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
  8144. + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
  8145. + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
  8146. + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
  8147. + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
  8148. + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
  8149. + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
  8150. + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
  8151. },
  8152. _eventHandler: function (e) {
  8153. var me = this,
  8154. $doc = me.defaultOpt.$doc;
  8155. switch (e.type) {
  8156. case 'mousedown':
  8157. var hand = e.target || e.srcElement, hand;
  8158. if (hand.className.indexOf('edui-scale-hand') != -1) {
  8159. me.dragId = hand.className.slice(-1);
  8160. me.startPos.x = me.prePos.x = e.clientX;
  8161. me.startPos.y = me.prePos.y = e.clientY;
  8162. $doc.bind('mousemove', $.proxy(me._eventHandler, me));
  8163. }
  8164. break;
  8165. case 'mousemove':
  8166. if (me.dragId != -1) {
  8167. me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
  8168. me.prePos.x = e.clientX;
  8169. me.prePos.y = e.clientY;
  8170. me.updateTargetElement();
  8171. }
  8172. break;
  8173. case 'mouseup':
  8174. if (me.dragId != -1) {
  8175. me.dragId = -1;
  8176. me.updateTargetElement();
  8177. var $target = me.data('$scaleTarget');
  8178. if ($target.parent()) me.attachTo(me.data('$scaleTarget'));
  8179. }
  8180. $doc.unbind('mousemove', $.proxy(me._eventHandler, me));
  8181. break;
  8182. default:
  8183. break;
  8184. }
  8185. },
  8186. updateTargetElement: function () {
  8187. var me = this,
  8188. $root = me.root(),
  8189. $target = me.data('$scaleTarget');
  8190. $target.css({width: $root.width(), height: $root.height()});
  8191. me.attachTo($target);
  8192. },
  8193. updateContainerStyle: function (dir, offset) {
  8194. var me = this,
  8195. $dom = me.root(),
  8196. tmp,
  8197. rect = [
  8198. //[left, top, width, height]
  8199. [0, 0, -1, -1],
  8200. [0, 0, 0, -1],
  8201. [0, 0, 1, -1],
  8202. [0, 0, -1, 0],
  8203. [0, 0, 1, 0],
  8204. [0, 0, -1, 1],
  8205. [0, 0, 0, 1],
  8206. [0, 0, 1, 1]
  8207. ];
  8208. if (rect[dir][0] != 0) {
  8209. tmp = parseInt($dom.offset().left) + offset.x;
  8210. $dom.css('left', me._validScaledProp('left', tmp));
  8211. }
  8212. if (rect[dir][1] != 0) {
  8213. tmp = parseInt($dom.offset().top) + offset.y;
  8214. $dom.css('top', me._validScaledProp('top', tmp));
  8215. }
  8216. if (rect[dir][2] != 0) {
  8217. tmp = $dom.width() + rect[dir][2] * offset.x;
  8218. $dom.css('width', me._validScaledProp('width', tmp));
  8219. }
  8220. if (rect[dir][3] != 0) {
  8221. tmp = $dom.height() + rect[dir][3] * offset.y;
  8222. $dom.css('height', me._validScaledProp('height', tmp));
  8223. }
  8224. },
  8225. _validScaledProp: function (prop, value) {
  8226. var $ele = this.root(),
  8227. $wrap = this.defaultOpt.$doc,
  8228. calc = function(val, a, b){
  8229. return (val + a) > b ? b - a : value;
  8230. };
  8231. value = isNaN(value) ? 0 : value;
  8232. switch (prop) {
  8233. case 'left':
  8234. return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());
  8235. case 'top':
  8236. return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height());
  8237. case 'width':
  8238. return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());
  8239. case 'height':
  8240. return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());
  8241. }
  8242. },
  8243. show: function ($obj) {
  8244. var me = this;
  8245. if ($obj) me.attachTo($obj);
  8246. me.root().bind('mousedown', $.proxy(me._eventHandler, me));
  8247. me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));
  8248. me.root().show();
  8249. me.trigger("aftershow");
  8250. },
  8251. hide: function () {
  8252. var me = this;
  8253. me.root().unbind('mousedown', $.proxy(me._eventHandler, me));
  8254. me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));
  8255. me.root().hide();
  8256. me.trigger('afterhide')
  8257. },
  8258. attachTo: function ($obj) {
  8259. var me = this,
  8260. imgPos = $obj.offset(),
  8261. $root = me.root(),
  8262. $wrap = me.defaultOpt.$wrap,
  8263. posObj = $wrap.offset();
  8264. me.data('$scaleTarget', $obj);
  8265. me.root().css({
  8266. position: 'absolute',
  8267. width: $obj.width(),
  8268. height: $obj.height(),
  8269. left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),
  8270. top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))
  8271. });
  8272. },
  8273. getScaleTarget: function () {
  8274. return this.data('$scaleTarget')[0];
  8275. }
  8276. });
  8277. //colorpicker 类
  8278. UM.ui.define('colorpicker', {
  8279. tpl: function (opt) {
  8280. var COLORS = (
  8281. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  8282. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  8283. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  8284. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  8285. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  8286. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  8287. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  8288. var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if (name){%> edui-colorpicker-<%=name%><%}%>" >' +
  8289. '<table unselectable="on" onmousedown="return false">' +
  8290. '<tr><td colspan="10">'+opt.lang_themeColor+'</td> </tr>' +
  8291. '<tr class="edui-colorpicker-firstrow" >';
  8292. for (var i = 0; i < COLORS.length; i++) {
  8293. if (i && i % 10 === 0) {
  8294. html += '</tr>' + (i == 60 ? '<tr><td colspan="10">'+opt.lang_standardColor+'</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';
  8295. }
  8296. html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +
  8297. ' data-color="#' + COLORS[i] + '"' +
  8298. ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
  8299. (i < 10 || i >= 60 ? 'border-width:1px;' :
  8300. i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
  8301. 'border-width:0 1px 0 1px;') +
  8302. '"' +
  8303. '></a></td>' : '';
  8304. }
  8305. html += '</tr></table></div>';
  8306. return html;
  8307. },
  8308. init: function (options) {
  8309. var me = this;
  8310. me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)),options)));
  8311. me.root().on("click",function (e) {
  8312. me.trigger('pickcolor', $(e.target).data('color'));
  8313. });
  8314. }
  8315. }, 'popup');
  8316. /**
  8317. * Created with JetBrains PhpStorm.
  8318. * User: hn
  8319. * Date: 13-5-29
  8320. * Time: 下午8:01
  8321. * To change this template use File | Settings | File Templates.
  8322. */
  8323. (function(){
  8324. var widgetName = 'combobox',
  8325. itemClassName = 'edui-combobox-item',
  8326. HOVER_CLASS = 'edui-combobox-item-hover',
  8327. ICON_CLASS = 'edui-combobox-checked-icon',
  8328. labelClassName = 'edui-combobox-item-label';
  8329. UM.ui.define( widgetName, ( function(){
  8330. return {
  8331. tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if (comboboxName!=='') {%> edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +
  8332. "<%if(autoRecord) {%>" +
  8333. "<%for( var i=0, len = recordStack.length; i<len; i++ ) {%>" +
  8334. "<%var index = recordStack[i];%>" +
  8335. "<li class=\"<%=itemClassName%><%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" +
  8336. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8337. "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ index ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=items[index]%></label>" +
  8338. "</li>" +
  8339. "<%}%>" +
  8340. "<%if( i ) {%>" +
  8341. "<li class=\"edui-combobox-item-separator\"></li>" +
  8342. "<%}%>" +
  8343. "<%}%>" +
  8344. "<%for( var i=0, label; label = items[i]; i++ ) {%>" +
  8345. "<li class=\"<%=itemClassName%><%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" +
  8346. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8347. "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ i ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=label%></label>" +
  8348. "</li>" +
  8349. "<%}%>" +
  8350. "</ul>",
  8351. defaultOpt: {
  8352. //记录栈初始列表
  8353. recordStack: [],
  8354. //可用项列表
  8355. items: [],
  8356. //item对应的值列表
  8357. value: [],
  8358. comboboxName: '',
  8359. selected: '',
  8360. //自动记录
  8361. autoRecord: true,
  8362. //最多记录条数
  8363. recordCount: 5
  8364. },
  8365. init: function( options ){
  8366. var me = this;
  8367. $.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), {
  8368. itemClassName: itemClassName,
  8369. iconClass: ICON_CLASS,
  8370. labelClassName: labelClassName
  8371. } );
  8372. this._transStack( options );
  8373. me.root( $( $.parseTmpl( me.tpl, options ) ) );
  8374. this.data( 'options', options ).initEvent();
  8375. },
  8376. initEvent: function(){
  8377. var me = this;
  8378. me.initSelectItem();
  8379. this.initItemActive();
  8380. },
  8381. /**
  8382. * 初始化选择项
  8383. */
  8384. initSelectItem: function(){
  8385. var me = this,
  8386. labelClass = "."+labelClassName;
  8387. me.root().delegate('.' + itemClassName, 'click', function(){
  8388. var $li = $(this),
  8389. index = $li.attr('data-item-index');
  8390. me.trigger('comboboxselect', {
  8391. index: index,
  8392. label: $li.find(labelClass).text(),
  8393. value: me.data('options').value[ index ]
  8394. }).select( index );
  8395. me.hide();
  8396. return false;
  8397. });
  8398. },
  8399. initItemActive: function(){
  8400. var fn = {
  8401. mouseenter: 'addClass',
  8402. mouseleave: 'removeClass'
  8403. };
  8404. if ($.IE6) {
  8405. this.root().delegate( '.'+itemClassName, 'mouseenter mouseleave', function( evt ){
  8406. $(this)[ fn[ evt.type ] ]( HOVER_CLASS );
  8407. }).one('afterhide', function(){
  8408. });
  8409. }
  8410. },
  8411. /**
  8412. * 选择给定索引的项
  8413. * @param index 项索引
  8414. * @returns {*} 如果存在对应索引的项,则返回该项;否则返回null
  8415. */
  8416. select: function( index ){
  8417. var itemCount = this.data('options').itemCount,
  8418. items = this.data('options').autowidthitem;
  8419. if ( items && !items.length ) {
  8420. items = this.data('options').items;
  8421. }
  8422. if( itemCount == 0 ) {
  8423. return null;
  8424. }
  8425. if( index < 0 ) {
  8426. index = itemCount + index % itemCount;
  8427. } else if ( index >= itemCount ) {
  8428. index = itemCount-1;
  8429. }
  8430. this.trigger( 'changebefore', items[ index ] );
  8431. this._update( index );
  8432. this.trigger( 'changeafter', items[ index ] );
  8433. return null;
  8434. },
  8435. selectItemByLabel: function( label ){
  8436. var itemMapping = this.data('options').itemMapping,
  8437. me = this,
  8438. index = null;
  8439. !$.isArray( label ) && ( label = [ label ] );
  8440. $.each( label, function( i, item ){
  8441. index = itemMapping[ item ];
  8442. if( index !== undefined ) {
  8443. me.select( index );
  8444. return false;
  8445. }
  8446. } );
  8447. },
  8448. /**
  8449. * 转换记录栈
  8450. */
  8451. _transStack: function( options ) {
  8452. var temp = [],
  8453. itemIndex = -1,
  8454. selected = -1;
  8455. $.each( options.recordStack, function( index, item ){
  8456. itemIndex = options.itemMapping[ item ];
  8457. if( $.isNumeric( itemIndex ) ) {
  8458. temp.push( itemIndex );
  8459. //selected的合法性检测
  8460. if( item == options.selected ) {
  8461. selected = itemIndex;
  8462. }
  8463. }
  8464. } );
  8465. options.recordStack = temp;
  8466. options.selected = selected;
  8467. temp = null;
  8468. },
  8469. _optionAdaptation: function( options ) {
  8470. if( !( 'itemStyles' in options ) ) {
  8471. options.itemStyles = [];
  8472. for( var i = 0, len = options.items.length; i < len; i++ ) {
  8473. options.itemStyles.push('');
  8474. }
  8475. }
  8476. options.autowidthitem = options.autowidthitem || options.items;
  8477. options.itemCount = options.items.length;
  8478. return options;
  8479. },
  8480. _createItemMapping: function( stackItem, items ){
  8481. var temp = {},
  8482. result = {
  8483. recordStack: [],
  8484. mapping: {}
  8485. };
  8486. $.each( items, function( index, item ){
  8487. temp[ item ] = index;
  8488. } );
  8489. result.itemMapping = temp;
  8490. $.each( stackItem, function( index, item ){
  8491. if( temp[ item ] !== undefined ) {
  8492. result.recordStack.push( temp[ item ] );
  8493. result.mapping[ item ] = temp[ item ];
  8494. }
  8495. } );
  8496. return result;
  8497. },
  8498. _update: function ( index ) {
  8499. var options = this.data("options"),
  8500. newStack = [],
  8501. newChilds = null;
  8502. $.each( options.recordStack, function( i, item ){
  8503. if( item != index ) {
  8504. newStack.push( item );
  8505. }
  8506. } );
  8507. //压入最新的记录
  8508. newStack.unshift( index );
  8509. if( newStack.length > options.recordCount ) {
  8510. newStack.length = options.recordCount;
  8511. }
  8512. options.recordStack = newStack;
  8513. options.selected = index;
  8514. newChilds = $( $.parseTmpl( this.tpl, options ) );
  8515. //重新渲染
  8516. this.root().html( newChilds.html() );
  8517. newChilds = null;
  8518. newStack = null;
  8519. }
  8520. };
  8521. } )(), 'menu' );
  8522. })();
  8523. /**
  8524. * Combox 抽象基类
  8525. * User: hn
  8526. * Date: 13-5-29
  8527. * Time: 下午8:01
  8528. * To change this template use File | Settings | File Templates.
  8529. */
  8530. (function(){
  8531. var widgetName = 'buttoncombobox';
  8532. UM.ui.define( widgetName, ( function(){
  8533. return {
  8534. defaultOpt: {
  8535. //按钮初始文字
  8536. label: '',
  8537. title: ''
  8538. },
  8539. init: function( options ) {
  8540. var me = this;
  8541. var btnWidget = $.eduibutton({
  8542. caret: true,
  8543. name: options.comboboxName,
  8544. title: options.title,
  8545. text: options.label,
  8546. click: function(){
  8547. me.show( this.root() );
  8548. }
  8549. });
  8550. me.supper.init.call( me, options );
  8551. //监听change, 改变button显示内容
  8552. me.on('changebefore', function( e, label ){
  8553. btnWidget.eduibutton('label', label );
  8554. });
  8555. me.data( 'button', btnWidget );
  8556. me.attachTo(btnWidget)
  8557. },
  8558. button: function(){
  8559. return this.data( 'button' );
  8560. }
  8561. }
  8562. } )(), 'combobox' );
  8563. })();
  8564. /*modal 类*/
  8565. UM.ui.define('modal', {
  8566. tpl: '<div class="edui-modal" tabindex="-1" >' +
  8567. '<div class="edui-modal-header">' +
  8568. '<div class="edui-close" data-hide="modal"></div>' +
  8569. '<h3 class="edui-title"><%=title%></h3>' +
  8570. '</div>' +
  8571. '<div class="edui-modal-body" style="<%if(width){%>width:<%=width%>px;<%}%>' +
  8572. '<%if(height){%>height:<%=height%>px;<%}%>">' +
  8573. ' </div>' +
  8574. '<% if(cancellabel || oklabel) {%>' +
  8575. '<div class="edui-modal-footer">' +
  8576. '<div class="edui-modal-tip"></div>' +
  8577. '<%if(oklabel){%><div class="edui-btn edui-btn-primary" data-ok="modal"><%=oklabel%></div><%}%>' +
  8578. '<%if(cancellabel){%><div class="edui-btn" data-hide="modal"><%=cancellabel%></div><%}%>' +
  8579. '</div>' +
  8580. '<%}%></div>',
  8581. defaultOpt: {
  8582. title: "",
  8583. cancellabel: "",
  8584. oklabel: "",
  8585. width: '',
  8586. height: '',
  8587. backdrop: true,
  8588. keyboard: true
  8589. },
  8590. init: function (options) {
  8591. var me = this;
  8592. me.root($($.parseTmpl(me.tpl, options || {})));
  8593. me.data("options", options);
  8594. if (options.okFn) {
  8595. me.on('ok', $.proxy(options.okFn, me))
  8596. }
  8597. if (options.cancelFn) {
  8598. me.on('beforehide', $.proxy(options.cancelFn, me))
  8599. }
  8600. me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))
  8601. .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));
  8602. $('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){
  8603. $(this).toggleClass('edui-hover')
  8604. });
  8605. },
  8606. toggle: function () {
  8607. var me = this;
  8608. return me[!me.data("isShown") ? 'show' : 'hide']();
  8609. },
  8610. show: function () {
  8611. var me = this;
  8612. me.trigger("beforeshow");
  8613. if (me.data("isShown")) return;
  8614. me.data("isShown", true);
  8615. me.escape();
  8616. me.backdrop(function () {
  8617. me.autoCenter();
  8618. me.root()
  8619. .show()
  8620. .focus()
  8621. .trigger('aftershow');
  8622. })
  8623. },
  8624. showTip: function ( text ) {
  8625. $( '.edui-modal-tip', this.root() ).html( text ).fadeIn();
  8626. },
  8627. hideTip: function ( text ) {
  8628. $( '.edui-modal-tip', this.root() ).fadeOut( function (){
  8629. $(this).html('');
  8630. } );
  8631. },
  8632. autoCenter: function () {
  8633. //ie6下不用处理了
  8634. !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));
  8635. },
  8636. hide: function () {
  8637. var me = this;
  8638. me.trigger("beforehide");
  8639. if (!me.data("isShown")) return;
  8640. me.data("isShown", false);
  8641. me.escape();
  8642. me.hideModal();
  8643. },
  8644. escape: function () {
  8645. var me = this;
  8646. if (me.data("isShown") && me.data("options").keyboard) {
  8647. me.root().on('keyup', function (e) {
  8648. e.which == 27 && me.hide();
  8649. })
  8650. }
  8651. else if (!me.data("isShown")) {
  8652. me.root().off('keyup');
  8653. }
  8654. },
  8655. hideModal: function () {
  8656. var me = this;
  8657. me.root().hide();
  8658. me.backdrop(function () {
  8659. me.removeBackdrop();
  8660. me.trigger('afterhide');
  8661. })
  8662. },
  8663. removeBackdrop: function () {
  8664. this.$backdrop && this.$backdrop.remove();
  8665. this.$backdrop = null;
  8666. },
  8667. backdrop: function (callback) {
  8668. var me = this;
  8669. if (me.data("isShown") && me.data("options").backdrop) {
  8670. me.$backdrop = $('<div class="edui-modal-backdrop" />').click(
  8671. me.data("options").backdrop == 'static' ?
  8672. $.proxy(me.root()[0].focus, me.root()[0])
  8673. : $.proxy(me.hide, me)
  8674. )
  8675. }
  8676. me.trigger('afterbackdrop');
  8677. callback && callback();
  8678. },
  8679. attachTo: function ($obj) {
  8680. var me = this
  8681. if (!$obj.data('$mergeObj')) {
  8682. $obj.data('$mergeObj', me.root());
  8683. $obj.on('click', function () {
  8684. me.toggle($obj)
  8685. });
  8686. me.data('$mergeObj', $obj)
  8687. }
  8688. },
  8689. ok: function () {
  8690. var me = this;
  8691. me.trigger('beforeok');
  8692. if (me.trigger("ok", me) === false) {
  8693. return;
  8694. }
  8695. me.hide();
  8696. },
  8697. getBodyContainer: function () {
  8698. return this.root().find('.edui-modal-body')
  8699. }
  8700. });
  8701. /*tooltip 类*/
  8702. UM.ui.define('tooltip', {
  8703. tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +
  8704. '<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +
  8705. '<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +
  8706. '</div>',
  8707. init: function (options) {
  8708. var me = this;
  8709. me.root($($.parseTmpl(me.tpl, options || {})));
  8710. },
  8711. content: function (e) {
  8712. var me = this,
  8713. title = $(e.currentTarget).attr("data-original-title");
  8714. me.root().find('.edui-tooltip-inner')['text'](title);
  8715. },
  8716. position: function (e) {
  8717. var me = this,
  8718. $obj = $(e.currentTarget);
  8719. me.root().css($.extend({display: 'block'}, $obj ? {
  8720. top: $obj.outerHeight(),
  8721. left: (($obj.outerWidth() - me.root().outerWidth()) / 2)
  8722. } : {}))
  8723. },
  8724. show: function (e) {
  8725. if ($(e.currentTarget).hasClass('edui-disabled')) return;
  8726. var me = this;
  8727. me.content(e);
  8728. me.root().appendTo($(e.currentTarget));
  8729. me.position(e);
  8730. me.root().css('display', 'block');
  8731. },
  8732. hide: function () {
  8733. var me = this;
  8734. me.root().css('display', 'none')
  8735. },
  8736. attachTo: function ($obj) {
  8737. var me = this;
  8738. function tmp($obj) {
  8739. var me = this;
  8740. if (!$.contains(document.body, me.root()[0])) {
  8741. me.root().appendTo($obj);
  8742. }
  8743. me.data('tooltip', me.root());
  8744. $obj.each(function () {
  8745. if ($(this).attr("data-original-title")) {
  8746. $(this).on('mouseenter', $.proxy(me.show, me))
  8747. .on('mouseleave click', $.proxy(me.hide, me))
  8748. }
  8749. });
  8750. }
  8751. if ($.type($obj) === "undefined") {
  8752. $("[data-original-title]").each(function (i, el) {
  8753. tmp.call(me, $(el));
  8754. })
  8755. } else {
  8756. if (!$obj.data('tooltip')) {
  8757. tmp.call(me, $obj);
  8758. }
  8759. }
  8760. }
  8761. });
  8762. /*tab 类*/
  8763. UM.ui.define('tab', {
  8764. init: function (options) {
  8765. var me = this,
  8766. slr = options.selector;
  8767. if ($.type(slr)) {
  8768. me.root($(slr, options.context));
  8769. me.data("context", options.context);
  8770. $(slr, me.data("context")).on('click', function (e) {
  8771. me.show(e);
  8772. });
  8773. }
  8774. },
  8775. show: function (e) {
  8776. var me = this,
  8777. $cur = $(e.target),
  8778. $ul = $cur.closest('ul'),
  8779. selector,
  8780. previous,
  8781. $target,
  8782. e;
  8783. selector = $cur.attr('data-context');
  8784. selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
  8785. var $tmp = $cur.parent('li');
  8786. if (!$tmp.length || $tmp.hasClass('edui-active')) return;
  8787. previous = $ul.find('.edui-active:last a')[0];
  8788. e = $.Event('beforeshow', {
  8789. target: $cur[0],
  8790. relatedTarget: previous
  8791. });
  8792. me.trigger(e);
  8793. if (e.isDefaultPrevented()) return;
  8794. $target = $(selector, me.data("context"));
  8795. me.activate($cur.parent('li'), $ul);
  8796. me.activate($target, $target.parent(), function () {
  8797. me.trigger({
  8798. type: 'aftershow', relatedTarget: previous
  8799. })
  8800. });
  8801. },
  8802. activate: function (element, container, callback) {
  8803. if (element === undefined) {
  8804. return $(".edui-tab-item.edui-active",this.root()).index();
  8805. }
  8806. var $active = container.find('> .edui-active');
  8807. $active.removeClass('edui-active');
  8808. element.addClass('edui-active');
  8809. callback && callback();
  8810. }
  8811. });
  8812. //button 类
  8813. UM.ui.define('separator', {
  8814. tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',
  8815. init: function (options) {
  8816. var me = this;
  8817. me.root($($.parseTmpl(me.tpl, options)));
  8818. return me;
  8819. }
  8820. });
  8821. /**
  8822. * @file adapter.js
  8823. * @desc adapt ui to editor
  8824. * @import core/Editor.js, core/utils.js
  8825. */
  8826. (function () {
  8827. var _editorUI = {},
  8828. _editors = {},
  8829. _readyFn = [],
  8830. _activeWidget = null,
  8831. _widgetData = {},
  8832. _widgetCallBack = {},
  8833. _cacheUI = {},
  8834. _maxZIndex = null;
  8835. utils.extend(UM, {
  8836. defaultWidth : 500,
  8837. defaultHeight : 500,
  8838. registerUI: function (name, fn) {
  8839. utils.each(name.split(/\s+/), function (uiname) {
  8840. _editorUI[uiname] = fn;
  8841. })
  8842. },
  8843. setEditor : function(editor){
  8844. !_editors[editor.id] && (_editors[editor.id] = editor);
  8845. },
  8846. registerWidget : function(name,pro,cb){
  8847. _widgetData[name] = $.extend2(pro,{
  8848. $root : '',
  8849. _preventDefault:false,
  8850. root:function($el){
  8851. return this.$root || (this.$root = $el);
  8852. },
  8853. preventDefault:function(){
  8854. this._preventDefault = true;
  8855. },
  8856. clear:false
  8857. });
  8858. if(cb){
  8859. _widgetCallBack[name] = cb;
  8860. }
  8861. },
  8862. getWidgetData : function(name){
  8863. return _widgetData[name]
  8864. },
  8865. setWidgetBody : function(name,$widget,editor){
  8866. if(!editor._widgetData){
  8867. utils.extend(editor,{
  8868. _widgetData : {},
  8869. getWidgetData : function(name){
  8870. return this._widgetData[name];
  8871. },
  8872. getWidgetCallback : function(widgetName){
  8873. var me = this;
  8874. return function(){
  8875. return _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0)))
  8876. }
  8877. }
  8878. })
  8879. }
  8880. var pro = _widgetData[name];
  8881. if(!pro){
  8882. return null;
  8883. }
  8884. pro = editor._widgetData[name];
  8885. if(!pro){
  8886. pro = _widgetData[name];
  8887. pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);
  8888. }
  8889. pro.root($widget.edui().getBodyContainer());
  8890. pro.initContent(editor,$widget);
  8891. if(!pro._preventDefault){
  8892. pro.initEvent(editor,$widget);
  8893. }
  8894. pro.width && $widget.width(pro.width);
  8895. },
  8896. setActiveWidget : function($widget){
  8897. _activeWidget = $widget;
  8898. },
  8899. getEditor: function (id, options) {
  8900. var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));
  8901. _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex');
  8902. return editor;
  8903. },
  8904. setTopEditor: function(editor){
  8905. $.each(_editors, function(i, o){
  8906. if(editor == o) {
  8907. editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);
  8908. } else {
  8909. o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));
  8910. }
  8911. });
  8912. },
  8913. clearCache : function(id){
  8914. if ( _editors[id]) {
  8915. delete _editors[id]
  8916. }
  8917. },
  8918. delEditor: function (id) {
  8919. var editor;
  8920. if (editor = _editors[id]) {
  8921. editor.destroy();
  8922. }
  8923. },
  8924. ready: function( fn ){
  8925. _readyFn.push( fn );
  8926. },
  8927. createEditor: function (id, opt) {
  8928. var editor = new UM.Editor(opt);
  8929. var T = this;
  8930. editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T));
  8931. function renderUI(){
  8932. var $container = this.createUI('#' + id, editor);
  8933. editor.key=id;
  8934. editor.ready(function(){
  8935. $.each( _readyFn, function( index, fn ){
  8936. $.proxy( fn, editor )();
  8937. } );
  8938. });
  8939. var options = editor.options;
  8940. if(options.initialFrameWidth){
  8941. options.minFrameWidth = options.initialFrameWidth
  8942. }else{
  8943. options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth;
  8944. }
  8945. $container.css({
  8946. width: options.initialFrameWidth,
  8947. zIndex:editor.getOpt('zIndex')
  8948. });
  8949. //ie6下缓存图片
  8950. UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);
  8951. editor.render(id);
  8952. //添加tooltip;
  8953. $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1);
  8954. $container.find('a').click(function(evt){
  8955. evt.preventDefault()
  8956. });
  8957. editor.fireEvent("afteruiready");
  8958. }
  8959. return editor;
  8960. },
  8961. createUI: function (id, editor) {
  8962. var $editorCont = $(id),
  8963. $container = $('<div class="edui-container"><div class="edui-editor-body"></div></div>').insertBefore($editorCont);
  8964. editor.$container = $container;
  8965. editor.container = $container[0];
  8966. editor.$body = $editorCont;
  8967. //修正在ie9+以上的版本中,自动长高收起时的,残影问题
  8968. if(browser.ie && browser.ie9above){
  8969. var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');
  8970. $span.insertAfter($container);
  8971. }
  8972. //初始化注册的ui组件
  8973. $.each(_editorUI,function(n,v){
  8974. var widget = v.call(editor,n);
  8975. if(widget){
  8976. _cacheUI[n] = widget;
  8977. }
  8978. });
  8979. $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));
  8980. $container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));
  8981. return $container;
  8982. },
  8983. createToolbar: function (options, editor) {
  8984. var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();
  8985. //创建下来菜单列表
  8986. if (options.toolbar && options.toolbar.length) {
  8987. var btns = [];
  8988. $.each(options.toolbar,function(i,uiNames){
  8989. $.each(uiNames.split(/\s+/),function(index,name){
  8990. if(name == '|'){
  8991. $.eduiseparator && btns.push($.eduiseparator());
  8992. }else{
  8993. var ui = _cacheUI[name];
  8994. if(name=="fullscreen"){
  8995. ui&&btns.unshift(ui);
  8996. }else{
  8997. ui && btns.push(ui);
  8998. }
  8999. }
  9000. });
  9001. btns.length && toolbar.appendToBtnmenu(btns);
  9002. });
  9003. } else {
  9004. $toolbar.find('.edui-btn-toolbar').remove()
  9005. }
  9006. return $toolbar;
  9007. }
  9008. })
  9009. })();
  9010. UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +
  9011. 'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',
  9012. function(name) {
  9013. var me = this;
  9014. var $btn = $.eduibutton({
  9015. icon : name,
  9016. click : function(){
  9017. me.execCommand(name);
  9018. },
  9019. title: this.getLang('labelMap')[name] || ''
  9020. });
  9021. this.addListener('selectionchange',function(){
  9022. var state = this.queryCommandState(name);
  9023. $btn.edui().disabled(state == -1).active(state == 1)
  9024. });
  9025. return $btn;
  9026. }
  9027. );
  9028. /**
  9029. * 全屏组件
  9030. */
  9031. (function(){
  9032. //状态缓存
  9033. var STATUS_CACHE = {},
  9034. //状态值列表
  9035. STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ],
  9036. CONTENT_AREA_STATUS = {},
  9037. //页面状态
  9038. DOCUMENT_STATUS = {},
  9039. DOCUMENT_ELEMENT_STATUS = {},
  9040. FULLSCREENS = {};
  9041. UM.registerUI('fullscreen', function( name ){
  9042. var me = this,
  9043. $button = $.eduibutton({
  9044. 'icon': 'fullscreen',
  9045. 'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9046. 'click': function(){
  9047. //切换
  9048. me.execCommand( name );
  9049. UM.setTopEditor(me);
  9050. }
  9051. });
  9052. me.addListener( "selectionchange", function () {
  9053. var state = this.queryCommandState( name );
  9054. $button.edui().disabled( state == -1 ).active( state == 1 );
  9055. } );
  9056. //切换至全屏
  9057. me.addListener('ready', function(){
  9058. me.options.fullscreen && Fullscreen.getInstance( me ).toggle();
  9059. });
  9060. return $button;
  9061. });
  9062. UM.commands[ 'fullscreen' ] = {
  9063. execCommand: function (cmdName) {
  9064. Fullscreen.getInstance( this ).toggle();
  9065. },
  9066. queryCommandState: function (cmdName) {
  9067. return this._edui_fullscreen_status;
  9068. },
  9069. notNeedUndo: 1
  9070. };
  9071. function Fullscreen( editor ) {
  9072. var me = this;
  9073. if( !editor ) {
  9074. throw new Error('invalid params, notfound editor');
  9075. }
  9076. me.editor = editor;
  9077. //记录初始化的全屏组件
  9078. FULLSCREENS[ editor.uid ] = this;
  9079. editor.addListener('destroy', function(){
  9080. delete FULLSCREENS[ editor.uid ];
  9081. me.editor = null;
  9082. });
  9083. }
  9084. Fullscreen.prototype = {
  9085. /**
  9086. * 全屏状态切换
  9087. */
  9088. toggle: function(){
  9089. var editor = this.editor,
  9090. //当前编辑器的缩放状态
  9091. _edui_fullscreen_status = this.isFullState();
  9092. editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status );
  9093. //更新状态
  9094. this.update( !_edui_fullscreen_status );
  9095. !_edui_fullscreen_status ? this.enlarge() : this.revert();
  9096. editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status );
  9097. if(editor.body.contentEditable === 'true'){
  9098. editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status );
  9099. }
  9100. editor.fireEvent( 'selectionchange' );
  9101. },
  9102. /**
  9103. * 执行放大
  9104. */
  9105. enlarge: function(){
  9106. this.saveSataus();
  9107. this.setDocumentStatus();
  9108. this.resize();
  9109. },
  9110. /**
  9111. * 全屏还原
  9112. */
  9113. revert: function(){
  9114. //还原CSS表达式
  9115. var options = this.editor.options,
  9116. height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));
  9117. $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  9118. //还原容器状态
  9119. this.revertContainerStatus();
  9120. this.revertContentAreaStatus();
  9121. this.revertDocumentStatus();
  9122. },
  9123. /**
  9124. * 更新状态
  9125. * @param isFull 当前状态是否是全屏状态
  9126. */
  9127. update: function( isFull ) {
  9128. this.editor._edui_fullscreen_status = isFull;
  9129. },
  9130. /**
  9131. * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态, 则不做调整
  9132. */
  9133. resize: function(){
  9134. var $win = null,
  9135. height = 0,
  9136. width = 0,
  9137. borderWidth = 0,
  9138. paddingWidth = 0,
  9139. editor = this.editor,
  9140. me = this,
  9141. bound = null,
  9142. editorBody = null;
  9143. if( !this.isFullState() ) {
  9144. return;
  9145. }
  9146. $win = $( window );
  9147. width = $win.width();
  9148. height = $win.height();
  9149. editorBody = this.getEditorHolder();
  9150. //文本编辑区border宽度
  9151. borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0;
  9152. //容器border宽度
  9153. borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0;
  9154. //容器padding
  9155. paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0;
  9156. //干掉css表达式
  9157. $.IE6 && editorBody.style.setExpression( 'height', null );
  9158. bound = this.getBound();
  9159. $( editor.container ).css( {
  9160. width: width + 'px',
  9161. height: height + 'px',
  9162. position: !$.IE6 ? 'fixed' : 'absolute',
  9163. top: bound.top,
  9164. left: bound.left,
  9165. margin: 0,
  9166. padding: 0,
  9167. overflowX: 'hidden',
  9168. overflowY: 'hidden'
  9169. } );
  9170. $( editorBody ).css({
  9171. width: width - 2*borderWidth - paddingWidth + 'px',
  9172. height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px',
  9173. overflowX: 'hidden',
  9174. overflowY: 'auto'
  9175. });
  9176. },
  9177. /**
  9178. * 保存状态
  9179. */
  9180. saveSataus: function(){
  9181. var styles = this.editor.container.style,
  9182. tmp = null,
  9183. cache = {};
  9184. for( var i= 0, len = STATUS_LIST.length; i<len; i++ ) {
  9185. tmp = STATUS_LIST[ i ];
  9186. cache[ tmp ] = styles[ tmp ];
  9187. }
  9188. STATUS_CACHE[ this.editor.uid ] = cache;
  9189. this.saveContentAreaStatus();
  9190. this.saveDocumentStatus();
  9191. },
  9192. saveContentAreaStatus: function(){
  9193. var $holder = $(this.getEditorHolder());
  9194. CONTENT_AREA_STATUS[ this.editor.uid ] = {
  9195. width: $holder.css("width"),
  9196. overflowX: $holder.css("overflowX"),
  9197. overflowY: $holder.css("overflowY"),
  9198. height: $holder.css("height")
  9199. };
  9200. },
  9201. /**
  9202. * 保存与指定editor相关的页面的状态
  9203. */
  9204. saveDocumentStatus: function(){
  9205. var $doc = $( this.getEditorDocumentBody() );
  9206. DOCUMENT_STATUS[ this.editor.uid ] = {
  9207. overflowX: $doc.css( 'overflowX' ),
  9208. overflowY: $doc.css( 'overflowY' )
  9209. };
  9210. DOCUMENT_ELEMENT_STATUS[ this.editor.uid ] = {
  9211. overflowX: $( this.getEditorDocumentElement() ).css( 'overflowX'),
  9212. overflowY: $( this.getEditorDocumentElement() ).css( 'overflowY' )
  9213. };
  9214. },
  9215. /**
  9216. * 恢复容器状态
  9217. */
  9218. revertContainerStatus: function(){
  9219. $( this.editor.container ).css( this.getEditorStatus() );
  9220. },
  9221. /**
  9222. * 恢复编辑区状态
  9223. */
  9224. revertContentAreaStatus: function(){
  9225. var holder = this.getEditorHolder(),
  9226. state = this.getContentAreaStatus();
  9227. if ( this.supportMin() ) {
  9228. delete state.height;
  9229. holder.style.height = null;
  9230. }
  9231. $( holder ).css( state );
  9232. },
  9233. /**
  9234. * 恢复页面状态
  9235. */
  9236. revertDocumentStatus: function() {
  9237. var status = this.getDocumentStatus();
  9238. $( this.getEditorDocumentBody() ).css( 'overflowX', status.body.overflowX );
  9239. $( this.getEditorDocumentElement() ).css( 'overflowY', status.html.overflowY );
  9240. },
  9241. setDocumentStatus: function(){
  9242. $(this.getEditorDocumentBody()).css( {
  9243. overflowX: 'hidden',
  9244. overflowY: 'hidden'
  9245. } );
  9246. $(this.getEditorDocumentElement()).css( {
  9247. overflowX: 'hidden',
  9248. overflowY: 'hidden'
  9249. } );
  9250. },
  9251. /**
  9252. * 检测当前编辑器是否处于全屏状态全屏状态
  9253. * @returns {boolean} 是否处于全屏状态
  9254. */
  9255. isFullState: function(){
  9256. return !!this.editor._edui_fullscreen_status;
  9257. },
  9258. /**
  9259. * 获取编辑器状态
  9260. */
  9261. getEditorStatus: function(){
  9262. return STATUS_CACHE[ this.editor.uid ];
  9263. },
  9264. getContentAreaStatus: function(){
  9265. return CONTENT_AREA_STATUS[ this.editor.uid ];
  9266. },
  9267. getEditorDocumentElement: function(){
  9268. return this.editor.container.ownerDocument.documentElement;
  9269. },
  9270. getEditorDocumentBody: function(){
  9271. return this.editor.container.ownerDocument.body;
  9272. },
  9273. /**
  9274. * 获取编辑区包裹对象
  9275. */
  9276. getEditorHolder: function(){
  9277. return this.editor.body;
  9278. },
  9279. /**
  9280. * 获取编辑器状态
  9281. * @returns {*}
  9282. */
  9283. getDocumentStatus: function(){
  9284. return {
  9285. 'body': DOCUMENT_STATUS[ this.editor.uid ],
  9286. 'html': DOCUMENT_ELEMENT_STATUS[ this.editor.uid ]
  9287. };
  9288. },
  9289. supportMin: function () {
  9290. var node = null;
  9291. if ( !this._support ) {
  9292. node = document.createElement("div");
  9293. this._support = "minWidth" in node.style;
  9294. node = null;
  9295. }
  9296. return this._support;
  9297. },
  9298. getBound: function () {
  9299. var tags = {
  9300. html: true,
  9301. body: true
  9302. },
  9303. result = {
  9304. top: 0,
  9305. left: 0
  9306. },
  9307. offsetParent = null;
  9308. if ( !$.IE6 ) {
  9309. return result;
  9310. }
  9311. offsetParent = this.editor.container.offsetParent;
  9312. if( offsetParent && !tags[ offsetParent.nodeName.toLowerCase() ] ) {
  9313. tags = offsetParent.getBoundingClientRect();
  9314. result.top = -tags.top;
  9315. result.left = -tags.left;
  9316. }
  9317. return result;
  9318. },
  9319. getStyleValue: function (attr) {
  9320. return parseInt(domUtils.getComputedStyle( this.getEditorHolder() ,attr));
  9321. }
  9322. };
  9323. $.extend( Fullscreen, {
  9324. /**
  9325. * 监听resize
  9326. */
  9327. listen: function(){
  9328. var timer = null;
  9329. if( Fullscreen._hasFullscreenListener ) {
  9330. return;
  9331. }
  9332. Fullscreen._hasFullscreenListener = true;
  9333. $( window ).on( 'resize', function(){
  9334. if( timer !== null ) {
  9335. window.clearTimeout( timer );
  9336. timer = null;
  9337. }
  9338. timer = window.setTimeout(function(){
  9339. for( var key in FULLSCREENS ) {
  9340. FULLSCREENS[ key ].resize();
  9341. }
  9342. timer = null;
  9343. }, 50);
  9344. } );
  9345. },
  9346. getInstance: function ( editor ) {
  9347. if ( !FULLSCREENS[editor.uid ] ) {
  9348. new Fullscreen( editor );
  9349. }
  9350. return FULLSCREENS[editor.uid ];
  9351. }
  9352. });
  9353. //开始监听
  9354. Fullscreen.listen();
  9355. })();
  9356. UM.registerUI('link image video map formula',function(name){
  9357. var me = this, currentRange, $dialog,
  9358. opt = {
  9359. title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9360. url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js'
  9361. };
  9362. var $btn = $.eduibutton({
  9363. icon: name,
  9364. title: this.getLang('labelMap')[name] || ''
  9365. });
  9366. //加载模版数据
  9367. utils.loadFile(document,{
  9368. src: opt.url,
  9369. tag: "script",
  9370. type: "text/javascript",
  9371. defer: "defer"
  9372. },function(){
  9373. //调整数据
  9374. var data = UM.getWidgetData(name);
  9375. if(!data) return;
  9376. if(data.buttons){
  9377. var ok = data.buttons.ok;
  9378. if(ok){
  9379. opt.oklabel = ok.label || me.getLang('ok');
  9380. if(ok.exec){
  9381. opt.okFn = function(){
  9382. return $.proxy(ok.exec,null,me,$dialog)()
  9383. }
  9384. }
  9385. }
  9386. var cancel = data.buttons.cancel;
  9387. if(cancel){
  9388. opt.cancellabel = cancel.label || me.getLang('cancel');
  9389. if(cancel.exec){
  9390. opt.cancelFn = function(){
  9391. return $.proxy(cancel.exec,null,me,$dialog)()
  9392. }
  9393. }
  9394. }
  9395. }
  9396. data.width && (opt.width = data.width);
  9397. data.height && (opt.height = data.height);
  9398. $dialog = $.eduimodal(opt);
  9399. $dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)
  9400. .find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');
  9401. $dialog.edui().on('beforehide',function () {
  9402. var rng = me.selection.getRange();
  9403. if (rng.equals(currentRange)) {
  9404. rng.select()
  9405. }
  9406. }).on('beforeshow', function () {
  9407. var $root = this.root(),
  9408. win = null,
  9409. offset = null;
  9410. currentRange = me.selection.getRange();
  9411. if (!$root.parent()[0]) {
  9412. me.$container.find('.edui-dialog-container').append($root);
  9413. }
  9414. //IE6下 特殊处理, 通过计算进行定位
  9415. if( $.IE6 ) {
  9416. win = {
  9417. width: $( window ).width(),
  9418. height: $( window ).height()
  9419. };
  9420. offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();
  9421. $root.css({
  9422. position: 'absolute',
  9423. margin: 0,
  9424. left: ( win.width - $root.width() ) / 2 - offset.left,
  9425. top: 100 - offset.top
  9426. });
  9427. }
  9428. UM.setWidgetBody(name,$dialog,me);
  9429. UM.setTopEditor(me);
  9430. }).on('afterbackdrop',function(){
  9431. this.$backdrop.css('zIndex',me.getOpt('zIndex')+1).appendTo(me.$container.find('.edui-dialog-container'))
  9432. $dialog.css('zIndex',me.getOpt('zIndex')+2)
  9433. }).on('beforeok',function(){
  9434. try{
  9435. currentRange.select()
  9436. }catch(e){}
  9437. }).attachTo($btn)
  9438. });
  9439. me.addListener('selectionchange', function () {
  9440. var state = this.queryCommandState(name);
  9441. $btn.edui().disabled(state == -1).active(state == 1)
  9442. });
  9443. return $btn;
  9444. });
  9445. UM.registerUI( 'emotion formula', function( name ){
  9446. var me = this,
  9447. url = me.options.UMEDITOR_HOME_URL + 'dialogs/' +name+ '/'+name+'.js';
  9448. var $btn = $.eduibutton({
  9449. icon: name,
  9450. title: this.getLang('labelMap')[name] || ''
  9451. });
  9452. //加载模版数据
  9453. utils.loadFile(document,{
  9454. src: url,
  9455. tag: "script",
  9456. type: "text/javascript",
  9457. defer: "defer"
  9458. },function(){
  9459. var opt = {
  9460. url : url
  9461. };
  9462. //调整数据
  9463. var data = UM.getWidgetData(name);
  9464. data.width && (opt.width = data.width);
  9465. data.height && (opt.height = data.height);
  9466. $.eduipopup(opt).css('zIndex',me.options.zIndex + 1)
  9467. .addClass('edui-popup-' + name)
  9468. .edui()
  9469. .on('beforeshow',function(){
  9470. var $root = this.root();
  9471. if(!$root.parent().length){
  9472. me.$container.find('.edui-dialog-container').append($root);
  9473. }
  9474. UM.setWidgetBody(name,$root,me);
  9475. UM.setTopEditor(me);
  9476. }).attachTo($btn,{
  9477. offsetTop:-5,
  9478. offsetLeft:10,
  9479. caretLeft:11,
  9480. caretTop:-8
  9481. });
  9482. me.addListener('selectionchange', function () {
  9483. var state = this.queryCommandState(name);
  9484. $btn.edui().disabled(state == -1).active(state == 1);
  9485. });
  9486. });
  9487. return $btn;
  9488. } );
  9489. UM.registerUI('imagescale',function () {
  9490. var me = this,
  9491. $imagescale;
  9492. me.setOpt('imageScaleEnabled', true);
  9493. if (browser.webkit && me.getOpt('imageScaleEnabled')) {
  9494. me.addListener('click', function (type, e) {
  9495. var range = me.selection.getRange(),
  9496. img = range.getClosedNode(),
  9497. target = e.target;
  9498. /* 点击第一个图片的后面,八个角不消失 fix:3652 */
  9499. if (img && img.tagName == 'IMG' && target == img) {
  9500. if (!$imagescale) {
  9501. $imagescale = $.eduiscale({'$wrap':me.$container}).css('zIndex', me.options.zIndex);
  9502. me.$container.append($imagescale);
  9503. var _keyDownHandler = function () {
  9504. $imagescale.edui().hide();
  9505. }, _mouseDownHandler = function (e) {
  9506. var ele = e.target || e.srcElement;
  9507. if (ele && ele.className.indexOf('edui-scale') == -1) {
  9508. _keyDownHandler(e);
  9509. }
  9510. }, timer;
  9511. $imagescale.edui()
  9512. .on('aftershow', function () {
  9513. $(document).bind('keydown', _keyDownHandler);
  9514. $(document).bind('mousedown', _mouseDownHandler);
  9515. me.selection.getNative().removeAllRanges();
  9516. })
  9517. .on('afterhide', function () {
  9518. $(document).unbind('keydown', _keyDownHandler);
  9519. $(document).unbind('mousedown', _mouseDownHandler);
  9520. var target = $imagescale.edui().getScaleTarget();
  9521. if (target.parentNode) {
  9522. me.selection.getRange().selectNode(target).select();
  9523. }
  9524. })
  9525. .on('mousedown', function (e) {
  9526. me.selection.getNative().removeAllRanges();
  9527. var ele = e.target || e.srcElement;
  9528. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9529. timer = setTimeout(function() {
  9530. $imagescale.edui().hide();
  9531. }, 200);
  9532. }
  9533. })
  9534. .on('mouseup', function (e) {
  9535. var ele = e.target || e.srcElement;
  9536. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9537. clearTimeout(timer);
  9538. }
  9539. });
  9540. }
  9541. $imagescale.edui().show($(img));
  9542. } else {
  9543. if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();
  9544. }
  9545. });
  9546. me.addListener('click', function (type, e) {
  9547. if (e.target.tagName == 'IMG') {
  9548. var range = new dom.Range(me.document, me.body);
  9549. range.selectNode(e.target).select();
  9550. }
  9551. });
  9552. }
  9553. });
  9554. UM.registerUI('autofloat',function(){
  9555. var me = this,
  9556. lang = me.getLang();
  9557. me.setOpt({
  9558. autoFloatEnabled: true,
  9559. topOffset: 0
  9560. });
  9561. var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
  9562. topOffset = me.options.topOffset;
  9563. //如果不固定toolbar的位置,则直接退出
  9564. if(!optsAutoFloatEnabled){
  9565. return;
  9566. }
  9567. me.ready(function(){
  9568. var LteIE6 = browser.ie && browser.version <= 6,
  9569. quirks = browser.quirks;
  9570. function checkHasUI(){
  9571. if(!UM.ui){
  9572. alert(lang.autofloatMsg);
  9573. return 0;
  9574. }
  9575. return 1;
  9576. }
  9577. function fixIE6FixedPos(){
  9578. var docStyle = document.body.style;
  9579. docStyle.backgroundImage = 'url("about:blank")';
  9580. docStyle.backgroundAttachment = 'fixed';
  9581. }
  9582. var bakCssText,
  9583. placeHolder = document.createElement('div'),
  9584. toolbarBox,orgTop,
  9585. getPosition=function(element){
  9586. var bcr;
  9587. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  9588. try{
  9589. bcr = element.getBoundingClientRect();
  9590. }catch(e){
  9591. bcr={left:0,top:0,height:0,width:0}
  9592. }
  9593. var rect = {
  9594. left: Math.round(bcr.left),
  9595. top: Math.round(bcr.top),
  9596. height: Math.round(bcr.bottom - bcr.top),
  9597. width: Math.round(bcr.right - bcr.left)
  9598. };
  9599. var doc;
  9600. while ((doc = element.ownerDocument) !== document &&
  9601. (element = domUtils.getWindow(doc).frameElement)) {
  9602. bcr = element.getBoundingClientRect();
  9603. rect.left += bcr.left;
  9604. rect.top += bcr.top;
  9605. }
  9606. rect.bottom = rect.top + rect.height;
  9607. rect.right = rect.left + rect.width;
  9608. return rect;
  9609. };
  9610. var isFullScreening = false;
  9611. function setFloating(){
  9612. if(isFullScreening){
  9613. return;
  9614. }
  9615. var toobarBoxPos = domUtils.getXY(toolbarBox),
  9616. origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
  9617. origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
  9618. toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
  9619. toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
  9620. toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
  9621. if (LteIE6 || (quirks && browser.ie)) {
  9622. if(toolbarBox.style.position != 'absolute'){
  9623. toolbarBox.style.position = 'absolute';
  9624. }
  9625. toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px';
  9626. } else {
  9627. if(toolbarBox.style.position != 'fixed'){
  9628. toolbarBox.style.position = 'fixed';
  9629. toolbarBox.style.top = topOffset +"px";
  9630. ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
  9631. }
  9632. }
  9633. }
  9634. function unsetFloating(){
  9635. if(placeHolder.parentNode){
  9636. placeHolder.parentNode.removeChild(placeHolder);
  9637. }
  9638. toolbarBox.style.cssText = bakCssText;
  9639. }
  9640. function updateFloating(){
  9641. var rect3 = getPosition(me.container);
  9642. var offset=me.options.toolbarTopOffset||0;
  9643. if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
  9644. setFloating();
  9645. }else{
  9646. unsetFloating();
  9647. }
  9648. }
  9649. var defer_updateFloating = utils.defer(function(){
  9650. updateFloating();
  9651. },browser.ie ? 200 : 100,true);
  9652. me.addListener('destroy',function(){
  9653. $(window).off('scroll resize',updateFloating);
  9654. me.removeListener('keydown', defer_updateFloating);
  9655. });
  9656. if(checkHasUI(me)){
  9657. toolbarBox = $('.edui-toolbar',me.container)[0];
  9658. me.addListener("afteruiready",function(){
  9659. setTimeout(function(){
  9660. orgTop = $(toolbarBox).offset().top;
  9661. },100);
  9662. });
  9663. bakCssText = toolbarBox.style.cssText;
  9664. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9665. if(LteIE6){
  9666. fixIE6FixedPos();
  9667. }
  9668. $(window).on('scroll resize',updateFloating);
  9669. me.addListener('keydown', defer_updateFloating);
  9670. me.addListener('resize', function(){
  9671. unsetFloating();
  9672. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9673. updateFloating();
  9674. });
  9675. me.addListener('beforefullscreenchange', function (t, enabled){
  9676. if (enabled) {
  9677. unsetFloating();
  9678. isFullScreening = enabled;
  9679. }
  9680. });
  9681. me.addListener('fullscreenchanged', function (t, enabled){
  9682. if (!enabled) {
  9683. updateFloating();
  9684. }
  9685. isFullScreening = enabled;
  9686. });
  9687. me.addListener('sourcemodechanged', function (t, enabled){
  9688. setTimeout(function (){
  9689. updateFloating();
  9690. },0);
  9691. });
  9692. me.addListener("clearDoc",function(){
  9693. setTimeout(function(){
  9694. updateFloating();
  9695. },0);
  9696. })
  9697. }
  9698. })
  9699. });
  9700. UM.registerUI('source',function(name){
  9701. var me = this;
  9702. me.addListener('fullscreenchanged',function(){
  9703. me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())
  9704. });
  9705. var $btn = $.eduibutton({
  9706. icon : name,
  9707. click : function(){
  9708. me.execCommand(name);
  9709. UM.setTopEditor(me);
  9710. },
  9711. title: this.getLang('labelMap')[name] || ''
  9712. });
  9713. this.addListener('selectionchange',function(){
  9714. var state = this.queryCommandState(name);
  9715. $btn.edui().disabled(state == -1).active(state == 1)
  9716. });
  9717. return $btn;
  9718. });
  9719. UM.registerUI('paragraph fontfamily fontsize', function( name ) {
  9720. var me = this,
  9721. label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9722. options = {
  9723. label: label,
  9724. title: label,
  9725. comboboxName: name,
  9726. items: me.options[ name ] || [],
  9727. itemStyles: [],
  9728. value: [],
  9729. autowidthitem: []
  9730. },
  9731. $combox = null,
  9732. comboboxWidget = null;
  9733. if(options.items.length == 0){
  9734. return null;
  9735. }
  9736. switch ( name ) {
  9737. case 'paragraph':
  9738. options = transForParagraph( options );
  9739. break;
  9740. case 'fontfamily':
  9741. options = transForFontfamily( options );
  9742. break;
  9743. case 'fontsize':
  9744. options = transForFontsize( options );
  9745. break;
  9746. }
  9747. //实例化
  9748. $combox = $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1);
  9749. comboboxWidget = $combox.edui();
  9750. comboboxWidget.on('comboboxselect', function( evt, res ){
  9751. me.execCommand( name, res.value );
  9752. }).on("beforeshow", function(){
  9753. if( $combox.parent().length === 0 ) {
  9754. $combox.appendTo( me.$container.find('.edui-dialog-container') );
  9755. }
  9756. UM.setTopEditor(me);
  9757. });
  9758. //状态反射
  9759. this.addListener('selectionchange',function( evt ){
  9760. var state = this.queryCommandState( name ),
  9761. value = this.queryCommandValue( name );
  9762. //设置按钮状态
  9763. comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 );
  9764. if(value){
  9765. //设置label
  9766. value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);
  9767. comboboxWidget.selectItemByLabel( value );
  9768. }
  9769. });
  9770. return comboboxWidget.button().addClass('edui-combobox');
  9771. /**
  9772. * 宽度自适应工具函数
  9773. * @param word 单词内容
  9774. * @param hasSuffix 是否含有后缀
  9775. */
  9776. function wordCountAdaptive ( word, hasSuffix ) {
  9777. var $tmpNode = $('<span>' ).html( word ).css( {
  9778. display: 'inline',
  9779. position: 'absolute',
  9780. top: -10000000,
  9781. left: -100000
  9782. } ).appendTo( document.body),
  9783. width = $tmpNode.width();
  9784. $tmpNode.remove();
  9785. $tmpNode = null;
  9786. if( width < 50 ) {
  9787. return word;
  9788. } else {
  9789. word = word.slice( 0, hasSuffix ? -4 : -1 );
  9790. if( !word.length ) {
  9791. return '...';
  9792. }
  9793. return wordCountAdaptive( word + '...', true );
  9794. }
  9795. }
  9796. //段落参数转换
  9797. function transForParagraph ( options ) {
  9798. var tempItems = [];
  9799. for( var key in options.items ) {
  9800. options.value.push( key );
  9801. tempItems.push( key );
  9802. options.autowidthitem.push( wordCountAdaptive( key ) );
  9803. }
  9804. options.items = tempItems;
  9805. options.autoRecord = false;
  9806. return options;
  9807. }
  9808. //字体参数转换
  9809. function transForFontfamily ( options ) {
  9810. var temp = null,
  9811. tempItems = [];
  9812. for( var i = 0, len = options.items.length; i < len; i++ ) {
  9813. temp = options.items[ i ].val;
  9814. tempItems.push( temp.split(/\s*,\s*/)[0] );
  9815. options.itemStyles.push('font-family: ' + temp);
  9816. options.value.push( temp );
  9817. options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) );
  9818. }
  9819. options.items = tempItems;
  9820. return options;
  9821. }
  9822. //字体大小参数转换
  9823. function transForFontsize ( options ) {
  9824. var temp = null,
  9825. tempItems = [];
  9826. options.itemStyles = [];
  9827. options.value = [];
  9828. for( var i = 0, len = options.items.length; i < len; i++ ) {
  9829. temp = options.items[ i ];
  9830. tempItems.push( temp );
  9831. options.itemStyles.push('font-size: ' + temp +'px');
  9832. }
  9833. options.value = options.items;
  9834. options.items = tempItems;
  9835. options.autoRecord = false;
  9836. return options;
  9837. }
  9838. });
  9839. UM.registerUI('forecolor backcolor', function( name ) {
  9840. function getCurrentColor() {
  9841. return domUtils.getComputedStyle( $colorLabel[0], 'background-color' );
  9842. }
  9843. var me = this,
  9844. $colorPickerWidget = null,
  9845. $colorLabel = null,
  9846. $btn = null;
  9847. //querycommand
  9848. this.addListener('selectionchange', function(){
  9849. var state = this.queryCommandState( name );
  9850. $btn.edui().disabled( state == -1 ).active( state == 1 );
  9851. });
  9852. $btn = $.eduicolorsplitbutton({
  9853. icon: name,
  9854. caret: true,
  9855. name: name,
  9856. title: me.getLang("labelMap")[name],
  9857. click: function() {
  9858. me.execCommand( name, getCurrentColor() );
  9859. }
  9860. });
  9861. $colorLabel = $btn.edui().colorLabel();
  9862. $colorPickerWidget = $.eduicolorpicker({
  9863. name: name,
  9864. lang_clearColor: me.getLang('clearColor') || '',
  9865. lang_themeColor: me.getLang('themeColor') || '',
  9866. lang_standardColor: me.getLang('standardColor') || ''
  9867. })
  9868. .on('pickcolor', function( evt, color ){
  9869. window.setTimeout( function(){
  9870. $colorLabel.css("backgroundColor", color);
  9871. me.execCommand( name, color );
  9872. }, 0 );
  9873. })
  9874. .on('show',function(){
  9875. UM.setActiveWidget( colorPickerWidget.root() );
  9876. }).css('zIndex',me.getOpt('zIndex') + 1);
  9877. $btn.edui().on('arrowclick',function(){
  9878. if(!$colorPickerWidget.parent().length){
  9879. me.$container.find('.edui-dialog-container').append($colorPickerWidget);
  9880. }
  9881. $colorPickerWidget.edui().show($btn,{
  9882. caretDir:"down",
  9883. offsetTop:-5,
  9884. offsetLeft:8,
  9885. caretLeft:11,
  9886. caretTop:-8
  9887. });
  9888. UM.setTopEditor(me);
  9889. }).register('click', $btn, function () {
  9890. $colorPickerWidget.edui().hide()
  9891. });
  9892. return $btn;
  9893. });
  9894. })(jQuery)