ueditor.all.js 634 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386
  1. (function(){
  2. // editor.js
  3. UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
  4. var baidu = window.baidu || {};
  5. window.baidu = baidu;
  6. window.UE = baidu.editor = window.UE || {};
  7. UE.plugins = {};
  8. UE.commands = {};
  9. UE.instants = {};
  10. UE.I18N = {};
  11. UE._customizeUI = {};
  12. UE.version = "1.4.3";
  13. var dom = UE.dom = {};
  14. // core/browser.js
  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 = UE.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. browser.ie11above = version > 10;
  165. browser.ie11below = version < 11;
  166. }
  167. // Gecko.
  168. if ( browser.gecko ){
  169. var geckoRelease = agent.match( /rv:([\d\.]+)/ );
  170. if ( geckoRelease )
  171. {
  172. geckoRelease = geckoRelease[1].split( '.' );
  173. version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
  174. }
  175. }
  176. /**
  177. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  178. * @warning 如果浏览器不是chrome, 则该值为undefined
  179. * @example
  180. * ```javascript
  181. * if ( UE.browser.chrome ) {
  182. * console.log( '当前浏览器是Chrome' );
  183. * }
  184. * ```
  185. */
  186. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  187. browser.chrome = + RegExp['\x241'];
  188. }
  189. /**
  190. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  191. * @warning 如果浏览器不是safari, 则该值为undefined
  192. * @example
  193. * ```javascript
  194. * if ( UE.browser.safari ) {
  195. * console.log( '当前浏览器是Safari' );
  196. * }
  197. * ```
  198. */
  199. if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
  200. browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
  201. }
  202. // Opera 9.50+
  203. if ( browser.opera )
  204. version = parseFloat( opera.version() );
  205. // WebKit 522+ (Safari 3+)
  206. if ( browser.webkit )
  207. version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
  208. /**
  209. * @property { Number } version 检测当前浏览器版本号
  210. * @remind
  211. * <ul>
  212. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  213. * <li>gecko系列会返回10900,158900等</li>
  214. * <li>webkit系列会返回其build号 (如 522等)</li>
  215. * </ul>
  216. * @example
  217. * ```javascript
  218. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  219. * ```
  220. */
  221. browser.version = version;
  222. /**
  223. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  224. * @example
  225. * ```javascript
  226. * if ( UE.browser.isCompatible ) {
  227. * console.log( '浏览器与UEditor能够良好兼容' );
  228. * }
  229. * ```
  230. */
  231. browser.isCompatible =
  232. !browser.mobile && (
  233. ( browser.ie && version >= 6 ) ||
  234. ( browser.gecko && version >= 10801 ) ||
  235. ( browser.opera && version >= 9.5 ) ||
  236. ( browser.air && version >= 1 ) ||
  237. ( browser.webkit && version >= 522 ) ||
  238. false );
  239. return browser;
  240. }();
  241. //快捷方式
  242. var ie = browser.ie,
  243. webkit = browser.webkit,
  244. gecko = browser.gecko,
  245. opera = browser.opera;
  246. // core/utils.js
  247. /**
  248. * 工具函数包
  249. * @file
  250. * @module UE.utils
  251. * @since 1.2.6.1
  252. */
  253. /**
  254. * UEditor封装使用的静态工具函数
  255. * @module UE.utils
  256. * @unfile
  257. */
  258. var utils = UE.utils = {
  259. /**
  260. * 用给定的迭代器遍历对象
  261. * @method each
  262. * @param { Object } obj 需要遍历的对象
  263. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  264. * @example
  265. * ```javascript
  266. * var demoObj = {
  267. * key1: 1,
  268. * key2: 2
  269. * };
  270. *
  271. * //output: key1: 1, key2: 2
  272. * UE.utils.each( demoObj, funciton ( value, key ) {
  273. *
  274. * console.log( key + ":" + value );
  275. *
  276. * } );
  277. * ```
  278. */
  279. /**
  280. * 用给定的迭代器遍历数组或类数组对象
  281. * @method each
  282. * @param { Array } array 需要遍历的数组或者类数组
  283. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  284. * @example
  285. * ```javascript
  286. * var divs = document.getElmentByTagNames( "div" );
  287. *
  288. * //output: 0: DIV, 1: DIV ...
  289. * UE.utils.each( divs, funciton ( value, key ) {
  290. *
  291. * console.log( key + ":" + value.tagName );
  292. *
  293. * } );
  294. * ```
  295. */
  296. each : function(obj, iterator, context) {
  297. if (obj == null) return;
  298. if (obj.length === +obj.length) {
  299. for (var i = 0, l = obj.length; i < l; i++) {
  300. if(iterator.call(context, obj[i], i, obj) === false)
  301. return false;
  302. }
  303. } else {
  304. for (var key in obj) {
  305. if (obj.hasOwnProperty(key)) {
  306. if(iterator.call(context, obj[key], key, obj) === false)
  307. return false;
  308. }
  309. }
  310. }
  311. },
  312. /**
  313. * 以给定对象作为原型创建一个新对象
  314. * @method makeInstance
  315. * @param { Object } protoObject 该对象将作为新创建对象的原型
  316. * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
  317. * @example
  318. * ```javascript
  319. *
  320. * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
  321. *
  322. * var newObject = UE.utils.makeInstance( protoObject );
  323. * //output: Hello UEditor!
  324. * newObject.sayHello();
  325. * ```
  326. */
  327. makeInstance:function (obj) {
  328. var noop = new Function();
  329. noop.prototype = obj;
  330. obj = new noop;
  331. noop.prototype = null;
  332. return obj;
  333. },
  334. /**
  335. * 将source对象中的属性扩展到target对象上
  336. * @method extend
  337. * @remind 该方法将强制把source对象上的属性复制到target对象上
  338. * @see UE.utils.extend(Object,Object,Boolean)
  339. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  340. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  341. * @return { Object } 返回target对象
  342. * @example
  343. * ```javascript
  344. *
  345. * var target = { name: 'target', sex: 1 },
  346. * source = { name: 'source', age: 17 };
  347. *
  348. * UE.utils.extend( target, source );
  349. *
  350. * //output: { name: 'source', sex: 1, age: 17 }
  351. * console.log( target );
  352. *
  353. * ```
  354. */
  355. /**
  356. * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
  357. * 源对象属性名相同的属性值。
  358. * @method extend
  359. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  360. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  361. * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
  362. * @return { Object } 返回target对象
  363. * @example
  364. * ```javascript
  365. *
  366. * var target = { name: 'target', sex: 1 },
  367. * source = { name: 'source', age: 17 };
  368. *
  369. * UE.utils.extend( target, source, true );
  370. *
  371. * //output: { name: 'target', sex: 1, age: 17 }
  372. * console.log( target );
  373. *
  374. * ```
  375. */
  376. extend:function (t, s, b) {
  377. if (s) {
  378. for (var k in s) {
  379. if (!b || !t.hasOwnProperty(k)) {
  380. t[k] = s[k];
  381. }
  382. }
  383. }
  384. return t;
  385. },
  386. /**
  387. * 将给定的多个对象的属性复制到目标对象target上
  388. * @method extend2
  389. * @remind 该方法将强制把源对象上的属性复制到target对象上
  390. * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
  391. * 将会覆盖掉之前的值。
  392. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  393. * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
  394. * @return { Object } 返回target对象
  395. * @example
  396. * ```javascript
  397. *
  398. * var target = {},
  399. * source1 = { name: 'source', age: 17 },
  400. * source2 = { title: 'dev' };
  401. *
  402. * UE.utils.extend2( target, source1, source2 );
  403. *
  404. * //output: { name: 'source', age: 17, title: 'dev' }
  405. * console.log( target );
  406. *
  407. * ```
  408. */
  409. extend2:function (t) {
  410. var a = arguments;
  411. for (var i = 1; i < a.length; i++) {
  412. var x = a[i];
  413. for (var k in x) {
  414. if (!t.hasOwnProperty(k)) {
  415. t[k] = x[k];
  416. }
  417. }
  418. }
  419. return t;
  420. },
  421. /**
  422. * 模拟继承机制, 使得subClass继承自superClass
  423. * @method inherits
  424. * @param { Object } subClass 子类对象
  425. * @param { Object } superClass 超类对象
  426. * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
  427. * @return { Object } 继承superClass后的子类对象
  428. * @example
  429. * ```javascript
  430. * function SuperClass(){
  431. * this.name = "小李";
  432. * }
  433. *
  434. * SuperClass.prototype = {
  435. * hello:function(str){
  436. * console.log(this.name + str);
  437. * }
  438. * }
  439. *
  440. * function SubClass(){
  441. * this.name = "小张";
  442. * }
  443. *
  444. * UE.utils.inherits(SubClass,SuperClass);
  445. *
  446. * var sub = new SubClass();
  447. * //output: '小张早上好!
  448. * sub.hello("早上好!");
  449. * ```
  450. */
  451. inherits:function (subClass, superClass) {
  452. var oldP = subClass.prototype,
  453. newP = utils.makeInstance(superClass.prototype);
  454. utils.extend(newP, oldP, true);
  455. subClass.prototype = newP;
  456. return (newP.constructor = subClass);
  457. },
  458. /**
  459. * 用指定的context对象作为函数fn的上下文
  460. * @method bind
  461. * @param { Function } fn 需要绑定上下文的函数对象
  462. * @param { Object } content 函数fn新的上下文对象
  463. * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
  464. * @example
  465. * ```javascript
  466. *
  467. * var name = 'window',
  468. * newTest = null;
  469. *
  470. * function test () {
  471. * console.log( this.name );
  472. * }
  473. *
  474. * newTest = UE.utils.bind( test, { name: 'object' } );
  475. *
  476. * //output: object
  477. * newTest();
  478. *
  479. * //output: window
  480. * test();
  481. *
  482. * ```
  483. */
  484. bind:function (fn, context) {
  485. return function () {
  486. return fn.apply(context, arguments);
  487. };
  488. },
  489. /**
  490. * 创建延迟指定时间后执行的函数fn
  491. * @method defer
  492. * @param { Function } fn 需要延迟执行的函数对象
  493. * @param { int } delay 延迟的时间, 单位是毫秒
  494. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  495. * 而不能保证刚好到达延迟时间时执行。
  496. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  497. * @example
  498. * ```javascript
  499. * var start = 0;
  500. *
  501. * function test(){
  502. * console.log( new Date() - start );
  503. * }
  504. *
  505. * var testDefer = UE.utils.defer( test, 1000 );
  506. * //
  507. * start = new Date();
  508. * //output: (大约在1000毫秒之后输出) 1000
  509. * testDefer();
  510. * ```
  511. */
  512. /**
  513. * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
  514. * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
  515. * @method defer
  516. * @param { Function } fn 需要延迟执行的函数对象
  517. * @param { int } delay 延迟的时间, 单位是毫秒
  518. * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
  519. * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
  520. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  521. * 而不能保证刚好到达延迟时间时执行。
  522. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  523. * @example
  524. * ```javascript
  525. *
  526. * function test(){
  527. * console.log(1);
  528. * }
  529. *
  530. * var testDefer = UE.utils.defer( test, 1000, true );
  531. *
  532. * //output: (两次调用仅有一次输出) 1
  533. * testDefer();
  534. * testDefer();
  535. * ```
  536. */
  537. defer:function (fn, delay, exclusion) {
  538. var timerID;
  539. return function () {
  540. if (exclusion) {
  541. clearTimeout(timerID);
  542. }
  543. timerID = setTimeout(fn, delay);
  544. };
  545. },
  546. /**
  547. * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
  548. * @method indexOf
  549. * @remind 该方法的匹配过程使用的是恒等“===”
  550. * @param { Array } array 需要查找的数组对象
  551. * @param { * } item 需要在目标数组中查找的值
  552. * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
  553. * @example
  554. * ```javascript
  555. * var item = 1,
  556. * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
  557. *
  558. * //output: 4
  559. * console.log( UE.utils.indexOf( arr, item ) );
  560. * ```
  561. */
  562. /**
  563. * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
  564. * @method indexOf
  565. * @remind 该方法的匹配过程使用的是恒等“===”
  566. * @param { Array } array 需要查找的数组对象
  567. * @param { * } item 需要在目标数组中查找的值
  568. * @param { int } start 搜索的起始位置
  569. * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
  570. * @example
  571. * ```javascript
  572. * var item = 1,
  573. * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
  574. *
  575. * //output: 9
  576. * console.log( UE.utils.indexOf( arr, item, 5 ) );
  577. * ```
  578. */
  579. indexOf:function (array, item, start) {
  580. var index = -1;
  581. start = this.isNumber(start) ? start : 0;
  582. this.each(array, function (v, i) {
  583. if (i >= start && v === item) {
  584. index = i;
  585. return false;
  586. }
  587. });
  588. return index;
  589. },
  590. /**
  591. * 移除数组array中所有的元素item
  592. * @method removeItem
  593. * @param { Array } array 要移除元素的目标数组
  594. * @param { * } item 将要被移除的元素
  595. * @remind 该方法的匹配过程使用的是恒等“===”
  596. * @example
  597. * ```javascript
  598. * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
  599. *
  600. * UE.utils.removeItem( arr, 4 );
  601. * //output: [ 5, 7, 1, 3, 6 ]
  602. * console.log( arr );
  603. *
  604. * ```
  605. */
  606. removeItem:function (array, item) {
  607. for (var i = 0, l = array.length; i < l; i++) {
  608. if (array[i] === item) {
  609. array.splice(i, 1);
  610. i--;
  611. }
  612. }
  613. },
  614. /**
  615. * 删除字符串str的首尾空格
  616. * @method trim
  617. * @param { String } str 需要删除首尾空格的字符串
  618. * @return { String } 删除了首尾的空格后的字符串
  619. * @example
  620. * ```javascript
  621. *
  622. * var str = " UEdtior ";
  623. *
  624. * //output: 9
  625. * console.log( str.length );
  626. *
  627. * //output: 7
  628. * console.log( UE.utils.trim( " UEdtior " ).length );
  629. *
  630. * //output: 9
  631. * console.log( str.length );
  632. *
  633. * ```
  634. */
  635. trim:function (str) {
  636. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
  637. },
  638. /**
  639. * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  640. * @method listToMap
  641. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  642. * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
  643. * @return { Object } 转化之后的hash对象
  644. * @example
  645. * ```javascript
  646. *
  647. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  648. * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
  649. *
  650. * ```
  651. */
  652. /**
  653. * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  654. * @method listToMap
  655. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  656. * @param { Array } arr 字符串数组
  657. * @return { Object } 转化之后的hash对象
  658. * @example
  659. * ```javascript
  660. *
  661. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  662. * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
  663. *
  664. * ```
  665. */
  666. listToMap:function (list) {
  667. if (!list)return {};
  668. list = utils.isArray(list) ? list : list.split(',');
  669. for (var i = 0, ci, obj = {}; ci = list[i++];) {
  670. obj[ci.toUpperCase()] = obj[ci] = 1;
  671. }
  672. return obj;
  673. },
  674. /**
  675. * 将str中的html符号转义,将转义“',&,<,",>”五个字符
  676. * @method unhtml
  677. * @param { String } str 需要转义的字符串
  678. * @return { String } 转义后的字符串
  679. * @example
  680. * ```javascript
  681. * var html = '<body>&</body>';
  682. *
  683. * //output: &lt;body&gt;&amp;&lt;/body&gt;
  684. * console.log( UE.utils.unhtml( html ) );
  685. *
  686. * ```
  687. */
  688. unhtml:function (str, reg) {
  689. return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
  690. if (b) {
  691. return a;
  692. } else {
  693. return {
  694. '<':'&lt;',
  695. '&':'&amp;',
  696. '"':'&quot;',
  697. '>':'&gt;',
  698. "'":'&#39;'
  699. }[a]
  700. }
  701. }) : '';
  702. },
  703. /**
  704. * 将str中的转义字符还原成html字符
  705. * @see UE.utils.unhtml(String);
  706. * @method html
  707. * @param { String } str 需要逆转义的字符串
  708. * @return { String } 逆转义后的字符串
  709. * @example
  710. * ```javascript
  711. *
  712. * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
  713. *
  714. * //output: <body>&</body>
  715. * console.log( UE.utils.html( str ) );
  716. *
  717. * ```
  718. */
  719. html:function (str) {
  720. return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
  721. return {
  722. '&lt;':'<',
  723. '&amp;':'&',
  724. '&quot;':'"',
  725. '&gt;':'>',
  726. '&#39;':"'",
  727. '&nbsp;':' '
  728. }[m]
  729. }) : '';
  730. },
  731. /**
  732. * 将css样式转换为驼峰的形式
  733. * @method cssStyleToDomStyle
  734. * @param { String } cssName 需要转换的css样式名
  735. * @return { String } 转换成驼峰形式后的css样式名
  736. * @example
  737. * ```javascript
  738. *
  739. * var str = 'border-top';
  740. *
  741. * //output: borderTop
  742. * console.log( UE.utils.cssStyleToDomStyle( str ) );
  743. *
  744. * ```
  745. */
  746. cssStyleToDomStyle:function () {
  747. var test = document.createElement('div').style,
  748. cache = {
  749. 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
  750. };
  751. return function (cssName) {
  752. return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
  753. return match.charAt(1).toUpperCase();
  754. }));
  755. };
  756. }(),
  757. /**
  758. * 动态加载文件到doc中
  759. * @method loadFile
  760. * @param { DomDocument } document 需要加载资源文件的文档对象
  761. * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
  762. * @example
  763. * ```javascript
  764. *
  765. * UE.utils.loadFile( document, {
  766. * src:"test.js",
  767. * tag:"script",
  768. * type:"text/javascript",
  769. * defer:"defer"
  770. * } );
  771. *
  772. * ```
  773. */
  774. /**
  775. * 动态加载文件到doc中,加载成功后执行的回调函数fn
  776. * @method loadFile
  777. * @param { DomDocument } document 需要加载资源文件的文档对象
  778. * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
  779. * @param { Function } fn 资源文件加载成功之后执行的回调
  780. * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
  781. * 在此之后的所有同一URL的请求, 将会直接触发回调。
  782. * @example
  783. * ```javascript
  784. *
  785. * UE.utils.loadFile( document, {
  786. * src:"test.js",
  787. * tag:"script",
  788. * type:"text/javascript",
  789. * defer:"defer"
  790. * }, function () {
  791. * console.log('加载成功');
  792. * } );
  793. *
  794. * ```
  795. */
  796. loadFile:function () {
  797. var tmpList = [];
  798. function getItem(doc, obj) {
  799. try {
  800. for (var i = 0, ci; ci = tmpList[i++];) {
  801. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  802. return ci;
  803. }
  804. }
  805. } catch (e) {
  806. return null;
  807. }
  808. }
  809. return function (doc, obj, fn) {
  810. var item = getItem(doc, obj);
  811. if (item) {
  812. if (item.ready) {
  813. fn && fn();
  814. } else {
  815. item.funs.push(fn)
  816. }
  817. return;
  818. }
  819. tmpList.push({
  820. doc:doc,
  821. url:obj.src || obj.href,
  822. funs:[fn]
  823. });
  824. if (!doc.body) {
  825. var html = [];
  826. for (var p in obj) {
  827. if (p == 'tag')continue;
  828. html.push(p + '="' + obj[p] + '"')
  829. }
  830. doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
  831. return;
  832. }
  833. if (obj.id && doc.getElementById(obj.id)) {
  834. return;
  835. }
  836. var element = doc.createElement(obj.tag);
  837. delete obj.tag;
  838. for (var p in obj) {
  839. element.setAttribute(p, obj[p]);
  840. }
  841. element.onload = element.onreadystatechange = function () {
  842. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  843. item = getItem(doc, obj);
  844. if (item.funs.length > 0) {
  845. item.ready = 1;
  846. for (var fi; fi = item.funs.pop();) {
  847. fi();
  848. }
  849. }
  850. element.onload = element.onreadystatechange = null;
  851. }
  852. };
  853. element.onerror = function () {
  854. throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
  855. };
  856. doc.getElementsByTagName("head")[0].appendChild(element);
  857. }
  858. }(),
  859. /**
  860. * 判断obj对象是否为空
  861. * @method isEmptyObject
  862. * @param { * } obj 需要判断的对象
  863. * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
  864. * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
  865. * @return { Boolean } 对象是否为空
  866. * @example
  867. * ```javascript
  868. *
  869. * //output: true
  870. * console.log( UE.utils.isEmptyObject( {} ) );
  871. *
  872. * //output: true
  873. * console.log( UE.utils.isEmptyObject( [] ) );
  874. *
  875. * //output: true
  876. * console.log( UE.utils.isEmptyObject( "" ) );
  877. *
  878. * //output: false
  879. * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
  880. *
  881. * //output: false
  882. * console.log( UE.utils.isEmptyObject( [1] ) );
  883. *
  884. * //output: false
  885. * console.log( UE.utils.isEmptyObject( "1" ) );
  886. *
  887. * ```
  888. */
  889. isEmptyObject:function (obj) {
  890. if (obj == null) return true;
  891. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
  892. for (var key in obj) if (obj.hasOwnProperty(key)) return false;
  893. return true;
  894. },
  895. /**
  896. * 把rgb格式的颜色值转换成16进制格式
  897. * @method fixColor
  898. * @param { String } rgb格式的颜色值
  899. * @param { String }
  900. * @example
  901. * rgb(255,255,255) => "#ffffff"
  902. */
  903. fixColor:function (name, value) {
  904. if (/color/i.test(name) && /rgba?/.test(value)) {
  905. var array = value.split(",");
  906. if (array.length > 3)
  907. return "";
  908. value = "#";
  909. for (var i = 0, color; color = array[i++];) {
  910. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
  911. value += color.length == 1 ? "0" + color : color;
  912. }
  913. value = value.toUpperCase();
  914. }
  915. return value;
  916. },
  917. /**
  918. * 只针对border,padding,margin做了处理,因为性能问题
  919. * @public
  920. * @function
  921. * @param {String} val style字符串
  922. */
  923. optCss:function (val) {
  924. var padding, margin, border;
  925. val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
  926. if (val.split(' ').length == 1) {
  927. switch (key) {
  928. case 'padding':
  929. !padding && (padding = {});
  930. padding[name] = val;
  931. return '';
  932. case 'margin':
  933. !margin && (margin = {});
  934. margin[name] = val;
  935. return '';
  936. case 'border':
  937. return val == 'initial' ? '' : str;
  938. }
  939. }
  940. return str;
  941. });
  942. function opt(obj, name) {
  943. if (!obj) {
  944. return '';
  945. }
  946. var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
  947. if (!t || !l || !b || !r) {
  948. for (var p in obj) {
  949. val += ';' + name + '-' + p + ':' + obj[p] + ';';
  950. }
  951. } else {
  952. val += ';' + name + ':' +
  953. (t == b && b == l && l == r ? t :
  954. t == b && l == r ? (t + ' ' + l) :
  955. l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
  956. }
  957. return val;
  958. }
  959. val += opt(padding, 'padding') + opt(margin, 'margin');
  960. return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
  961. .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
  962. return b ? b + ";;" : ';'
  963. });
  964. },
  965. /**
  966. * 克隆对象
  967. * @method clone
  968. * @param { Object } source 源对象
  969. * @return { Object } source的一个副本
  970. */
  971. /**
  972. * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
  973. * @method clone
  974. * @param { Object } source 源对象
  975. * @param { Object } target 目标对象
  976. * @return { Object } 附加了source对象所有属性的target对象
  977. */
  978. clone:function (source, target) {
  979. var tmp;
  980. target = target || {};
  981. for (var i in source) {
  982. if (source.hasOwnProperty(i)) {
  983. tmp = source[i];
  984. if (typeof tmp == 'object') {
  985. target[i] = utils.isArray(tmp) ? [] : {};
  986. utils.clone(source[i], target[i])
  987. } else {
  988. target[i] = tmp;
  989. }
  990. }
  991. }
  992. return target;
  993. },
  994. /**
  995. * 把cm/pt为单位的值转换为px为单位的值
  996. * @method transUnitToPx
  997. * @param { String } 待转换的带单位的字符串
  998. * @return { String } 转换为px为计量单位的值的字符串
  999. * @example
  1000. * ```javascript
  1001. *
  1002. * //output: 500px
  1003. * console.log( UE.utils.transUnitToPx( '20cm' ) );
  1004. *
  1005. * //output: 27px
  1006. * console.log( UE.utils.transUnitToPx( '20pt' ) );
  1007. *
  1008. * ```
  1009. */
  1010. transUnitToPx:function (val) {
  1011. if (!/(pt|cm)/.test(val)) {
  1012. return val
  1013. }
  1014. var unit;
  1015. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  1016. val = v;
  1017. unit = u;
  1018. });
  1019. switch (unit) {
  1020. case 'cm':
  1021. val = parseFloat(val) * 25;
  1022. break;
  1023. case 'pt':
  1024. val = Math.round(parseFloat(val) * 96 / 72);
  1025. }
  1026. return val + (val ? 'px' : '');
  1027. },
  1028. /**
  1029. * 在dom树ready之后执行给定的回调函数
  1030. * @method domReady
  1031. * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
  1032. * @param { Function } fn dom树ready之后的回调函数
  1033. * @example
  1034. * ```javascript
  1035. *
  1036. * UE.utils.domReady( function () {
  1037. *
  1038. * console.log('123');
  1039. *
  1040. * } );
  1041. *
  1042. * ```
  1043. */
  1044. domReady:function () {
  1045. var fnArr = [];
  1046. function doReady(doc) {
  1047. //确保onready只执行一次
  1048. doc.isReady = true;
  1049. for (var ci; ci = fnArr.pop(); ci()) {
  1050. }
  1051. }
  1052. return function (onready, win) {
  1053. win = win || window;
  1054. var doc = win.document;
  1055. onready && fnArr.push(onready);
  1056. if (doc.readyState === "complete") {
  1057. doReady(doc);
  1058. } else {
  1059. doc.isReady && doReady(doc);
  1060. if (browser.ie && browser.version != 11) {
  1061. (function () {
  1062. if (doc.isReady) return;
  1063. try {
  1064. doc.documentElement.doScroll("left");
  1065. } catch (error) {
  1066. setTimeout(arguments.callee, 0);
  1067. return;
  1068. }
  1069. doReady(doc);
  1070. })();
  1071. win.attachEvent('onload', function () {
  1072. doReady(doc)
  1073. });
  1074. } else {
  1075. doc.addEventListener("DOMContentLoaded", function () {
  1076. doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
  1077. doReady(doc);
  1078. }, false);
  1079. win.addEventListener('load', function () {
  1080. doReady(doc)
  1081. }, false);
  1082. }
  1083. }
  1084. }
  1085. }(),
  1086. /**
  1087. * 动态添加css样式
  1088. * @method cssRule
  1089. * @param { String } 节点名称
  1090. * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  1091. * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  1092. * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  1093. * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
  1094. * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  1095. */
  1096. cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
  1097. var indexList, index;
  1098. if(style === undefined || style && style.nodeType && style.nodeType == 9){
  1099. //获取样式
  1100. doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
  1101. indexList = doc.indexList || (doc.indexList = {});
  1102. index = indexList[key];
  1103. if(index !== undefined){
  1104. return doc.styleSheets[index].cssText
  1105. }
  1106. return undefined;
  1107. }
  1108. doc = doc || document;
  1109. indexList = doc.indexList || (doc.indexList = {});
  1110. index = indexList[key];
  1111. //清除样式
  1112. if(style === ''){
  1113. if(index!== undefined){
  1114. doc.styleSheets[index].cssText = '';
  1115. delete indexList[key];
  1116. return true
  1117. }
  1118. return false;
  1119. }
  1120. //添加样式
  1121. if(index!== undefined){
  1122. sheetStyle = doc.styleSheets[index];
  1123. }else{
  1124. sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
  1125. indexList[key] = index;
  1126. }
  1127. sheetStyle.cssText = style;
  1128. }: function (key, style, doc) {
  1129. var head, node;
  1130. if(style === undefined || style && style.nodeType && style.nodeType == 9){
  1131. //获取样式
  1132. doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
  1133. node = doc.getElementById(key);
  1134. return node ? node.innerHTML : undefined;
  1135. }
  1136. doc = doc || document;
  1137. node = doc.getElementById(key);
  1138. //清除样式
  1139. if(style === ''){
  1140. if(node){
  1141. node.parentNode.removeChild(node);
  1142. return true
  1143. }
  1144. return false;
  1145. }
  1146. //添加样式
  1147. if(node){
  1148. node.innerHTML = style;
  1149. }else{
  1150. node = doc.createElement('style');
  1151. node.id = key;
  1152. node.innerHTML = style;
  1153. doc.getElementsByTagName('head')[0].appendChild(node);
  1154. }
  1155. },
  1156. sort:function(array,compareFn){
  1157. compareFn = compareFn || function(item1, item2){ return item1.localeCompare(item2);};
  1158. for(var i= 0,len = array.length; i<len; i++){
  1159. for(var j = i,length = array.length; j<length; j++){
  1160. if(compareFn(array[i], array[j]) > 0){
  1161. var t = array[i];
  1162. array[i] = array[j];
  1163. array[j] = t;
  1164. }
  1165. }
  1166. }
  1167. return array;
  1168. },
  1169. serializeParam:function (json) {
  1170. var strArr = [];
  1171. for (var i in json) {
  1172. //忽略默认的几个参数
  1173. if(i=="method" || i=="timeout" || i=="async") continue;
  1174. //传递过来的对象和函数不在提交之列
  1175. if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
  1176. strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
  1177. } else if (utils.isArray(json[i])) {
  1178. //支持传数组内容
  1179. for(var j = 0; j < json[i].length; j++) {
  1180. strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
  1181. }
  1182. }
  1183. }
  1184. return strArr.join("&");
  1185. },
  1186. formatUrl:function (url) {
  1187. var u = url.replace(/&&/g, '&');
  1188. u = u.replace(/\?&/g, '?');
  1189. u = u.replace(/&$/g, '');
  1190. u = u.replace(/&#/g, '#');
  1191. u = u.replace(/&+/g, '&');
  1192. return u;
  1193. },
  1194. isCrossDomainUrl:function (url) {
  1195. var a = document.createElement('a');
  1196. a.href = url;
  1197. if (browser.ie) {
  1198. a.href = a.href;
  1199. }
  1200. return !(a.protocol == location.protocol && a.hostname == location.hostname &&
  1201. (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80')));
  1202. },
  1203. clearEmptyAttrs : function(obj){
  1204. for(var p in obj){
  1205. if(obj[p] === ''){
  1206. delete obj[p]
  1207. }
  1208. }
  1209. return obj;
  1210. },
  1211. str2json : function(s){
  1212. if (!utils.isString(s)) return null;
  1213. if (window.JSON) {
  1214. return JSON.parse(s);
  1215. } else {
  1216. return (new Function("return " + utils.trim(s || '')))();
  1217. }
  1218. },
  1219. json2str : (function(){
  1220. if (window.JSON) {
  1221. return JSON.stringify;
  1222. } else {
  1223. var escapeMap = {
  1224. "\b": '\\b',
  1225. "\t": '\\t',
  1226. "\n": '\\n',
  1227. "\f": '\\f',
  1228. "\r": '\\r',
  1229. '"' : '\\"',
  1230. "\\": '\\\\'
  1231. };
  1232. function encodeString(source) {
  1233. if (/["\\\x00-\x1f]/.test(source)) {
  1234. source = source.replace(
  1235. /["\\\x00-\x1f]/g,
  1236. function (match) {
  1237. var c = escapeMap[match];
  1238. if (c) {
  1239. return c;
  1240. }
  1241. c = match.charCodeAt();
  1242. return "\\u00"
  1243. + Math.floor(c / 16).toString(16)
  1244. + (c % 16).toString(16);
  1245. });
  1246. }
  1247. return '"' + source + '"';
  1248. }
  1249. function encodeArray(source) {
  1250. var result = ["["],
  1251. l = source.length,
  1252. preComma, i, item;
  1253. for (i = 0; i < l; i++) {
  1254. item = source[i];
  1255. switch (typeof item) {
  1256. case "undefined":
  1257. case "function":
  1258. case "unknown":
  1259. break;
  1260. default:
  1261. if(preComma) {
  1262. result.push(',');
  1263. }
  1264. result.push(utils.json2str(item));
  1265. preComma = 1;
  1266. }
  1267. }
  1268. result.push("]");
  1269. return result.join("");
  1270. }
  1271. function pad(source) {
  1272. return source < 10 ? '0' + source : source;
  1273. }
  1274. function encodeDate(source){
  1275. return '"' + source.getFullYear() + "-"
  1276. + pad(source.getMonth() + 1) + "-"
  1277. + pad(source.getDate()) + "T"
  1278. + pad(source.getHours()) + ":"
  1279. + pad(source.getMinutes()) + ":"
  1280. + pad(source.getSeconds()) + '"';
  1281. }
  1282. return function (value) {
  1283. switch (typeof value) {
  1284. case 'undefined':
  1285. return 'undefined';
  1286. case 'number':
  1287. return isFinite(value) ? String(value) : "null";
  1288. case 'string':
  1289. return encodeString(value);
  1290. case 'boolean':
  1291. return String(value);
  1292. default:
  1293. if (value === null) {
  1294. return 'null';
  1295. } else if (utils.isArray(value)) {
  1296. return encodeArray(value);
  1297. } else if (utils.isDate(value)) {
  1298. return encodeDate(value);
  1299. } else {
  1300. var result = ['{'],
  1301. encode = utils.json2str,
  1302. preComma,
  1303. item;
  1304. for (var key in value) {
  1305. if (Object.prototype.hasOwnProperty.call(value, key)) {
  1306. item = value[key];
  1307. switch (typeof item) {
  1308. case 'undefined':
  1309. case 'unknown':
  1310. case 'function':
  1311. break;
  1312. default:
  1313. if (preComma) {
  1314. result.push(',');
  1315. }
  1316. preComma = 1;
  1317. result.push(encode(key) + ':' + encode(item));
  1318. }
  1319. }
  1320. }
  1321. result.push('}');
  1322. return result.join('');
  1323. }
  1324. }
  1325. };
  1326. }
  1327. })()
  1328. };
  1329. /**
  1330. * 判断给定的对象是否是字符串
  1331. * @method isString
  1332. * @param { * } object 需要判断的对象
  1333. * @return { Boolean } 给定的对象是否是字符串
  1334. */
  1335. /**
  1336. * 判断给定的对象是否是数组
  1337. * @method isArray
  1338. * @param { * } object 需要判断的对象
  1339. * @return { Boolean } 给定的对象是否是数组
  1340. */
  1341. /**
  1342. * 判断给定的对象是否是一个Function
  1343. * @method isFunction
  1344. * @param { * } object 需要判断的对象
  1345. * @return { Boolean } 给定的对象是否是Function
  1346. */
  1347. /**
  1348. * 判断给定的对象是否是Number
  1349. * @method isNumber
  1350. * @param { * } object 需要判断的对象
  1351. * @return { Boolean } 给定的对象是否是Number
  1352. */
  1353. /**
  1354. * 判断给定的对象是否是一个正则表达式
  1355. * @method isRegExp
  1356. * @param { * } object 需要判断的对象
  1357. * @return { Boolean } 给定的对象是否是正则表达式
  1358. */
  1359. /**
  1360. * 判断给定的对象是否是一个普通对象
  1361. * @method isObject
  1362. * @param { * } object 需要判断的对象
  1363. * @return { Boolean } 给定的对象是否是普通对象
  1364. */
  1365. utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) {
  1366. UE.utils['is' + v] = function (obj) {
  1367. return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
  1368. }
  1369. });
  1370. // core/EventBase.js
  1371. /**
  1372. * UE采用的事件基类
  1373. * @file
  1374. * @module UE
  1375. * @class EventBase
  1376. * @since 1.2.6.1
  1377. */
  1378. /**
  1379. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  1380. * @unfile
  1381. * @module UE
  1382. */
  1383. /**
  1384. * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  1385. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  1386. * @unfile
  1387. * @module UE
  1388. * @class EventBase
  1389. */
  1390. /**
  1391. * 通过此构造器,子类可以继承EventBase获取事件监听的方法
  1392. * @constructor
  1393. * @example
  1394. * ```javascript
  1395. * UE.EventBase.call(editor);
  1396. * ```
  1397. */
  1398. var EventBase = UE.EventBase = function () {};
  1399. EventBase.prototype = {
  1400. /**
  1401. * 注册事件监听器
  1402. * @method addListener
  1403. * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
  1404. * @param { Function } fn 监听的事件被触发时,会执行该回调函数
  1405. * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
  1406. * @example
  1407. * ```javascript
  1408. * editor.addListener('selectionchange',function(){
  1409. * console.log("选区已经变化!");
  1410. * })
  1411. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  1412. * if(type == 'beforegetcontent'){
  1413. * //do something
  1414. * }else{
  1415. * //do something
  1416. * }
  1417. * console.log(this.getContent) // this是注册的事件的编辑器实例
  1418. * })
  1419. * ```
  1420. * @see UE.EventBase:fireEvent(String)
  1421. */
  1422. addListener:function (types, listener) {
  1423. types = utils.trim(types).split(/\s+/);
  1424. for (var i = 0, ti; ti = types[i++];) {
  1425. getListener(this, ti, true).push(listener);
  1426. }
  1427. },
  1428. on : function(types, listener){
  1429. return this.addListener(types,listener);
  1430. },
  1431. off : function(types, listener){
  1432. return this.removeListener(types, listener)
  1433. },
  1434. trigger:function(){
  1435. return this.fireEvent.apply(this,arguments);
  1436. },
  1437. /**
  1438. * 移除事件监听器
  1439. * @method removeListener
  1440. * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
  1441. * @param { Function } fn 移除监听事件的函数引用
  1442. * @example
  1443. * ```javascript
  1444. * //changeCallback为方法体
  1445. * editor.removeListener("selectionchange",changeCallback);
  1446. * ```
  1447. */
  1448. removeListener:function (types, listener) {
  1449. types = utils.trim(types).split(/\s+/);
  1450. for (var i = 0, ti; ti = types[i++];) {
  1451. utils.removeItem(getListener(this, ti) || [], listener);
  1452. }
  1453. },
  1454. /**
  1455. * 触发事件
  1456. * @method fireEvent
  1457. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1458. * @remind 该方法会触发addListener
  1459. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1460. * @example
  1461. * ```javascript
  1462. * editor.fireEvent("selectionchange");
  1463. * ```
  1464. */
  1465. /**
  1466. * 触发事件
  1467. * @method fireEvent
  1468. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1469. * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
  1470. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1471. * @example
  1472. * ```javascript
  1473. *
  1474. * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
  1475. *
  1476. * console.log( arg1 + " " + arg2 );
  1477. *
  1478. * } );
  1479. *
  1480. * //触发selectionchange事件, 会执行上面的事件监听器
  1481. * //output: Hello World
  1482. * editor.fireEvent("selectionchange", "Hello", "World");
  1483. * ```
  1484. */
  1485. fireEvent:function () {
  1486. var types = arguments[0];
  1487. types = utils.trim(types).split(' ');
  1488. for (var i = 0, ti; ti = types[i++];) {
  1489. var listeners = getListener(this, ti),
  1490. r, t, k;
  1491. if (listeners) {
  1492. k = listeners.length;
  1493. while (k--) {
  1494. if(!listeners[k])continue;
  1495. t = listeners[k].apply(this, arguments);
  1496. if(t === true){
  1497. return t;
  1498. }
  1499. if (t !== undefined) {
  1500. r = t;
  1501. }
  1502. }
  1503. }
  1504. if (t = this['on' + ti.toLowerCase()]) {
  1505. r = t.apply(this, arguments);
  1506. }
  1507. }
  1508. return r;
  1509. }
  1510. };
  1511. /**
  1512. * 获得对象所拥有监听类型的所有监听器
  1513. * @unfile
  1514. * @module UE
  1515. * @since 1.2.6.1
  1516. * @method getListener
  1517. * @public
  1518. * @param { Object } obj 查询监听器的对象
  1519. * @param { String } type 事件类型
  1520. * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  1521. * @return { Array } 监听器数组
  1522. */
  1523. function getListener(obj, type, force) {
  1524. var allListeners;
  1525. type = type.toLowerCase();
  1526. return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
  1527. && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
  1528. }
  1529. // core/dtd.js
  1530. ///import editor.js
  1531. ///import core/dom/dom.js
  1532. ///import core/utils.js
  1533. /**
  1534. * dtd html语义化的体现类
  1535. * @constructor
  1536. * @namespace dtd
  1537. */
  1538. var dtd = dom.dtd = (function() {
  1539. function _( s ) {
  1540. for (var k in s) {
  1541. s[k.toUpperCase()] = s[k];
  1542. }
  1543. return s;
  1544. }
  1545. var X = utils.extend2;
  1546. var A = _({isindex:1,fieldset:1}),
  1547. B = _({input:1,button:1,select:1,textarea:1,label:1}),
  1548. C = X( _({a:1}), B ),
  1549. D = X( {iframe:1}, C ),
  1550. 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}),
  1551. F = _({ins:1,del:1,script:1,style:1}),
  1552. 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 ),
  1553. 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 ),
  1554. I = X( _({p:1}), H ),
  1555. J = X( _({iframe:1}), H, B ),
  1556. 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}),
  1557. L = X( _({a:0}), J ),//a不能被切开,所以把他
  1558. M = _({tr:1}),
  1559. N = _({'#':1}),
  1560. O = X( _({param:1}), K ),
  1561. P = X( _({form:1}), A, D, E, I ),
  1562. Q = _({li:1,ol:1,ul:1}),
  1563. R = _({style:1,script:1}),
  1564. S = _({base:1,link:1,meta:1,title:1}),
  1565. T = X( S, R ),
  1566. U = _({head:1,body:1}),
  1567. V = _({html:1});
  1568. 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}),
  1569. 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});
  1570. return _({
  1571. // $ 表示自定的属性
  1572. // body外的元素列表.
  1573. $nonBodyContent: X( V, U, S ),
  1574. //块结构元素列表
  1575. $block : block,
  1576. //内联元素列表
  1577. $inline : L,
  1578. $inlineWithA : X(_({a:1}),L),
  1579. $body : X( _({script:1,style:1}), block ),
  1580. $cdata : _({script:1,style:1}),
  1581. //自闭和元素
  1582. $empty : empty,
  1583. //不是自闭合,但不能让range选中里边
  1584. $nonChild : _({iframe:1,textarea:1}),
  1585. //列表元素列表
  1586. $listItem : _({dd:1,dt:1,li:1}),
  1587. //列表根元素列表
  1588. $list: _({ul:1,ol:1,dl:1}),
  1589. //不能认为是空的元素
  1590. $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}),
  1591. //如果没有子节点就可以删除的元素列表,像span,a
  1592. $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}),
  1593. $removeEmptyBlock : _({'p':1,'div':1}),
  1594. //在table元素里的元素列表
  1595. $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
  1596. //不转换的标签
  1597. $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
  1598. html: U,
  1599. head: T,
  1600. style: N,
  1601. script: N,
  1602. body: P,
  1603. base: {},
  1604. link: {},
  1605. meta: {},
  1606. title: N,
  1607. col : {},
  1608. tr : _({td:1,th:1}),
  1609. img : {},
  1610. embed: {},
  1611. colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
  1612. noscript : P,
  1613. td : P,
  1614. br : {},
  1615. th : P,
  1616. center : P,
  1617. kbd : L,
  1618. button : X( I, E ),
  1619. basefont : {},
  1620. h5 : L,
  1621. h4 : L,
  1622. samp : L,
  1623. h6 : L,
  1624. ol : Q,
  1625. h1 : L,
  1626. h3 : L,
  1627. option : N,
  1628. h2 : L,
  1629. form : X( A, D, E, I ),
  1630. select : _({optgroup:1,option:1}),
  1631. font : L,
  1632. ins : L,
  1633. menu : Q,
  1634. abbr : L,
  1635. label : L,
  1636. table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
  1637. code : L,
  1638. tfoot : M,
  1639. cite : L,
  1640. li : P,
  1641. input : {},
  1642. iframe : P,
  1643. strong : L,
  1644. textarea : N,
  1645. noframes : P,
  1646. big : L,
  1647. small : L,
  1648. //trace:
  1649. span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
  1650. hr : L,
  1651. dt : L,
  1652. sub : L,
  1653. optgroup : _({option:1}),
  1654. param : {},
  1655. bdo : L,
  1656. 'var' : L,
  1657. div : P,
  1658. object : O,
  1659. sup : L,
  1660. dd : P,
  1661. strike : L,
  1662. area : {},
  1663. dir : Q,
  1664. map : X( _({area:1,form:1,p:1}), A, F, E ),
  1665. applet : O,
  1666. dl : _({dt:1,dd:1}),
  1667. del : L,
  1668. isindex : {},
  1669. fieldset : X( _({legend:1}), K ),
  1670. thead : M,
  1671. ul : Q,
  1672. acronym : L,
  1673. b : L,
  1674. a : X( _({a:1}), J ),
  1675. blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
  1676. caption : L,
  1677. i : L,
  1678. u : L,
  1679. tbody : M,
  1680. s : L,
  1681. address : X( D, I ),
  1682. tt : L,
  1683. legend : L,
  1684. q : L,
  1685. pre : X( G, C ),
  1686. p : X(_({'a':1}),L),
  1687. em :L,
  1688. dfn : L
  1689. });
  1690. })();
  1691. // core/domUtils.js
  1692. /**
  1693. * Dom操作工具包
  1694. * @file
  1695. * @module UE.dom.domUtils
  1696. * @since 1.2.6.1
  1697. */
  1698. /**
  1699. * Dom操作工具包
  1700. * @unfile
  1701. * @module UE.dom.domUtils
  1702. */
  1703. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  1704. var tmpNode = startFromChild && node[start],
  1705. parent;
  1706. !tmpNode && (tmpNode = node[ltr]);
  1707. while (!tmpNode && (parent = (parent || node).parentNode)) {
  1708. if (parent.tagName == 'BODY' || guard && !guard(parent)) {
  1709. return null;
  1710. }
  1711. tmpNode = parent[ltr];
  1712. }
  1713. if (tmpNode && fn && !fn(tmpNode)) {
  1714. return getDomNode(tmpNode, start, ltr, false, fn);
  1715. }
  1716. return tmpNode;
  1717. }
  1718. var attrFix = ie && browser.version < 9 ? {
  1719. tabindex:"tabIndex",
  1720. readonly:"readOnly",
  1721. "for":"htmlFor",
  1722. "class":"className",
  1723. maxlength:"maxLength",
  1724. cellspacing:"cellSpacing",
  1725. cellpadding:"cellPadding",
  1726. rowspan:"rowSpan",
  1727. colspan:"colSpan",
  1728. usemap:"useMap",
  1729. frameborder:"frameBorder"
  1730. } : {
  1731. tabindex:"tabIndex",
  1732. readonly:"readOnly"
  1733. },
  1734. styleBlock = utils.listToMap([
  1735. '-webkit-box', '-moz-box', 'block' ,
  1736. 'list-item' , 'table' , 'table-row-group' ,
  1737. 'table-header-group', 'table-footer-group' ,
  1738. 'table-row' , 'table-column-group' , 'table-column' ,
  1739. 'table-cell' , 'table-caption'
  1740. ]);
  1741. var domUtils = dom.domUtils = {
  1742. //节点常量
  1743. NODE_ELEMENT:1,
  1744. NODE_DOCUMENT:9,
  1745. NODE_TEXT:3,
  1746. NODE_COMMENT:8,
  1747. NODE_DOCUMENT_FRAGMENT:11,
  1748. //位置关系
  1749. POSITION_IDENTICAL:0,
  1750. POSITION_DISCONNECTED:1,
  1751. POSITION_FOLLOWING:2,
  1752. POSITION_PRECEDING:4,
  1753. POSITION_IS_CONTAINED:8,
  1754. POSITION_CONTAINS:16,
  1755. //ie6使用其他的会有一段空白出现
  1756. fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B',
  1757. //-------------------------Node部分--------------------------------
  1758. keys:{
  1759. /*Backspace*/ 8:1, /*Delete*/ 46:1,
  1760. /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
  1761. 37:1, 38:1, 39:1, 40:1,
  1762. 13:1 /*enter*/
  1763. },
  1764. /**
  1765. * 获取节点A相对于节点B的位置关系
  1766. * @method getPosition
  1767. * @param { Node } nodeA 需要查询位置关系的节点A
  1768. * @param { Node } nodeB 需要查询位置关系的节点B
  1769. * @return { Number } 节点A与节点B的关系
  1770. * @example
  1771. * ```javascript
  1772. * //output: 20
  1773. * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
  1774. *
  1775. * switch ( position ) {
  1776. *
  1777. * //0
  1778. * case UE.dom.domUtils.POSITION_IDENTICAL:
  1779. * console.log('元素相同');
  1780. * break;
  1781. * //1
  1782. * case UE.dom.domUtils.POSITION_DISCONNECTED:
  1783. * console.log('两个节点在不同的文档中');
  1784. * break;
  1785. * //2
  1786. * case UE.dom.domUtils.POSITION_FOLLOWING:
  1787. * console.log('节点A在节点B之后');
  1788. * break;
  1789. * //4
  1790. * case UE.dom.domUtils.POSITION_PRECEDING;
  1791. * console.log('节点A在节点B之前');
  1792. * break;
  1793. * //8
  1794. * case UE.dom.domUtils.POSITION_IS_CONTAINED:
  1795. * console.log('节点A被节点B包含');
  1796. * break;
  1797. * case 10:
  1798. * console.log('节点A被节点B包含且节点A在节点B之后');
  1799. * break;
  1800. * //16
  1801. * case UE.dom.domUtils.POSITION_CONTAINS:
  1802. * console.log('节点A包含节点B');
  1803. * break;
  1804. * case 20:
  1805. * console.log('节点A包含节点B且节点A在节点B之前');
  1806. * break;
  1807. *
  1808. * }
  1809. * ```
  1810. */
  1811. getPosition:function (nodeA, nodeB) {
  1812. // 如果两个节点是同一个节点
  1813. if (nodeA === nodeB) {
  1814. // domUtils.POSITION_IDENTICAL
  1815. return 0;
  1816. }
  1817. var node,
  1818. parentsA = [nodeA],
  1819. parentsB = [nodeB];
  1820. node = nodeA;
  1821. while (node = node.parentNode) {
  1822. // 如果nodeB是nodeA的祖先节点
  1823. if (node === nodeB) {
  1824. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  1825. return 10;
  1826. }
  1827. parentsA.push(node);
  1828. }
  1829. node = nodeB;
  1830. while (node = node.parentNode) {
  1831. // 如果nodeA是nodeB的祖先节点
  1832. if (node === nodeA) {
  1833. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  1834. return 20;
  1835. }
  1836. parentsB.push(node);
  1837. }
  1838. parentsA.reverse();
  1839. parentsB.reverse();
  1840. if (parentsA[0] !== parentsB[0]) {
  1841. // domUtils.POSITION_DISCONNECTED
  1842. return 1;
  1843. }
  1844. var i = -1;
  1845. while (i++, parentsA[i] === parentsB[i]) {
  1846. }
  1847. nodeA = parentsA[i];
  1848. nodeB = parentsB[i];
  1849. while (nodeA = nodeA.nextSibling) {
  1850. if (nodeA === nodeB) {
  1851. // domUtils.POSITION_PRECEDING
  1852. return 4
  1853. }
  1854. }
  1855. // domUtils.POSITION_FOLLOWING
  1856. return 2;
  1857. },
  1858. /**
  1859. * 检测节点node在父节点中的索引位置
  1860. * @method getNodeIndex
  1861. * @param { Node } node 需要检测的节点对象
  1862. * @return { Number } 该节点在父节点中的位置
  1863. * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
  1864. */
  1865. /**
  1866. * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
  1867. * @method getNodeIndex
  1868. * @param { Node } node 需要检测的节点对象
  1869. * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
  1870. * @return { Number } 该节点在父节点中的位置
  1871. * @example
  1872. * ```javascript
  1873. *
  1874. * var node = document.createElement("div");
  1875. *
  1876. * node.appendChild( document.createTextNode( "hello" ) );
  1877. * node.appendChild( document.createTextNode( "world" ) );
  1878. * node.appendChild( node = document.createElement( "div" ) );
  1879. *
  1880. * //output: 2
  1881. * console.log( UE.dom.domUtils.getNodeIndex( node ) );
  1882. *
  1883. * //output: 1
  1884. * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
  1885. *
  1886. * ```
  1887. */
  1888. getNodeIndex:function (node, ignoreTextNode) {
  1889. var preNode = node,
  1890. i = 0;
  1891. while (preNode = preNode.previousSibling) {
  1892. if (ignoreTextNode && preNode.nodeType == 3) {
  1893. if(preNode.nodeType != preNode.nextSibling.nodeType ){
  1894. i++;
  1895. }
  1896. continue;
  1897. }
  1898. i++;
  1899. }
  1900. return i;
  1901. },
  1902. /**
  1903. * 检测节点node是否在给定的document对象上
  1904. * @method inDoc
  1905. * @param { Node } node 需要检测的节点对象
  1906. * @param { DomDocument } doc 需要检测的document对象
  1907. * @return { Boolean } 该节点node是否在给定的document的dom树上
  1908. * @example
  1909. * ```javascript
  1910. *
  1911. * var node = document.createElement("div");
  1912. *
  1913. * //output: false
  1914. * console.log( UE.do.domUtils.inDoc( node, document ) );
  1915. *
  1916. * document.body.appendChild( node );
  1917. *
  1918. * //output: true
  1919. * console.log( UE.do.domUtils.inDoc( node, document ) );
  1920. *
  1921. * ```
  1922. */
  1923. inDoc:function (node, doc) {
  1924. return domUtils.getPosition(node, doc) == 10;
  1925. },
  1926. /**
  1927. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  1928. * 查找的起点是给定node节点的父节点。
  1929. * @method findParent
  1930. * @param { Node } node 需要查找的节点
  1931. * @param { Function } filterFn 自定义的过滤方法。
  1932. * @warning 查找的终点是到body节点为止
  1933. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  1934. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  1935. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  1936. * @example
  1937. * ```javascript
  1938. * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
  1939. *
  1940. * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
  1941. * return node.tagName === "HTML";
  1942. *
  1943. * } );
  1944. *
  1945. * //output: true
  1946. * console.log( filterNode === null );
  1947. * ```
  1948. */
  1949. /**
  1950. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  1951. * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
  1952. * @method findParent
  1953. * @param { Node } node 需要查找的节点
  1954. * @param { Function } filterFn 自定义的过滤方法。
  1955. * @param { Boolean } includeSelf 查找过程是否包含自身
  1956. * @warning 查找的终点是到body节点为止
  1957. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  1958. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  1959. * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
  1960. * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
  1961. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  1962. * @example
  1963. * ```html
  1964. * <body>
  1965. *
  1966. * <div id="test">
  1967. * </div>
  1968. *
  1969. * <script type="text/javascript">
  1970. *
  1971. * //output: DIV, BODY
  1972. * var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
  1973. *
  1974. * console.log( node.tagName );
  1975. * return false;
  1976. *
  1977. * }, true );
  1978. *
  1979. * </script>
  1980. * </body>
  1981. * ```
  1982. */
  1983. findParent:function (node, filterFn, includeSelf) {
  1984. if (node && !domUtils.isBody(node)) {
  1985. node = includeSelf ? node : node.parentNode;
  1986. while (node) {
  1987. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  1988. return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
  1989. }
  1990. node = node.parentNode;
  1991. }
  1992. }
  1993. return null;
  1994. },
  1995. /**
  1996. * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
  1997. * @method findParentByTagName
  1998. * @param { Node } node 需要查找的节点对象
  1999. * @param { Array } tagNames 需要查找的父节点的名称数组
  2000. * @warning 查找的终点是到body节点为止
  2001. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2002. * @example
  2003. * ```javascript
  2004. * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
  2005. * //output: BODY
  2006. * console.log( node.tagName );
  2007. * ```
  2008. */
  2009. /**
  2010. * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
  2011. * 否则, 起点是node的父节点。
  2012. * @method findParentByTagName
  2013. * @param { Node } node 需要查找的节点对象
  2014. * @param { Array } tagNames 需要查找的父节点的名称数组
  2015. * @param { Boolean } includeSelf 查找过程是否包含node节点自身
  2016. * @warning 查找的终点是到body节点为止
  2017. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2018. * @example
  2019. * ```javascript
  2020. * var queryTarget = document.getElementsByTagName("div")[0];
  2021. * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
  2022. * //output: true
  2023. * console.log( queryTarget === node );
  2024. * ```
  2025. */
  2026. findParentByTagName:function (node, tagNames, includeSelf, excludeFn) {
  2027. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  2028. return domUtils.findParent(node, function (node) {
  2029. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  2030. }, includeSelf);
  2031. },
  2032. /**
  2033. * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
  2034. * @method findParents
  2035. * @param { Node } node 需要查找的节点对象
  2036. * @return { Array } 给定节点的祖先节点数组
  2037. * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  2038. * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  2039. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  2040. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  2041. */
  2042. /**
  2043. * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
  2044. * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
  2045. * @method findParents
  2046. * @param { Node } node 需要查找的节点对象
  2047. * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
  2048. * @return { Array } 给定节点的祖先节点数组
  2049. */
  2050. findParents:function (node, includeSelf, filterFn, closerFirst) {
  2051. var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
  2052. while (node = domUtils.findParent(node, filterFn)) {
  2053. parents.push(node);
  2054. }
  2055. return closerFirst ? parents : parents.reverse();
  2056. },
  2057. /**
  2058. * 在节点node后面插入新节点newNode
  2059. * @method insertAfter
  2060. * @param { Node } node 目标节点
  2061. * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
  2062. * @return { Node } 新插入的节点
  2063. */
  2064. insertAfter:function (node, newNode) {
  2065. return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling):
  2066. node.parentNode.appendChild(newNode);
  2067. },
  2068. /**
  2069. * 删除节点node及其下属的所有节点
  2070. * @method remove
  2071. * @param { Node } node 需要删除的节点对象
  2072. * @return { Node } 返回刚删除的节点对象
  2073. * @example
  2074. * ```html
  2075. * <div id="test">
  2076. * <div id="child">你好</div>
  2077. * </div>
  2078. * <script>
  2079. * UE.dom.domUtils.remove( document.body, false );
  2080. * //output: false
  2081. * console.log( document.getElementById( "child" ) !== null );
  2082. * </script>
  2083. * ```
  2084. */
  2085. /**
  2086. * 删除节点node,并根据keepChildren的值决定是否保留子节点
  2087. * @method remove
  2088. * @param { Node } node 需要删除的节点对象
  2089. * @param { Boolean } keepChildren 是否需要保留子节点
  2090. * @return { Node } 返回刚删除的节点对象
  2091. * @example
  2092. * ```html
  2093. * <div id="test">
  2094. * <div id="child">你好</div>
  2095. * </div>
  2096. * <script>
  2097. * UE.dom.domUtils.remove( document.body, true );
  2098. * //output: true
  2099. * console.log( document.getElementById( "child" ) !== null );
  2100. * </script>
  2101. * ```
  2102. */
  2103. remove:function (node, keepChildren) {
  2104. var parent = node.parentNode,
  2105. child;
  2106. if (parent) {
  2107. if (keepChildren && node.hasChildNodes()) {
  2108. while (child = node.firstChild) {
  2109. parent.insertBefore(child, node);
  2110. }
  2111. }
  2112. parent.removeChild(node);
  2113. }
  2114. return node;
  2115. },
  2116. /**
  2117. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  2118. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  2119. * @method getNextDomNode
  2120. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2121. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2122. * @example
  2123. * ```html
  2124. * <body>
  2125. * <div id="test">
  2126. * <span></span>
  2127. * </div>
  2128. * <i>xxx</i>
  2129. * </body>
  2130. * <script>
  2131. *
  2132. * //output: i节点
  2133. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2134. *
  2135. * </script>
  2136. * ```
  2137. * @example
  2138. * ```html
  2139. * <body>
  2140. * <div>
  2141. * <span></span>
  2142. * <i id="test">xxx</i>
  2143. * </div>
  2144. * <b>xxx</b>
  2145. * </body>
  2146. * <script>
  2147. *
  2148. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  2149. * //output: b节点
  2150. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2151. *
  2152. * </script>
  2153. * ```
  2154. */
  2155. /**
  2156. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  2157. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  2158. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  2159. * @method getNextDomNode
  2160. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2161. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  2162. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2163. * @see UE.dom.domUtils.getNextDomNode(Node)
  2164. */
  2165. getNextDomNode:function (node, startFromChild, filterFn, guard) {
  2166. return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
  2167. },
  2168. getPreDomNode:function (node, startFromChild, filterFn, guard) {
  2169. return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
  2170. },
  2171. /**
  2172. * 检测节点node是否属是UEditor定义的bookmark节点
  2173. * @method isBookmarkNode
  2174. * @private
  2175. * @param { Node } node 需要检测的节点对象
  2176. * @return { Boolean } 是否是bookmark节点
  2177. * @example
  2178. * ```html
  2179. * <span id="_baidu_bookmark_1"></span>
  2180. * <script>
  2181. * var bookmarkNode = document.getElementById("_baidu_bookmark_1");
  2182. * //output: true
  2183. * console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
  2184. * </script>
  2185. * ```
  2186. */
  2187. isBookmarkNode:function (node) {
  2188. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  2189. },
  2190. /**
  2191. * 获取节点node所属的window对象
  2192. * @method getWindow
  2193. * @param { Node } node 节点对象
  2194. * @return { Window } 当前节点所属的window对象
  2195. * @example
  2196. * ```javascript
  2197. * //output: true
  2198. * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
  2199. * ```
  2200. */
  2201. getWindow:function (node) {
  2202. var doc = node.ownerDocument || node;
  2203. return doc.defaultView || doc.parentWindow;
  2204. },
  2205. /**
  2206. * 获取离nodeA与nodeB最近的公共的祖先节点
  2207. * @method getCommonAncestor
  2208. * @param { Node } nodeA 第一个节点
  2209. * @param { Node } nodeB 第二个节点
  2210. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  2211. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  2212. * @example
  2213. * ```javascript
  2214. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  2215. * //output: true
  2216. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  2217. * ```
  2218. */
  2219. getCommonAncestor:function (nodeA, nodeB) {
  2220. if (nodeA === nodeB)
  2221. return nodeA;
  2222. var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
  2223. while (parent = parent.parentNode) {
  2224. if (parent === nodeB) {
  2225. return parent;
  2226. }
  2227. parentsA.push(parent);
  2228. }
  2229. parent = nodeB;
  2230. while (parent = parent.parentNode) {
  2231. if (parent === nodeA)
  2232. return parent;
  2233. parentsB.push(parent);
  2234. }
  2235. parentsA.reverse();
  2236. parentsB.reverse();
  2237. while (i++, parentsA[i] === parentsB[i]) {
  2238. }
  2239. return i == 0 ? null : parentsA[i - 1];
  2240. },
  2241. /**
  2242. * 清除node节点左右连续为空的兄弟inline节点
  2243. * @method clearEmptySibling
  2244. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2245. * 则这些兄弟节点将被删除
  2246. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  2247. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  2248. * @example
  2249. * ```html
  2250. * <body>
  2251. * <div></div>
  2252. * <span id="test"></span>
  2253. * <i></i>
  2254. * <b></b>
  2255. * <em>xxx</em>
  2256. * <span></span>
  2257. * </body>
  2258. * <script>
  2259. *
  2260. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  2261. *
  2262. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  2263. * console.log( document.body.innerHTML );
  2264. *
  2265. * </script>
  2266. * ```
  2267. */
  2268. /**
  2269. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2270. * 则忽略对右边兄弟节点的操作。
  2271. * @method clearEmptySibling
  2272. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2273. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2274. * 则这些兄弟节点将被删除
  2275. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2276. */
  2277. /**
  2278. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2279. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  2280. * @method clearEmptySibling
  2281. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2282. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2283. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  2284. * 则这些兄弟节点将被删除
  2285. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2286. */
  2287. clearEmptySibling:function (node, ignoreNext, ignorePre) {
  2288. function clear(next, dir) {
  2289. var tmpNode;
  2290. while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
  2291. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  2292. || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
  2293. tmpNode = next[dir];
  2294. domUtils.remove(next);
  2295. next = tmpNode;
  2296. }
  2297. }
  2298. !ignoreNext && clear(node.nextSibling, 'nextSibling');
  2299. !ignorePre && clear(node.previousSibling, 'previousSibling');
  2300. },
  2301. /**
  2302. * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
  2303. * @method split
  2304. * @param { Node } textNode 需要拆分的文本节点对象
  2305. * @param { int } offset 需要拆分的位置, 位置计算从0开始
  2306. * @return { Node } 拆分后形成的新节点
  2307. * @example
  2308. * ```html
  2309. * <div id="test">abcdef</div>
  2310. * <script>
  2311. * var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
  2312. * //output: def
  2313. * console.log( newNode.nodeValue );
  2314. * </script>
  2315. * ```
  2316. */
  2317. split:function (node, offset) {
  2318. var doc = node.ownerDocument;
  2319. if (browser.ie && offset == node.nodeValue.length) {
  2320. var next = doc.createTextNode('');
  2321. return domUtils.insertAfter(node, next);
  2322. }
  2323. var retval = node.splitText(offset);
  2324. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  2325. if (browser.ie8) {
  2326. var tmpNode = doc.createTextNode('');
  2327. domUtils.insertAfter(retval, tmpNode);
  2328. domUtils.remove(tmpNode);
  2329. }
  2330. return retval;
  2331. },
  2332. /**
  2333. * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
  2334. * @method isWhitespace
  2335. * @param { Node } node 需要检测的节点对象
  2336. * @return { Boolean } 检测的节点是否为空
  2337. * @example
  2338. * ```html
  2339. * <div id="test">
  2340. *
  2341. * </div>
  2342. * <script>
  2343. * //output: true
  2344. * console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
  2345. * </script>
  2346. * ```
  2347. */
  2348. isWhitespace:function (node) {
  2349. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
  2350. },
  2351. /**
  2352. * 获取元素element相对于viewport的位置坐标
  2353. * @method getXY
  2354. * @param { Node } element 需要计算位置的节点对象
  2355. * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
  2356. * y代表垂直偏移距离。
  2357. *
  2358. * @example
  2359. * ```javascript
  2360. * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
  2361. * //output: test的坐标为: 12, 24
  2362. * console.log( 'test的坐标为: ', location.x, ',', location.y );
  2363. * ```
  2364. */
  2365. getXY:function (element) {
  2366. var x = 0, y = 0;
  2367. while (element.offsetParent) {
  2368. y += element.offsetTop;
  2369. x += element.offsetLeft;
  2370. element = element.offsetParent;
  2371. }
  2372. return { 'x':x, 'y':y};
  2373. },
  2374. /**
  2375. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2376. * @method on
  2377. * @param { Node } element 需要绑定事件的节点对象
  2378. * @param { String } type 绑定的事件类型
  2379. * @param { Function } handler 事件处理器
  2380. * @example
  2381. * ```javascript
  2382. * UE.dom.domUtils.on(document.body,"click",function(e){
  2383. * //e为事件对象,this为被点击元素对戏那个
  2384. * });
  2385. * ```
  2386. */
  2387. /**
  2388. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2389. * @method on
  2390. * @param { Node } element 需要绑定事件的节点对象
  2391. * @param { Array } type 绑定的事件类型数组
  2392. * @param { Function } handler 事件处理器
  2393. * @example
  2394. * ```javascript
  2395. * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
  2396. * //evt为事件对象,this为被点击元素对象
  2397. * });
  2398. * ```
  2399. */
  2400. on:function (element, type, handler) {
  2401. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2402. k = types.length;
  2403. if (k) while (k--) {
  2404. type = types[k];
  2405. if (element.addEventListener) {
  2406. element.addEventListener(type, handler, false);
  2407. } else {
  2408. if (!handler._d) {
  2409. handler._d = {
  2410. els : []
  2411. };
  2412. }
  2413. var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element);
  2414. if (!handler._d[key] || index == -1) {
  2415. if(index == -1){
  2416. handler._d.els.push(element);
  2417. }
  2418. if(!handler._d[key]){
  2419. handler._d[key] = function (evt) {
  2420. return handler.call(evt.srcElement, evt || window.event);
  2421. };
  2422. }
  2423. element.attachEvent('on' + type, handler._d[key]);
  2424. }
  2425. }
  2426. }
  2427. element = null;
  2428. },
  2429. /**
  2430. * 解除DOM事件绑定
  2431. * @method un
  2432. * @param { Node } element 需要解除事件绑定的节点对象
  2433. * @param { String } type 需要接触绑定的事件类型
  2434. * @param { Function } handler 对应的事件处理器
  2435. * @example
  2436. * ```javascript
  2437. * UE.dom.domUtils.un(document.body,"click",function(evt){
  2438. * //evt为事件对象,this为被点击元素对象
  2439. * });
  2440. * ```
  2441. */
  2442. /**
  2443. * 解除DOM事件绑定
  2444. * @method un
  2445. * @param { Node } element 需要解除事件绑定的节点对象
  2446. * @param { Array } type 需要接触绑定的事件类型数组
  2447. * @param { Function } handler 对应的事件处理器
  2448. * @example
  2449. * ```javascript
  2450. * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
  2451. * //evt为事件对象,this为被点击元素对象
  2452. * });
  2453. * ```
  2454. */
  2455. un:function (element, type, handler) {
  2456. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2457. k = types.length;
  2458. if (k) while (k--) {
  2459. type = types[k];
  2460. if (element.removeEventListener) {
  2461. element.removeEventListener(type, handler, false);
  2462. } else {
  2463. var key = type + handler.toString();
  2464. try{
  2465. element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
  2466. }catch(e){}
  2467. if (handler._d && handler._d[key]) {
  2468. var index = utils.indexOf(handler._d.els,element);
  2469. if(index!=-1){
  2470. handler._d.els.splice(index,1);
  2471. }
  2472. handler._d.els.length == 0 && delete handler._d[key];
  2473. }
  2474. }
  2475. }
  2476. },
  2477. /**
  2478. * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
  2479. * @method isSameElement
  2480. * @param { Node } nodeA 需要比较的节点
  2481. * @param { Node } nodeB 需要比较的节点
  2482. * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
  2483. * @example
  2484. * ```html
  2485. * <span style="font-size:12px">ssss</span>
  2486. * <span style="font-size:12px">bbbbb</span>
  2487. * <span style="font-size:13px">ssss</span>
  2488. * <span style="font-size:14px">bbbbb</span>
  2489. *
  2490. * <script>
  2491. *
  2492. * var nodes = document.getElementsByTagName( "span" );
  2493. *
  2494. * //output: true
  2495. * console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
  2496. *
  2497. * //output: false
  2498. * console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
  2499. *
  2500. * </script>
  2501. * ```
  2502. */
  2503. isSameElement:function (nodeA, nodeB) {
  2504. if (nodeA.tagName != nodeB.tagName) {
  2505. return false;
  2506. }
  2507. var thisAttrs = nodeA.attributes,
  2508. otherAttrs = nodeB.attributes;
  2509. if (!ie && thisAttrs.length != otherAttrs.length) {
  2510. return false;
  2511. }
  2512. var attrA, attrB, al = 0, bl = 0;
  2513. for (var i = 0; attrA = thisAttrs[i++];) {
  2514. if (attrA.nodeName == 'style') {
  2515. if (attrA.specified) {
  2516. al++;
  2517. }
  2518. if (domUtils.isSameStyle(nodeA, nodeB)) {
  2519. continue;
  2520. } else {
  2521. return false;
  2522. }
  2523. }
  2524. if (ie) {
  2525. if (attrA.specified) {
  2526. al++;
  2527. attrB = otherAttrs.getNamedItem(attrA.nodeName);
  2528. } else {
  2529. continue;
  2530. }
  2531. } else {
  2532. attrB = nodeB.attributes[attrA.nodeName];
  2533. }
  2534. if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
  2535. return false;
  2536. }
  2537. }
  2538. // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
  2539. if (ie) {
  2540. for (i = 0; attrB = otherAttrs[i++];) {
  2541. if (attrB.specified) {
  2542. bl++;
  2543. }
  2544. }
  2545. if (al != bl) {
  2546. return false;
  2547. }
  2548. }
  2549. return true;
  2550. },
  2551. /**
  2552. * 判断节点nodeA与节点nodeB的元素的style属性是否一致
  2553. * @method isSameStyle
  2554. * @param { Node } nodeA 需要比较的节点
  2555. * @param { Node } nodeB 需要比较的节点
  2556. * @return { Boolean } 两个节点是否具有相同的style属性值
  2557. * @example
  2558. * ```html
  2559. * <span style="font-size:12px">ssss</span>
  2560. * <span style="font-size:12px">bbbbb</span>
  2561. * <span style="font-size:13px">ssss</span>
  2562. * <span style="font-size:14px">bbbbb</span>
  2563. *
  2564. * <script>
  2565. *
  2566. * var nodes = document.getElementsByTagName( "span" );
  2567. *
  2568. * //output: true
  2569. * console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
  2570. *
  2571. * //output: false
  2572. * console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
  2573. *
  2574. * </script>
  2575. * ```
  2576. */
  2577. isSameStyle:function (nodeA, nodeB) {
  2578. var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
  2579. styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
  2580. if (browser.opera) {
  2581. styleA = nodeA.style;
  2582. styleB = nodeB.style;
  2583. if (styleA.length != styleB.length)
  2584. return false;
  2585. for (var p in styleA) {
  2586. if (/^(\d+|csstext)$/i.test(p)) {
  2587. continue;
  2588. }
  2589. if (styleA[p] != styleB[p]) {
  2590. return false;
  2591. }
  2592. }
  2593. return true;
  2594. }
  2595. if (!styleA || !styleB) {
  2596. return styleA == styleB;
  2597. }
  2598. styleA = styleA.split(';');
  2599. styleB = styleB.split(';');
  2600. if (styleA.length != styleB.length) {
  2601. return false;
  2602. }
  2603. for (var i = 0, ci; ci = styleA[i++];) {
  2604. if (utils.indexOf(styleB, ci) == -1) {
  2605. return false;
  2606. }
  2607. }
  2608. return true;
  2609. },
  2610. /**
  2611. * 检查节点node是否为block元素
  2612. * @method isBlockElm
  2613. * @param { Node } node 需要检测的节点对象
  2614. * @return { Boolean } 是否是block元素节点
  2615. * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
  2616. * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
  2617. * @example
  2618. * ```html
  2619. * <span id="test1" style="display: block"></span>
  2620. * <span id="test2"></span>
  2621. * <div id="test3" style="display: inline"></div>
  2622. *
  2623. * <script>
  2624. *
  2625. * //output: true
  2626. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
  2627. *
  2628. * //output: false
  2629. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
  2630. *
  2631. * //output: true
  2632. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
  2633. *
  2634. * </script>
  2635. * ```
  2636. */
  2637. isBlockElm:function (node) {
  2638. return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
  2639. },
  2640. /**
  2641. * 检测node节点是否为body节点
  2642. * @method isBody
  2643. * @param { Element } node 需要检测的dom元素
  2644. * @return { Boolean } 给定的元素是否是body元素
  2645. * @example
  2646. * ```javascript
  2647. * //output: true
  2648. * console.log( UE.dom.domUtils.isBody( document.body ) );
  2649. * ```
  2650. */
  2651. isBody:function (node) {
  2652. return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
  2653. },
  2654. /**
  2655. * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
  2656. * 拆分形成的两个节点之间是node节点
  2657. * @method breakParent
  2658. * @param { Node } node 作为分界的节点对象
  2659. * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
  2660. * @return { Node } 给定的node分界节点
  2661. * @example
  2662. * ```javascript
  2663. *
  2664. * var node = document.createElement("span"),
  2665. * wrapNode = document.createElement( "div" ),
  2666. * parent = document.createElement("p");
  2667. *
  2668. * parent.appendChild( node );
  2669. * wrapNode.appendChild( parent );
  2670. *
  2671. * //拆分前
  2672. * //output: <p><span></span></p>
  2673. * console.log( wrapNode.innerHTML );
  2674. *
  2675. *
  2676. * UE.dom.domUtils.breakParent( node, parent );
  2677. * //拆分后
  2678. * //output: <p></p><span></span><p></p>
  2679. * console.log( wrapNode.innerHTML );
  2680. *
  2681. * ```
  2682. */
  2683. breakParent:function (node, parent) {
  2684. var tmpNode,
  2685. parentClone = node,
  2686. clone = node,
  2687. leftNodes,
  2688. rightNodes;
  2689. do {
  2690. parentClone = parentClone.parentNode;
  2691. if (leftNodes) {
  2692. tmpNode = parentClone.cloneNode(false);
  2693. tmpNode.appendChild(leftNodes);
  2694. leftNodes = tmpNode;
  2695. tmpNode = parentClone.cloneNode(false);
  2696. tmpNode.appendChild(rightNodes);
  2697. rightNodes = tmpNode;
  2698. } else {
  2699. leftNodes = parentClone.cloneNode(false);
  2700. rightNodes = leftNodes.cloneNode(false);
  2701. }
  2702. while (tmpNode = clone.previousSibling) {
  2703. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  2704. }
  2705. while (tmpNode = clone.nextSibling) {
  2706. rightNodes.appendChild(tmpNode);
  2707. }
  2708. clone = parentClone;
  2709. } while (parent !== parentClone);
  2710. tmpNode = parent.parentNode;
  2711. tmpNode.insertBefore(leftNodes, parent);
  2712. tmpNode.insertBefore(rightNodes, parent);
  2713. tmpNode.insertBefore(node, rightNodes);
  2714. domUtils.remove(parent);
  2715. return node;
  2716. },
  2717. /**
  2718. * 检查节点node是否是空inline节点
  2719. * @method isEmptyInlineElement
  2720. * @param { Node } node 需要检测的节点对象
  2721. * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
  2722. * @example
  2723. * ```html
  2724. * <b><i></i></b> => 1
  2725. * <b><i></i><u></u></b> => 1
  2726. * <b></b> => 1
  2727. * <b>xx<i></i></b> => 0
  2728. * ```
  2729. */
  2730. isEmptyInlineElement:function (node) {
  2731. if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
  2732. return 0;
  2733. }
  2734. node = node.firstChild;
  2735. while (node) {
  2736. //如果是创建的bookmark就跳过
  2737. if (domUtils.isBookmarkNode(node)) {
  2738. return 0;
  2739. }
  2740. if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
  2741. node.nodeType == 3 && !domUtils.isWhitespace(node)
  2742. ) {
  2743. return 0;
  2744. }
  2745. node = node.nextSibling;
  2746. }
  2747. return 1;
  2748. },
  2749. /**
  2750. * 删除node节点下首尾两端的空白文本子节点
  2751. * @method trimWhiteTextNode
  2752. * @param { Element } node 需要执行删除操作的元素对象
  2753. * @example
  2754. * ```javascript
  2755. * var node = document.createElement("div");
  2756. *
  2757. * node.appendChild( document.createTextNode( "" ) );
  2758. *
  2759. * node.appendChild( document.createElement("div") );
  2760. *
  2761. * node.appendChild( document.createTextNode( "" ) );
  2762. *
  2763. * //3
  2764. * console.log( node.childNodes.length );
  2765. *
  2766. * UE.dom.domUtils.trimWhiteTextNode( node );
  2767. *
  2768. * //1
  2769. * console.log( node.childNodes.length );
  2770. * ```
  2771. */
  2772. trimWhiteTextNode:function (node) {
  2773. function remove(dir) {
  2774. var child;
  2775. while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
  2776. node.removeChild(child);
  2777. }
  2778. }
  2779. remove('firstChild');
  2780. remove('lastChild');
  2781. },
  2782. /**
  2783. * 合并node节点下相同的子节点
  2784. * @name mergeChild
  2785. * @desc
  2786. * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
  2787. * @example
  2788. * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
  2789. * ==> UE.dom.domUtils.mergeChild(node,'span')
  2790. * <p><span style="font-size:12px;">xxaaxx</span></p>
  2791. */
  2792. mergeChild:function (node, tagName, attrs) {
  2793. var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
  2794. for (var i = 0, ci; ci = list[i++];) {
  2795. if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
  2796. continue;
  2797. }
  2798. //span单独处理
  2799. if (ci.tagName.toLowerCase() == 'span') {
  2800. if (node === ci.parentNode) {
  2801. domUtils.trimWhiteTextNode(node);
  2802. if (node.childNodes.length == 1) {
  2803. node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
  2804. domUtils.remove(ci, true);
  2805. continue;
  2806. }
  2807. }
  2808. ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
  2809. if (attrs) {
  2810. var style = attrs.style;
  2811. if (style) {
  2812. style = style.split(';');
  2813. for (var j = 0, s; s = style[j++];) {
  2814. ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
  2815. }
  2816. }
  2817. }
  2818. if (domUtils.isSameStyle(ci, node)) {
  2819. domUtils.remove(ci, true);
  2820. }
  2821. continue;
  2822. }
  2823. if (domUtils.isSameElement(node, ci)) {
  2824. domUtils.remove(ci, true);
  2825. }
  2826. }
  2827. },
  2828. /**
  2829. * 原生方法getElementsByTagName的封装
  2830. * @method getElementsByTagName
  2831. * @param { Node } node 目标节点对象
  2832. * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
  2833. * @return { Array } 符合条件的节点集合
  2834. */
  2835. getElementsByTagName:function (node, name,filter) {
  2836. if(filter && utils.isString(filter)){
  2837. var className = filter;
  2838. filter = function(node){return domUtils.hasClass(node,className)}
  2839. }
  2840. name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' ');
  2841. var arr = [];
  2842. for(var n = 0,ni;ni=name[n++];){
  2843. var list = node.getElementsByTagName(ni);
  2844. for (var i = 0, ci; ci = list[i++];) {
  2845. if(!filter || filter(ci))
  2846. arr.push(ci);
  2847. }
  2848. }
  2849. return arr;
  2850. },
  2851. /**
  2852. * 将节点node提取到父节点上
  2853. * @method mergeToParent
  2854. * @param { Element } node 需要提取的元素对象
  2855. * @example
  2856. * ```html
  2857. * <div id="parent">
  2858. * <div id="sub">
  2859. * <span id="child"></span>
  2860. * </div>
  2861. * </div>
  2862. *
  2863. * <script>
  2864. *
  2865. * var child = document.getElementById( "child" );
  2866. *
  2867. * //output: sub
  2868. * console.log( child.parentNode.id );
  2869. *
  2870. * UE.dom.domUtils.mergeToParent( child );
  2871. *
  2872. * //output: parent
  2873. * console.log( child.parentNode.id );
  2874. *
  2875. * </script>
  2876. * ```
  2877. */
  2878. mergeToParent:function (node) {
  2879. var parent = node.parentNode;
  2880. while (parent && dtd.$removeEmpty[parent.tagName]) {
  2881. if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
  2882. domUtils.trimWhiteTextNode(parent);
  2883. //span需要特殊处理 不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
  2884. if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
  2885. || (parent.tagName == 'A' && node.tagName == 'SPAN')) {
  2886. if (parent.childNodes.length > 1 || parent !== node.parentNode) {
  2887. node.style.cssText = parent.style.cssText + ";" + node.style.cssText;
  2888. parent = parent.parentNode;
  2889. continue;
  2890. } else {
  2891. parent.style.cssText += ";" + node.style.cssText;
  2892. //trace:952 a标签要保持下划线
  2893. if (parent.tagName == 'A') {
  2894. parent.style.textDecoration = 'underline';
  2895. }
  2896. }
  2897. }
  2898. if (parent.tagName != 'A') {
  2899. parent === node.parentNode && domUtils.remove(node, true);
  2900. break;
  2901. }
  2902. }
  2903. parent = parent.parentNode;
  2904. }
  2905. },
  2906. /**
  2907. * 合并节点node的左右兄弟节点
  2908. * @method mergeSibling
  2909. * @param { Element } node 需要合并的目标节点
  2910. * @example
  2911. * ```html
  2912. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  2913. *
  2914. * <script>
  2915. * var demoNode = document.getElementById("test");
  2916. * UE.dom.domUtils.mergeSibling( demoNode );
  2917. * //output: xxxxoooxxxx
  2918. * console.log( demoNode.innerHTML );
  2919. * </script>
  2920. * ```
  2921. */
  2922. /**
  2923. * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
  2924. * @method mergeSibling
  2925. * @param { Element } node 需要合并的目标节点
  2926. * @param { Boolean } ignorePre 是否忽略合并左节点
  2927. * @example
  2928. * ```html
  2929. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  2930. *
  2931. * <script>
  2932. * var demoNode = document.getElementById("test");
  2933. * UE.dom.domUtils.mergeSibling( demoNode, true );
  2934. * //output: oooxxxx
  2935. * console.log( demoNode.innerHTML );
  2936. * </script>
  2937. * ```
  2938. */
  2939. /**
  2940. * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
  2941. * @method mergeSibling
  2942. * @param { Element } node 需要合并的目标节点
  2943. * @param { Boolean } ignorePre 是否忽略合并左节点
  2944. * @param { Boolean } ignoreNext 是否忽略合并右节点
  2945. * @remind 如果同时忽略左右节点, 则该操作什么也不会做
  2946. * @example
  2947. * ```html
  2948. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  2949. *
  2950. * <script>
  2951. * var demoNode = document.getElementById("test");
  2952. * UE.dom.domUtils.mergeSibling( demoNode, false, true );
  2953. * //output: xxxxooo
  2954. * console.log( demoNode.innerHTML );
  2955. * </script>
  2956. * ```
  2957. */
  2958. mergeSibling:function (node, ignorePre, ignoreNext) {
  2959. function merge(rtl, start, node) {
  2960. var next;
  2961. if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
  2962. while (next.firstChild) {
  2963. if (start == 'firstChild') {
  2964. node.insertBefore(next.lastChild, node.firstChild);
  2965. } else {
  2966. node.appendChild(next.firstChild);
  2967. }
  2968. }
  2969. domUtils.remove(next);
  2970. }
  2971. }
  2972. !ignorePre && merge('previousSibling', 'firstChild', node);
  2973. !ignoreNext && merge('nextSibling', 'lastChild', node);
  2974. },
  2975. /**
  2976. * 设置节点node及其子节点不会被选中
  2977. * @method unSelectable
  2978. * @param { Element } node 需要执行操作的dom元素
  2979. * @remind 执行该操作后的节点, 将不能被鼠标选中
  2980. * @example
  2981. * ```javascript
  2982. * UE.dom.domUtils.unSelectable( document.body );
  2983. * ```
  2984. */
  2985. unSelectable:ie && browser.ie9below || browser.opera ? function (node) {
  2986. //for ie9
  2987. node.onselectstart = function () {
  2988. return false;
  2989. };
  2990. node.onclick = node.onkeyup = node.onkeydown = function () {
  2991. return false;
  2992. };
  2993. node.unselectable = 'on';
  2994. node.setAttribute("unselectable", "on");
  2995. for (var i = 0, ci; ci = node.all[i++];) {
  2996. switch (ci.tagName.toLowerCase()) {
  2997. case 'iframe' :
  2998. case 'textarea' :
  2999. case 'input' :
  3000. case 'select' :
  3001. break;
  3002. default :
  3003. ci.unselectable = 'on';
  3004. node.setAttribute("unselectable", "on");
  3005. }
  3006. }
  3007. } : function (node) {
  3008. node.style.MozUserSelect =
  3009. node.style.webkitUserSelect =
  3010. node.style.msUserSelect =
  3011. node.style.KhtmlUserSelect = 'none';
  3012. },
  3013. /**
  3014. * 删除节点node上的指定属性名称的属性
  3015. * @method removeAttributes
  3016. * @param { Node } node 需要删除属性的节点对象
  3017. * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
  3018. * @example
  3019. * ```html
  3020. * <div id="wrap">
  3021. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3022. * </div>
  3023. *
  3024. * <script>
  3025. *
  3026. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
  3027. *
  3028. * //output: <span style="font-size:14px;">xxxxx</span>
  3029. * console.log( document.getElementById("wrap").innerHTML );
  3030. *
  3031. * </script>
  3032. * ```
  3033. */
  3034. /**
  3035. * 删除节点node上的指定属性名称的属性
  3036. * @method removeAttributes
  3037. * @param { Node } node 需要删除属性的节点对象
  3038. * @param { Array } attrNames 需要删除的属性名数组
  3039. * @example
  3040. * ```html
  3041. * <div id="wrap">
  3042. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3043. * </div>
  3044. *
  3045. * <script>
  3046. *
  3047. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
  3048. *
  3049. * //output: <span style="font-size:14px;">xxxxx</span>
  3050. * console.log( document.getElementById("wrap").innerHTML );
  3051. *
  3052. * </script>
  3053. * ```
  3054. */
  3055. removeAttributes:function (node, attrNames) {
  3056. attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' ');
  3057. for (var i = 0, ci; ci = attrNames[i++];) {
  3058. ci = attrFix[ci] || ci;
  3059. switch (ci) {
  3060. case 'className':
  3061. node[ci] = '';
  3062. break;
  3063. case 'style':
  3064. node.style.cssText = '';
  3065. var val = node.getAttributeNode('style');
  3066. !browser.ie && val && node.removeAttributeNode(val);
  3067. }
  3068. node.removeAttribute(ci);
  3069. }
  3070. },
  3071. /**
  3072. * 在doc下创建一个标签名为tag,属性为attrs的元素
  3073. * @method createElement
  3074. * @param { DomDocument } doc 新创建的元素属于该document节点创建
  3075. * @param { String } tagName 需要创建的元素的标签名
  3076. * @param { Object } attrs 新创建的元素的属性key-value集合
  3077. * @return { Element } 新创建的元素对象
  3078. * @example
  3079. * ```javascript
  3080. * var ele = UE.dom.domUtils.createElement( document, 'div', {
  3081. * id: 'test'
  3082. * } );
  3083. *
  3084. * //output: DIV
  3085. * console.log( ele.tagName );
  3086. *
  3087. * //output: test
  3088. * console.log( ele.id );
  3089. *
  3090. * ```
  3091. */
  3092. createElement:function (doc, tag, attrs) {
  3093. return domUtils.setAttributes(doc.createElement(tag), attrs)
  3094. },
  3095. /**
  3096. * 为节点node添加属性attrs,attrs为属性键值对
  3097. * @method setAttributes
  3098. * @param { Element } node 需要设置属性的元素对象
  3099. * @param { Object } attrs 需要设置的属性名-值对
  3100. * @return { Element } 设置属性的元素对象
  3101. * @example
  3102. * ```html
  3103. * <span id="test"></span>
  3104. *
  3105. * <script>
  3106. *
  3107. * var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
  3108. * id: 'demo'
  3109. * } );
  3110. *
  3111. * //output: demo
  3112. * console.log( testNode.id );
  3113. *
  3114. * </script>
  3115. *
  3116. */
  3117. setAttributes:function (node, attrs) {
  3118. for (var attr in attrs) {
  3119. if(attrs.hasOwnProperty(attr)){
  3120. var value = attrs[attr];
  3121. switch (attr) {
  3122. case 'class':
  3123. //ie下要这样赋值,setAttribute不起作用
  3124. node.className = value;
  3125. break;
  3126. case 'style' :
  3127. node.style.cssText = node.style.cssText + ";" + value;
  3128. break;
  3129. case 'innerHTML':
  3130. node[attr] = value;
  3131. break;
  3132. case 'value':
  3133. node.value = value;
  3134. break;
  3135. default:
  3136. node.setAttribute(attrFix[attr] || attr, value);
  3137. }
  3138. }
  3139. }
  3140. return node;
  3141. },
  3142. /**
  3143. * 获取元素element经过计算后的样式值
  3144. * @method getComputedStyle
  3145. * @param { Element } element 需要获取样式的元素对象
  3146. * @param { String } styleName 需要获取的样式名
  3147. * @return { String } 获取到的样式值
  3148. * @example
  3149. * ```html
  3150. * <style type="text/css">
  3151. * #test {
  3152. * font-size: 15px;
  3153. * }
  3154. * </style>
  3155. *
  3156. * <span id="test"></span>
  3157. *
  3158. * <script>
  3159. * //output: 15px
  3160. * console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
  3161. * </script>
  3162. * ```
  3163. */
  3164. getComputedStyle:function (element, styleName) {
  3165. //一下的属性单独处理
  3166. var pros = 'width height top left';
  3167. if(pros.indexOf(styleName) > -1){
  3168. return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px';
  3169. }
  3170. //忽略文本节点
  3171. if (element.nodeType == 3) {
  3172. element = element.parentNode;
  3173. }
  3174. //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
  3175. if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize &&
  3176. !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
  3177. var span = element.ownerDocument.createElement('span');
  3178. span.style.cssText = 'padding:0;border:0;font-family:simsun;';
  3179. span.innerHTML = '.';
  3180. element.appendChild(span);
  3181. var result = span.offsetHeight;
  3182. element.removeChild(span);
  3183. span = null;
  3184. return result + 'px';
  3185. }
  3186. try {
  3187. var value = domUtils.getStyle(element, styleName) ||
  3188. (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
  3189. ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]);
  3190. } catch (e) {
  3191. return "";
  3192. }
  3193. return utils.transUnitToPx(utils.fixColor(styleName, value));
  3194. },
  3195. /**
  3196. * 删除元素element指定的className
  3197. * @method removeClasses
  3198. * @param { Element } ele 需要删除class的元素节点
  3199. * @param { String } classNames 需要删除的className, 多个className之间以空格分开
  3200. * @example
  3201. * ```html
  3202. * <span id="test" class="test1 test2 test3">xxx</span>
  3203. *
  3204. * <script>
  3205. *
  3206. * var testNode = document.getElementById( "test" );
  3207. * UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
  3208. *
  3209. * //output: test3
  3210. * console.log( testNode.className );
  3211. *
  3212. * </script>
  3213. * ```
  3214. */
  3215. /**
  3216. * 删除元素element指定的className
  3217. * @method removeClasses
  3218. * @param { Element } ele 需要删除class的元素节点
  3219. * @param { Array } classNames 需要删除的className数组
  3220. * @example
  3221. * ```html
  3222. * <span id="test" class="test1 test2 test3">xxx</span>
  3223. *
  3224. * <script>
  3225. *
  3226. * var testNode = document.getElementById( "test" );
  3227. * UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
  3228. *
  3229. * //output: test3
  3230. * console.log( testNode.className );
  3231. *
  3232. * </script>
  3233. * ```
  3234. */
  3235. removeClasses:function (elm, classNames) {
  3236. classNames = utils.isArray(classNames) ? classNames :
  3237. utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
  3238. for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
  3239. cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'')
  3240. }
  3241. cls = utils.trim(cls).replace(/[ ]{2,}/g,' ');
  3242. if(cls){
  3243. elm.className = cls;
  3244. }else{
  3245. domUtils.removeAttributes(elm,['class']);
  3246. }
  3247. },
  3248. /**
  3249. * 给元素element添加className
  3250. * @method addClass
  3251. * @param { Node } ele 需要增加className的元素
  3252. * @param { String } classNames 需要添加的className, 多个className之间以空格分割
  3253. * @remind 相同的类名不会被重复添加
  3254. * @example
  3255. * ```html
  3256. * <span id="test" class="cls1 cls2"></span>
  3257. *
  3258. * <script>
  3259. * var testNode = document.getElementById("test");
  3260. *
  3261. * UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
  3262. *
  3263. * //output: cl1 cls2 cls3 cls4
  3264. * console.log( testNode.className );
  3265. *
  3266. * <script>
  3267. * ```
  3268. */
  3269. /**
  3270. * 给元素element添加className
  3271. * @method addClass
  3272. * @param { Node } ele 需要增加className的元素
  3273. * @param { Array } classNames 需要添加的className的数组
  3274. * @remind 相同的类名不会被重复添加
  3275. * @example
  3276. * ```html
  3277. * <span id="test" class="cls1 cls2"></span>
  3278. *
  3279. * <script>
  3280. * var testNode = document.getElementById("test");
  3281. *
  3282. * UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
  3283. *
  3284. * //output: cl1 cls2 cls3 cls4
  3285. * console.log( testNode.className );
  3286. *
  3287. * <script>
  3288. * ```
  3289. */
  3290. addClass:function (elm, classNames) {
  3291. if(!elm)return;
  3292. classNames = utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
  3293. for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
  3294. if(!new RegExp('\\b' + ci + '\\b').test(cls)){
  3295. cls += ' ' + ci;
  3296. }
  3297. }
  3298. elm.className = utils.trim(cls);
  3299. },
  3300. /**
  3301. * 判断元素element是否包含给定的样式类名className
  3302. * @method hasClass
  3303. * @param { Node } ele 需要检测的元素
  3304. * @param { String } classNames 需要检测的className, 多个className之间用空格分割
  3305. * @return { Boolean } 元素是否包含所有给定的className
  3306. * @example
  3307. * ```html
  3308. * <span id="test1" class="cls1 cls2"></span>
  3309. *
  3310. * <script>
  3311. * var test1 = document.getElementById("test1");
  3312. *
  3313. * //output: false
  3314. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
  3315. *
  3316. * //output: true
  3317. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
  3318. * </script>
  3319. * ```
  3320. */
  3321. /**
  3322. * 判断元素element是否包含给定的样式类名className
  3323. * @method hasClass
  3324. * @param { Node } ele 需要检测的元素
  3325. * @param { Array } classNames 需要检测的className数组
  3326. * @return { Boolean } 元素是否包含所有给定的className
  3327. * @example
  3328. * ```html
  3329. * <span id="test1" class="cls1 cls2"></span>
  3330. *
  3331. * <script>
  3332. * var test1 = document.getElementById("test1");
  3333. *
  3334. * //output: false
  3335. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
  3336. *
  3337. * //output: true
  3338. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
  3339. * </script>
  3340. * ```
  3341. */
  3342. hasClass:function (element, className) {
  3343. if(utils.isRegExp(className)){
  3344. return className.test(element.className)
  3345. }
  3346. className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' ');
  3347. for(var i = 0,ci,cls = element.className;ci=className[i++];){
  3348. if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){
  3349. return false;
  3350. }
  3351. }
  3352. return i - 1 == className.length;
  3353. },
  3354. /**
  3355. * 阻止事件默认行为
  3356. * @method preventDefault
  3357. * @param { Event } evt 需要阻止默认行为的事件对象
  3358. * @example
  3359. * ```javascript
  3360. * UE.dom.domUtils.preventDefault( evt );
  3361. * ```
  3362. */
  3363. preventDefault:function (evt) {
  3364. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  3365. },
  3366. /**
  3367. * 删除元素element指定的样式
  3368. * @method removeStyle
  3369. * @param { Element } element 需要删除样式的元素
  3370. * @param { String } styleName 需要删除的样式名
  3371. * @example
  3372. * ```html
  3373. * <span id="test" style="color: red; background: blue;"></span>
  3374. *
  3375. * <script>
  3376. *
  3377. * var testNode = document.getElementById("test");
  3378. *
  3379. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  3380. *
  3381. * //output: background: blue;
  3382. * console.log( testNode.style.cssText );
  3383. *
  3384. * </script>
  3385. * ```
  3386. */
  3387. removeStyle:function (element, name) {
  3388. if(browser.ie ){
  3389. //针对color先单独处理一下
  3390. if(name == 'color'){
  3391. name = '(^|;)' + name;
  3392. }
  3393. element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
  3394. }else{
  3395. if (element.style.removeProperty) {
  3396. element.style.removeProperty (name);
  3397. }else {
  3398. element.style.removeAttribute (utils.cssStyleToDomStyle(name));
  3399. }
  3400. }
  3401. if (!element.style.cssText) {
  3402. domUtils.removeAttributes(element, ['style']);
  3403. }
  3404. },
  3405. /**
  3406. * 获取元素element的style属性的指定值
  3407. * @method getStyle
  3408. * @param { Element } element 需要获取属性值的元素
  3409. * @param { String } styleName 需要获取的style的名称
  3410. * @warning 该方法仅获取元素style属性中所标明的值
  3411. * @return { String } 该元素包含指定的style属性值
  3412. * @example
  3413. * ```html
  3414. * <div id="test" style="color: red;"></div>
  3415. *
  3416. * <script>
  3417. *
  3418. * var testNode = document.getElementById( "test" );
  3419. *
  3420. * //output: red
  3421. * console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
  3422. *
  3423. * //output: ""
  3424. * console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
  3425. *
  3426. * </script>
  3427. * ```
  3428. */
  3429. getStyle:function (element, name) {
  3430. var value = element.style[ utils.cssStyleToDomStyle(name) ];
  3431. return utils.fixColor(name, value);
  3432. },
  3433. /**
  3434. * 为元素element设置样式属性值
  3435. * @method setStyle
  3436. * @param { Element } element 需要设置样式的元素
  3437. * @param { String } styleName 样式名
  3438. * @param { String } styleValue 样式值
  3439. * @example
  3440. * ```html
  3441. * <div id="test"></div>
  3442. *
  3443. * <script>
  3444. *
  3445. * var testNode = document.getElementById( "test" );
  3446. *
  3447. * //output: ""
  3448. * console.log( testNode.style.color );
  3449. *
  3450. * UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
  3451. * //output: "red"
  3452. * console.log( testNode.style.color );
  3453. *
  3454. * </script>
  3455. * ```
  3456. */
  3457. setStyle:function (element, name, value) {
  3458. element.style[utils.cssStyleToDomStyle(name)] = value;
  3459. if(!utils.trim(element.style.cssText)){
  3460. this.removeAttributes(element,'style')
  3461. }
  3462. },
  3463. /**
  3464. * 为元素element设置多个样式属性值
  3465. * @method setStyles
  3466. * @param { Element } element 需要设置样式的元素
  3467. * @param { Object } styles 样式名值对
  3468. * @example
  3469. * ```html
  3470. * <div id="test"></div>
  3471. *
  3472. * <script>
  3473. *
  3474. * var testNode = document.getElementById( "test" );
  3475. *
  3476. * //output: ""
  3477. * console.log( testNode.style.color );
  3478. *
  3479. * UE.dom.domUtils.setStyles( testNode, {
  3480. * 'color': 'red'
  3481. * } );
  3482. * //output: "red"
  3483. * console.log( testNode.style.color );
  3484. *
  3485. * </script>
  3486. * ```
  3487. */
  3488. setStyles:function (element, styles) {
  3489. for (var name in styles) {
  3490. if (styles.hasOwnProperty(name)) {
  3491. domUtils.setStyle(element, name, styles[name]);
  3492. }
  3493. }
  3494. },
  3495. /**
  3496. * 删除_moz_dirty属性
  3497. * @private
  3498. * @method removeDirtyAttr
  3499. */
  3500. removeDirtyAttr:function (node) {
  3501. for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
  3502. ci.removeAttribute('_moz_dirty');
  3503. }
  3504. node.removeAttribute('_moz_dirty');
  3505. },
  3506. /**
  3507. * 获取子节点的数量
  3508. * @method getChildCount
  3509. * @param { Element } node 需要检测的元素
  3510. * @return { Number } 给定的node元素的子节点数量
  3511. * @example
  3512. * ```html
  3513. * <div id="test">
  3514. * <span></span>
  3515. * </div>
  3516. *
  3517. * <script>
  3518. *
  3519. * //output: 3
  3520. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
  3521. *
  3522. * </script>
  3523. * ```
  3524. */
  3525. /**
  3526. * 根据给定的过滤规则, 获取符合条件的子节点的数量
  3527. * @method getChildCount
  3528. * @param { Element } node 需要检测的元素
  3529. * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
  3530. * @return { Number } 符合过滤条件的node元素的子节点数量
  3531. * @example
  3532. * ```html
  3533. * <div id="test">
  3534. * <span></span>
  3535. * </div>
  3536. *
  3537. * <script>
  3538. *
  3539. * //output: 1
  3540. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
  3541. *
  3542. * return node.nodeType === 1;
  3543. *
  3544. * } ) );
  3545. *
  3546. * </script>
  3547. * ```
  3548. */
  3549. getChildCount:function (node, fn) {
  3550. var count = 0, first = node.firstChild;
  3551. fn = fn || function () {
  3552. return 1;
  3553. };
  3554. while (first) {
  3555. if (fn(first)) {
  3556. count++;
  3557. }
  3558. first = first.nextSibling;
  3559. }
  3560. return count;
  3561. },
  3562. /**
  3563. * 判断给定节点是否为空节点
  3564. * @method isEmptyNode
  3565. * @param { Node } node 需要检测的节点对象
  3566. * @return { Boolean } 节点是否为空
  3567. * @example
  3568. * ```javascript
  3569. * UE.dom.domUtils.isEmptyNode( document.body );
  3570. * ```
  3571. */
  3572. isEmptyNode:function (node) {
  3573. return !node.firstChild || domUtils.getChildCount(node, function (node) {
  3574. return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
  3575. }) == 0
  3576. },
  3577. clearSelectedArr:function (nodes) {
  3578. var node;
  3579. while (node = nodes.pop()) {
  3580. domUtils.removeAttributes(node, ['class']);
  3581. }
  3582. },
  3583. /**
  3584. * 将显示区域滚动到指定节点的位置
  3585. * @method scrollToView
  3586. * @param {Node} node 节点
  3587. * @param {window} win window对象
  3588. * @param {Number} offsetTop 距离上方的偏移量
  3589. */
  3590. scrollToView:function (node, win, offsetTop) {
  3591. var getViewPaneSize = function () {
  3592. var doc = win.document,
  3593. mode = doc.compatMode == 'CSS1Compat';
  3594. return {
  3595. width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
  3596. height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
  3597. };
  3598. },
  3599. getScrollPosition = function (win) {
  3600. if ('pageXOffset' in win) {
  3601. return {
  3602. x:win.pageXOffset || 0,
  3603. y:win.pageYOffset || 0
  3604. };
  3605. }
  3606. else {
  3607. var doc = win.document;
  3608. return {
  3609. x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
  3610. y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
  3611. };
  3612. }
  3613. };
  3614. var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
  3615. offset += (node.offsetHeight || 0);
  3616. var elementPosition = domUtils.getXY(node);
  3617. offset += elementPosition.y;
  3618. var currentScroll = getScrollPosition(win).y;
  3619. // offset += 50;
  3620. if (offset > currentScroll || offset < currentScroll - winHeight) {
  3621. win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
  3622. }
  3623. },
  3624. /**
  3625. * 判断给定节点是否为br
  3626. * @method isBr
  3627. * @param { Node } node 需要判断的节点对象
  3628. * @return { Boolean } 给定的节点是否是br节点
  3629. */
  3630. isBr:function (node) {
  3631. return node.nodeType == 1 && node.tagName == 'BR';
  3632. },
  3633. /**
  3634. * 判断给定的节点是否是一个“填充”节点
  3635. * @private
  3636. * @method isFillChar
  3637. * @param { Node } node 需要判断的节点
  3638. * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
  3639. * @returns { Boolean } 节点是否是填充节点
  3640. */
  3641. isFillChar:function (node,isInStart) {
  3642. if(node.nodeType != 3)
  3643. return false;
  3644. var text = node.nodeValue;
  3645. if(isInStart){
  3646. return new RegExp('^' + domUtils.fillChar).test(text)
  3647. }
  3648. return !text.replace(new RegExp(domUtils.fillChar,'g'), '').length
  3649. },
  3650. isStartInblock:function (range) {
  3651. var tmpRange = range.cloneRange(),
  3652. flag = 0,
  3653. start = tmpRange.startContainer,
  3654. tmp;
  3655. if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){
  3656. start = start.childNodes[tmpRange.startOffset];
  3657. var pre = start.previousSibling;
  3658. while(pre && domUtils.isFillChar(pre)){
  3659. start = pre;
  3660. pre = pre.previousSibling;
  3661. }
  3662. }
  3663. if(this.isFillChar(start,true) && tmpRange.startOffset == 1){
  3664. tmpRange.setStartBefore(start);
  3665. start = tmpRange.startContainer;
  3666. }
  3667. while (start && domUtils.isFillChar(start)) {
  3668. tmp = start;
  3669. start = start.previousSibling
  3670. }
  3671. if (tmp) {
  3672. tmpRange.setStartBefore(tmp);
  3673. start = tmpRange.startContainer;
  3674. }
  3675. if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
  3676. tmpRange.setStart(start, 0).collapse(true);
  3677. }
  3678. while (!tmpRange.startOffset) {
  3679. start = tmpRange.startContainer;
  3680. if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
  3681. flag = 1;
  3682. break;
  3683. }
  3684. var pre = tmpRange.startContainer.previousSibling,
  3685. tmpNode;
  3686. if (!pre) {
  3687. tmpRange.setStartBefore(tmpRange.startContainer);
  3688. } else {
  3689. while (pre && domUtils.isFillChar(pre)) {
  3690. tmpNode = pre;
  3691. pre = pre.previousSibling;
  3692. }
  3693. if (tmpNode) {
  3694. tmpRange.setStartBefore(tmpNode);
  3695. } else {
  3696. tmpRange.setStartBefore(tmpRange.startContainer);
  3697. }
  3698. }
  3699. }
  3700. return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
  3701. },
  3702. /**
  3703. * 判断给定的元素是否是一个空元素
  3704. * @method isEmptyBlock
  3705. * @param { Element } node 需要判断的元素
  3706. * @return { Boolean } 是否是空元素
  3707. * @example
  3708. * ```html
  3709. * <div id="test"></div>
  3710. *
  3711. * <script>
  3712. * //output: true
  3713. * console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
  3714. * </script>
  3715. * ```
  3716. */
  3717. /**
  3718. * 根据指定的判断规则判断给定的元素是否是一个空元素
  3719. * @method isEmptyBlock
  3720. * @param { Element } node 需要判断的元素
  3721. * @param { RegExp } reg 对内容执行判断的正则表达式对象
  3722. * @return { Boolean } 是否是空元素
  3723. */
  3724. isEmptyBlock:function (node,reg) {
  3725. if(node.nodeType != 1)
  3726. return 0;
  3727. reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g');
  3728. if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
  3729. return 0;
  3730. }
  3731. for (var n in dtd.$isNotEmpty) {
  3732. if (node.getElementsByTagName(n).length) {
  3733. return 0;
  3734. }
  3735. }
  3736. return 1;
  3737. },
  3738. /**
  3739. * 移动元素使得该元素的位置移动指定的偏移量的距离
  3740. * @method setViewportOffset
  3741. * @param { Element } element 需要设置偏移量的元素
  3742. * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
  3743. * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
  3744. * offset.top的距离
  3745. * @example
  3746. * ```html
  3747. * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
  3748. *
  3749. * <script>
  3750. *
  3751. * var testNode = document.getElementById("test");
  3752. *
  3753. * UE.dom.domUtils.setViewportOffset( testNode, {
  3754. * left: 200,
  3755. * top: 50
  3756. * } );
  3757. *
  3758. * //output: top: 300px; left: 100px; position: absolute;
  3759. * console.log( testNode.style.cssText );
  3760. *
  3761. * </script>
  3762. * ```
  3763. */
  3764. setViewportOffset:function (element, offset) {
  3765. var left = parseInt(element.style.left) | 0;
  3766. var top = parseInt(element.style.top) | 0;
  3767. var rect = element.getBoundingClientRect();
  3768. var offsetLeft = offset.left - rect.left;
  3769. var offsetTop = offset.top - rect.top;
  3770. if (offsetLeft) {
  3771. element.style.left = left + offsetLeft + 'px';
  3772. }
  3773. if (offsetTop) {
  3774. element.style.top = top + offsetTop + 'px';
  3775. }
  3776. },
  3777. /**
  3778. * 用“填充字符”填充节点
  3779. * @method fillNode
  3780. * @private
  3781. * @param { DomDocument } doc 填充的节点所在的docment对象
  3782. * @param { Node } node 需要填充的节点对象
  3783. * @example
  3784. * ```html
  3785. * <div id="test"></div>
  3786. *
  3787. * <script>
  3788. * var testNode = document.getElementById("test");
  3789. *
  3790. * //output: 0
  3791. * console.log( testNode.childNodes.length );
  3792. *
  3793. * UE.dom.domUtils.fillNode( document, testNode );
  3794. *
  3795. * //output: 1
  3796. * console.log( testNode.childNodes.length );
  3797. *
  3798. * </script>
  3799. * ```
  3800. */
  3801. fillNode:function (doc, node) {
  3802. var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
  3803. node.innerHTML = '';
  3804. node.appendChild(tmpNode);
  3805. },
  3806. /**
  3807. * 把节点src的所有子节点追加到另一个节点tag上去
  3808. * @method moveChild
  3809. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  3810. * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
  3811. * @example
  3812. * ```html
  3813. * <div id="test1">
  3814. * <span></span>
  3815. * </div>
  3816. * <div id="test2">
  3817. * <div></div>
  3818. * </div>
  3819. *
  3820. * <script>
  3821. *
  3822. * var test1 = document.getElementById("test1"),
  3823. * test2 = document.getElementById("test2");
  3824. *
  3825. * UE.dom.domUtils.moveChild( test1, test2 );
  3826. *
  3827. * //output: ""(空字符串)
  3828. * console.log( test1.innerHTML );
  3829. *
  3830. * //output: "<div></div><span></span>"
  3831. * console.log( test2.innerHTML );
  3832. *
  3833. * </script>
  3834. * ```
  3835. */
  3836. /**
  3837. * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
  3838. * @method moveChild
  3839. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  3840. * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
  3841. * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
  3842. * @example
  3843. * ```html
  3844. * <div id="test1">
  3845. * <span></span>
  3846. * </div>
  3847. * <div id="test2">
  3848. * <div></div>
  3849. * </div>
  3850. *
  3851. * <script>
  3852. *
  3853. * var test1 = document.getElementById("test1"),
  3854. * test2 = document.getElementById("test2");
  3855. *
  3856. * UE.dom.domUtils.moveChild( test1, test2, true );
  3857. *
  3858. * //output: ""(空字符串)
  3859. * console.log( test1.innerHTML );
  3860. *
  3861. * //output: "<span></span><div></div>"
  3862. * console.log( test2.innerHTML );
  3863. *
  3864. * </script>
  3865. * ```
  3866. */
  3867. moveChild:function (src, tag, dir) {
  3868. while (src.firstChild) {
  3869. if (dir && tag.firstChild) {
  3870. tag.insertBefore(src.lastChild, tag.firstChild);
  3871. } else {
  3872. tag.appendChild(src.firstChild);
  3873. }
  3874. }
  3875. },
  3876. /**
  3877. * 判断节点的标签上是否不存在任何属性
  3878. * @method hasNoAttributes
  3879. * @private
  3880. * @param { Node } node 需要检测的节点对象
  3881. * @return { Boolean } 节点是否不包含任何属性
  3882. * @example
  3883. * ```html
  3884. * <div id="test"><span>xxxx</span></div>
  3885. *
  3886. * <script>
  3887. *
  3888. * //output: false
  3889. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
  3890. *
  3891. * //output: true
  3892. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
  3893. *
  3894. * </script>
  3895. * ```
  3896. */
  3897. hasNoAttributes:function (node) {
  3898. return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0;
  3899. },
  3900. /**
  3901. * 检测节点是否是UEditor所使用的辅助节点
  3902. * @method isCustomeNode
  3903. * @private
  3904. * @param { Node } node 需要检测的节点
  3905. * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
  3906. * @return { Boolean } 给定的节点是否是一个辅助节点
  3907. */
  3908. isCustomeNode:function (node) {
  3909. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
  3910. },
  3911. /**
  3912. * 检测节点的标签是否是给定的标签
  3913. * @method isTagNode
  3914. * @param { Node } node 需要检测的节点对象
  3915. * @param { String } tagName 标签
  3916. * @return { Boolean } 节点的标签是否是给定的标签
  3917. * @example
  3918. * ```html
  3919. * <div id="test"></div>
  3920. *
  3921. * <script>
  3922. *
  3923. * //output: true
  3924. * console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
  3925. *
  3926. * </script>
  3927. * ```
  3928. */
  3929. isTagNode:function (node, tagNames) {
  3930. return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b','i').test(tagNames)
  3931. },
  3932. /**
  3933. * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
  3934. * @method filterNodeList
  3935. * @param { Array } nodeList 需要过滤的节点数组
  3936. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  3937. * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
  3938. * @example
  3939. * ```javascript
  3940. * var divNodes = document.getElementsByTagName("div");
  3941. * divNodes = [].slice.call( divNodes, 0 );
  3942. *
  3943. * //output: null
  3944. * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  3945. * return node.tagName.toLowerCase() !== 'div';
  3946. * } ) );
  3947. * ```
  3948. */
  3949. /**
  3950. * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
  3951. * @method filterNodeList
  3952. * @param { Array } nodeList 需要过滤的节点数组
  3953. * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
  3954. * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
  3955. * @example
  3956. * ```javascript
  3957. * var divNodes = document.getElementsByTagName("div");
  3958. * divNodes = [].slice.call( divNodes, 0 );
  3959. *
  3960. * //output: null
  3961. * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
  3962. * ```
  3963. */
  3964. /**
  3965. * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
  3966. * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
  3967. * @method filterNodeList
  3968. * @param { Array } nodeList 需要过滤的节点数组
  3969. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  3970. * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
  3971. * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
  3972. * 过滤条件的节点数组或第一个节点, 否则返回NULL
  3973. * @example
  3974. * ```javascript
  3975. * var divNodes = document.getElementsByTagName("div");
  3976. * divNodes = [].slice.call( divNodes, 0 );
  3977. *
  3978. * //output: 3(假定有3个div)
  3979. * console.log( divNodes.length );
  3980. *
  3981. * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  3982. * return node.tagName.toLowerCase() === 'div';
  3983. * }, true );
  3984. *
  3985. * //output: 3
  3986. * console.log( nodes.length );
  3987. *
  3988. * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  3989. * return node.tagName.toLowerCase() === 'div';
  3990. * }, false );
  3991. *
  3992. * //output: div
  3993. * console.log( node.nodeName );
  3994. * ```
  3995. */
  3996. filterNodeList : function(nodelist,filter,forAll){
  3997. var results = [];
  3998. if(!utils .isFunction(filter)){
  3999. var str = filter;
  4000. filter = function(n){
  4001. return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1
  4002. };
  4003. }
  4004. utils.each(nodelist,function(n){
  4005. filter(n) && results.push(n)
  4006. });
  4007. return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
  4008. },
  4009. /**
  4010. * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
  4011. * @method isInNodeEndBoundary
  4012. * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
  4013. * @param node 需要检测的节点对象
  4014. * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
  4015. */
  4016. isInNodeEndBoundary : function (rng,node){
  4017. var start = rng.startContainer;
  4018. if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){
  4019. return 0;
  4020. }
  4021. if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){
  4022. return 0;
  4023. }
  4024. while(start !== node){
  4025. if(start.nextSibling){
  4026. return 0
  4027. };
  4028. start = start.parentNode;
  4029. }
  4030. return 1;
  4031. },
  4032. isBoundaryNode : function (node,dir){
  4033. var tmp;
  4034. while(!domUtils.isBody(node)){
  4035. tmp = node;
  4036. node = node.parentNode;
  4037. if(tmp !== node[dir]){
  4038. return false;
  4039. }
  4040. }
  4041. return true;
  4042. },
  4043. fillHtml : browser.ie11below ? '&nbsp;' : '<br/>'
  4044. };
  4045. var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  4046. // core/Range.js
  4047. /**
  4048. * Range封装
  4049. * @file
  4050. * @module UE.dom
  4051. * @class Range
  4052. * @since 1.2.6.1
  4053. */
  4054. /**
  4055. * dom操作封装
  4056. * @unfile
  4057. * @module UE.dom
  4058. */
  4059. /**
  4060. * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
  4061. * @unfile
  4062. * @module UE.dom
  4063. * @class Range
  4064. */
  4065. (function () {
  4066. var guid = 0,
  4067. fillChar = domUtils.fillChar,
  4068. fillData;
  4069. /**
  4070. * 更新range的collapse状态
  4071. * @param {Range} range range对象
  4072. */
  4073. function updateCollapse(range) {
  4074. range.collapsed =
  4075. range.startContainer && range.endContainer &&
  4076. range.startContainer === range.endContainer &&
  4077. range.startOffset == range.endOffset;
  4078. }
  4079. function selectOneNode(rng){
  4080. return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
  4081. }
  4082. function setEndPoint(toStart, node, offset, range) {
  4083. //如果node是自闭合标签要处理
  4084. if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
  4085. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
  4086. node = node.parentNode;
  4087. }
  4088. if (toStart) {
  4089. range.startContainer = node;
  4090. range.startOffset = offset;
  4091. if (!range.endContainer) {
  4092. range.collapse(true);
  4093. }
  4094. } else {
  4095. range.endContainer = node;
  4096. range.endOffset = offset;
  4097. if (!range.startContainer) {
  4098. range.collapse(false);
  4099. }
  4100. }
  4101. updateCollapse(range);
  4102. return range;
  4103. }
  4104. function execContentsAction(range, action) {
  4105. //调整边界
  4106. //range.includeBookmark();
  4107. var start = range.startContainer,
  4108. end = range.endContainer,
  4109. startOffset = range.startOffset,
  4110. endOffset = range.endOffset,
  4111. doc = range.document,
  4112. frag = doc.createDocumentFragment(),
  4113. tmpStart, tmpEnd;
  4114. if (start.nodeType == 1) {
  4115. start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
  4116. }
  4117. if (end.nodeType == 1) {
  4118. end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
  4119. }
  4120. if (start === end && start.nodeType == 3) {
  4121. frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
  4122. //is not clone
  4123. if (action) {
  4124. start.deleteData(startOffset, endOffset - startOffset);
  4125. range.collapse(true);
  4126. }
  4127. return frag;
  4128. }
  4129. var current, currentLevel, clone = frag,
  4130. startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
  4131. for (var i = 0; startParents[i] == endParents[i];) {
  4132. i++;
  4133. }
  4134. for (var j = i, si; si = startParents[j]; j++) {
  4135. current = si.nextSibling;
  4136. if (si == start) {
  4137. if (!tmpStart) {
  4138. if (range.startContainer.nodeType == 3) {
  4139. clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
  4140. //is not clone
  4141. if (action) {
  4142. start.deleteData(startOffset, start.nodeValue.length - startOffset);
  4143. }
  4144. } else {
  4145. clone.appendChild(!action ? start.cloneNode(true) : start);
  4146. }
  4147. }
  4148. } else {
  4149. currentLevel = si.cloneNode(false);
  4150. clone.appendChild(currentLevel);
  4151. }
  4152. while (current) {
  4153. if (current === end || current === endParents[j]) {
  4154. break;
  4155. }
  4156. si = current.nextSibling;
  4157. clone.appendChild(!action ? current.cloneNode(true) : current);
  4158. current = si;
  4159. }
  4160. clone = currentLevel;
  4161. }
  4162. clone = frag;
  4163. if (!startParents[i]) {
  4164. clone.appendChild(startParents[i - 1].cloneNode(false));
  4165. clone = clone.firstChild;
  4166. }
  4167. for (var j = i, ei; ei = endParents[j]; j++) {
  4168. current = ei.previousSibling;
  4169. if (ei == end) {
  4170. if (!tmpEnd && range.endContainer.nodeType == 3) {
  4171. clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
  4172. //is not clone
  4173. if (action) {
  4174. end.deleteData(0, endOffset);
  4175. }
  4176. }
  4177. } else {
  4178. currentLevel = ei.cloneNode(false);
  4179. clone.appendChild(currentLevel);
  4180. }
  4181. //如果两端同级,右边第一次已经被开始做了
  4182. if (j != i || !startParents[i]) {
  4183. while (current) {
  4184. if (current === start) {
  4185. break;
  4186. }
  4187. ei = current.previousSibling;
  4188. clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
  4189. current = ei;
  4190. }
  4191. }
  4192. clone = currentLevel;
  4193. }
  4194. if (action) {
  4195. range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
  4196. }
  4197. tmpStart && domUtils.remove(tmpStart);
  4198. tmpEnd && domUtils.remove(tmpEnd);
  4199. return frag;
  4200. }
  4201. /**
  4202. * 创建一个跟document绑定的空的Range实例
  4203. * @constructor
  4204. * @param { Document } document 新建的选区所属的文档对象
  4205. */
  4206. /**
  4207. * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
  4208. */
  4209. /**
  4210. * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
  4211. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4212. */
  4213. /**
  4214. * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
  4215. */
  4216. /**
  4217. * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
  4218. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4219. */
  4220. /**
  4221. * @property { Boolean } collapsed 当前Range是否闭合
  4222. * @default true
  4223. * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
  4224. */
  4225. /**
  4226. * @property { Document } document 当前Range所属的Document对象
  4227. * @remind 不同range的的document属性可以是不同的
  4228. */
  4229. var Range = dom.Range = function (document) {
  4230. var me = this;
  4231. me.startContainer =
  4232. me.startOffset =
  4233. me.endContainer =
  4234. me.endOffset = null;
  4235. me.document = document;
  4236. me.collapsed = true;
  4237. };
  4238. /**
  4239. * 删除fillData
  4240. * @param doc
  4241. * @param excludeNode
  4242. */
  4243. function removeFillData(doc, excludeNode) {
  4244. try {
  4245. if (fillData && domUtils.inDoc(fillData, doc)) {
  4246. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  4247. var tmpNode = fillData.parentNode;
  4248. domUtils.remove(fillData);
  4249. while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
  4250. //safari的contains有bug
  4251. (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
  4252. ) {
  4253. fillData = tmpNode.parentNode;
  4254. domUtils.remove(tmpNode);
  4255. tmpNode = fillData;
  4256. }
  4257. } else {
  4258. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
  4259. }
  4260. }
  4261. } catch (e) {
  4262. }
  4263. }
  4264. /**
  4265. * @param node
  4266. * @param dir
  4267. */
  4268. function mergeSibling(node, dir) {
  4269. var tmpNode;
  4270. node = node[dir];
  4271. while (node && domUtils.isFillChar(node)) {
  4272. tmpNode = node[dir];
  4273. domUtils.remove(node);
  4274. node = tmpNode;
  4275. }
  4276. }
  4277. Range.prototype = {
  4278. /**
  4279. * 克隆选区的内容到一个DocumentFragment里
  4280. * @method cloneContents
  4281. * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
  4282. * @example
  4283. * ```html
  4284. * <body>
  4285. * <!-- 中括号表示选区 -->
  4286. * <b>x<i>x[x</i>xx]x</b>
  4287. *
  4288. * <script>
  4289. * //range是已选中的选区
  4290. * var fragment = range.cloneContents(),
  4291. * node = document.createElement("div");
  4292. *
  4293. * node.appendChild( fragment );
  4294. *
  4295. * //output: <i>x</i>xx
  4296. * console.log( node.innerHTML );
  4297. *
  4298. * </script>
  4299. * </body>
  4300. * ```
  4301. */
  4302. cloneContents:function () {
  4303. return this.collapsed ? null : execContentsAction(this, 0);
  4304. },
  4305. /**
  4306. * 删除当前选区范围中的所有内容
  4307. * @method deleteContents
  4308. * @remind 执行完该操作后, 当前Range对象变成了闭合状态
  4309. * @return { UE.dom.Range } 当前操作的Range对象
  4310. * @example
  4311. * ```html
  4312. * <body>
  4313. * <!-- 中括号表示选区 -->
  4314. * <b>x<i>x[x</i>xx]x</b>
  4315. *
  4316. * <script>
  4317. * //range是已选中的选区
  4318. * range.deleteContents();
  4319. *
  4320. * //竖线表示闭合后的选区位置
  4321. * //output: <b>x<i>x</i>|x</b>
  4322. * console.log( document.body.innerHTML );
  4323. *
  4324. * //此时, range的各项属性为
  4325. * //output: B
  4326. * console.log( range.startContainer.tagName );
  4327. * //output: 2
  4328. * console.log( range.startOffset );
  4329. * //output: B
  4330. * console.log( range.endContainer.tagName );
  4331. * //output: 2
  4332. * console.log( range.endOffset );
  4333. * //output: true
  4334. * console.log( range.collapsed );
  4335. *
  4336. * </script>
  4337. * </body>
  4338. * ```
  4339. */
  4340. deleteContents:function () {
  4341. var txt;
  4342. if (!this.collapsed) {
  4343. execContentsAction(this, 1);
  4344. }
  4345. if (browser.webkit) {
  4346. txt = this.startContainer;
  4347. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  4348. this.setStartBefore(txt).collapse(true);
  4349. domUtils.remove(txt);
  4350. }
  4351. }
  4352. return this;
  4353. },
  4354. /**
  4355. * 将当前选区的内容提取到一个DocumentFragment里
  4356. * @method extractContents
  4357. * @remind 执行该操作后, 选区将变成闭合状态
  4358. * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
  4359. * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
  4360. * @example
  4361. * ```html
  4362. * <body>
  4363. * <!-- 中括号表示选区 -->
  4364. * <b>x<i>x[x</i>xx]x</b>
  4365. *
  4366. * <script>
  4367. * //range是已选中的选区
  4368. * var fragment = range.extractContents(),
  4369. * node = document.createElement( "div" );
  4370. *
  4371. * node.appendChild( fragment );
  4372. *
  4373. * //竖线表示闭合后的选区位置
  4374. *
  4375. * //output: <b>x<i>x</i>|x</b>
  4376. * console.log( document.body.innerHTML );
  4377. * //output: <i>x</i>xx
  4378. * console.log( node.innerHTML );
  4379. *
  4380. * //此时, range的各项属性为
  4381. * //output: B
  4382. * console.log( range.startContainer.tagName );
  4383. * //output: 2
  4384. * console.log( range.startOffset );
  4385. * //output: B
  4386. * console.log( range.endContainer.tagName );
  4387. * //output: 2
  4388. * console.log( range.endOffset );
  4389. * //output: true
  4390. * console.log( range.collapsed );
  4391. *
  4392. * </script>
  4393. * </body>
  4394. */
  4395. extractContents:function () {
  4396. return this.collapsed ? null : execContentsAction(this, 2);
  4397. },
  4398. /**
  4399. * 设置Range的开始容器节点和偏移量
  4400. * @method setStart
  4401. * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
  4402. * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
  4403. * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
  4404. * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
  4405. * 中的索引
  4406. * @param { Node } node 将被设为当前选区开始边界容器的节点对象
  4407. * @param { int } offset 选区的开始位置偏移量
  4408. * @return { UE.dom.Range } 当前range对象
  4409. * @example
  4410. * ```html
  4411. * <!-- 选区 -->
  4412. * <b>xxx<i>x<span>xx</span>xx<em>xx</em>xxx</i>[xxx]</b>
  4413. *
  4414. * <script>
  4415. *
  4416. * //执行操作
  4417. * range.setStart( document.getElementsByTagName("i")[0], 1 );
  4418. *
  4419. * //此时, 选区变成了
  4420. * //<b>xxx<i>x[<span>xx</span>xx<em>xx</em>xxx</i>xxx]</b>
  4421. *
  4422. * </script>
  4423. * ```
  4424. * @example
  4425. * ```html
  4426. * <!-- 选区 -->
  4427. * <b>xxx<img>[xx]x</b>
  4428. *
  4429. * <script>
  4430. *
  4431. * //执行操作
  4432. * range.setStart( document.getElementsByTagName("img")[0], 3 );
  4433. *
  4434. * //此时, 选区变成了
  4435. * //<b>xxx[<img>xx]x</b>
  4436. *
  4437. * </script>
  4438. * ```
  4439. */
  4440. setStart:function (node, offset) {
  4441. return setEndPoint(true, node, offset, this);
  4442. },
  4443. /**
  4444. * 设置Range的结束容器和偏移量
  4445. * @method setEnd
  4446. * @param { Node } node 作为当前选区结束边界容器的节点对象
  4447. * @param { int } offset 结束边界的偏移量
  4448. * @see UE.dom.Range:setStart(Node,int)
  4449. * @return { UE.dom.Range } 当前range对象
  4450. */
  4451. setEnd:function (node, offset) {
  4452. return setEndPoint(false, node, offset, this);
  4453. },
  4454. /**
  4455. * 将Range开始位置设置到node节点之后
  4456. * @method setStartAfter
  4457. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
  4458. * @param { Node } node 选区的开始边界将紧接着该节点之后
  4459. * @return { UE.dom.Range } 当前range对象
  4460. * @example
  4461. * ```html
  4462. * <!-- 选区示例 -->
  4463. * <b>xx<i>xxx</i><span>xx[x</span>xxx]</b>
  4464. *
  4465. * <script>
  4466. *
  4467. * //执行操作
  4468. * range.setStartAfter( document.getElementsByTagName("i")[0] );
  4469. *
  4470. * //结果选区
  4471. * //<b>xx<i>xxx</i>[<span>xxx</span>xxx]</b>
  4472. *
  4473. * </script>
  4474. * ```
  4475. */
  4476. setStartAfter:function (node) {
  4477. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
  4478. },
  4479. /**
  4480. * 将Range开始位置设置到node节点之前
  4481. * @method setStartBefore
  4482. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
  4483. * @param { Node } node 新的选区开始位置在该节点之前
  4484. * @see UE.dom.Range:setStartAfter(Node)
  4485. * @return { UE.dom.Range } 当前range对象
  4486. */
  4487. setStartBefore:function (node) {
  4488. return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
  4489. },
  4490. /**
  4491. * 将Range结束位置设置到node节点之后
  4492. * @method setEndAfter
  4493. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
  4494. * @param { Node } node 目标节点
  4495. * @see UE.dom.Range:setStartAfter(Node)
  4496. * @return { UE.dom.Range } 当前range对象
  4497. * @example
  4498. * ```html
  4499. * <!-- 选区示例 -->
  4500. * <b>[xx<i>xxx</i><span>xx]x</span>xxx</b>
  4501. *
  4502. * <script>
  4503. *
  4504. * //执行操作
  4505. * range.setStartAfter( document.getElementsByTagName("span")[0] );
  4506. *
  4507. * //结果选区
  4508. * //<b>[xx<i>xxx</i><span>xxx</span>]xxx</b>
  4509. *
  4510. * </script>
  4511. * ```
  4512. */
  4513. setEndAfter:function (node) {
  4514. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
  4515. },
  4516. /**
  4517. * 将Range结束位置设置到node节点之前
  4518. * @method setEndBefore
  4519. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
  4520. * @param { Node } node 目标节点
  4521. * @see UE.dom.Range:setEndAfter(Node)
  4522. * @return { UE.dom.Range } 当前range对象
  4523. */
  4524. setEndBefore:function (node) {
  4525. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
  4526. },
  4527. /**
  4528. * 设置Range的开始位置到node节点内的第一个子节点之前
  4529. * @method setStartAtFirst
  4530. * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
  4531. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  4532. * @param { Node } node 目标节点
  4533. * @see UE.dom.Range:setStartBefore(Node)
  4534. * @return { UE.dom.Range } 当前range对象
  4535. * @example
  4536. * ```html
  4537. * <!-- 选区示例 -->
  4538. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4539. *
  4540. * <script>
  4541. *
  4542. * //执行操作
  4543. * range.setStartAtFirst( document.getElementsByTagName("i")[0] );
  4544. *
  4545. * //结果选区
  4546. * //<b>xx<i>[xxx</i><span>xx]x</span>xxx</b>
  4547. *
  4548. * </script>
  4549. * ```
  4550. */
  4551. setStartAtFirst:function (node) {
  4552. return this.setStart(node, 0);
  4553. },
  4554. /**
  4555. * 设置Range的开始位置到node节点内的最后一个节点之后
  4556. * @method setStartAtLast
  4557. * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
  4558. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  4559. * @param { Node } node 目标节点
  4560. * @see UE.dom.Range:setStartAtFirst(Node)
  4561. * @return { UE.dom.Range } 当前range对象
  4562. */
  4563. setStartAtLast:function (node) {
  4564. return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  4565. },
  4566. /**
  4567. * 设置Range的结束位置到node节点内的第一个节点之前
  4568. * @method setEndAtFirst
  4569. * @param { Node } node 目标节点
  4570. * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
  4571. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  4572. * @see UE.dom.Range:setStartAtFirst(Node)
  4573. * @return { UE.dom.Range } 当前range对象
  4574. */
  4575. setEndAtFirst:function (node) {
  4576. return this.setEnd(node, 0);
  4577. },
  4578. /**
  4579. * 设置Range的结束位置到node节点内的最后一个节点之后
  4580. * @method setEndAtLast
  4581. * @param { Node } node 目标节点
  4582. * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
  4583. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  4584. * @see UE.dom.Range:setStartAtFirst(Node)
  4585. * @return { UE.dom.Range } 当前range对象
  4586. */
  4587. setEndAtLast:function (node) {
  4588. return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  4589. },
  4590. /**
  4591. * 选中给定节点
  4592. * @method selectNode
  4593. * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
  4594. * 而endOffset为startOffset+1
  4595. * @param { Node } node 需要选中的节点
  4596. * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
  4597. * @example
  4598. * ```html
  4599. * <!-- 选区示例 -->
  4600. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4601. *
  4602. * <script>
  4603. *
  4604. * //执行操作
  4605. * range.selectNode( document.getElementsByTagName("i")[0] );
  4606. *
  4607. * //结果选区
  4608. * //<b>xx[<i>xxx</i>]<span>xxx</span>xxx</b>
  4609. *
  4610. * </script>
  4611. * ```
  4612. */
  4613. selectNode:function (node) {
  4614. return this.setStartBefore(node).setEndAfter(node);
  4615. },
  4616. /**
  4617. * 选中给定节点内部的所有节点
  4618. * @method selectNodeContents
  4619. * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
  4620. * 而endOffset是该节点的子节点数。
  4621. * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
  4622. * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
  4623. * @example
  4624. * ```html
  4625. * <!-- 选区示例 -->
  4626. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4627. *
  4628. * <script>
  4629. *
  4630. * //执行操作
  4631. * range.selectNode( document.getElementsByTagName("b")[0] );
  4632. *
  4633. * //结果选区
  4634. * //<b>[xx<i>xxx</i><span>xxx</span>xxx]</b>
  4635. *
  4636. * </script>
  4637. * ```
  4638. */
  4639. selectNodeContents:function (node) {
  4640. return this.setStart(node, 0).setEndAtLast(node);
  4641. },
  4642. /**
  4643. * clone当前Range对象
  4644. * @method cloneRange
  4645. * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
  4646. * @return { UE.dom.Range } 当前range对象的一个副本
  4647. */
  4648. cloneRange:function () {
  4649. var me = this;
  4650. return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  4651. },
  4652. /**
  4653. * 向当前选区的结束处闭合选区
  4654. * @method collapse
  4655. * @return { UE.dom.Range } 当前range对象
  4656. * @example
  4657. * ```html
  4658. * <!-- 选区示例 -->
  4659. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4660. *
  4661. * <script>
  4662. *
  4663. * //执行操作
  4664. * range.collapse();
  4665. *
  4666. * //结果选区
  4667. * //“|”表示选区已闭合
  4668. * //<b>xx<i>xxx</i><span>xx|x</span>xxx</b>
  4669. *
  4670. * </script>
  4671. * ```
  4672. */
  4673. /**
  4674. * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
  4675. * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
  4676. * @method collapse
  4677. * @param { Boolean } toStart 是否向选区开始处闭合
  4678. * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
  4679. * @see UE.dom.Range:collapse()
  4680. * @example
  4681. * ```html
  4682. * <!-- 选区示例 -->
  4683. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4684. *
  4685. * <script>
  4686. *
  4687. * //执行操作
  4688. * range.collapse( true );
  4689. *
  4690. * //结果选区
  4691. * //“|”表示选区已闭合
  4692. * //<b>xx<i>xxx</i><span>|xxx</span>xxx</b>
  4693. *
  4694. * </script>
  4695. * ```
  4696. */
  4697. collapse:function (toStart) {
  4698. var me = this;
  4699. if (toStart) {
  4700. me.endContainer = me.startContainer;
  4701. me.endOffset = me.startOffset;
  4702. } else {
  4703. me.startContainer = me.endContainer;
  4704. me.startOffset = me.endOffset;
  4705. }
  4706. me.collapsed = true;
  4707. return me;
  4708. },
  4709. /**
  4710. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
  4711. * @method shrinkBoundary
  4712. * @return { UE.dom.Range } 当前range对象
  4713. * @example
  4714. * ```html
  4715. * <span>xx<b>xx[</b>xxxxx]</span> => <span>xx<b>xx</b>[xxxxx]</span>
  4716. * ```
  4717. *
  4718. * @example
  4719. * ```html
  4720. * <!-- 选区示例 -->
  4721. * <b>x[xx</b><i>]xxx</i>
  4722. *
  4723. * <script>
  4724. *
  4725. * //执行收缩
  4726. * range.shrinkBoundary();
  4727. *
  4728. * //结果选区
  4729. * //<b>x[xx]</b><i>xxx</i>
  4730. * </script>
  4731. * ```
  4732. *
  4733. * @example
  4734. * ```html
  4735. * [<b><i>xxxx</i>xxxxxxx</b>] => <b><i>[xxxx</i>xxxxxxx]</b>
  4736. * ```
  4737. */
  4738. /**
  4739. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
  4740. * 如果ignoreEnd的值为true,则忽略对结束位置的调整
  4741. * @method shrinkBoundary
  4742. * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
  4743. * @return { UE.dom.Range } 当前range对象
  4744. * @see UE.dom.domUtils.Range:shrinkBoundary()
  4745. */
  4746. shrinkBoundary:function (ignoreEnd) {
  4747. var me = this, child,
  4748. collapsed = me.collapsed;
  4749. function check(node){
  4750. return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
  4751. }
  4752. while (me.startContainer.nodeType == 1 //是element
  4753. && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
  4754. && check(child)) {
  4755. me.setStart(child, 0);
  4756. }
  4757. if (collapsed) {
  4758. return me.collapse(true);
  4759. }
  4760. if (!ignoreEnd) {
  4761. while (me.endContainer.nodeType == 1//是element
  4762. && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  4763. && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
  4764. && check(child)) {
  4765. me.setEnd(child, child.childNodes.length);
  4766. }
  4767. }
  4768. return me;
  4769. },
  4770. /**
  4771. * 获取离当前选区内包含的所有节点最近的公共祖先节点,
  4772. * @method getCommonAncestor
  4773. * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
  4774. * @return { Node } 当前range对象内所有节点的公共祖先节点
  4775. * @example
  4776. * ```html
  4777. * //选区示例
  4778. * <span>xxx<b>x[x<em>xx]x</em>xxx</b>xx</span>
  4779. * <script>
  4780. *
  4781. * var node = range.getCommonAncestor();
  4782. *
  4783. * //公共祖先节点是: b节点
  4784. * //输出: B
  4785. * console.log(node.tagName);
  4786. *
  4787. * </script>
  4788. * ```
  4789. */
  4790. /**
  4791. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  4792. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  4793. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
  4794. * @method getCommonAncestor
  4795. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  4796. * @return { Node } 当前range对象内所有节点的公共祖先节点
  4797. * @see UE.dom.Range:getCommonAncestor()
  4798. * @example
  4799. * ```html
  4800. * <body>
  4801. *
  4802. * <!-- 选区示例 -->
  4803. * <b>xxx<i>xxxx<span>xx[x</span>xx]x</i>xxxxxxx</b>
  4804. *
  4805. * <script>
  4806. *
  4807. * var node = range.getCommonAncestor( false );
  4808. *
  4809. * //这里的公共祖先节点是B而不是I, 是因为参数限制了获取到的节点不能是容器节点
  4810. * //output: B
  4811. * console.log( node.tagName );
  4812. *
  4813. * </script>
  4814. *
  4815. * </body>
  4816. * ```
  4817. */
  4818. /**
  4819. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  4820. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  4821. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
  4822. * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
  4823. * @method getCommonAncestor
  4824. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  4825. * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
  4826. * @return { Node } 当前range对象内所有节点的公共祖先节点
  4827. * @see UE.dom.Range:getCommonAncestor()
  4828. * @see UE.dom.Range:getCommonAncestor(Boolean)
  4829. * @example
  4830. * ```html
  4831. * <body>
  4832. *
  4833. * <!-- 选区示例 -->
  4834. * <b>xxx<i>xxxx<span>x[x]x</span>xxx</i>xxxxxxx</b>
  4835. *
  4836. * <script>
  4837. *
  4838. * var node = range.getCommonAncestor( true, false );
  4839. *
  4840. * //output: SPAN
  4841. * console.log( node.tagName );
  4842. *
  4843. * </script>
  4844. *
  4845. * </body>
  4846. * ```
  4847. */
  4848. getCommonAncestor:function (includeSelf, ignoreTextNode) {
  4849. var me = this,
  4850. start = me.startContainer,
  4851. end = me.endContainer;
  4852. if (start === end) {
  4853. if (includeSelf && selectOneNode(this)) {
  4854. start = start.childNodes[me.startOffset];
  4855. if(start.nodeType == 1)
  4856. return start;
  4857. }
  4858. //只有在上来就相等的情况下才会出现是文本的情况
  4859. return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
  4860. }
  4861. return domUtils.getCommonAncestor(start, end);
  4862. },
  4863. /**
  4864. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
  4865. * @method trimBoundary
  4866. * @remind 该操作有可能会引起文本节点被切开
  4867. * @return { UE.dom.Range } 当前range对象
  4868. * @example
  4869. * ```html
  4870. *
  4871. * //选区示例
  4872. * <b>xxx<i>[xxxxx]</i>xxx</b>
  4873. *
  4874. * <script>
  4875. * //未调整前, 选区的开始容器和结束都是文本节点
  4876. * //执行调整
  4877. * range.trimBoundary();
  4878. *
  4879. * //调整之后, 容器节点变成了i节点
  4880. * //<b>xxx[<i>xxxxx</i>]xxx</b>
  4881. * </script>
  4882. * ```
  4883. */
  4884. /**
  4885. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
  4886. * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
  4887. * @method trimBoundary
  4888. * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
  4889. * @return { UE.dom.Range } 当前range对象
  4890. * @example
  4891. * ```html
  4892. *
  4893. * //选区示例
  4894. * <b>xxx<i>[xxxxx]</i>xxx</b>
  4895. *
  4896. * <script>
  4897. * //未调整前, 选区的开始容器和结束都是文本节点
  4898. * //执行调整
  4899. * range.trimBoundary( true );
  4900. *
  4901. * //调整之后, 开始容器节点变成了i节点
  4902. * //但是, 结束容器没有发生变化
  4903. * //<b>xxx[<i>xxxxx]</i>xxx</b>
  4904. * </script>
  4905. * ```
  4906. */
  4907. trimBoundary:function (ignoreEnd) {
  4908. this.txtToElmBoundary();
  4909. var start = this.startContainer,
  4910. offset = this.startOffset,
  4911. collapsed = this.collapsed,
  4912. end = this.endContainer;
  4913. if (start.nodeType == 3) {
  4914. if (offset == 0) {
  4915. this.setStartBefore(start);
  4916. } else {
  4917. if (offset >= start.nodeValue.length) {
  4918. this.setStartAfter(start);
  4919. } else {
  4920. var textNode = domUtils.split(start, offset);
  4921. //跟新结束边界
  4922. if (start === end) {
  4923. this.setEnd(textNode, this.endOffset - offset);
  4924. } else if (start.parentNode === end) {
  4925. this.endOffset += 1;
  4926. }
  4927. this.setStartBefore(textNode);
  4928. }
  4929. }
  4930. if (collapsed) {
  4931. return this.collapse(true);
  4932. }
  4933. }
  4934. if (!ignoreEnd) {
  4935. offset = this.endOffset;
  4936. end = this.endContainer;
  4937. if (end.nodeType == 3) {
  4938. if (offset == 0) {
  4939. this.setEndBefore(end);
  4940. } else {
  4941. offset < end.nodeValue.length && domUtils.split(end, offset);
  4942. this.setEndAfter(end);
  4943. }
  4944. }
  4945. }
  4946. return this;
  4947. },
  4948. /**
  4949. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
  4950. * @method txtToElmBoundary
  4951. * @remind 该操作不会修改dom节点
  4952. * @return { UE.dom.Range } 当前range对象
  4953. */
  4954. /**
  4955. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
  4956. * ignoreCollapsed 的值决定是否执行该调整
  4957. * @method txtToElmBoundary
  4958. * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
  4959. * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
  4960. * @return { UE.dom.Range } 当前range对象
  4961. */
  4962. txtToElmBoundary:function (ignoreCollapsed) {
  4963. function adjust(r, c) {
  4964. var container = r[c + 'Container'],
  4965. offset = r[c + 'Offset'];
  4966. if (container.nodeType == 3) {
  4967. if (!offset) {
  4968. r['set' + c.replace(/(\w)/, function (a) {
  4969. return a.toUpperCase();
  4970. }) + 'Before'](container);
  4971. } else if (offset >= container.nodeValue.length) {
  4972. r['set' + c.replace(/(\w)/, function (a) {
  4973. return a.toUpperCase();
  4974. }) + 'After' ](container);
  4975. }
  4976. }
  4977. }
  4978. if (ignoreCollapsed || !this.collapsed) {
  4979. adjust(this, 'start');
  4980. adjust(this, 'end');
  4981. }
  4982. return this;
  4983. },
  4984. /**
  4985. * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
  4986. * @method insertNode
  4987. * @param { Node } node 需要插入的节点
  4988. * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
  4989. * @return { UE.dom.Range } 当前range对象
  4990. */
  4991. insertNode:function (node) {
  4992. var first = node, length = 1;
  4993. if (node.nodeType == 11) {
  4994. first = node.firstChild;
  4995. length = node.childNodes.length;
  4996. }
  4997. this.trimBoundary(true);
  4998. var start = this.startContainer,
  4999. offset = this.startOffset;
  5000. var nextNode = start.childNodes[ offset ];
  5001. if (nextNode) {
  5002. start.insertBefore(node, nextNode);
  5003. } else {
  5004. start.appendChild(node);
  5005. }
  5006. if (first.parentNode === this.endContainer) {
  5007. this.endOffset = this.endOffset + length;
  5008. }
  5009. return this.setStartBefore(first);
  5010. },
  5011. /**
  5012. * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
  5013. * @method setCursor
  5014. * @return { UE.dom.Range } 当前range对象
  5015. * @see UE.dom.Range:collapse()
  5016. */
  5017. /**
  5018. * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
  5019. * @method setCursor
  5020. * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
  5021. * 反之,则向开始容器方向闭合
  5022. * @return { UE.dom.Range } 当前range对象
  5023. * @see UE.dom.Range:collapse(Boolean)
  5024. */
  5025. setCursor:function (toEnd, noFillData) {
  5026. return this.collapse(!toEnd).select(noFillData);
  5027. },
  5028. /**
  5029. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  5030. * @method createBookmark
  5031. * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
  5032. * 返回标记位置的ID, 反之则返回标记位置节点的引用
  5033. * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
  5034. * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
  5035. * 返回的记录的类型为ID, 反之则为引用
  5036. */
  5037. createBookmark:function (serialize, same) {
  5038. var endNode,
  5039. startNode = this.document.createElement('span');
  5040. startNode.style.cssText = 'display:none;line-height:0px;';
  5041. startNode.appendChild(this.document.createTextNode('\u200D'));
  5042. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  5043. if (!this.collapsed) {
  5044. endNode = startNode.cloneNode(true);
  5045. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
  5046. }
  5047. this.insertNode(startNode);
  5048. if (endNode) {
  5049. this.collapse().insertNode(endNode).setEndBefore(endNode);
  5050. }
  5051. this.setStartAfter(startNode);
  5052. return {
  5053. start:serialize ? startNode.id : startNode,
  5054. end:endNode ? serialize ? endNode.id : endNode : null,
  5055. id:serialize
  5056. }
  5057. },
  5058. /**
  5059. * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
  5060. * @method moveToBookmark
  5061. * @param { BookMark } bookmark createBookmark所创建的标签对象
  5062. * @return { UE.dom.Range } 当前range对象
  5063. * @see UE.dom.Range:createBookmark(Boolean)
  5064. */
  5065. moveToBookmark:function (bookmark) {
  5066. var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
  5067. end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
  5068. this.setStartBefore(start);
  5069. domUtils.remove(start);
  5070. if (end) {
  5071. this.setEndBefore(end);
  5072. domUtils.remove(end);
  5073. } else {
  5074. this.collapse(true);
  5075. }
  5076. return this;
  5077. },
  5078. /**
  5079. * 调整range的边界,使其"放大"到最近的父节点
  5080. * @method enlarge
  5081. * @remind 会引起选区的变化
  5082. * @return { UE.dom.Range } 当前range对象
  5083. */
  5084. /**
  5085. * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
  5086. * 要求扩大之后的父节点是block节点
  5087. * @method enlarge
  5088. * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
  5089. * @return { UE.dom.Range } 当前range对象
  5090. */
  5091. enlarge:function (toBlock, stopFn) {
  5092. var isBody = domUtils.isBody,
  5093. pre, node, tmp = this.document.createTextNode('');
  5094. if (toBlock) {
  5095. node = this.startContainer;
  5096. if (node.nodeType == 1) {
  5097. if (node.childNodes[this.startOffset]) {
  5098. pre = node = node.childNodes[this.startOffset]
  5099. } else {
  5100. node.appendChild(tmp);
  5101. pre = node = tmp;
  5102. }
  5103. } else {
  5104. pre = node;
  5105. }
  5106. while (1) {
  5107. if (domUtils.isBlockElm(node)) {
  5108. node = pre;
  5109. while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
  5110. node = pre;
  5111. }
  5112. this.setStartBefore(node);
  5113. break;
  5114. }
  5115. pre = node;
  5116. node = node.parentNode;
  5117. }
  5118. node = this.endContainer;
  5119. if (node.nodeType == 1) {
  5120. if (pre = node.childNodes[this.endOffset]) {
  5121. node.insertBefore(tmp, pre);
  5122. } else {
  5123. node.appendChild(tmp);
  5124. }
  5125. pre = node = tmp;
  5126. } else {
  5127. pre = node;
  5128. }
  5129. while (1) {
  5130. if (domUtils.isBlockElm(node)) {
  5131. node = pre;
  5132. while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
  5133. node = pre;
  5134. }
  5135. this.setEndAfter(node);
  5136. break;
  5137. }
  5138. pre = node;
  5139. node = node.parentNode;
  5140. }
  5141. if (tmp.parentNode === this.endContainer) {
  5142. this.endOffset--;
  5143. }
  5144. domUtils.remove(tmp);
  5145. }
  5146. // 扩展边界到最大
  5147. if (!this.collapsed) {
  5148. while (this.startOffset == 0) {
  5149. if (stopFn && stopFn(this.startContainer)) {
  5150. break;
  5151. }
  5152. if (isBody(this.startContainer)) {
  5153. break;
  5154. }
  5155. this.setStartBefore(this.startContainer);
  5156. }
  5157. while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) {
  5158. if (stopFn && stopFn(this.endContainer)) {
  5159. break;
  5160. }
  5161. if (isBody(this.endContainer)) {
  5162. break;
  5163. }
  5164. this.setEndAfter(this.endContainer);
  5165. }
  5166. }
  5167. return this;
  5168. },
  5169. enlargeToBlockElm:function(ignoreEnd){
  5170. while(!domUtils.isBlockElm(this.startContainer)){
  5171. this.setStartBefore(this.startContainer);
  5172. }
  5173. if(!ignoreEnd){
  5174. while(!domUtils.isBlockElm(this.endContainer)){
  5175. this.setEndAfter(this.endContainer);
  5176. }
  5177. }
  5178. return this;
  5179. },
  5180. /**
  5181. * 调整Range的边界,使其"缩小"到最合适的位置
  5182. * @method adjustmentBoundary
  5183. * @return { UE.dom.Range } 当前range对象
  5184. * @see UE.dom.Range:shrinkBoundary()
  5185. */
  5186. adjustmentBoundary:function () {
  5187. if (!this.collapsed) {
  5188. while (!domUtils.isBody(this.startContainer) &&
  5189. this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
  5190. this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  5191. ) {
  5192. this.setStartAfter(this.startContainer);
  5193. }
  5194. while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
  5195. this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  5196. ) {
  5197. this.setEndBefore(this.endContainer);
  5198. }
  5199. }
  5200. return this;
  5201. },
  5202. /**
  5203. * 给range选区中的内容添加给定的inline标签
  5204. * @method applyInlineStyle
  5205. * @param { String } tagName 需要添加的标签名
  5206. * @example
  5207. * ```html
  5208. * <p>xxxx[xxxx]x</p> ==> range.applyInlineStyle("strong") ==> <p>xxxx[<strong>xxxx</strong>]x</p>
  5209. * ```
  5210. */
  5211. /**
  5212. * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
  5213. * @method applyInlineStyle
  5214. * @param { String } tagName 需要添加的标签名
  5215. * @param { Object } attrs 跟随新添加的标签的属性
  5216. * @return { UE.dom.Range } 当前选区
  5217. * @example
  5218. * ```html
  5219. * <p>xxxx[xxxx]x</p>
  5220. *
  5221. * ==>
  5222. *
  5223. * <!-- 执行操作 -->
  5224. * range.applyInlineStyle("strong",{"style":"font-size:12px"})
  5225. *
  5226. * ==>
  5227. *
  5228. * <p>xxxx[<strong style="font-size:12px">xxxx</strong>]x</p>
  5229. * ```
  5230. */
  5231. applyInlineStyle:function (tagName, attrs, list) {
  5232. if (this.collapsed)return this;
  5233. this.trimBoundary().enlarge(false,
  5234. function (node) {
  5235. return node.nodeType == 1 && domUtils.isBlockElm(node)
  5236. }).adjustmentBoundary();
  5237. var bookmark = this.createBookmark(),
  5238. end = bookmark.end,
  5239. filterFn = function (node) {
  5240. return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
  5241. },
  5242. current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
  5243. node,
  5244. pre,
  5245. range = this.cloneRange();
  5246. while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
  5247. if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
  5248. range.setStartBefore(current);
  5249. node = current;
  5250. while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
  5251. pre = node;
  5252. node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
  5253. return dtd[tagName][parent.tagName];
  5254. });
  5255. }
  5256. var frag = range.setEndAfter(pre).extractContents(), elm;
  5257. if (list && list.length > 0) {
  5258. var level, top;
  5259. top = level = list[0].cloneNode(false);
  5260. for (var i = 1, ci; ci = list[i++];) {
  5261. level.appendChild(ci.cloneNode(false));
  5262. level = level.firstChild;
  5263. }
  5264. elm = level;
  5265. } else {
  5266. elm = range.document.createElement(tagName);
  5267. }
  5268. if (attrs) {
  5269. domUtils.setAttributes(elm, attrs);
  5270. }
  5271. elm.appendChild(frag);
  5272. range.insertNode(list ? top : elm);
  5273. //处理下滑线在a上的情况
  5274. var aNode;
  5275. if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
  5276. domUtils.setAttributes(aNode, attrs);
  5277. domUtils.remove(elm, true);
  5278. elm = aNode;
  5279. } else {
  5280. domUtils.mergeSibling(elm);
  5281. domUtils.clearEmptySibling(elm);
  5282. }
  5283. //去除子节点相同的
  5284. domUtils.mergeChild(elm, attrs);
  5285. current = domUtils.getNextDomNode(elm, false, filterFn);
  5286. domUtils.mergeToParent(elm);
  5287. if (node === end) {
  5288. break;
  5289. }
  5290. } else {
  5291. current = domUtils.getNextDomNode(current, true, filterFn);
  5292. }
  5293. }
  5294. return this.moveToBookmark(bookmark);
  5295. },
  5296. /**
  5297. * 移除当前选区内指定的inline标签,但保留其中的内容
  5298. * @method removeInlineStyle
  5299. * @param { String } tagName 需要移除的标签名
  5300. * @return { UE.dom.Range } 当前的range对象
  5301. * @example
  5302. * ```html
  5303. * xx[x<span>xxx<em>yyy</em>zz]z</span> => range.removeInlineStyle(["em"]) => xx[x<span>xxxyyyzz]z</span>
  5304. * ```
  5305. */
  5306. /**
  5307. * 移除当前选区内指定的一组inline标签,但保留其中的内容
  5308. * @method removeInlineStyle
  5309. * @param { Array } tagNameArr 需要移除的标签名的数组
  5310. * @return { UE.dom.Range } 当前的range对象
  5311. * @see UE.dom.Range:removeInlineStyle(String)
  5312. */
  5313. removeInlineStyle:function (tagNames) {
  5314. if (this.collapsed)return this;
  5315. tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
  5316. this.shrinkBoundary().adjustmentBoundary();
  5317. var start = this.startContainer, end = this.endContainer;
  5318. while (1) {
  5319. if (start.nodeType == 1) {
  5320. if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
  5321. break;
  5322. }
  5323. if (start.tagName.toLowerCase() == 'body') {
  5324. start = null;
  5325. break;
  5326. }
  5327. }
  5328. start = start.parentNode;
  5329. }
  5330. while (1) {
  5331. if (end.nodeType == 1) {
  5332. if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
  5333. break;
  5334. }
  5335. if (end.tagName.toLowerCase() == 'body') {
  5336. end = null;
  5337. break;
  5338. }
  5339. }
  5340. end = end.parentNode;
  5341. }
  5342. var bookmark = this.createBookmark(),
  5343. frag,
  5344. tmpRange;
  5345. if (start) {
  5346. tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
  5347. frag = tmpRange.extractContents();
  5348. tmpRange.insertNode(frag);
  5349. domUtils.clearEmptySibling(start, true);
  5350. start.parentNode.insertBefore(bookmark.start, start);
  5351. }
  5352. if (end) {
  5353. tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
  5354. frag = tmpRange.extractContents();
  5355. tmpRange.insertNode(frag);
  5356. domUtils.clearEmptySibling(end, false, true);
  5357. end.parentNode.insertBefore(bookmark.end, end.nextSibling);
  5358. }
  5359. var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
  5360. return node.nodeType == 1;
  5361. }), next;
  5362. while (current && current !== bookmark.end) {
  5363. next = domUtils.getNextDomNode(current, true, function (node) {
  5364. return node.nodeType == 1;
  5365. });
  5366. if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
  5367. domUtils.remove(current, true);
  5368. }
  5369. current = next;
  5370. }
  5371. return this.moveToBookmark(bookmark);
  5372. },
  5373. /**
  5374. * 获取当前选中的自闭合的节点
  5375. * @method getClosedNode
  5376. * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
  5377. */
  5378. getClosedNode:function () {
  5379. var node;
  5380. if (!this.collapsed) {
  5381. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
  5382. if (selectOneNode(range)) {
  5383. var child = range.startContainer.childNodes[range.startOffset];
  5384. if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
  5385. node = child;
  5386. }
  5387. }
  5388. }
  5389. return node;
  5390. },
  5391. /**
  5392. * 在页面上高亮range所表示的选区
  5393. * @method select
  5394. * @return { UE.dom.Range } 返回当前Range对象
  5395. */
  5396. //这里不区分ie9以上,trace:3824
  5397. select:browser.ie ? function (noFillData, textRange) {
  5398. var nativeRange;
  5399. if (!this.collapsed)
  5400. this.shrinkBoundary();
  5401. var node = this.getClosedNode();
  5402. if (node && !textRange) {
  5403. try {
  5404. nativeRange = this.document.body.createControlRange();
  5405. nativeRange.addElement(node);
  5406. nativeRange.select();
  5407. } catch (e) {}
  5408. return this;
  5409. }
  5410. var bookmark = this.createBookmark(),
  5411. start = bookmark.start,
  5412. end;
  5413. nativeRange = this.document.body.createTextRange();
  5414. nativeRange.moveToElementText(start);
  5415. nativeRange.moveStart('character', 1);
  5416. if (!this.collapsed) {
  5417. var nativeRangeEnd = this.document.body.createTextRange();
  5418. end = bookmark.end;
  5419. nativeRangeEnd.moveToElementText(end);
  5420. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
  5421. } else {
  5422. if (!noFillData && this.startContainer.nodeType != 3) {
  5423. //使用<span>|x<span>固定住光标
  5424. var tmpText = this.document.createTextNode(fillChar),
  5425. tmp = this.document.createElement('span');
  5426. tmp.appendChild(this.document.createTextNode(fillChar));
  5427. start.parentNode.insertBefore(tmp, start);
  5428. start.parentNode.insertBefore(tmpText, start);
  5429. //当点b,i,u时,不能清除i上边的b
  5430. removeFillData(this.document, tmpText);
  5431. fillData = tmpText;
  5432. mergeSibling(tmp, 'previousSibling');
  5433. mergeSibling(start, 'nextSibling');
  5434. nativeRange.moveStart('character', -1);
  5435. nativeRange.collapse(true);
  5436. }
  5437. }
  5438. this.moveToBookmark(bookmark);
  5439. tmp && domUtils.remove(tmp);
  5440. //IE在隐藏状态下不支持range操作,catch一下
  5441. try {
  5442. nativeRange.select();
  5443. } catch (e) {
  5444. }
  5445. return this;
  5446. } : function (notInsertFillData) {
  5447. function checkOffset(rng){
  5448. function check(node,offset,dir){
  5449. if(node.nodeType == 3 && node.nodeValue.length < offset){
  5450. rng[dir + 'Offset'] = node.nodeValue.length
  5451. }
  5452. }
  5453. check(rng.startContainer,rng.startOffset,'start');
  5454. check(rng.endContainer,rng.endOffset,'end');
  5455. }
  5456. var win = domUtils.getWindow(this.document),
  5457. sel = win.getSelection(),
  5458. txtNode;
  5459. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  5460. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  5461. browser.gecko ? this.document.body.focus() : win.focus();
  5462. if (sel) {
  5463. sel.removeAllRanges();
  5464. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  5465. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  5466. if (this.collapsed && !notInsertFillData) {
  5467. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  5468. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  5469. // var tmp = this.document.createTextNode('');
  5470. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  5471. // }
  5472. //
  5473. //处理光标落在文本节点的情况
  5474. //处理以下的情况
  5475. //<b>|xxxx</b>
  5476. //<b>xxxx</b>|xxxx
  5477. //xxxx<b>|</b>
  5478. var start = this.startContainer,child = start;
  5479. if(start.nodeType == 1){
  5480. child = start.childNodes[this.startOffset];
  5481. }
  5482. if( !(start.nodeType == 3 && this.startOffset) &&
  5483. (child ?
  5484. (!child.previousSibling || child.previousSibling.nodeType != 3)
  5485. :
  5486. (!start.lastChild || start.lastChild.nodeType != 3)
  5487. )
  5488. ){
  5489. txtNode = this.document.createTextNode(fillChar);
  5490. //跟着前边走
  5491. this.insertNode(txtNode);
  5492. removeFillData(this.document, txtNode);
  5493. mergeSibling(txtNode, 'previousSibling');
  5494. mergeSibling(txtNode, 'nextSibling');
  5495. fillData = txtNode;
  5496. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
  5497. }
  5498. }
  5499. var nativeRange = this.document.createRange();
  5500. if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
  5501. var child = this.startContainer.childNodes[this.startOffset];
  5502. if(!child){
  5503. //往前靠拢
  5504. child = this.startContainer.lastChild;
  5505. if( child && domUtils.isBr(child)){
  5506. this.setStartBefore(child).collapse(true);
  5507. }
  5508. }else{
  5509. //向后靠拢
  5510. while(child && domUtils.isBlockElm(child)){
  5511. if(child.nodeType == 1 && child.childNodes[0]){
  5512. child = child.childNodes[0]
  5513. }else{
  5514. break;
  5515. }
  5516. }
  5517. child && this.setStartBefore(child).collapse(true)
  5518. }
  5519. }
  5520. //是createAddress最后一位算的不准,现在这里进行微调
  5521. checkOffset(this);
  5522. nativeRange.setStart(this.startContainer, this.startOffset);
  5523. nativeRange.setEnd(this.endContainer, this.endOffset);
  5524. sel.addRange(nativeRange);
  5525. }
  5526. return this;
  5527. },
  5528. /**
  5529. * 滚动到当前range开始的位置
  5530. * @method scrollToView
  5531. * @param { Window } win 当前range对象所属的window对象
  5532. * @return { UE.dom.Range } 当前Range对象
  5533. */
  5534. /**
  5535. * 滚动到距离当前range开始位置 offset 的位置处
  5536. * @method scrollToView
  5537. * @param { Window } win 当前range对象所属的window对象
  5538. * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
  5539. * @return { UE.dom.Range } 当前Range对象
  5540. */
  5541. scrollToView:function (win, offset) {
  5542. win = win ? window : domUtils.getWindow(this.document);
  5543. var me = this,
  5544. span = me.document.createElement('span');
  5545. //trace:717
  5546. span.innerHTML = '&nbsp;';
  5547. me.cloneRange().insertNode(span);
  5548. domUtils.scrollToView(span, win, offset);
  5549. domUtils.remove(span);
  5550. return me;
  5551. },
  5552. /**
  5553. * 判断当前选区内容是否占位符
  5554. * @private
  5555. * @method inFillChar
  5556. * @return { Boolean } 如果是占位符返回true,否则返回false
  5557. */
  5558. inFillChar : function(){
  5559. var start = this.startContainer;
  5560. if(this.collapsed && start.nodeType == 3
  5561. && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
  5562. ){
  5563. return true;
  5564. }
  5565. return false;
  5566. },
  5567. /**
  5568. * 保存
  5569. * @method createAddress
  5570. * @private
  5571. * @return { Boolean } 返回开始和结束的位置
  5572. * @example
  5573. * ```html
  5574. * <body>
  5575. * <p>
  5576. * aaaa
  5577. * <em>
  5578. * <!-- 选区开始 -->
  5579. * bbbb
  5580. * <!-- 选区结束 -->
  5581. * </em>
  5582. * </p>
  5583. *
  5584. * <script>
  5585. * //output: {startAddress:[0,1,0,0],endAddress:[0,1,0,4]}
  5586. * console.log( range.createAddress() );
  5587. * </script>
  5588. * </body>
  5589. * ```
  5590. */
  5591. createAddress : function(ignoreEnd,ignoreTxt){
  5592. var addr = {},me = this;
  5593. function getAddress(isStart){
  5594. var node = isStart ? me.startContainer : me.endContainer;
  5595. var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
  5596. addrs = [];
  5597. for(var i = 0,ci;ci = parents[i++];){
  5598. addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
  5599. }
  5600. var firstIndex = 0;
  5601. if(ignoreTxt){
  5602. if(node.nodeType == 3){
  5603. var tmpNode = node.previousSibling;
  5604. while(tmpNode && tmpNode.nodeType == 3){
  5605. firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
  5606. tmpNode = tmpNode.previousSibling;
  5607. }
  5608. firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  5609. }else{
  5610. node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
  5611. if(node){
  5612. firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
  5613. }else{
  5614. node = isStart ? me.startContainer : me.endContainer;
  5615. var first = node.firstChild;
  5616. while(first){
  5617. if(domUtils.isFillChar(first)){
  5618. first = first.nextSibling;
  5619. continue;
  5620. }
  5621. firstIndex++;
  5622. if(first.nodeType == 3){
  5623. while( first && first.nodeType == 3){
  5624. first = first.nextSibling;
  5625. }
  5626. }else{
  5627. first = first.nextSibling;
  5628. }
  5629. }
  5630. }
  5631. }
  5632. }else{
  5633. firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
  5634. }
  5635. if(firstIndex < 0){
  5636. firstIndex = 0;
  5637. }
  5638. addrs.push(firstIndex);
  5639. return addrs;
  5640. }
  5641. addr.startAddress = getAddress(true);
  5642. if(!ignoreEnd){
  5643. addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
  5644. }
  5645. return addr;
  5646. },
  5647. /**
  5648. * 保存
  5649. * @method createAddress
  5650. * @private
  5651. * @return { Boolean } 返回开始和结束的位置
  5652. * @example
  5653. * ```html
  5654. * <body>
  5655. * <p>
  5656. * aaaa
  5657. * <em>
  5658. * <!-- 选区开始 -->
  5659. * bbbb
  5660. * <!-- 选区结束 -->
  5661. * </em>
  5662. * </p>
  5663. *
  5664. * <script>
  5665. * var range = editor.selection.getRange();
  5666. * range.moveToAddress({startAddress:[0,1,0,0],endAddress:[0,1,0,4]});
  5667. * range.select();
  5668. * //output: 'bbbb'
  5669. * console.log(editor.selection.getText());
  5670. * </script>
  5671. * </body>
  5672. * ```
  5673. */
  5674. moveToAddress : function(addr,ignoreEnd){
  5675. var me = this;
  5676. function getNode(address,isStart){
  5677. var tmpNode = me.document.body,
  5678. parentNode,offset;
  5679. for(var i= 0,ci,l=address.length;i<l;i++){
  5680. ci = address[i];
  5681. parentNode = tmpNode;
  5682. tmpNode = tmpNode.childNodes[ci];
  5683. if(!tmpNode){
  5684. offset = ci;
  5685. break;
  5686. }
  5687. }
  5688. if(isStart){
  5689. if(tmpNode){
  5690. me.setStartBefore(tmpNode)
  5691. }else{
  5692. me.setStart(parentNode,offset)
  5693. }
  5694. }else{
  5695. if(tmpNode){
  5696. me.setEndBefore(tmpNode)
  5697. }else{
  5698. me.setEnd(parentNode,offset)
  5699. }
  5700. }
  5701. }
  5702. getNode(addr.startAddress,true);
  5703. !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
  5704. return me;
  5705. },
  5706. /**
  5707. * 判断给定的Range对象是否和当前Range对象表示的是同一个选区
  5708. * @method equals
  5709. * @param { UE.dom.Range } 需要判断的Range对象
  5710. * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false
  5711. */
  5712. equals : function(rng){
  5713. for(var p in this){
  5714. if(this.hasOwnProperty(p)){
  5715. if(this[p] !== rng[p])
  5716. return false
  5717. }
  5718. }
  5719. return true;
  5720. },
  5721. /**
  5722. * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  5723. * 作为其参数。
  5724. * @method traversal
  5725. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  5726. * @return { UE.dom.Range } 当前range对象
  5727. * @example
  5728. * ```html
  5729. *
  5730. * <body>
  5731. *
  5732. * <!-- 选区开始 -->
  5733. * <span></span>
  5734. * <a></a>
  5735. * <!-- 选区结束 -->
  5736. * </body>
  5737. *
  5738. * <script>
  5739. *
  5740. * //output: <span></span><a></a>
  5741. * console.log( range.cloneContents() );
  5742. *
  5743. * range.traversal( function ( node ) {
  5744. *
  5745. * if ( node.nodeType === 1 ) {
  5746. * node.className = "test";
  5747. * }
  5748. *
  5749. * } );
  5750. *
  5751. * //output: <span class="test"></span><a class="test"></a>
  5752. * console.log( range.cloneContents() );
  5753. *
  5754. * </script>
  5755. * ```
  5756. */
  5757. /**
  5758. * 遍历range内的节点。
  5759. * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  5760. * 作为其参数。
  5761. * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
  5762. * 发doFn函数的执行
  5763. * @method traversal
  5764. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  5765. * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
  5766. * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
  5767. * 会触发doFn。
  5768. * @return { UE.dom.Range } 当前range对象
  5769. * @see UE.dom.Range:traversal(Function)
  5770. * @example
  5771. * ```html
  5772. *
  5773. * <body>
  5774. *
  5775. * <!-- 选区开始 -->
  5776. * <span></span>
  5777. * <a></a>
  5778. * <!-- 选区结束 -->
  5779. * </body>
  5780. *
  5781. * <script>
  5782. *
  5783. * //output: <span></span><a></a>
  5784. * console.log( range.cloneContents() );
  5785. *
  5786. * range.traversal( function ( node ) {
  5787. *
  5788. * node.className = "test";
  5789. *
  5790. * }, function ( node ) {
  5791. * return node.nodeType === 1;
  5792. * } );
  5793. *
  5794. * //output: <span class="test"></span><a class="test"></a>
  5795. * console.log( range.cloneContents() );
  5796. *
  5797. * </script>
  5798. * ```
  5799. */
  5800. traversal:function(doFn,filterFn){
  5801. if (this.collapsed)
  5802. return this;
  5803. var bookmark = this.createBookmark(),
  5804. end = bookmark.end,
  5805. current = domUtils.getNextDomNode(bookmark.start, false, filterFn);
  5806. while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
  5807. var tmpNode = domUtils.getNextDomNode(current,false,filterFn);
  5808. doFn(current);
  5809. current = tmpNode;
  5810. }
  5811. return this.moveToBookmark(bookmark);
  5812. }
  5813. };
  5814. })();
  5815. // core/Selection.js
  5816. /**
  5817. * 选集
  5818. * @file
  5819. * @module UE.dom
  5820. * @class Selection
  5821. * @since 1.2.6.1
  5822. */
  5823. /**
  5824. * 选区集合
  5825. * @unfile
  5826. * @module UE.dom
  5827. * @class Selection
  5828. */
  5829. (function () {
  5830. function getBoundaryInformation( range, start ) {
  5831. var getIndex = domUtils.getNodeIndex;
  5832. range = range.duplicate();
  5833. range.collapse( start );
  5834. var parent = range.parentElement();
  5835. //如果节点里没有子节点,直接退出
  5836. if ( !parent.hasChildNodes() ) {
  5837. return {container:parent, offset:0};
  5838. }
  5839. var siblings = parent.children,
  5840. child,
  5841. testRange = range.duplicate(),
  5842. startIndex = 0, endIndex = siblings.length - 1, index = -1,
  5843. distance;
  5844. while ( startIndex <= endIndex ) {
  5845. index = Math.floor( (startIndex + endIndex) / 2 );
  5846. child = siblings[index];
  5847. testRange.moveToElementText( child );
  5848. var position = testRange.compareEndPoints( 'StartToStart', range );
  5849. if ( position > 0 ) {
  5850. endIndex = index - 1;
  5851. } else if ( position < 0 ) {
  5852. startIndex = index + 1;
  5853. } else {
  5854. //trace:1043
  5855. return {container:parent, offset:getIndex( child )};
  5856. }
  5857. }
  5858. if ( index == -1 ) {
  5859. testRange.moveToElementText( parent );
  5860. testRange.setEndPoint( 'StartToStart', range );
  5861. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  5862. siblings = parent.childNodes;
  5863. if ( !distance ) {
  5864. child = siblings[siblings.length - 1];
  5865. return {container:child, offset:child.nodeValue.length};
  5866. }
  5867. var i = siblings.length;
  5868. while ( distance > 0 ){
  5869. distance -= siblings[ --i ].nodeValue.length;
  5870. }
  5871. return {container:siblings[i], offset:-distance};
  5872. }
  5873. testRange.collapse( position > 0 );
  5874. testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
  5875. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  5876. if ( !distance ) {
  5877. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
  5878. {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
  5879. {container:child, offset:position > 0 ? 0 : child.childNodes.length}
  5880. }
  5881. while ( distance > 0 ) {
  5882. try {
  5883. var pre = child;
  5884. child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
  5885. distance -= child.nodeValue.length;
  5886. } catch ( e ) {
  5887. return {container:parent, offset:getIndex( pre )};
  5888. }
  5889. }
  5890. return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
  5891. }
  5892. /**
  5893. * 将ieRange转换为Range对象
  5894. * @param {Range} ieRange ieRange对象
  5895. * @param {Range} range Range对象
  5896. * @return {Range} range 返回转换后的Range对象
  5897. */
  5898. function transformIERangeToRange( ieRange, range ) {
  5899. if ( ieRange.item ) {
  5900. range.selectNode( ieRange.item( 0 ) );
  5901. } else {
  5902. var bi = getBoundaryInformation( ieRange, true );
  5903. range.setStart( bi.container, bi.offset );
  5904. if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
  5905. bi = getBoundaryInformation( ieRange, false );
  5906. range.setEnd( bi.container, bi.offset );
  5907. }
  5908. }
  5909. return range;
  5910. }
  5911. /**
  5912. * 获得ieRange
  5913. * @param {Selection} sel Selection对象
  5914. * @return {ieRange} 得到ieRange
  5915. */
  5916. function _getIERange( sel ) {
  5917. var ieRange;
  5918. //ie下有可能报错
  5919. try {
  5920. ieRange = sel.getNative().createRange();
  5921. } catch ( e ) {
  5922. return null;
  5923. }
  5924. var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
  5925. if ( ( el.ownerDocument || el ) === sel.document ) {
  5926. return ieRange;
  5927. }
  5928. return null;
  5929. }
  5930. var Selection = dom.Selection = function ( doc ) {
  5931. var me = this, iframe;
  5932. me.document = doc;
  5933. if ( browser.ie9below ) {
  5934. iframe = domUtils.getWindow( doc ).frameElement;
  5935. domUtils.on( iframe, 'beforedeactivate', function () {
  5936. me._bakIERange = me.getIERange();
  5937. } );
  5938. domUtils.on( iframe, 'activate', function () {
  5939. try {
  5940. if ( !_getIERange( me ) && me._bakIERange ) {
  5941. me._bakIERange.select();
  5942. }
  5943. } catch ( ex ) {
  5944. }
  5945. me._bakIERange = null;
  5946. } );
  5947. }
  5948. iframe = doc = null;
  5949. };
  5950. Selection.prototype = {
  5951. rangeInBody : function(rng,txtRange){
  5952. var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  5953. return node === this.document.body || domUtils.inDoc(node,this.document);
  5954. },
  5955. /**
  5956. * 获取原生seleciton对象
  5957. * @method getNative
  5958. * @return { Object } 获得selection对象
  5959. * @example
  5960. * ```javascript
  5961. * editor.selection.getNative();
  5962. * ```
  5963. */
  5964. getNative:function () {
  5965. var doc = this.document;
  5966. try {
  5967. return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow( doc ).getSelection();
  5968. } catch ( e ) {
  5969. return null;
  5970. }
  5971. },
  5972. /**
  5973. * 获得ieRange
  5974. * @method getIERange
  5975. * @return { Object } 返回ie原生的Range
  5976. * @example
  5977. * ```javascript
  5978. * editor.selection.getIERange();
  5979. * ```
  5980. */
  5981. getIERange:function () {
  5982. var ieRange = _getIERange( this );
  5983. if ( !ieRange ) {
  5984. if ( this._bakIERange ) {
  5985. return this._bakIERange;
  5986. }
  5987. }
  5988. return ieRange;
  5989. },
  5990. /**
  5991. * 缓存当前选区的range和选区的开始节点
  5992. * @method cache
  5993. */
  5994. cache:function () {
  5995. this.clear();
  5996. this._cachedRange = this.getRange();
  5997. this._cachedStartElement = this.getStart();
  5998. this._cachedStartElementPath = this.getStartElementPath();
  5999. },
  6000. /**
  6001. * 获取选区开始位置的父节点到body
  6002. * @method getStartElementPath
  6003. * @return { Array } 返回父节点集合
  6004. * @example
  6005. * ```javascript
  6006. * editor.selection.getStartElementPath();
  6007. * ```
  6008. */
  6009. getStartElementPath:function () {
  6010. if ( this._cachedStartElementPath ) {
  6011. return this._cachedStartElementPath;
  6012. }
  6013. var start = this.getStart();
  6014. if ( start ) {
  6015. return domUtils.findParents( start, true, null, true )
  6016. }
  6017. return [];
  6018. },
  6019. /**
  6020. * 清空缓存
  6021. * @method clear
  6022. */
  6023. clear:function () {
  6024. this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
  6025. },
  6026. /**
  6027. * 编辑器是否得到了选区
  6028. * @method isFocus
  6029. */
  6030. isFocus:function () {
  6031. try {
  6032. if(browser.ie9below){
  6033. var nativeRange = _getIERange(this);
  6034. return !!(nativeRange && this.rangeInBody(nativeRange));
  6035. }else{
  6036. return !!this.getNative().rangeCount;
  6037. }
  6038. } catch ( e ) {
  6039. return false;
  6040. }
  6041. },
  6042. /**
  6043. * 获取选区对应的Range
  6044. * @method getRange
  6045. * @return { Object } 得到Range对象
  6046. * @example
  6047. * ```javascript
  6048. * editor.selection.getRange();
  6049. * ```
  6050. */
  6051. getRange:function () {
  6052. var me = this;
  6053. function optimze( range ) {
  6054. var child = me.document.body.firstChild,
  6055. collapsed = range.collapsed;
  6056. while ( child && child.firstChild ) {
  6057. range.setStart( child, 0 );
  6058. child = child.firstChild;
  6059. }
  6060. if ( !range.startContainer ) {
  6061. range.setStart( me.document.body, 0 )
  6062. }
  6063. if ( collapsed ) {
  6064. range.collapse( true );
  6065. }
  6066. }
  6067. if ( me._cachedRange != null ) {
  6068. return this._cachedRange;
  6069. }
  6070. var range = new baidu.editor.dom.Range( me.document );
  6071. if ( browser.ie9below ) {
  6072. var nativeRange = me.getIERange();
  6073. if ( nativeRange ) {
  6074. //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
  6075. try{
  6076. transformIERangeToRange( nativeRange, range );
  6077. }catch(e){
  6078. optimze( range );
  6079. }
  6080. } else {
  6081. optimze( range );
  6082. }
  6083. } else {
  6084. var sel = me.getNative();
  6085. if ( sel && sel.rangeCount ) {
  6086. var firstRange = sel.getRangeAt( 0 );
  6087. var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
  6088. range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
  6089. if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
  6090. optimze( range );
  6091. }
  6092. } else {
  6093. //trace:1734 有可能已经不在dom树上了,标识的节点
  6094. if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){
  6095. return this._bakRange;
  6096. }
  6097. optimze( range );
  6098. }
  6099. }
  6100. return this._bakRange = range;
  6101. },
  6102. /**
  6103. * 获取开始元素,用于状态反射
  6104. * @method getStart
  6105. * @return { Element } 获得开始元素
  6106. * @example
  6107. * ```javascript
  6108. * editor.selection.getStart();
  6109. * ```
  6110. */
  6111. getStart:function () {
  6112. if ( this._cachedStartElement ) {
  6113. return this._cachedStartElement;
  6114. }
  6115. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  6116. tmpRange,
  6117. start, tmp, parent;
  6118. if ( browser.ie9below ) {
  6119. if ( !range ) {
  6120. //todo 给第一个值可能会有问题
  6121. return this.document.body.firstChild;
  6122. }
  6123. //control元素
  6124. if ( range.item ){
  6125. return range.item( 0 );
  6126. }
  6127. tmpRange = range.duplicate();
  6128. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  6129. tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
  6130. tmpRange.collapse( 1 );
  6131. start = tmpRange.parentElement();
  6132. parent = tmp = range.parentElement();
  6133. while ( tmp = tmp.parentNode ) {
  6134. if ( tmp == start ) {
  6135. start = parent;
  6136. break;
  6137. }
  6138. }
  6139. } else {
  6140. range.shrinkBoundary();
  6141. start = range.startContainer;
  6142. if ( start.nodeType == 1 && start.hasChildNodes() ){
  6143. start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
  6144. }
  6145. if ( start.nodeType == 3 ){
  6146. return start.parentNode;
  6147. }
  6148. }
  6149. return start;
  6150. },
  6151. /**
  6152. * 得到选区中的文本
  6153. * @method getText
  6154. * @return { String } 选区中包含的文本
  6155. * @example
  6156. * ```javascript
  6157. * editor.selection.getText();
  6158. * ```
  6159. */
  6160. getText:function () {
  6161. var nativeSel, nativeRange;
  6162. if ( this.isFocus() && (nativeSel = this.getNative()) ) {
  6163. nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
  6164. return browser.ie9below ? nativeRange.text : nativeRange.toString();
  6165. }
  6166. return '';
  6167. },
  6168. /**
  6169. * 清除选区
  6170. * @method clearRange
  6171. * @example
  6172. * ```javascript
  6173. * editor.selection.clearRange();
  6174. * ```
  6175. */
  6176. clearRange : function(){
  6177. this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
  6178. }
  6179. };
  6180. })();
  6181. // core/Editor.js
  6182. /**
  6183. * 编辑器主类,包含编辑器提供的大部分公用接口
  6184. * @file
  6185. * @module UE
  6186. * @class Editor
  6187. * @since 1.2.6.1
  6188. */
  6189. /**
  6190. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  6191. * @unfile
  6192. * @module UE
  6193. */
  6194. /**
  6195. * UEditor的核心类,为用户提供与编辑器交互的接口。
  6196. * @unfile
  6197. * @module UE
  6198. * @class Editor
  6199. */
  6200. (function () {
  6201. var uid = 0, _selectionChangeTimer;
  6202. /**
  6203. * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
  6204. * @private
  6205. * @method setValue
  6206. * @param { UE.Editor } editor 编辑器事例
  6207. */
  6208. function setValue(form, editor) {
  6209. var textarea;
  6210. if (editor.textarea) {
  6211. if (utils.isString(editor.textarea)) {
  6212. for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
  6213. if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
  6214. textarea = ti;
  6215. break;
  6216. }
  6217. }
  6218. } else {
  6219. textarea = editor.textarea;
  6220. }
  6221. }
  6222. if (!textarea) {
  6223. form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
  6224. 'name': editor.options.textarea,
  6225. 'id': 'ueditor_textarea_' + editor.options.textarea,
  6226. 'style': "display:none"
  6227. }));
  6228. //不要产生多个textarea
  6229. editor.textarea = textarea;
  6230. }
  6231. !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea );
  6232. textarea.value = editor.hasContents() ?
  6233. (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
  6234. ''
  6235. }
  6236. function loadPlugins(me){
  6237. //初始化插件
  6238. for (var pi in UE.plugins) {
  6239. UE.plugins[pi].call(me);
  6240. }
  6241. }
  6242. function checkCurLang(I18N){
  6243. for(var lang in I18N){
  6244. return lang
  6245. }
  6246. }
  6247. function langReadied(me){
  6248. me.langIsReady = true;
  6249. me.fireEvent("langReady");
  6250. }
  6251. /**
  6252. * 编辑器准备就绪后会触发该事件
  6253. * @module UE
  6254. * @class Editor
  6255. * @event ready
  6256. * @remind render方法执行完成之后,会触发该事件
  6257. * @remind
  6258. * @example
  6259. * ```javascript
  6260. * editor.addListener( 'ready', function( editor ) {
  6261. * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
  6262. * } );
  6263. * ```
  6264. */
  6265. /**
  6266. * 执行destroy方法,会触发该事件
  6267. * @module UE
  6268. * @class Editor
  6269. * @event destroy
  6270. * @see UE.Editor:destroy()
  6271. */
  6272. /**
  6273. * 执行reset方法,会触发该事件
  6274. * @module UE
  6275. * @class Editor
  6276. * @event reset
  6277. * @see UE.Editor:reset()
  6278. */
  6279. /**
  6280. * 执行focus方法,会触发该事件
  6281. * @module UE
  6282. * @class Editor
  6283. * @event focus
  6284. * @see UE.Editor:focus(Boolean)
  6285. */
  6286. /**
  6287. * 语言加载完成会触发该事件
  6288. * @module UE
  6289. * @class Editor
  6290. * @event langReady
  6291. */
  6292. /**
  6293. * 运行命令之后会触发该命令
  6294. * @module UE
  6295. * @class Editor
  6296. * @event beforeExecCommand
  6297. */
  6298. /**
  6299. * 运行命令之后会触发该命令
  6300. * @module UE
  6301. * @class Editor
  6302. * @event afterExecCommand
  6303. */
  6304. /**
  6305. * 运行命令之前会触发该命令
  6306. * @module UE
  6307. * @class Editor
  6308. * @event firstBeforeExecCommand
  6309. */
  6310. /**
  6311. * 在getContent方法执行之前会触发该事件
  6312. * @module UE
  6313. * @class Editor
  6314. * @event beforeGetContent
  6315. * @see UE.Editor:getContent()
  6316. */
  6317. /**
  6318. * 在getContent方法执行之后会触发该事件
  6319. * @module UE
  6320. * @class Editor
  6321. * @event afterGetContent
  6322. * @see UE.Editor:getContent()
  6323. */
  6324. /**
  6325. * 在getAllHtml方法执行时会触发该事件
  6326. * @module UE
  6327. * @class Editor
  6328. * @event getAllHtml
  6329. * @see UE.Editor:getAllHtml()
  6330. */
  6331. /**
  6332. * 在setContent方法执行之前会触发该事件
  6333. * @module UE
  6334. * @class Editor
  6335. * @event beforeSetContent
  6336. * @see UE.Editor:setContent(String)
  6337. */
  6338. /**
  6339. * 在setContent方法执行之后会触发该事件
  6340. * @module UE
  6341. * @class Editor
  6342. * @event afterSetContent
  6343. * @see UE.Editor:setContent(String)
  6344. */
  6345. /**
  6346. * 每当编辑器内部选区发生改变时,将触发该事件
  6347. * @event selectionchange
  6348. * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
  6349. * @example
  6350. * ```javascript
  6351. * editor.addListener( 'selectionchange', function( editor ) {
  6352. * console.log('选区发生改变');
  6353. * }
  6354. */
  6355. /**
  6356. * 在所有selectionchange的监听函数执行之前,会触发该事件
  6357. * @module UE
  6358. * @class Editor
  6359. * @event beforeSelectionChange
  6360. * @see UE.Editor:selectionchange
  6361. */
  6362. /**
  6363. * 在所有selectionchange的监听函数执行完之后,会触发该事件
  6364. * @module UE
  6365. * @class Editor
  6366. * @event afterSelectionChange
  6367. * @see UE.Editor:selectionchange
  6368. */
  6369. /**
  6370. * 编辑器内容发生改变时会触发该事件
  6371. * @module UE
  6372. * @class Editor
  6373. * @event contentChange
  6374. */
  6375. /**
  6376. * 以默认参数构建一个编辑器实例
  6377. * @constructor
  6378. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  6379. * @example
  6380. * ```javascript
  6381. * var editor = new UE.Editor();
  6382. * editor.execCommand('blod');
  6383. * ```
  6384. * @see UE.Config
  6385. */
  6386. /**
  6387. * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
  6388. * @constructor
  6389. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  6390. * @param { Object } setting 创建编辑器的参数
  6391. * @example
  6392. * ```javascript
  6393. * var editor = new UE.Editor();
  6394. * editor.execCommand('blod');
  6395. * ```
  6396. * @see UE.Config
  6397. */
  6398. var Editor = UE.Editor = function (options) {
  6399. var me = this;
  6400. me.uid = uid++;
  6401. EventBase.call(me);
  6402. me.commands = {};
  6403. me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true);
  6404. me.shortcutkeys = {};
  6405. me.inputRules = [];
  6406. me.outputRules = [];
  6407. //设置默认的常用属性
  6408. me.setOpt(Editor.defaultOptions(me));
  6409. /* 尝试异步加载后台配置 */
  6410. me.loadServerConfig();
  6411. if(!utils.isEmptyObject(UE.I18N)){
  6412. //修改默认的语言类型
  6413. me.options.lang = checkCurLang(UE.I18N);
  6414. UE.plugin.load(me);
  6415. langReadied(me);
  6416. }else{
  6417. utils.loadFile(document, {
  6418. src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
  6419. tag: "script",
  6420. type: "text/javascript",
  6421. defer: "defer"
  6422. }, function () {
  6423. UE.plugin.load(me);
  6424. langReadied(me);
  6425. });
  6426. }
  6427. UE.instants['ueditorInstant' + me.uid] = me;
  6428. };
  6429. Editor.prototype = {
  6430. registerCommand : function(name,obj){
  6431. this.commands[name] = obj;
  6432. },
  6433. /**
  6434. * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
  6435. * @method ready
  6436. * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
  6437. * 立即触发该回调。
  6438. * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
  6439. * @example
  6440. * ```javascript
  6441. * editor.ready( function( editor ) {
  6442. * editor.setContent('初始化完毕');
  6443. * } );
  6444. * ```
  6445. * @see UE.Editor.event:ready
  6446. */
  6447. ready: function (fn) {
  6448. var me = this;
  6449. if (fn) {
  6450. me.isReady ? fn.apply(me) : me.addListener('ready', fn);
  6451. }
  6452. },
  6453. /**
  6454. * 该方法是提供给插件里面使用,设置配置项默认值
  6455. * @method setOpt
  6456. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  6457. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  6458. * @param { String } key 编辑器的可接受的选项名称
  6459. * @param { * } val 该选项可接受的值
  6460. * @example
  6461. * ```javascript
  6462. * editor.setOpt( 'initContent', '欢迎使用编辑器' );
  6463. * ```
  6464. */
  6465. /**
  6466. * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
  6467. * @method setOpt
  6468. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  6469. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  6470. * @param { Object } options 将要设置的选项的键值对对象
  6471. * @example
  6472. * ```javascript
  6473. * editor.setOpt( {
  6474. * 'initContent': '欢迎使用编辑器'
  6475. * } );
  6476. * ```
  6477. */
  6478. setOpt: function (key, val) {
  6479. var obj = {};
  6480. if (utils.isString(key)) {
  6481. obj[key] = val
  6482. } else {
  6483. obj = key;
  6484. }
  6485. utils.extend(this.options, obj, true);
  6486. },
  6487. getOpt:function(key){
  6488. return this.options[key]
  6489. },
  6490. /**
  6491. * 销毁编辑器实例,使用textarea代替
  6492. * @method destroy
  6493. * @example
  6494. * ```javascript
  6495. * editor.destroy();
  6496. * ```
  6497. */
  6498. destroy: function () {
  6499. var me = this;
  6500. me.fireEvent('destroy');
  6501. var container = me.container.parentNode;
  6502. var textarea = me.textarea;
  6503. if (!textarea) {
  6504. textarea = document.createElement('textarea');
  6505. container.parentNode.insertBefore(textarea, container);
  6506. } else {
  6507. textarea.style.display = ''
  6508. }
  6509. textarea.style.width = me.iframe.offsetWidth + 'px';
  6510. textarea.style.height = me.iframe.offsetHeight + 'px';
  6511. textarea.value = me.getContent();
  6512. textarea.id = me.key;
  6513. container.innerHTML = '';
  6514. domUtils.remove(container);
  6515. var key = me.key;
  6516. //trace:2004
  6517. for (var p in me) {
  6518. if (me.hasOwnProperty(p)) {
  6519. delete this[p];
  6520. }
  6521. }
  6522. UE.delEditor(key);
  6523. },
  6524. /**
  6525. * 渲染编辑器的DOM到指定容器
  6526. * @method render
  6527. * @param { String } containerId 指定一个容器ID
  6528. * @remind 执行该方法,会触发ready事件
  6529. * @warning 必须且只能调用一次
  6530. */
  6531. /**
  6532. * 渲染编辑器的DOM到指定容器
  6533. * @method render
  6534. * @param { Element } containerDom 直接指定容器对象
  6535. * @remind 执行该方法,会触发ready事件
  6536. * @warning 必须且只能调用一次
  6537. */
  6538. render: function (container) {
  6539. var me = this,
  6540. options = me.options,
  6541. getStyleValue=function(attr){
  6542. return parseInt(domUtils.getComputedStyle(container,attr));
  6543. };
  6544. if (utils.isString(container)) {
  6545. container = document.getElementById(container);
  6546. }
  6547. if (container) {
  6548. if(options.initialFrameWidth){
  6549. options.minFrameWidth = options.initialFrameWidth
  6550. }else{
  6551. options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
  6552. }
  6553. if(options.initialFrameHeight){
  6554. options.minFrameHeight = options.initialFrameHeight
  6555. }else{
  6556. options.initialFrameHeight = options.minFrameHeight = container.offsetHeight;
  6557. }
  6558. container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth-
  6559. getStyleValue("padding-left")- getStyleValue("padding-right") +'px';
  6560. container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight -
  6561. getStyleValue("padding-top")- getStyleValue("padding-bottom") +'px';
  6562. container.style.zIndex = options.zIndex;
  6563. var html = ( ie && browser.version < 9 ? '' : '<!DOCTYPE html>') +
  6564. '<html xmlns=\'http://www.w3.org/1999/xhtml\' class=\'view\' ><head>' +
  6565. '<style type=\'text/css\'>' +
  6566. //设置四周的留边
  6567. '.view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n' +
  6568. //设置默认字体和字号
  6569. //font-family不能呢随便改,在safari下fillchar会有解析问题
  6570. 'body{margin:8px;font-family:sans-serif;font-size:16px;}' +
  6571. //设置段落间距
  6572. 'p{margin:5px 0;}</style>' +
  6573. ( options.iframeCssUrl ? '<link rel=\'stylesheet\' type=\'text/css\' href=\'' + utils.unhtml(options.iframeCssUrl) + '\'/>' : '' ) +
  6574. (options.initialStyle ? '<style>' + options.initialStyle + '</style>' : '') +
  6575. '</head><body class=\'view\' ></body>' +
  6576. '<script type=\'text/javascript\' ' + (ie ? 'defer=\'defer\'' : '' ) +' id=\'_initialScript\'>' +
  6577. 'setTimeout(function(){editor = window.parent.UE.instants[\'ueditorInstant' + me.uid + '\'];editor._setup(document);},0);' +
  6578. 'var _tmpScript = document.getElementById(\'_initialScript\');_tmpScript.parentNode.removeChild(_tmpScript);</script></html>';
  6579. container.appendChild(domUtils.createElement(document, 'iframe', {
  6580. id: 'ueditor_' + me.uid,
  6581. width: "100%",
  6582. height: "100%",
  6583. frameborder: "0",
  6584. //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
  6585. // scrolling :'no',
  6586. src: 'javascript:void(function(){document.open();' + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : '') +
  6587. 'document.write("' + html + '");document.close();}())'
  6588. }));
  6589. container.style.overflow = 'hidden';
  6590. //解决如果是给定的百分比,会导致高度算不对的问题
  6591. setTimeout(function(){
  6592. if( /%$/.test(options.initialFrameWidth)){
  6593. options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
  6594. //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
  6595. // container.style.width = options.initialFrameWidth + 'px';
  6596. }
  6597. if(/%$/.test(options.initialFrameHeight)){
  6598. options.minFrameHeight = options.initialFrameHeight = container.offsetHeight;
  6599. container.style.height = options.initialFrameHeight + 'px';
  6600. }
  6601. })
  6602. }
  6603. },
  6604. /**
  6605. * 编辑器初始化
  6606. * @method _setup
  6607. * @private
  6608. * @param { Element } doc 编辑器Iframe中的文档对象
  6609. */
  6610. _setup: function (doc) {
  6611. var me = this,
  6612. options = me.options;
  6613. if (ie) {
  6614. doc.body.disabled = true;
  6615. doc.body.contentEditable = true;
  6616. doc.body.disabled = false;
  6617. } else {
  6618. doc.body.contentEditable = true;
  6619. }
  6620. doc.body.spellcheck = false;
  6621. me.document = doc;
  6622. me.window = doc.defaultView || doc.parentWindow;
  6623. me.iframe = me.window.frameElement;
  6624. me.body = doc.body;
  6625. me.selection = new dom.Selection(doc);
  6626. //gecko初始化就能得到range,无法判断isFocus了
  6627. var geckoSel;
  6628. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  6629. geckoSel.removeAllRanges();
  6630. }
  6631. this._initEvents();
  6632. //为form提交提供一个隐藏的textarea
  6633. for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) {
  6634. if (form.tagName == 'FORM') {
  6635. me.form = form;
  6636. if(me.options.autoSyncData){
  6637. domUtils.on(me.window,'blur',function(){
  6638. setValue(form,me);
  6639. });
  6640. }else{
  6641. domUtils.on(form, 'submit', function () {
  6642. setValue(this, me);
  6643. });
  6644. }
  6645. break;
  6646. }
  6647. }
  6648. if (options.initialContent) {
  6649. if (options.autoClearinitialContent) {
  6650. var oldExecCommand = me.execCommand;
  6651. me.execCommand = function () {
  6652. me.fireEvent('firstBeforeExecCommand');
  6653. return oldExecCommand.apply(me, arguments);
  6654. };
  6655. this._setDefaultContent(options.initialContent);
  6656. } else
  6657. this.setContent(options.initialContent, false, true);
  6658. }
  6659. //编辑器不能为空内容
  6660. if (domUtils.isEmptyNode(me.body)) {
  6661. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  6662. }
  6663. //如果要求focus, 就把光标定位到内容开始
  6664. if (options.focus) {
  6665. setTimeout(function () {
  6666. me.focus(me.options.focusInEnd);
  6667. //如果自动清除开着,就不需要做selectionchange;
  6668. !me.options.autoClearinitialContent && me._selectionChange();
  6669. }, 0);
  6670. }
  6671. if (!me.container) {
  6672. me.container = this.iframe.parentNode;
  6673. }
  6674. if (options.fullscreen && me.ui) {
  6675. me.ui.setFullScreen(true);
  6676. }
  6677. try {
  6678. me.document.execCommand('2D-position', false, false);
  6679. } catch (e) {
  6680. }
  6681. try {
  6682. me.document.execCommand('enableInlineTableEditing', false, false);
  6683. } catch (e) {
  6684. }
  6685. try {
  6686. me.document.execCommand('enableObjectResizing', false, false);
  6687. } catch (e) {
  6688. }
  6689. //挂接快捷键
  6690. me._bindshortcutKeys();
  6691. me.isReady = 1;
  6692. me.fireEvent('ready');
  6693. options.onready && options.onready.call(me);
  6694. if (!browser.ie9below) {
  6695. domUtils.on(me.window, ['blur', 'focus'], function (e) {
  6696. //chrome下会出现alt+tab切换时,导致选区位置不对
  6697. if (e.type == 'blur') {
  6698. me._bakRange = me.selection.getRange();
  6699. try {
  6700. me._bakNativeRange = me.selection.getNative().getRangeAt(0);
  6701. me.selection.getNative().removeAllRanges();
  6702. } catch (e) {
  6703. me._bakNativeRange = null;
  6704. }
  6705. } else {
  6706. try {
  6707. me._bakRange && me._bakRange.select();
  6708. } catch (e) {
  6709. }
  6710. }
  6711. });
  6712. }
  6713. //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
  6714. if (browser.gecko && browser.version <= 10902) {
  6715. //修复ff3.6初始化进来,不能点击获得焦点
  6716. me.body.contentEditable = false;
  6717. setTimeout(function () {
  6718. me.body.contentEditable = true;
  6719. }, 100);
  6720. setInterval(function () {
  6721. me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
  6722. }, 100)
  6723. }
  6724. !options.isShow && me.setHide();
  6725. options.readonly && me.setDisabled();
  6726. },
  6727. /**
  6728. * 同步数据到编辑器所在的form
  6729. * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
  6730. * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
  6731. * @method sync
  6732. * @example
  6733. * ```javascript
  6734. * editor.sync();
  6735. * form.sumbit(); //form变量已经指向了form元素
  6736. * ```
  6737. */
  6738. /**
  6739. * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
  6740. * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
  6741. * @method sync
  6742. * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  6743. */
  6744. sync: function (formId) {
  6745. var me = this,
  6746. form = formId ? document.getElementById(formId) :
  6747. domUtils.findParent(me.iframe.parentNode, function (node) {
  6748. return node.tagName == 'FORM'
  6749. }, true);
  6750. form && setValue(form, me);
  6751. },
  6752. /**
  6753. * 设置编辑器高度
  6754. * @method setHeight
  6755. * @remind 当配置项autoHeightEnabled为真时,该方法无效
  6756. * @param { Number } number 设置的高度值,纯数值,不带单位
  6757. * @example
  6758. * ```javascript
  6759. * editor.setHeight(number);
  6760. * ```
  6761. */
  6762. setHeight: function (height,notSetHeight) {
  6763. if (height !== parseInt(this.iframe.parentNode.style.height)) {
  6764. this.iframe.parentNode.style.height = height + 'px';
  6765. }
  6766. !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height);
  6767. this.body.style.height = height + 'px';
  6768. !notSetHeight && this.trigger('setHeight')
  6769. },
  6770. /**
  6771. * 为编辑器的编辑命令提供快捷键
  6772. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  6773. * @method addshortcutkey
  6774. * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
  6775. * @example
  6776. * ```javascript
  6777. * editor.addshortcutkey({
  6778. * "Bold" : "ctrl+66",//^B
  6779. * "Italic" : "ctrl+73", //^I
  6780. * });
  6781. * ```
  6782. */
  6783. /**
  6784. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  6785. * @method addshortcutkey
  6786. * @param { String } cmd 触发快捷键时,响应的命令
  6787. * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
  6788. * @example
  6789. * ```javascript
  6790. * editor.addshortcutkey("Underline", "ctrl+85"); //^U
  6791. * ```
  6792. */
  6793. addshortcutkey: function (cmd, keys) {
  6794. var obj = {};
  6795. if (keys) {
  6796. obj[cmd] = keys
  6797. } else {
  6798. obj = cmd;
  6799. }
  6800. utils.extend(this.shortcutkeys, obj)
  6801. },
  6802. /**
  6803. * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
  6804. * @method _bindshortcutKeys
  6805. * @private
  6806. */
  6807. _bindshortcutKeys: function () {
  6808. var me = this, shortcutkeys = this.shortcutkeys;
  6809. me.addListener('keydown', function (type, e) {
  6810. var keyCode = e.keyCode || e.which;
  6811. for (var i in shortcutkeys) {
  6812. var tmp = shortcutkeys[i].split(',');
  6813. for (var t = 0, ti; ti = tmp[t++];) {
  6814. ti = ti.split(':');
  6815. var key = ti[0], param = ti[1];
  6816. if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
  6817. if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
  6818. && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
  6819. && keyCode == RegExp.$3
  6820. ) ||
  6821. keyCode == RegExp.$1
  6822. ) {
  6823. if (me.queryCommandState(i,param) != -1)
  6824. me.execCommand(i, param);
  6825. domUtils.preventDefault(e);
  6826. }
  6827. }
  6828. }
  6829. }
  6830. });
  6831. },
  6832. /**
  6833. * 获取编辑器的内容
  6834. * @method getContent
  6835. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  6836. * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”&lt;p&gt;&lt;br/&gt;&lt;/p&gt;“), 则返回空字符串
  6837. * @example
  6838. * ```javascript
  6839. * //编辑器html内容:<p>1<strong>2<em>34</em>5</strong>6</p>
  6840. * var content = editor.getContent(); //返回值:<p>1<strong>2<em>34</em>5</strong>6</p>
  6841. * ```
  6842. */
  6843. /**
  6844. * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
  6845. * @method getContent
  6846. * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
  6847. * 代表当前编辑器的内容是否空,
  6848. * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
  6849. * 经过内置过滤规则处理后的内容。
  6850. * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
  6851. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  6852. * @return { String } 编辑器的内容字符串
  6853. * @example
  6854. * ```javascript
  6855. * // editor 是一个编辑器的实例
  6856. * var content = editor.getContent( function ( editor ) {
  6857. * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
  6858. * } );
  6859. * ```
  6860. */
  6861. getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
  6862. var me = this;
  6863. if (cmd && utils.isFunction(cmd)) {
  6864. fn = cmd;
  6865. cmd = '';
  6866. }
  6867. if (fn ? !fn() : !this.hasContents()) {
  6868. return '';
  6869. }
  6870. me.fireEvent('beforegetcontent');
  6871. var root = UE.htmlparser(me.body.innerHTML,ignoreBlank);
  6872. me.filterOutputRule(root);
  6873. me.fireEvent('aftergetcontent', cmd,root);
  6874. return root.toHtml(formatter);
  6875. },
  6876. /**
  6877. * 取得完整的html代码,可以直接显示成完整的html文档
  6878. * @method getAllHtml
  6879. * @return { String } 编辑器的内容html文档字符串
  6880. * @eaxmple
  6881. * ```javascript
  6882. * editor.getAllHtml(); //返回格式大致是: <html><head>...</head><body>...</body></html>
  6883. * ```
  6884. */
  6885. getAllHtml: function () {
  6886. var me = this,
  6887. headHtml = [],
  6888. html = '';
  6889. me.fireEvent('getAllHtml', headHtml);
  6890. if (browser.ie && browser.version > 8) {
  6891. var headHtmlForIE9 = '';
  6892. utils.each(me.document.styleSheets, function (si) {
  6893. headHtmlForIE9 += ( si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
  6894. });
  6895. utils.each(me.document.getElementsByTagName('script'), function (si) {
  6896. headHtmlForIE9 += si.outerHTML;
  6897. });
  6898. }
  6899. return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
  6900. + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
  6901. + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
  6902. },
  6903. /**
  6904. * 得到编辑器的纯文本内容,但会保留段落格式
  6905. * @method getPlainTxt
  6906. * @return { String } 编辑器带段落格式的纯文本内容字符串
  6907. * @example
  6908. * ```javascript
  6909. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  6910. * console.log(editor.getPlainTxt()); //输出:"1\n2\n
  6911. * ```
  6912. */
  6913. getPlainTxt: function () {
  6914. var reg = new RegExp(domUtils.fillChar, 'g'),
  6915. html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
  6916. html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  6917. .replace(/<br\/?>/gi, '\n')
  6918. .replace(/<[^>/]+>/g, '')
  6919. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  6920. return dtd.$block[c] ? '\n' : b ? b : '';
  6921. });
  6922. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  6923. return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
  6924. },
  6925. /**
  6926. * 获取编辑器中的纯文本内容,没有段落格式
  6927. * @method getContentTxt
  6928. * @return { String } 编辑器不带段落格式的纯文本内容字符串
  6929. * @example
  6930. * ```javascript
  6931. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  6932. * console.log(editor.getPlainTxt()); //输出:"12
  6933. * ```
  6934. */
  6935. getContentTxt: function () {
  6936. var reg = new RegExp(domUtils.fillChar, 'g');
  6937. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  6938. return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
  6939. },
  6940. /**
  6941. * 设置编辑器的内容,可修改编辑器当前的html内容
  6942. * @method setContent
  6943. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  6944. * @warning 该方法会触发selectionchange事件
  6945. * @param { String } html 要插入的html内容
  6946. * @example
  6947. * ```javascript
  6948. * editor.getContent('<p>test</p>');
  6949. * ```
  6950. */
  6951. /**
  6952. * 设置编辑器的内容,可修改编辑器当前的html内容
  6953. * @method setContent
  6954. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  6955. * @warning 该方法会触发selectionchange事件
  6956. * @param { String } html 要插入的html内容
  6957. * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
  6958. * @example
  6959. * ```javascript
  6960. * //假设设置前的编辑器内容是 <p>old text</p>
  6961. * editor.setContent('<p>new text</p>', true); //插入的结果是<p>old text</p><p>new text</p>
  6962. * ```
  6963. */
  6964. setContent: function (html, isAppendTo, notFireSelectionchange) {
  6965. var me = this;
  6966. me.fireEvent('beforesetcontent', html);
  6967. var root = UE.htmlparser(html);
  6968. me.filterInputRule(root);
  6969. html = root.toHtml();
  6970. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  6971. function isCdataDiv(node){
  6972. return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
  6973. }
  6974. //给文本或者inline节点套p标签
  6975. if (me.options.enterTag == 'p') {
  6976. var child = this.body.firstChild, tmpNode;
  6977. if (!child || child.nodeType == 1 &&
  6978. (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
  6979. domUtils.isCustomeNode(child)
  6980. )
  6981. && child === this.body.lastChild) {
  6982. this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  6983. } else {
  6984. var p = me.document.createElement('p');
  6985. while (child) {
  6986. while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
  6987. tmpNode = child.nextSibling;
  6988. p.appendChild(child);
  6989. child = tmpNode;
  6990. }
  6991. if (p.firstChild) {
  6992. if (!child) {
  6993. me.body.appendChild(p);
  6994. break;
  6995. } else {
  6996. child.parentNode.insertBefore(p, child);
  6997. p = me.document.createElement('p');
  6998. }
  6999. }
  7000. child = child.nextSibling;
  7001. }
  7002. }
  7003. }
  7004. me.fireEvent('aftersetcontent');
  7005. me.fireEvent('contentchange');
  7006. !notFireSelectionchange && me._selectionChange();
  7007. //清除保存的选区
  7008. me._bakRange = me._bakIERange = me._bakNativeRange = null;
  7009. //trace:1742 setContent后gecko能得到焦点问题
  7010. var geckoSel;
  7011. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  7012. geckoSel.removeAllRanges();
  7013. }
  7014. if(me.options.autoSyncData){
  7015. me.form && setValue(me.form,me);
  7016. }
  7017. },
  7018. /**
  7019. * 让编辑器获得焦点,默认focus到编辑器头部
  7020. * @method focus
  7021. * @example
  7022. * ```javascript
  7023. * editor.focus()
  7024. * ```
  7025. */
  7026. /**
  7027. * 让编辑器获得焦点,toEnd确定focus位置
  7028. * @method focus
  7029. * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
  7030. * @example
  7031. * ```javascript
  7032. * editor.focus(true)
  7033. * ```
  7034. */
  7035. focus: function (toEnd) {
  7036. try {
  7037. var me = this,
  7038. rng = me.selection.getRange();
  7039. if (toEnd) {
  7040. var node = me.body.lastChild;
  7041. if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
  7042. if(domUtils.isEmptyBlock(node)){
  7043. rng.setStartAtFirst(node)
  7044. }else{
  7045. rng.setStartAtLast(node)
  7046. }
  7047. rng.collapse(true);
  7048. }
  7049. rng.setCursor(true);
  7050. } else {
  7051. if(!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0){
  7052. var node = me.body.firstChild;
  7053. if(node && node.nodeType == 1 && !dtd.$empty[node.tagName]){
  7054. rng.setStartAtFirst(node).collapse(true);
  7055. }
  7056. }
  7057. rng.select(true);
  7058. }
  7059. this.fireEvent('focus selectionchange');
  7060. } catch (e) {
  7061. }
  7062. },
  7063. isFocus:function(){
  7064. return this.selection.isFocus();
  7065. },
  7066. blur:function(){
  7067. var sel = this.selection.getNative();
  7068. if(sel.empty && browser.ie){
  7069. var nativeRng = document.body.createTextRange();
  7070. nativeRng.moveToElementText(document.body);
  7071. nativeRng.collapse(true);
  7072. nativeRng.select();
  7073. sel.empty()
  7074. }else{
  7075. sel.removeAllRanges()
  7076. }
  7077. //this.fireEvent('blur selectionchange');
  7078. },
  7079. /**
  7080. * 初始化UE事件及部分事件代理
  7081. * @method _initEvents
  7082. * @private
  7083. */
  7084. _initEvents: function () {
  7085. var me = this,
  7086. doc = me.document,
  7087. win = me.window;
  7088. me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
  7089. domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me._proxyDomEvent);
  7090. domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent);
  7091. domUtils.on(me.body,'drop',function(e){
  7092. //阻止ff下默认的弹出新页面打开图片
  7093. if(browser.gecko && e.stopPropagation) { e.stopPropagation(); }
  7094. me.fireEvent('contentchange')
  7095. });
  7096. domUtils.on(doc, ['mouseup', 'keydown'], function (evt) {
  7097. //特殊键不触发selectionchange
  7098. if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
  7099. return;
  7100. }
  7101. if (evt.button == 2)return;
  7102. me._selectionChange(250, evt);
  7103. });
  7104. },
  7105. /**
  7106. * 触发事件代理
  7107. * @method _proxyDomEvent
  7108. * @private
  7109. * @return { * } fireEvent的返回值
  7110. * @see UE.EventBase:fireEvent(String)
  7111. */
  7112. _proxyDomEvent: function (evt) {
  7113. if(this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false){
  7114. return false;
  7115. }
  7116. if(this.fireEvent(evt.type.replace(/^on/, ''), evt) === false){
  7117. return false;
  7118. }
  7119. return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase())
  7120. },
  7121. /**
  7122. * 变化选区
  7123. * @method _selectionChange
  7124. * @private
  7125. */
  7126. _selectionChange: function (delay, evt) {
  7127. var me = this;
  7128. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  7129. // if ( !me.selection.isFocus() ){
  7130. // return;
  7131. // }
  7132. var hackForMouseUp = false;
  7133. var mouseX, mouseY;
  7134. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  7135. var range = this.selection.getRange();
  7136. if (!range.collapsed) {
  7137. hackForMouseUp = true;
  7138. mouseX = evt.clientX;
  7139. mouseY = evt.clientY;
  7140. }
  7141. }
  7142. clearTimeout(_selectionChangeTimer);
  7143. _selectionChangeTimer = setTimeout(function () {
  7144. if (!me.selection || !me.selection.getNative()) {
  7145. return;
  7146. }
  7147. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  7148. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  7149. var ieRange;
  7150. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  7151. ieRange = me.document.body.createTextRange();
  7152. try {
  7153. ieRange.moveToPoint(mouseX, mouseY);
  7154. } catch (ex) {
  7155. ieRange = null;
  7156. }
  7157. }
  7158. var bakGetIERange;
  7159. if (ieRange) {
  7160. bakGetIERange = me.selection.getIERange;
  7161. me.selection.getIERange = function () {
  7162. return ieRange;
  7163. };
  7164. }
  7165. me.selection.cache();
  7166. if (bakGetIERange) {
  7167. me.selection.getIERange = bakGetIERange;
  7168. }
  7169. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  7170. me.fireEvent('beforeselectionchange');
  7171. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  7172. me.fireEvent('selectionchange', !!evt);
  7173. me.fireEvent('afterselectionchange');
  7174. me.selection.clear();
  7175. }
  7176. }, delay || 50);
  7177. },
  7178. /**
  7179. * 执行编辑命令
  7180. * @method _callCmdFn
  7181. * @private
  7182. * @param { String } fnName 函数名称
  7183. * @param { * } args 传给命令函数的参数
  7184. * @return { * } 返回命令函数运行的返回值
  7185. */
  7186. _callCmdFn: function (fnName, args) {
  7187. var cmdName = args[0].toLowerCase(),
  7188. cmd, cmdFn;
  7189. cmd = this.commands[cmdName] || UE.commands[cmdName];
  7190. cmdFn = cmd && cmd[fnName];
  7191. //没有querycommandstate或者没有command的都默认返回0
  7192. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  7193. return 0;
  7194. } else if (cmdFn) {
  7195. return cmdFn.apply(this, args);
  7196. }
  7197. },
  7198. /**
  7199. * 执行编辑命令cmdName,完成富文本编辑效果
  7200. * @method execCommand
  7201. * @param { String } cmdName 需要执行的命令
  7202. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7203. * @return { * } 返回命令函数运行的返回值
  7204. * @example
  7205. * ```javascript
  7206. * editor.execCommand(cmdName);
  7207. * ```
  7208. */
  7209. execCommand: function (cmdName) {
  7210. cmdName = cmdName.toLowerCase();
  7211. var me = this,
  7212. result,
  7213. cmd = me.commands[cmdName] || UE.commands[cmdName];
  7214. if (!cmd || !cmd.execCommand) {
  7215. return null;
  7216. }
  7217. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  7218. me.__hasEnterExecCommand = true;
  7219. if (me.queryCommandState.apply(me,arguments) != -1) {
  7220. me.fireEvent('saveScene');
  7221. me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments));
  7222. result = this._callCmdFn('execCommand', arguments);
  7223. //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
  7224. // (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  7225. me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments));
  7226. me.fireEvent('saveScene');
  7227. }
  7228. me.__hasEnterExecCommand = false;
  7229. } else {
  7230. result = this._callCmdFn('execCommand', arguments);
  7231. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
  7232. }
  7233. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
  7234. return result;
  7235. },
  7236. /**
  7237. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  7238. * @method queryCommandState
  7239. * @param { String } cmdName 需要查询的命令名称
  7240. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7241. * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
  7242. * @example
  7243. * ```javascript
  7244. * editor.queryCommandState(cmdName) => (-1|0|1)
  7245. * ```
  7246. * @see COMMAND.LIST
  7247. */
  7248. queryCommandState: function (cmdName) {
  7249. return this._callCmdFn('queryCommandState', arguments);
  7250. },
  7251. /**
  7252. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  7253. * @method queryCommandValue
  7254. * @param { String } cmdName 需要查询的命令名称
  7255. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7256. * @remind 只有部分插件有此方法
  7257. * @return { * } 返回每个命令特定的当前状态值
  7258. * @grammar editor.queryCommandValue(cmdName) => {*}
  7259. * @see COMMAND.LIST
  7260. */
  7261. queryCommandValue: function (cmdName) {
  7262. return this._callCmdFn('queryCommandValue', arguments);
  7263. },
  7264. /**
  7265. * 检查编辑区域中是否有内容
  7266. * @method hasContents
  7267. * @remind 默认有文本内容,或者有以下节点都不认为是空
  7268. * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
  7269. * @return { Boolean } 检查有内容返回true,否则返回false
  7270. * @example
  7271. * ```javascript
  7272. * editor.hasContents()
  7273. * ```
  7274. */
  7275. /**
  7276. * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
  7277. * @method hasContents
  7278. * @param { Array } tags 传入数组判断时用到的节点类型
  7279. * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
  7280. * @example
  7281. * ```javascript
  7282. * editor.hasContents(['span']);
  7283. * ```
  7284. */
  7285. hasContents: function (tags) {
  7286. if (tags) {
  7287. for (var i = 0, ci; ci = tags[i++];) {
  7288. if (this.document.getElementsByTagName(ci).length > 0) {
  7289. return true;
  7290. }
  7291. }
  7292. }
  7293. if (!domUtils.isEmptyBlock(this.body)) {
  7294. return true
  7295. }
  7296. //随时添加,定义的特殊标签如果存在,不能认为是空
  7297. tags = ['div'];
  7298. for (i = 0; ci = tags[i++];) {
  7299. var nodes = domUtils.getElementsByTagName(this.document, ci);
  7300. for (var n = 0, cn; cn = nodes[n++];) {
  7301. if (domUtils.isCustomeNode(cn)) {
  7302. return true;
  7303. }
  7304. }
  7305. }
  7306. return false;
  7307. },
  7308. /**
  7309. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  7310. * @method reset
  7311. * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
  7312. * @example
  7313. * ```javascript
  7314. * editor.reset()
  7315. * ```
  7316. */
  7317. reset: function () {
  7318. this.fireEvent('reset');
  7319. },
  7320. /**
  7321. * 设置当前编辑区域可以编辑
  7322. * @method setEnabled
  7323. * @example
  7324. * ```javascript
  7325. * editor.setEnabled()
  7326. * ```
  7327. */
  7328. setEnabled: function () {
  7329. var me = this, range;
  7330. if (me.body.contentEditable == 'false') {
  7331. me.body.contentEditable = true;
  7332. range = me.selection.getRange();
  7333. //有可能内容丢失了
  7334. try {
  7335. range.moveToBookmark(me.lastBk);
  7336. delete me.lastBk
  7337. } catch (e) {
  7338. range.setStartAtFirst(me.body).collapse(true)
  7339. }
  7340. range.select(true);
  7341. if (me.bkqueryCommandState) {
  7342. me.queryCommandState = me.bkqueryCommandState;
  7343. delete me.bkqueryCommandState;
  7344. }
  7345. if (me.bkqueryCommandValue) {
  7346. me.queryCommandValue = me.bkqueryCommandValue;
  7347. delete me.bkqueryCommandValue;
  7348. }
  7349. me.fireEvent('selectionchange');
  7350. }
  7351. },
  7352. enable: function () {
  7353. return this.setEnabled();
  7354. },
  7355. /** 设置当前编辑区域不可编辑
  7356. * @method setDisabled
  7357. */
  7358. /** 设置当前编辑区域不可编辑,except中的命令除外
  7359. * @method setDisabled
  7360. * @param { String } except 例外命令的字符串
  7361. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  7362. * @example
  7363. * ```javascript
  7364. * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
  7365. * ```
  7366. */
  7367. /** 设置当前编辑区域不可编辑,except中的命令除外
  7368. * @method setDisabled
  7369. * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
  7370. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  7371. * @example
  7372. * ```javascript
  7373. * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
  7374. * ```
  7375. */
  7376. setDisabled: function (except) {
  7377. var me = this;
  7378. except = except ? utils.isArray(except) ? except : [except] : [];
  7379. if (me.body.contentEditable == 'true') {
  7380. if (!me.lastBk) {
  7381. me.lastBk = me.selection.getRange().createBookmark(true);
  7382. }
  7383. me.body.contentEditable = false;
  7384. me.bkqueryCommandState = me.queryCommandState;
  7385. me.bkqueryCommandValue = me.queryCommandValue;
  7386. me.queryCommandState = function (type) {
  7387. if (utils.indexOf(except, type) != -1) {
  7388. return me.bkqueryCommandState.apply(me, arguments);
  7389. }
  7390. return -1;
  7391. };
  7392. me.queryCommandValue = function (type) {
  7393. if (utils.indexOf(except, type) != -1) {
  7394. return me.bkqueryCommandValue.apply(me, arguments);
  7395. }
  7396. return null;
  7397. };
  7398. me.fireEvent('selectionchange');
  7399. }
  7400. },
  7401. disable: function (except) {
  7402. return this.setDisabled(except);
  7403. },
  7404. /**
  7405. * 设置默认内容
  7406. * @method _setDefaultContent
  7407. * @private
  7408. * @param { String } cont 要存入的内容
  7409. */
  7410. _setDefaultContent: function () {
  7411. function clear() {
  7412. var me = this;
  7413. if (me.document.getElementById('initContent')) {
  7414. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
  7415. me.removeListener('firstBeforeExecCommand focus', clear);
  7416. setTimeout(function () {
  7417. me.focus();
  7418. me._selectionChange();
  7419. }, 0)
  7420. }
  7421. }
  7422. return function (cont) {
  7423. var me = this;
  7424. me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  7425. me.addListener('firstBeforeExecCommand focus', clear);
  7426. }
  7427. }(),
  7428. /**
  7429. * 显示编辑器
  7430. * @method setShow
  7431. * @example
  7432. * ```javascript
  7433. * editor.setShow()
  7434. * ```
  7435. */
  7436. setShow: function () {
  7437. var me = this, range = me.selection.getRange();
  7438. if (me.container.style.display == 'none') {
  7439. //有可能内容丢失了
  7440. try {
  7441. range.moveToBookmark(me.lastBk);
  7442. delete me.lastBk
  7443. } catch (e) {
  7444. range.setStartAtFirst(me.body).collapse(true)
  7445. }
  7446. //ie下focus实效,所以做了个延迟
  7447. setTimeout(function () {
  7448. range.select(true);
  7449. }, 100);
  7450. me.container.style.display = '';
  7451. }
  7452. },
  7453. show: function () {
  7454. return this.setShow();
  7455. },
  7456. /**
  7457. * 隐藏编辑器
  7458. * @method setHide
  7459. * @example
  7460. * ```javascript
  7461. * editor.setHide()
  7462. * ```
  7463. */
  7464. setHide: function () {
  7465. var me = this;
  7466. if (!me.lastBk) {
  7467. me.lastBk = me.selection.getRange().createBookmark(true);
  7468. }
  7469. me.container.style.display = 'none'
  7470. },
  7471. hide: function () {
  7472. return this.setHide();
  7473. },
  7474. /**
  7475. * 根据指定的路径,获取对应的语言资源
  7476. * @method getLang
  7477. * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
  7478. * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
  7479. * @example
  7480. * ```javascript
  7481. * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
  7482. * ```
  7483. */
  7484. getLang: function (path) {
  7485. var lang = UE.I18N[this.options.lang];
  7486. if (!lang) {
  7487. throw Error("not import language file");
  7488. }
  7489. path = (path || "").split(".");
  7490. for (var i = 0, ci; ci = path[i++];) {
  7491. lang = lang[ci];
  7492. if (!lang)break;
  7493. }
  7494. return lang;
  7495. },
  7496. /**
  7497. * 计算编辑器html内容字符串的长度
  7498. * @method getContentLength
  7499. * @return { Number } 返回计算的长度
  7500. * @example
  7501. * ```javascript
  7502. * //编辑器html内容<p><strong>132</strong></p>
  7503. * editor.getContentLength() //返回27
  7504. * ```
  7505. */
  7506. /**
  7507. * 计算编辑器当前纯文本内容的长度
  7508. * @method getContentLength
  7509. * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
  7510. * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
  7511. * @example
  7512. * ```javascript
  7513. * //编辑器html内容<p><strong>132</strong></p>
  7514. * editor.getContentLength() //返回3
  7515. * ```
  7516. */
  7517. getContentLength: function (ingoneHtml, tagNames) {
  7518. var count = this.getContent(false,false,true).length;
  7519. if (ingoneHtml) {
  7520. tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
  7521. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
  7522. for (var i = 0, ci; ci = tagNames[i++];) {
  7523. count += this.document.getElementsByTagName(ci).length;
  7524. }
  7525. }
  7526. return count;
  7527. },
  7528. /**
  7529. * 注册输入过滤规则
  7530. * @method addInputRule
  7531. * @param { Function } rule 要添加的过滤规则
  7532. * @example
  7533. * ```javascript
  7534. * editor.addInputRule(function(root){
  7535. * $.each(root.getNodesByTagName('div'),function(i,node){
  7536. * node.tagName="p";
  7537. * });
  7538. * });
  7539. * ```
  7540. */
  7541. addInputRule: function (rule) {
  7542. this.inputRules.push(rule);
  7543. },
  7544. /**
  7545. * 执行注册的过滤规则
  7546. * @method filterInputRule
  7547. * @param { UE.uNode } root 要过滤的uNode节点
  7548. * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
  7549. * @example
  7550. * ```javascript
  7551. * editor.filterInputRule(editor.body);
  7552. * ```
  7553. * @see UE.Editor:addInputRule
  7554. */
  7555. filterInputRule: function (root) {
  7556. for (var i = 0, ci; ci = this.inputRules[i++];) {
  7557. ci.call(this, root)
  7558. }
  7559. },
  7560. /**
  7561. * 注册输出过滤规则
  7562. * @method addOutputRule
  7563. * @param { Function } rule 要添加的过滤规则
  7564. * @example
  7565. * ```javascript
  7566. * editor.addOutputRule(function(root){
  7567. * $.each(root.getNodesByTagName('p'),function(i,node){
  7568. * node.tagName="div";
  7569. * });
  7570. * });
  7571. * ```
  7572. */
  7573. addOutputRule: function (rule) {
  7574. this.outputRules.push(rule)
  7575. },
  7576. /**
  7577. * 根据输出过滤规则,过滤编辑器内容
  7578. * @method filterOutputRule
  7579. * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
  7580. * @param { UE.uNode } root 要过滤的uNode节点
  7581. * @example
  7582. * ```javascript
  7583. * editor.filterOutputRule(editor.body);
  7584. * ```
  7585. * @see UE.Editor:addOutputRule
  7586. */
  7587. filterOutputRule: function (root) {
  7588. for (var i = 0, ci; ci = this.outputRules[i++];) {
  7589. ci.call(this, root)
  7590. }
  7591. },
  7592. /**
  7593. * 根据action名称获取请求的路径
  7594. * @method getActionUrl
  7595. * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
  7596. * @param { String } action action名称
  7597. * @example
  7598. * ```javascript
  7599. * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
  7600. * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
  7601. * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
  7602. * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
  7603. * ```
  7604. */
  7605. getActionUrl: function(action){
  7606. var actionName = this.getOpt(action) || action,
  7607. imageUrl = this.getOpt('imageUrl'),
  7608. serverUrl = this.getOpt('serverUrl');
  7609. if(!serverUrl && imageUrl) {
  7610. serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2');
  7611. }
  7612. if(serverUrl) {
  7613. serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?':'&') + 'action=' + (actionName || '');
  7614. return utils.formatUrl(serverUrl);
  7615. } else {
  7616. return '';
  7617. }
  7618. }
  7619. };
  7620. utils.inherits(Editor, EventBase);
  7621. })();
  7622. // core/Editor.defaultoptions.js
  7623. //维护编辑器一下默认的不在插件中的配置项
  7624. UE.Editor.defaultOptions = function(editor){
  7625. var _url = editor.options.UEDITOR_HOME_URL;
  7626. return {
  7627. isShow: true,
  7628. initialContent: '',
  7629. initialStyle:'',
  7630. autoClearinitialContent: false,
  7631. iframeCssUrl: _url + 'themes/iframe.css',
  7632. textarea: 'editorValue',
  7633. focus: false,
  7634. focusInEnd: true,
  7635. autoClearEmptyNode: true,
  7636. fullscreen: false,
  7637. readonly: false,
  7638. zIndex: 999,
  7639. imagePopup: true,
  7640. enterTag: 'p',
  7641. customDomain: false,
  7642. lang: 'zh-cn',
  7643. langPath: _url + 'lang/',
  7644. theme: 'default',
  7645. themePath: _url + 'themes/',
  7646. allHtmlEnabled: false,
  7647. scaleEnabled: false,
  7648. tableNativeEditInFF: false,
  7649. autoSyncData : true,
  7650. fileNameFormat: '{time}{rand:6}'
  7651. }
  7652. };
  7653. // core/loadconfig.js
  7654. (function(){
  7655. UE.Editor.prototype.loadServerConfig = function(){
  7656. var me = this;
  7657. setTimeout(function(){
  7658. try{
  7659. me.options.imageUrl && me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'));
  7660. var configUrl = me.getActionUrl('config'),
  7661. isJsonp = utils.isCrossDomainUrl(configUrl);
  7662. /* 发出ajax请求 */
  7663. me._serverConfigLoaded = false;
  7664. configUrl && UE.ajax.request(configUrl,{
  7665. 'method': 'GET',
  7666. 'dataType': isJsonp ? 'jsonp':'',
  7667. 'onsuccess':function(r){
  7668. try {
  7669. var config = isJsonp ? r:eval("("+r.responseText+")");
  7670. utils.extend(me.options, config);
  7671. me.fireEvent('serverConfigLoaded');
  7672. me._serverConfigLoaded = true;
  7673. } catch (e) {
  7674. showErrorMsg(me.getLang('loadconfigFormatError'));
  7675. }
  7676. },
  7677. 'onerror':function(){
  7678. showErrorMsg(me.getLang('loadconfigHttpError'));
  7679. }
  7680. });
  7681. } catch(e){
  7682. showErrorMsg(me.getLang('loadconfigError'));
  7683. }
  7684. });
  7685. function showErrorMsg(msg) {
  7686. console && console.error(msg);
  7687. //me.fireEvent('showMessage', {
  7688. // 'title': msg,
  7689. // 'type': 'error'
  7690. //});
  7691. }
  7692. };
  7693. UE.Editor.prototype.isServerConfigLoaded = function(){
  7694. var me = this;
  7695. return me._serverConfigLoaded || false;
  7696. };
  7697. UE.Editor.prototype.afterConfigReady = function(handler){
  7698. if (!handler || !utils.isFunction(handler)) return;
  7699. var me = this;
  7700. var readyHandler = function(){
  7701. handler.apply(me, arguments);
  7702. me.removeListener('serverConfigLoaded', readyHandler);
  7703. };
  7704. if (me.isServerConfigLoaded()) {
  7705. handler.call(me, 'serverConfigLoaded');
  7706. } else {
  7707. me.addListener('serverConfigLoaded', readyHandler);
  7708. }
  7709. };
  7710. })();
  7711. // core/ajax.js
  7712. /**
  7713. * @file
  7714. * @module UE.ajax
  7715. * @since 1.2.6.1
  7716. */
  7717. /**
  7718. * 提供对ajax请求的支持
  7719. * @module UE.ajax
  7720. */
  7721. UE.ajax = function() {
  7722. //创建一个ajaxRequest对象
  7723. var fnStr = 'XMLHttpRequest()';
  7724. try {
  7725. new ActiveXObject("Msxml2.XMLHTTP");
  7726. fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')';
  7727. } catch (e) {
  7728. try {
  7729. new ActiveXObject("Microsoft.XMLHTTP");
  7730. fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
  7731. } catch (e) {
  7732. }
  7733. }
  7734. var creatAjaxRequest = new Function('return new ' + fnStr);
  7735. /**
  7736. * 将json参数转化成适合ajax提交的参数列表
  7737. * @param json
  7738. */
  7739. function json2str(json) {
  7740. var strArr = [];
  7741. for (var i in json) {
  7742. //忽略默认的几个参数
  7743. if(i=="method" || i=="timeout" || i=="async" || i=="dataType" || i=="callback") continue;
  7744. //忽略控制
  7745. if(json[i] == undefined || json[i] == null) continue;
  7746. //传递过来的对象和函数不在提交之列
  7747. if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
  7748. strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
  7749. } else if (utils.isArray(json[i])) {
  7750. //支持传数组内容
  7751. for(var j = 0; j < json[i].length; j++) {
  7752. strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
  7753. }
  7754. }
  7755. }
  7756. return strArr.join("&");
  7757. }
  7758. function doAjax(url, ajaxOptions) {
  7759. var xhr = creatAjaxRequest(),
  7760. //是否超时
  7761. timeIsOut = false,
  7762. //默认参数
  7763. defaultAjaxOptions = {
  7764. method:"POST",
  7765. timeout:5000,
  7766. async:true,
  7767. data:{},//需要传递对象的话只能覆盖
  7768. onsuccess:function() {
  7769. },
  7770. onerror:function() {
  7771. }
  7772. };
  7773. if (typeof url === "object") {
  7774. ajaxOptions = url;
  7775. url = ajaxOptions.url;
  7776. }
  7777. if (!xhr || !url) return;
  7778. var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions,ajaxOptions) : defaultAjaxOptions;
  7779. var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  7780. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  7781. if (!utils.isEmptyObject(ajaxOpts.data)){
  7782. submitStr += (submitStr? "&":"") + json2str(ajaxOpts.data);
  7783. }
  7784. //超时检测
  7785. var timerID = setTimeout(function() {
  7786. if (xhr.readyState != 4) {
  7787. timeIsOut = true;
  7788. xhr.abort();
  7789. clearTimeout(timerID);
  7790. }
  7791. }, ajaxOpts.timeout);
  7792. var method = ajaxOpts.method.toUpperCase();
  7793. var str = url + (url.indexOf("?")==-1?"?":"&") + (method=="POST"?"":submitStr+ "&noCache=" + +new Date);
  7794. xhr.open(method, str, ajaxOpts.async);
  7795. xhr.onreadystatechange = function() {
  7796. if (xhr.readyState == 4) {
  7797. if (!timeIsOut && xhr.status == 200) {
  7798. ajaxOpts.onsuccess(xhr);
  7799. } else {
  7800. ajaxOpts.onerror(xhr);
  7801. }
  7802. }
  7803. };
  7804. if (method == "POST") {
  7805. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  7806. xhr.send(submitStr);
  7807. } else {
  7808. xhr.send(null);
  7809. }
  7810. }
  7811. function doJsonp(url, opts) {
  7812. var successhandler = opts.onsuccess || function(){},
  7813. scr = document.createElement('SCRIPT'),
  7814. options = opts || {},
  7815. charset = options['charset'],
  7816. callbackField = options['jsonp'] || 'callback',
  7817. callbackFnName,
  7818. timeOut = options['timeOut'] || 0,
  7819. timer,
  7820. reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'),
  7821. matches;
  7822. if (utils.isFunction(successhandler)) {
  7823. callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36);
  7824. window[callbackFnName] = getCallBack(0);
  7825. } else if(utils.isString(successhandler)){
  7826. callbackFnName = successhandler;
  7827. } else {
  7828. if (matches = reg.exec(url)) {
  7829. callbackFnName = matches[2];
  7830. }
  7831. }
  7832. url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName);
  7833. if (url.search(reg) < 0) {
  7834. url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName;
  7835. }
  7836. var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  7837. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  7838. if (!utils.isEmptyObject(opts.data)){
  7839. queryStr += (queryStr? "&":"") + json2str(opts.data);
  7840. }
  7841. if (queryStr) {
  7842. url = url.replace(/\?/, '?' + queryStr + '&');
  7843. }
  7844. scr.onerror = getCallBack(1);
  7845. if( timeOut ){
  7846. timer = setTimeout(getCallBack(1), timeOut);
  7847. }
  7848. createScriptTag(scr, url, charset);
  7849. function createScriptTag(scr, url, charset) {
  7850. scr.setAttribute('type', 'text/javascript');
  7851. scr.setAttribute('defer', 'defer');
  7852. charset && scr.setAttribute('charset', charset);
  7853. scr.setAttribute('src', url);
  7854. document.getElementsByTagName('head')[0].appendChild(scr);
  7855. }
  7856. function getCallBack(onTimeOut){
  7857. return function(){
  7858. try {
  7859. if(onTimeOut){
  7860. options.onerror && options.onerror();
  7861. }else{
  7862. try{
  7863. clearTimeout(timer);
  7864. successhandler.apply(window, arguments);
  7865. } catch (e){}
  7866. }
  7867. } catch (exception) {
  7868. options.onerror && options.onerror.call(window, exception);
  7869. } finally {
  7870. options.oncomplete && options.oncomplete.apply(window, arguments);
  7871. scr.parentNode && scr.parentNode.removeChild(scr);
  7872. window[callbackFnName] = null;
  7873. try {
  7874. delete window[callbackFnName];
  7875. }catch(e){}
  7876. }
  7877. }
  7878. }
  7879. }
  7880. return {
  7881. /**
  7882. * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  7883. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
  7884. * @method request
  7885. * @param { URLString } url ajax请求的url地址
  7886. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  7887. * @example
  7888. * ```javascript
  7889. * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
  7890. * UE.ajax.requeset( 'sayhello.php', {
  7891. *
  7892. * //请求方法。可选值: 'GET', 'POST',默认值是'POST'
  7893. * method: 'GET',
  7894. *
  7895. * //超时时间。 默认为5000, 单位是ms
  7896. * timeout: 10000,
  7897. *
  7898. * //是否是异步请求。 true为异步请求, false为同步请求
  7899. * async: true,
  7900. *
  7901. * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
  7902. * data: {
  7903. * name: 'ueditor'
  7904. * },
  7905. *
  7906. * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
  7907. * onsuccess: function ( xhr ) {
  7908. * console.log( xhr.responseText );
  7909. * },
  7910. *
  7911. * //请求失败或者超时后的回调。
  7912. * onerror: function ( xhr ) {
  7913. * alert( 'Ajax请求失败' );
  7914. * }
  7915. *
  7916. * } );
  7917. * ```
  7918. */
  7919. /**
  7920. * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  7921. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
  7922. * @method request
  7923. * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
  7924. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  7925. * @example
  7926. * ```javascript
  7927. *
  7928. * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
  7929. * UE.ajax.requeset( 'sayhello.php', {
  7930. *
  7931. * //请求的地址, 该项是必须的。
  7932. * url: 'sayhello.php'
  7933. *
  7934. * } );
  7935. * ```
  7936. */
  7937. request:function(url, opts) {
  7938. if (opts && opts.dataType == 'jsonp') {
  7939. doJsonp(url, opts);
  7940. } else {
  7941. doAjax(url, opts);
  7942. }
  7943. },
  7944. getJSONP:function(url, data, fn) {
  7945. var opts = {
  7946. 'data': data,
  7947. 'oncomplete': fn
  7948. };
  7949. doJsonp(url, opts);
  7950. }
  7951. };
  7952. }();
  7953. // core/filterword.js
  7954. /**
  7955. * UE过滤word的静态方法
  7956. * @file
  7957. */
  7958. /**
  7959. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  7960. * @module UE
  7961. */
  7962. /**
  7963. * 根据传入html字符串过滤word
  7964. * @module UE
  7965. * @since 1.2.6.1
  7966. * @method filterWord
  7967. * @param { String } html html字符串
  7968. * @return { String } 已过滤后的结果字符串
  7969. * @example
  7970. * ```javascript
  7971. * UE.filterWord(html);
  7972. * ```
  7973. */
  7974. var filterWord = UE.filterWord = function () {
  7975. //是否是word过来的内容
  7976. function isWordDocument( str ) {
  7977. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
  7978. }
  7979. //去掉小数
  7980. function transUnit( v ) {
  7981. v = v.replace( /[\d.]+\w+/g, function ( m ) {
  7982. return utils.transUnitToPx(m);
  7983. } );
  7984. return v;
  7985. }
  7986. function filterPasteWord( str ) {
  7987. return str.replace(/[\t\r\n]+/g,' ')
  7988. .replace( /<!--[\s\S]*?-->/ig, "" )
  7989. //转换图片
  7990. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
  7991. //opera能自己解析出image所这里直接返回空
  7992. if(browser.opera){
  7993. return '';
  7994. }
  7995. try{
  7996. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  7997. if(/Bitmap/i.test(str)){
  7998. return '';
  7999. }
  8000. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  8001. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  8002. src = str.match(/src=\s*"([^"]*)"/i)[1];
  8003. return '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
  8004. } catch(e){
  8005. return '';
  8006. }
  8007. })
  8008. //针对wps添加的多余标签处理
  8009. .replace(/<\/?div[^>]*>/g,'')
  8010. //去掉多余的属性
  8011. .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
  8012. .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
  8013. .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
  8014. //去掉多余的属性
  8015. .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
  8016. //保留list的标示
  8017. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  8018. })
  8019. //清除多余的font/span不能匹配&nbsp;有可能是空格
  8020. .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
  8021. return c.replace(/[\t\r\n ]+/g,' ')
  8022. })
  8023. //处理style的问题
  8024. .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
  8025. var n = [],
  8026. s = style.replace( /^\s+|\s+$/, '' )
  8027. .replace(/&#39;/g,'\'')
  8028. .replace( /&quot;/gi, "'" )
  8029. .replace(/[\d.]+(cm|pt)/g,function(str){
  8030. return utils.transUnitToPx(str)
  8031. })
  8032. .split( /;\s*/g );
  8033. for ( var i = 0,v; v = s[i];i++ ) {
  8034. var name, value,
  8035. parts = v.split( ":" );
  8036. if ( parts.length == 2 ) {
  8037. name = parts[0].toLowerCase();
  8038. value = parts[1].toLowerCase();
  8039. if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
  8040. ||
  8041. /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
  8042. ){
  8043. continue;
  8044. }
  8045. switch ( name ) {
  8046. case "mso-padding-alt":
  8047. case "mso-padding-top-alt":
  8048. case "mso-padding-right-alt":
  8049. case "mso-padding-bottom-alt":
  8050. case "mso-padding-left-alt":
  8051. case "mso-margin-alt":
  8052. case "mso-margin-top-alt":
  8053. case "mso-margin-right-alt":
  8054. case "mso-margin-bottom-alt":
  8055. case "mso-margin-left-alt":
  8056. //ie下会出现挤到一起的情况
  8057. //case "mso-table-layout-alt":
  8058. case "mso-height":
  8059. case "mso-width":
  8060. case "mso-vertical-align-alt":
  8061. //trace:1819 ff下会解析出padding在table上
  8062. if(!/<table/.test(tag))
  8063. n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
  8064. continue;
  8065. case "horiz-align":
  8066. n[i] = "text-align:" + value;
  8067. continue;
  8068. case "vert-align":
  8069. n[i] = "vertical-align:" + value;
  8070. continue;
  8071. case "font-color":
  8072. case "mso-foreground":
  8073. n[i] = "color:" + value;
  8074. continue;
  8075. case "mso-background":
  8076. case "mso-highlight":
  8077. n[i] = "background:" + value;
  8078. continue;
  8079. case "mso-default-height":
  8080. n[i] = "min-height:" + transUnit( value );
  8081. continue;
  8082. case "mso-default-width":
  8083. n[i] = "min-width:" + transUnit( value );
  8084. continue;
  8085. case "mso-padding-between-alt":
  8086. n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
  8087. continue;
  8088. case "text-line-through":
  8089. if ( (value == "single") || (value == "double") ) {
  8090. n[i] = "text-decoration:line-through";
  8091. }
  8092. continue;
  8093. case "mso-zero-height":
  8094. if ( value == "yes" ) {
  8095. n[i] = "display:none";
  8096. }
  8097. continue;
  8098. // case 'background':
  8099. // break;
  8100. case 'margin':
  8101. if ( !/[1-9]/.test( value ) ) {
  8102. continue;
  8103. }
  8104. }
  8105. 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 )
  8106. ||
  8107. /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
  8108. ) {
  8109. continue;
  8110. }
  8111. n[i] = name + ":" + parts[1];
  8112. }
  8113. }
  8114. return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
  8115. })
  8116. }
  8117. return function ( html ) {
  8118. return (isWordDocument( html ) ? filterPasteWord( html ) : html);
  8119. };
  8120. }();
  8121. // core/node.js
  8122. /**
  8123. * 编辑器模拟的节点类
  8124. * @file
  8125. * @module UE
  8126. * @class uNode
  8127. * @since 1.2.6.1
  8128. */
  8129. /**
  8130. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  8131. * @unfile
  8132. * @module UE
  8133. */
  8134. (function () {
  8135. /**
  8136. * 编辑器模拟的节点类
  8137. * @unfile
  8138. * @module UE
  8139. * @class uNode
  8140. */
  8141. /**
  8142. * 通过一个键值对,创建一个uNode对象
  8143. * @constructor
  8144. * @param { Object } attr 传入要创建的uNode的初始属性
  8145. * @example
  8146. * ```javascript
  8147. * var node = new uNode({
  8148. * type:'element',
  8149. * tagName:'span',
  8150. * attrs:{style:'font-size:14px;'}
  8151. * }
  8152. * ```
  8153. */
  8154. var uNode = UE.uNode = function (obj) {
  8155. this.type = obj.type;
  8156. this.data = obj.data;
  8157. this.tagName = obj.tagName;
  8158. this.parentNode = obj.parentNode;
  8159. this.attrs = obj.attrs || {};
  8160. this.children = obj.children;
  8161. };
  8162. var notTransAttrs = {
  8163. 'href':1,
  8164. 'src':1,
  8165. '_src':1,
  8166. '_href':1,
  8167. 'cdata_data':1
  8168. };
  8169. var notTransTagName = {
  8170. style:1,
  8171. script:1
  8172. };
  8173. var indentChar = ' ',
  8174. breakChar = '\n';
  8175. function insertLine(arr, current, begin) {
  8176. arr.push(breakChar);
  8177. return current + (begin ? 1 : -1);
  8178. }
  8179. function insertIndent(arr, current) {
  8180. //插入缩进
  8181. for (var i = 0; i < current; i++) {
  8182. arr.push(indentChar);
  8183. }
  8184. }
  8185. //创建uNode的静态方法
  8186. //支持标签和html
  8187. uNode.createElement = function (html) {
  8188. if (/[<>]/.test(html)) {
  8189. return UE.htmlparser(html).children[0]
  8190. } else {
  8191. return new uNode({
  8192. type:'element',
  8193. children:[],
  8194. tagName:html
  8195. })
  8196. }
  8197. };
  8198. uNode.createText = function (data,noTrans) {
  8199. return new UE.uNode({
  8200. type:'text',
  8201. 'data':noTrans ? data : utils.unhtml(data || '')
  8202. })
  8203. };
  8204. function nodeToHtml(node, arr, formatter, current) {
  8205. switch (node.type) {
  8206. case 'root':
  8207. for (var i = 0, ci; ci = node.children[i++];) {
  8208. //插入新行
  8209. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  8210. insertLine(arr, current, true);
  8211. insertIndent(arr, current)
  8212. }
  8213. nodeToHtml(ci, arr, formatter, current)
  8214. }
  8215. break;
  8216. case 'text':
  8217. isText(node, arr);
  8218. break;
  8219. case 'element':
  8220. isElement(node, arr, formatter, current);
  8221. break;
  8222. case 'comment':
  8223. isComment(node, arr, formatter);
  8224. }
  8225. return arr;
  8226. }
  8227. function isText(node, arr) {
  8228. if(node.parentNode.tagName == 'pre'){
  8229. //源码模式下输入html标签,不能做转换处理,直接输出
  8230. arr.push(node.data)
  8231. }else{
  8232. arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' &nbsp;'))
  8233. }
  8234. }
  8235. function isElement(node, arr, formatter, current) {
  8236. var attrhtml = '';
  8237. if (node.attrs) {
  8238. attrhtml = [];
  8239. var attrs = node.attrs;
  8240. for (var a in attrs) {
  8241. //这里就针对
  8242. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  8243. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  8244. //有可能做的不够
  8245. attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  8246. return '&quot;'
  8247. }) : utils.unhtml(attrs[a])) + '"' : ''))
  8248. }
  8249. attrhtml = attrhtml.join(' ');
  8250. }
  8251. arr.push('<' + node.tagName +
  8252. (attrhtml ? ' ' + attrhtml : '') +
  8253. (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
  8254. );
  8255. //插入新行
  8256. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  8257. if(node.children && node.children.length){
  8258. current = insertLine(arr, current, true);
  8259. insertIndent(arr, current)
  8260. }
  8261. }
  8262. if (node.children && node.children.length) {
  8263. for (var i = 0, ci; ci = node.children[i++];) {
  8264. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  8265. insertLine(arr, current);
  8266. insertIndent(arr, current)
  8267. }
  8268. nodeToHtml(ci, arr, formatter, current)
  8269. }
  8270. }
  8271. if (!dtd.$empty[node.tagName]) {
  8272. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  8273. if(node.children && node.children.length){
  8274. current = insertLine(arr, current);
  8275. insertIndent(arr, current)
  8276. }
  8277. }
  8278. arr.push('<\/' + node.tagName + '>');
  8279. }
  8280. }
  8281. function isComment(node, arr) {
  8282. arr.push('<!--' + node.data + '-->');
  8283. }
  8284. function getNodeById(root, id) {
  8285. var node;
  8286. if (root.type == 'element' && root.getAttr('id') == id) {
  8287. return root;
  8288. }
  8289. if (root.children && root.children.length) {
  8290. for (var i = 0, ci; ci = root.children[i++];) {
  8291. if (node = getNodeById(ci, id)) {
  8292. return node;
  8293. }
  8294. }
  8295. }
  8296. }
  8297. function getNodesByTagName(node, tagName, arr) {
  8298. if (node.type == 'element' && node.tagName == tagName) {
  8299. arr.push(node);
  8300. }
  8301. if (node.children && node.children.length) {
  8302. for (var i = 0, ci; ci = node.children[i++];) {
  8303. getNodesByTagName(ci, tagName, arr)
  8304. }
  8305. }
  8306. }
  8307. function nodeTraversal(root,fn){
  8308. if(root.children && root.children.length){
  8309. for(var i= 0,ci;ci=root.children[i];){
  8310. nodeTraversal(ci,fn);
  8311. //ci被替换的情况,这里就不再走 fn了
  8312. if(ci.parentNode ){
  8313. if(ci.children && ci.children.length){
  8314. fn(ci)
  8315. }
  8316. if(ci.parentNode) i++
  8317. }
  8318. }
  8319. }else{
  8320. fn(root)
  8321. }
  8322. }
  8323. uNode.prototype = {
  8324. /**
  8325. * 当前节点对象,转换成html文本
  8326. * @method toHtml
  8327. * @return { String } 返回转换后的html字符串
  8328. * @example
  8329. * ```javascript
  8330. * node.toHtml();
  8331. * ```
  8332. */
  8333. /**
  8334. * 当前节点对象,转换成html文本
  8335. * @method toHtml
  8336. * @param { Boolean } formatter 是否格式化返回值
  8337. * @return { String } 返回转换后的html字符串
  8338. * @example
  8339. * ```javascript
  8340. * node.toHtml( true );
  8341. * ```
  8342. */
  8343. toHtml:function (formatter) {
  8344. var arr = [];
  8345. nodeToHtml(this, arr, formatter, 0);
  8346. return arr.join('')
  8347. },
  8348. /**
  8349. * 获取节点的html内容
  8350. * @method innerHTML
  8351. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8352. * @return { String } 返回节点的html内容
  8353. * @example
  8354. * ```javascript
  8355. * var htmlstr = node.innerHTML();
  8356. * ```
  8357. */
  8358. /**
  8359. * 设置节点的html内容
  8360. * @method innerHTML
  8361. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8362. * @param { String } htmlstr 传入要设置的html内容
  8363. * @return { UE.uNode } 返回节点本身
  8364. * @example
  8365. * ```javascript
  8366. * node.innerHTML('<span>text</span>');
  8367. * ```
  8368. */
  8369. innerHTML:function (htmlstr) {
  8370. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8371. return this;
  8372. }
  8373. if (utils.isString(htmlstr)) {
  8374. if(this.children){
  8375. for (var i = 0, ci; ci = this.children[i++];) {
  8376. ci.parentNode = null;
  8377. }
  8378. }
  8379. this.children = [];
  8380. var tmpRoot = UE.htmlparser(htmlstr);
  8381. for (var i = 0, ci; ci = tmpRoot.children[i++];) {
  8382. this.children.push(ci);
  8383. ci.parentNode = this;
  8384. }
  8385. return this;
  8386. } else {
  8387. var tmpRoot = new UE.uNode({
  8388. type:'root',
  8389. children:this.children
  8390. });
  8391. return tmpRoot.toHtml();
  8392. }
  8393. },
  8394. /**
  8395. * 获取节点的纯文本内容
  8396. * @method innerText
  8397. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8398. * @return { String } 返回节点的存文本内容
  8399. * @example
  8400. * ```javascript
  8401. * var textStr = node.innerText();
  8402. * ```
  8403. */
  8404. /**
  8405. * 设置节点的纯文本内容
  8406. * @method innerText
  8407. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8408. * @param { String } textStr 传入要设置的文本内容
  8409. * @return { UE.uNode } 返回节点本身
  8410. * @example
  8411. * ```javascript
  8412. * node.innerText('<span>text</span>');
  8413. * ```
  8414. */
  8415. innerText:function (textStr,noTrans) {
  8416. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8417. return this;
  8418. }
  8419. if (textStr) {
  8420. if(this.children){
  8421. for (var i = 0, ci; ci = this.children[i++];) {
  8422. ci.parentNode = null;
  8423. }
  8424. }
  8425. this.children = [];
  8426. this.appendChild(uNode.createText(textStr,noTrans));
  8427. return this;
  8428. } else {
  8429. return this.toHtml().replace(/<[^>]+>/g, '');
  8430. }
  8431. },
  8432. /**
  8433. * 获取当前对象的data属性
  8434. * @method getData
  8435. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  8436. * @example
  8437. * ```javascript
  8438. * node.getData();
  8439. * ```
  8440. */
  8441. getData:function () {
  8442. if (this.type == 'element')
  8443. return '';
  8444. return this.data
  8445. },
  8446. /**
  8447. * 获取当前节点下的第一个子节点
  8448. * @method firstChild
  8449. * @return { UE.uNode } 返回第一个子节点
  8450. * @example
  8451. * ```javascript
  8452. * node.firstChild(); //返回第一个子节点
  8453. * ```
  8454. */
  8455. firstChild:function () {
  8456. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8457. // return this;
  8458. // }
  8459. return this.children ? this.children[0] : null;
  8460. },
  8461. /**
  8462. * 获取当前节点下的最后一个子节点
  8463. * @method lastChild
  8464. * @return { UE.uNode } 返回最后一个子节点
  8465. * @example
  8466. * ```javascript
  8467. * node.lastChild(); //返回最后一个子节点
  8468. * ```
  8469. */
  8470. lastChild:function () {
  8471. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  8472. // return this;
  8473. // }
  8474. return this.children ? this.children[this.children.length - 1] : null;
  8475. },
  8476. /**
  8477. * 获取和当前节点有相同父亲节点的前一个节点
  8478. * @method previousSibling
  8479. * @return { UE.uNode } 返回前一个节点
  8480. * @example
  8481. * ```javascript
  8482. * node.children[2].previousSibling(); //返回子节点node.children[1]
  8483. * ```
  8484. */
  8485. previousSibling : function(){
  8486. var parent = this.parentNode;
  8487. for (var i = 0, ci; ci = parent.children[i]; i++) {
  8488. if (ci === this) {
  8489. return i == 0 ? null : parent.children[i-1];
  8490. }
  8491. }
  8492. },
  8493. /**
  8494. * 获取和当前节点有相同父亲节点的后一个节点
  8495. * @method nextSibling
  8496. * @return { UE.uNode } 返回后一个节点,找不到返回null
  8497. * @example
  8498. * ```javascript
  8499. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  8500. * ```
  8501. */
  8502. nextSibling : function(){
  8503. var parent = this.parentNode;
  8504. for (var i = 0, ci; ci = parent.children[i++];) {
  8505. if (ci === this) {
  8506. return parent.children[i];
  8507. }
  8508. }
  8509. },
  8510. /**
  8511. * 用新的节点替换当前节点
  8512. * @method replaceChild
  8513. * @param { UE.uNode } target 要替换成该节点参数
  8514. * @param { UE.uNode } source 要被替换掉的节点
  8515. * @return { UE.uNode } 返回替换之后的节点对象
  8516. * @example
  8517. * ```javascript
  8518. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  8519. * ```
  8520. */
  8521. replaceChild:function (target, source) {
  8522. if (this.children) {
  8523. if(target.parentNode){
  8524. target.parentNode.removeChild(target);
  8525. }
  8526. for (var i = 0, ci; ci = this.children[i]; i++) {
  8527. if (ci === source) {
  8528. this.children.splice(i, 1, target);
  8529. source.parentNode = null;
  8530. target.parentNode = this;
  8531. return target;
  8532. }
  8533. }
  8534. }
  8535. },
  8536. /**
  8537. * 在节点的子节点列表最后位置插入一个节点
  8538. * @method appendChild
  8539. * @param { UE.uNode } node 要插入的节点
  8540. * @return { UE.uNode } 返回刚插入的子节点
  8541. * @example
  8542. * ```javascript
  8543. * node.appendChild( newNode ); //在node内插入子节点newNode
  8544. * ```
  8545. */
  8546. appendChild:function (node) {
  8547. if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
  8548. if (!this.children) {
  8549. this.children = []
  8550. }
  8551. if(node.parentNode){
  8552. node.parentNode.removeChild(node);
  8553. }
  8554. for (var i = 0, ci; ci = this.children[i]; i++) {
  8555. if (ci === node) {
  8556. this.children.splice(i, 1);
  8557. break;
  8558. }
  8559. }
  8560. this.children.push(node);
  8561. node.parentNode = this;
  8562. return node;
  8563. }
  8564. },
  8565. /**
  8566. * 在传入节点的前面插入一个节点
  8567. * @method insertBefore
  8568. * @param { UE.uNode } target 要插入的节点
  8569. * @param { UE.uNode } source 在该参数节点前面插入
  8570. * @return { UE.uNode } 返回刚插入的子节点
  8571. * @example
  8572. * ```javascript
  8573. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  8574. * ```
  8575. */
  8576. insertBefore:function (target, source) {
  8577. if (this.children) {
  8578. if(target.parentNode){
  8579. target.parentNode.removeChild(target);
  8580. }
  8581. for (var i = 0, ci; ci = this.children[i]; i++) {
  8582. if (ci === source) {
  8583. this.children.splice(i, 0, target);
  8584. target.parentNode = this;
  8585. return target;
  8586. }
  8587. }
  8588. }
  8589. },
  8590. /**
  8591. * 在传入节点的后面插入一个节点
  8592. * @method insertAfter
  8593. * @param { UE.uNode } target 要插入的节点
  8594. * @param { UE.uNode } source 在该参数节点后面插入
  8595. * @return { UE.uNode } 返回刚插入的子节点
  8596. * @example
  8597. * ```javascript
  8598. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  8599. * ```
  8600. */
  8601. insertAfter:function (target, source) {
  8602. if (this.children) {
  8603. if(target.parentNode){
  8604. target.parentNode.removeChild(target);
  8605. }
  8606. for (var i = 0, ci; ci = this.children[i]; i++) {
  8607. if (ci === source) {
  8608. this.children.splice(i + 1, 0, target);
  8609. target.parentNode = this;
  8610. return target;
  8611. }
  8612. }
  8613. }
  8614. },
  8615. /**
  8616. * 从当前节点的子节点列表中,移除节点
  8617. * @method removeChild
  8618. * @param { UE.uNode } node 要移除的节点引用
  8619. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  8620. * @return { * } 返回刚移除的子节点
  8621. * @example
  8622. * ```javascript
  8623. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  8624. * ```
  8625. */
  8626. removeChild:function (node,keepChildren) {
  8627. if (this.children) {
  8628. for (var i = 0, ci; ci = this.children[i]; i++) {
  8629. if (ci === node) {
  8630. this.children.splice(i, 1);
  8631. ci.parentNode = null;
  8632. if(keepChildren && ci.children && ci.children.length){
  8633. for(var j= 0,cj;cj=ci.children[j];j++){
  8634. this.children.splice(i+j,0,cj);
  8635. cj.parentNode = this;
  8636. }
  8637. }
  8638. return ci;
  8639. }
  8640. }
  8641. }
  8642. },
  8643. /**
  8644. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  8645. * @method getAttr
  8646. * @param { String } attrName 要获取的属性名称
  8647. * @return { * } 返回attrs对象下的属性值
  8648. * @example
  8649. * ```javascript
  8650. * node.getAttr('title');
  8651. * ```
  8652. */
  8653. getAttr:function (attrName) {
  8654. return this.attrs && this.attrs[attrName.toLowerCase()]
  8655. },
  8656. /**
  8657. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  8658. * @method setAttr
  8659. * @param { String } attrName 要设置的属性名称
  8660. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  8661. * @return { * } 返回attrs对象下的属性值
  8662. * @example
  8663. * ```javascript
  8664. * node.setAttr('title','标题');
  8665. * ```
  8666. */
  8667. setAttr:function (attrName, attrVal) {
  8668. if (!attrName) {
  8669. delete this.attrs;
  8670. return;
  8671. }
  8672. if(!this.attrs){
  8673. this.attrs = {};
  8674. }
  8675. if (utils.isObject(attrName)) {
  8676. for (var a in attrName) {
  8677. if (!attrName[a]) {
  8678. delete this.attrs[a]
  8679. } else {
  8680. this.attrs[a.toLowerCase()] = attrName[a];
  8681. }
  8682. }
  8683. } else {
  8684. if (!attrVal) {
  8685. delete this.attrs[attrName]
  8686. } else {
  8687. this.attrs[attrName.toLowerCase()] = attrVal;
  8688. }
  8689. }
  8690. },
  8691. /**
  8692. * 获取当前节点在父节点下的位置索引
  8693. * @method getIndex
  8694. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  8695. * @example
  8696. * ```javascript
  8697. * node.getIndex();
  8698. * ```
  8699. */
  8700. getIndex:function(){
  8701. var parent = this.parentNode;
  8702. for(var i= 0,ci;ci=parent.children[i];i++){
  8703. if(ci === this){
  8704. return i;
  8705. }
  8706. }
  8707. return -1;
  8708. },
  8709. /**
  8710. * 在当前节点下,根据id查找节点
  8711. * @method getNodeById
  8712. * @param { String } id 要查找的id
  8713. * @return { UE.uNode } 返回找到的节点
  8714. * @example
  8715. * ```javascript
  8716. * node.getNodeById('textId');
  8717. * ```
  8718. */
  8719. getNodeById:function (id) {
  8720. var node;
  8721. if (this.children && this.children.length) {
  8722. for (var i = 0, ci; ci = this.children[i++];) {
  8723. if (node = getNodeById(ci, id)) {
  8724. return node;
  8725. }
  8726. }
  8727. }
  8728. },
  8729. /**
  8730. * 在当前节点下,根据元素名称查找节点列表
  8731. * @method getNodesByTagName
  8732. * @param { String } tagNames 要查找的元素名称
  8733. * @return { Array } 返回找到的节点列表
  8734. * @example
  8735. * ```javascript
  8736. * node.getNodesByTagName('span');
  8737. * ```
  8738. */
  8739. getNodesByTagName:function (tagNames) {
  8740. tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
  8741. var arr = [], me = this;
  8742. utils.each(tagNames, function (tagName) {
  8743. if (me.children && me.children.length) {
  8744. for (var i = 0, ci; ci = me.children[i++];) {
  8745. getNodesByTagName(ci, tagName, arr)
  8746. }
  8747. }
  8748. });
  8749. return arr;
  8750. },
  8751. /**
  8752. * 根据样式名称,获取节点的样式值
  8753. * @method getStyle
  8754. * @param { String } name 要获取的样式名称
  8755. * @return { String } 返回样式值
  8756. * @example
  8757. * ```javascript
  8758. * node.getStyle('font-size');
  8759. * ```
  8760. */
  8761. getStyle:function (name) {
  8762. var cssStyle = this.getAttr('style');
  8763. if (!cssStyle) {
  8764. return ''
  8765. }
  8766. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
  8767. var match = cssStyle.match(reg);
  8768. if (match && match[0]) {
  8769. return match[2]
  8770. }
  8771. return '';
  8772. },
  8773. /**
  8774. * 给节点设置样式
  8775. * @method setStyle
  8776. * @param { String } name 要设置的的样式名称
  8777. * @param { String } val 要设置的的样值
  8778. * @example
  8779. * ```javascript
  8780. * node.setStyle('font-size', '12px');
  8781. * ```
  8782. */
  8783. setStyle:function (name, val) {
  8784. function exec(name, val) {
  8785. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
  8786. cssStyle = cssStyle.replace(reg, '$1');
  8787. if (val) {
  8788. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  8789. }
  8790. }
  8791. var cssStyle = this.getAttr('style');
  8792. if (!cssStyle) {
  8793. cssStyle = '';
  8794. }
  8795. if (utils.isObject(name)) {
  8796. for (var a in name) {
  8797. exec(a, name[a])
  8798. }
  8799. } else {
  8800. exec(name, val)
  8801. }
  8802. this.setAttr('style', utils.trim(cssStyle))
  8803. },
  8804. /**
  8805. * 传入一个函数,递归遍历当前节点下的所有节点
  8806. * @method traversal
  8807. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  8808. * @example
  8809. * ```javascript
  8810. * traversal(node, function(){
  8811. * console.log(node.type);
  8812. * });
  8813. * ```
  8814. */
  8815. traversal:function(fn){
  8816. if(this.children && this.children.length){
  8817. nodeTraversal(this,fn);
  8818. }
  8819. return this;
  8820. }
  8821. }
  8822. })();
  8823. // core/htmlparser.js
  8824. /**
  8825. * html字符串转换成uNode节点
  8826. * @file
  8827. * @module UE
  8828. * @since 1.2.6.1
  8829. */
  8830. /**
  8831. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  8832. * @unfile
  8833. * @module UE
  8834. */
  8835. /**
  8836. * html字符串转换成uNode节点的静态方法
  8837. * @method htmlparser
  8838. * @param { String } htmlstr 要转换的html代码
  8839. * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
  8840. * @return { uNode } 给定的html片段转换形成的uNode对象
  8841. * @example
  8842. * ```javascript
  8843. * var root = UE.htmlparser('<p><b>htmlparser</b></p>', true);
  8844. * ```
  8845. */
  8846. var htmlparser = UE.htmlparser = function (htmlstr,ignoreBlank) {
  8847. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  8848. //先去掉了,加上的原因忘了,这里先记录
  8849. var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  8850. re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  8851. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  8852. var allowEmptyTags = {
  8853. b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
  8854. sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
  8855. };
  8856. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
  8857. if(!ignoreBlank){
  8858. htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
  8859. //br暂时单独处理
  8860. if(b && allowEmptyTags[b.toLowerCase()]){
  8861. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
  8862. }
  8863. return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
  8864. });
  8865. }
  8866. var notTransAttrs = {
  8867. 'href':1,
  8868. 'src':1
  8869. };
  8870. var uNode = UE.uNode,
  8871. needParentNode = {
  8872. 'td':'tr',
  8873. 'tr':['tbody','thead','tfoot'],
  8874. 'tbody':'table',
  8875. 'th':'tr',
  8876. 'thead':'table',
  8877. 'tfoot':'table',
  8878. 'caption':'table',
  8879. 'li':['ul', 'ol'],
  8880. 'dt':'dl',
  8881. 'dd':'dl',
  8882. 'option':'select'
  8883. },
  8884. needChild = {
  8885. 'ol':'li',
  8886. 'ul':'li'
  8887. };
  8888. function text(parent, data) {
  8889. if(needChild[parent.tagName]){
  8890. var tmpNode = uNode.createElement(needChild[parent.tagName]);
  8891. parent.appendChild(tmpNode);
  8892. tmpNode.appendChild(uNode.createText(data));
  8893. parent = tmpNode;
  8894. }else{
  8895. parent.appendChild(uNode.createText(data));
  8896. }
  8897. }
  8898. function element(parent, tagName, htmlattr) {
  8899. var needParentTag;
  8900. if (needParentTag = needParentNode[tagName]) {
  8901. var tmpParent = parent,hasParent;
  8902. while(tmpParent.type != 'root'){
  8903. if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
  8904. parent = tmpParent;
  8905. hasParent = true;
  8906. break;
  8907. }
  8908. tmpParent = tmpParent.parentNode;
  8909. }
  8910. if(!hasParent){
  8911. parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
  8912. }
  8913. }
  8914. //按dtd处理嵌套
  8915. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  8916. // parent = parent.parentNode;
  8917. var elm = new uNode({
  8918. parentNode:parent,
  8919. type:'element',
  8920. tagName:tagName.toLowerCase(),
  8921. //是自闭合的处理一下
  8922. children:dtd.$empty[tagName] ? null : []
  8923. });
  8924. //如果属性存在,处理属性
  8925. if (htmlattr) {
  8926. var attrs = {}, match;
  8927. while (match = re_attr.exec(htmlattr)) {
  8928. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
  8929. }
  8930. elm.attrs = attrs;
  8931. }
  8932. //trace:3970
  8933. // //如果parent下不能放elm
  8934. // if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
  8935. // parent = parent.parentNode;
  8936. // elm.parentNode = parent;
  8937. // }
  8938. parent.children.push(elm);
  8939. //如果是自闭合节点返回父亲节点
  8940. return dtd.$empty[tagName] ? parent : elm
  8941. }
  8942. function comment(parent, data) {
  8943. parent.children.push(new uNode({
  8944. type:'comment',
  8945. data:data,
  8946. parentNode:parent
  8947. }));
  8948. }
  8949. var match, currentIndex = 0, nextIndex = 0;
  8950. //设置根节点
  8951. var root = new uNode({
  8952. type:'root',
  8953. children:[]
  8954. });
  8955. var currentParent = root;
  8956. while (match = re_tag.exec(htmlstr)) {
  8957. currentIndex = match.index;
  8958. try{
  8959. if (currentIndex > nextIndex) {
  8960. //text node
  8961. text(currentParent, htmlstr.slice(nextIndex, currentIndex));
  8962. }
  8963. if (match[3]) {
  8964. if(dtd.$cdata[currentParent.tagName]){
  8965. text(currentParent, match[0]);
  8966. }else{
  8967. //start tag
  8968. currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
  8969. }
  8970. } else if (match[1]) {
  8971. if(currentParent.type != 'root'){
  8972. if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
  8973. text(currentParent, match[0]);
  8974. }else{
  8975. var tmpParent = currentParent;
  8976. while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
  8977. currentParent = currentParent.parentNode;
  8978. if(currentParent.type == 'root'){
  8979. currentParent = tmpParent;
  8980. throw 'break'
  8981. }
  8982. }
  8983. //end tag
  8984. currentParent = currentParent.parentNode;
  8985. }
  8986. }
  8987. } else if (match[2]) {
  8988. //comment
  8989. comment(currentParent, match[2])
  8990. }
  8991. }catch(e){}
  8992. nextIndex = re_tag.lastIndex;
  8993. }
  8994. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  8995. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  8996. if (nextIndex < htmlstr.length) {
  8997. text(currentParent, htmlstr.slice(nextIndex));
  8998. }
  8999. return root;
  9000. };
  9001. // core/filternode.js
  9002. /**
  9003. * UE过滤节点的静态方法
  9004. * @file
  9005. */
  9006. /**
  9007. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  9008. * @module UE
  9009. */
  9010. /**
  9011. * 根据传入节点和过滤规则过滤相应节点
  9012. * @module UE
  9013. * @since 1.2.6.1
  9014. * @method filterNode
  9015. * @param { Object } root 指定root节点
  9016. * @param { Object } rules 过滤规则json对象
  9017. * @example
  9018. * ```javascript
  9019. * UE.filterNode(root,editor.options.filterRules);
  9020. * ```
  9021. */
  9022. var filterNode = UE.filterNode = function () {
  9023. function filterNode(node,rules){
  9024. switch (node.type) {
  9025. case 'text':
  9026. break;
  9027. case 'element':
  9028. var val;
  9029. if(val = rules[node.tagName]){
  9030. if(val === '-'){
  9031. node.parentNode.removeChild(node)
  9032. }else if(utils.isFunction(val)){
  9033. var parentNode = node.parentNode,
  9034. index = node.getIndex();
  9035. val(node);
  9036. if(node.parentNode){
  9037. if(node.children){
  9038. for(var i = 0,ci;ci=node.children[i];){
  9039. filterNode(ci,rules);
  9040. if(ci.parentNode){
  9041. i++;
  9042. }
  9043. }
  9044. }
  9045. }else{
  9046. for(var i = index,ci;ci=parentNode.children[i];){
  9047. filterNode(ci,rules);
  9048. if(ci.parentNode){
  9049. i++;
  9050. }
  9051. }
  9052. }
  9053. }else{
  9054. var attrs = val['$'];
  9055. if(attrs && node.attrs){
  9056. var tmpAttrs = {},tmpVal;
  9057. for(var a in attrs){
  9058. tmpVal = node.getAttr(a);
  9059. //todo 只先对style单独处理
  9060. if(a == 'style' && utils.isArray(attrs[a])){
  9061. var tmpCssStyle = [];
  9062. utils.each(attrs[a],function(v){
  9063. var tmp;
  9064. if(tmp = node.getStyle(v)){
  9065. tmpCssStyle.push(v + ':' + tmp);
  9066. }
  9067. });
  9068. tmpVal = tmpCssStyle.join(';')
  9069. }
  9070. if(tmpVal){
  9071. tmpAttrs[a] = tmpVal;
  9072. }
  9073. }
  9074. node.attrs = tmpAttrs;
  9075. }
  9076. if(node.children){
  9077. for(var i = 0,ci;ci=node.children[i];){
  9078. filterNode(ci,rules);
  9079. if(ci.parentNode){
  9080. i++;
  9081. }
  9082. }
  9083. }
  9084. }
  9085. }else{
  9086. //如果不在名单里扣出子节点并删除该节点,cdata除外
  9087. if(dtd.$cdata[node.tagName]){
  9088. node.parentNode.removeChild(node)
  9089. }else{
  9090. var parentNode = node.parentNode,
  9091. index = node.getIndex();
  9092. node.parentNode.removeChild(node,true);
  9093. for(var i = index,ci;ci=parentNode.children[i];){
  9094. filterNode(ci,rules);
  9095. if(ci.parentNode){
  9096. i++;
  9097. }
  9098. }
  9099. }
  9100. }
  9101. break;
  9102. case 'comment':
  9103. node.parentNode.removeChild(node)
  9104. }
  9105. }
  9106. return function(root,rules){
  9107. if(utils.isEmptyObject(rules)){
  9108. return root;
  9109. }
  9110. var val;
  9111. if(val = rules['-']){
  9112. utils.each(val.split(' '),function(k){
  9113. rules[k] = '-'
  9114. })
  9115. }
  9116. for(var i= 0,ci;ci=root.children[i];){
  9117. filterNode(ci,rules);
  9118. if(ci.parentNode){
  9119. i++;
  9120. }
  9121. }
  9122. return root;
  9123. }
  9124. }();
  9125. // core/plugin.js
  9126. /**
  9127. * Created with JetBrains PhpStorm.
  9128. * User: campaign
  9129. * Date: 10/8/13
  9130. * Time: 6:15 PM
  9131. * To change this template use File | Settings | File Templates.
  9132. */
  9133. UE.plugin = function(){
  9134. var _plugins = {};
  9135. return {
  9136. register : function(pluginName,fn,oldOptionName,afterDisabled){
  9137. if(oldOptionName && utils.isFunction(oldOptionName)){
  9138. afterDisabled = oldOptionName;
  9139. oldOptionName = null
  9140. }
  9141. _plugins[pluginName] = {
  9142. optionName : oldOptionName || pluginName,
  9143. execFn : fn,
  9144. //当插件被禁用时执行
  9145. afterDisabled : afterDisabled
  9146. }
  9147. },
  9148. load : function(editor){
  9149. utils.each(_plugins,function(plugin){
  9150. var _export = plugin.execFn.call(editor);
  9151. if(editor.options[plugin.optionName] !== false){
  9152. if(_export){
  9153. //后边需要再做扩展
  9154. utils.each(_export,function(v,k){
  9155. switch(k.toLowerCase()){
  9156. case 'shortcutkey':
  9157. editor.addshortcutkey(v);
  9158. break;
  9159. case 'bindevents':
  9160. utils.each(v,function(fn,eventName){
  9161. editor.addListener(eventName,fn);
  9162. });
  9163. break;
  9164. case 'bindmultievents':
  9165. utils.each(utils.isArray(v) ? v:[v],function(event){
  9166. var types = utils.trim(event.type).split(/\s+/);
  9167. utils.each(types,function(eventName){
  9168. editor.addListener(eventName, event.handler);
  9169. });
  9170. });
  9171. break;
  9172. case 'commands':
  9173. utils.each(v,function(execFn,execName){
  9174. editor.commands[execName] = execFn
  9175. });
  9176. break;
  9177. case 'outputrule':
  9178. editor.addOutputRule(v);
  9179. break;
  9180. case 'inputrule':
  9181. editor.addInputRule(v);
  9182. break;
  9183. case 'defaultoptions':
  9184. editor.setOpt(v)
  9185. }
  9186. })
  9187. }
  9188. }else if(plugin.afterDisabled){
  9189. plugin.afterDisabled.call(editor)
  9190. }
  9191. });
  9192. //向下兼容
  9193. utils.each(UE.plugins,function(plugin){
  9194. plugin.call(editor);
  9195. });
  9196. },
  9197. run : function(pluginName,editor){
  9198. var plugin = _plugins[pluginName];
  9199. if(plugin){
  9200. plugin.exeFn.call(editor)
  9201. }
  9202. }
  9203. }
  9204. }();
  9205. // core/keymap.js
  9206. var keymap = UE.keymap = {
  9207. 'Backspace' : 8,
  9208. 'Tab' : 9,
  9209. 'Enter' : 13,
  9210. 'Shift':16,
  9211. 'Control':17,
  9212. 'Alt':18,
  9213. 'CapsLock':20,
  9214. 'Esc':27,
  9215. 'Spacebar':32,
  9216. 'PageUp':33,
  9217. 'PageDown':34,
  9218. 'End':35,
  9219. 'Home':36,
  9220. 'Left':37,
  9221. 'Up':38,
  9222. 'Right':39,
  9223. 'Down':40,
  9224. 'Insert':45,
  9225. 'Del':46,
  9226. 'NumLock':144,
  9227. 'Cmd':91,
  9228. '=':187,
  9229. '-':189,
  9230. "b":66,
  9231. 'i':73,
  9232. //回退
  9233. 'z':90,
  9234. 'y':89,
  9235. //粘贴
  9236. 'v' : 86,
  9237. 'x' : 88,
  9238. 's' : 83,
  9239. 'n' : 78
  9240. };
  9241. // core/localstorage.js
  9242. //存储媒介封装
  9243. var LocalStorage = UE.LocalStorage = (function () {
  9244. var storage = window.localStorage || getUserData() || null,
  9245. LOCAL_FILE = 'localStorage';
  9246. return {
  9247. saveLocalData: function (key, data) {
  9248. if (storage && data) {
  9249. storage.setItem(key, data);
  9250. return true;
  9251. }
  9252. return false;
  9253. },
  9254. getLocalData: function (key) {
  9255. if (storage) {
  9256. return storage.getItem(key);
  9257. }
  9258. return null;
  9259. },
  9260. removeItem: function (key) {
  9261. storage && storage.removeItem(key);
  9262. }
  9263. };
  9264. function getUserData() {
  9265. var container = document.createElement("div");
  9266. container.style.display = "none";
  9267. if (!container.addBehavior) {
  9268. return null;
  9269. }
  9270. container.addBehavior("#default#userdata");
  9271. return {
  9272. getItem: function (key) {
  9273. var result = null;
  9274. try {
  9275. document.body.appendChild(container);
  9276. container.load(LOCAL_FILE);
  9277. result = container.getAttribute(key);
  9278. document.body.removeChild(container);
  9279. } catch (e) {
  9280. }
  9281. return result;
  9282. },
  9283. setItem: function (key, value) {
  9284. document.body.appendChild(container);
  9285. container.setAttribute(key, value);
  9286. container.save(LOCAL_FILE);
  9287. document.body.removeChild(container);
  9288. },
  9289. //// 暂时没有用到
  9290. //clear: function () {
  9291. //
  9292. // var expiresTime = new Date();
  9293. // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
  9294. // document.body.appendChild(container);
  9295. // container.expires = expiresTime.toUTCString();
  9296. // container.save(LOCAL_FILE);
  9297. // document.body.removeChild(container);
  9298. //
  9299. //},
  9300. removeItem: function (key) {
  9301. document.body.appendChild(container);
  9302. container.removeAttribute(key);
  9303. container.save(LOCAL_FILE);
  9304. document.body.removeChild(container);
  9305. }
  9306. };
  9307. }
  9308. })();
  9309. (function () {
  9310. var ROOTKEY = 'ueditor_preference';
  9311. UE.Editor.prototype.setPreferences = function(key,value){
  9312. var obj = {};
  9313. if (utils.isString(key)) {
  9314. obj[ key ] = value;
  9315. } else {
  9316. obj = key;
  9317. }
  9318. var data = LocalStorage.getLocalData(ROOTKEY);
  9319. if (data && (data = utils.str2json(data))) {
  9320. utils.extend(data, obj);
  9321. } else {
  9322. data = obj;
  9323. }
  9324. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
  9325. };
  9326. UE.Editor.prototype.getPreferences = function(key){
  9327. var data = LocalStorage.getLocalData(ROOTKEY);
  9328. if (data && (data = utils.str2json(data))) {
  9329. return key ? data[key] : data
  9330. }
  9331. return null;
  9332. };
  9333. UE.Editor.prototype.removePreferences = function (key) {
  9334. var data = LocalStorage.getLocalData(ROOTKEY);
  9335. if (data && (data = utils.str2json(data))) {
  9336. data[key] = undefined;
  9337. delete data[key]
  9338. }
  9339. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
  9340. };
  9341. })();
  9342. // plugins/defaultfilter.js
  9343. ///import core
  9344. ///plugin 编辑器默认的过滤转换机制
  9345. UE.plugins['defaultfilter'] = function () {
  9346. var me = this;
  9347. me.setOpt({
  9348. 'allowDivTransToP':true,
  9349. 'disabledTableInTable':true
  9350. });
  9351. //默认的过滤处理
  9352. //进入编辑器的内容处理
  9353. me.addInputRule(function (root) {
  9354. var allowDivTransToP = this.options.allowDivTransToP;
  9355. var val;
  9356. function tdParent(node){
  9357. while(node && node.type == 'element'){
  9358. if(node.tagName == 'td'){
  9359. return true;
  9360. }
  9361. node = node.parentNode;
  9362. }
  9363. return false;
  9364. }
  9365. //进行默认的处理
  9366. root.traversal(function (node) {
  9367. if (node.type == 'element') {
  9368. if (!dtd.$cdata[node.tagName] && me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9369. if (!node.firstChild()) node.parentNode.removeChild(node);
  9370. else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9371. node.parentNode.removeChild(node, true)
  9372. }
  9373. return;
  9374. }
  9375. switch (node.tagName) {
  9376. case 'style':
  9377. case 'script':
  9378. node.setAttr({
  9379. cdata_tag: node.tagName,
  9380. cdata_data: (node.innerHTML() || ''),
  9381. '_ue_custom_node_':'true'
  9382. });
  9383. node.tagName = 'div';
  9384. node.innerHTML('');
  9385. break;
  9386. case 'a':
  9387. if (val = node.getAttr('href')) {
  9388. node.setAttr('_href', val)
  9389. }
  9390. break;
  9391. case 'img':
  9392. //todo base64暂时去掉,后边做远程图片上传后,干掉这个
  9393. if (val = node.getAttr('src')) {
  9394. if (/^data:/.test(val)) {
  9395. node.parentNode.removeChild(node);
  9396. break;
  9397. }
  9398. }
  9399. node.setAttr('_src', node.getAttr('src'));
  9400. break;
  9401. case 'span':
  9402. if (browser.webkit && (val = node.getStyle('white-space'))) {
  9403. if (/nowrap|normal/.test(val)) {
  9404. node.setStyle('white-space', '');
  9405. if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
  9406. node.parentNode.removeChild(node, true)
  9407. }
  9408. }
  9409. }
  9410. val = node.getAttr('id');
  9411. if(val && /^_baidu_bookmark_/i.test(val)){
  9412. node.parentNode.removeChild(node)
  9413. }
  9414. break;
  9415. case 'p':
  9416. if (val = node.getAttr('align')) {
  9417. node.setAttr('align');
  9418. node.setStyle('text-align', val)
  9419. }
  9420. //trace:3431
  9421. // var cssStyle = node.getAttr('style');
  9422. // if (cssStyle) {
  9423. // cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
  9424. // node.setAttr('style', cssStyle)
  9425. //
  9426. // }
  9427. //p标签不允许嵌套
  9428. utils.each(node.children,function(n){
  9429. if(n.type == 'element' && n.tagName == 'p'){
  9430. var next = n.nextSibling();
  9431. node.parentNode.insertAfter(n,node);
  9432. var last = n;
  9433. while(next){
  9434. var tmp = next.nextSibling();
  9435. node.parentNode.insertAfter(next,last);
  9436. last = next;
  9437. next = tmp;
  9438. }
  9439. return false;
  9440. }
  9441. });
  9442. if (!node.firstChild()) {
  9443. node.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
  9444. }
  9445. break;
  9446. case 'div':
  9447. if(node.getAttr('cdata_tag')){
  9448. break;
  9449. }
  9450. //针对代码这里不处理插入代码的div
  9451. val = node.getAttr('class');
  9452. if(val && /^line number\d+/.test(val)){
  9453. break;
  9454. }
  9455. if(!allowDivTransToP){
  9456. break;
  9457. }
  9458. var tmpNode, p = UE.uNode.createElement('p');
  9459. while (tmpNode = node.firstChild()) {
  9460. if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
  9461. p.appendChild(tmpNode);
  9462. } else {
  9463. if (p.firstChild()) {
  9464. node.parentNode.insertBefore(p, node);
  9465. p = UE.uNode.createElement('p');
  9466. } else {
  9467. node.parentNode.insertBefore(tmpNode, node);
  9468. }
  9469. }
  9470. }
  9471. if (p.firstChild()) {
  9472. node.parentNode.insertBefore(p, node);
  9473. }
  9474. node.parentNode.removeChild(node);
  9475. break;
  9476. case 'dl':
  9477. node.tagName = 'ul';
  9478. break;
  9479. case 'dt':
  9480. case 'dd':
  9481. node.tagName = 'li';
  9482. break;
  9483. case 'li':
  9484. var className = node.getAttr('class');
  9485. if (!className || !/list\-/.test(className)) {
  9486. node.setAttr()
  9487. }
  9488. var tmpNodes = node.getNodesByTagName('ol ul');
  9489. UE.utils.each(tmpNodes, function (n) {
  9490. node.parentNode.insertAfter(n, node);
  9491. });
  9492. break;
  9493. case 'td':
  9494. case 'th':
  9495. case 'caption':
  9496. if(!node.children || !node.children.length){
  9497. node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br'))
  9498. }
  9499. break;
  9500. case 'table':
  9501. if(me.options.disabledTableInTable && tdParent(node)){
  9502. node.parentNode.insertBefore(UE.uNode.createText(node.innerText()),node);
  9503. node.parentNode.removeChild(node)
  9504. }
  9505. }
  9506. }
  9507. // if(node.type == 'comment'){
  9508. // node.parentNode.removeChild(node);
  9509. // }
  9510. })
  9511. });
  9512. //从编辑器出去的内容处理
  9513. me.addOutputRule(function (root) {
  9514. var val;
  9515. root.traversal(function (node) {
  9516. if (node.type == 'element') {
  9517. if (me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9518. if (!node.firstChild()) node.parentNode.removeChild(node);
  9519. else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9520. node.parentNode.removeChild(node, true)
  9521. }
  9522. return;
  9523. }
  9524. switch (node.tagName) {
  9525. case 'div':
  9526. if (val = node.getAttr('cdata_tag')) {
  9527. node.tagName = val;
  9528. node.appendChild(UE.uNode.createText(node.getAttr('cdata_data')));
  9529. node.setAttr({cdata_tag: '', cdata_data: '','_ue_custom_node_':''});
  9530. }
  9531. break;
  9532. case 'a':
  9533. if (val = node.getAttr('_href')) {
  9534. node.setAttr({
  9535. 'href': utils.html(val),
  9536. '_href': ''
  9537. })
  9538. }
  9539. break;
  9540. break;
  9541. case 'span':
  9542. val = node.getAttr('id');
  9543. if(val && /^_baidu_bookmark_/i.test(val)){
  9544. node.parentNode.removeChild(node)
  9545. }
  9546. break;
  9547. case 'img':
  9548. if (val = node.getAttr('_src')) {
  9549. node.setAttr({
  9550. 'src': node.getAttr('_src'),
  9551. '_src': ''
  9552. })
  9553. }
  9554. }
  9555. }
  9556. })
  9557. });
  9558. };
  9559. // plugins/inserthtml.js
  9560. /**
  9561. * 插入html字符串插件
  9562. * @file
  9563. * @since 1.2.6.1
  9564. */
  9565. /**
  9566. * 插入html代码
  9567. * @command inserthtml
  9568. * @method execCommand
  9569. * @param { String } cmd 命令字符串
  9570. * @param { String } html 插入的html字符串
  9571. * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
  9572. * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
  9573. * @example
  9574. * ```javascript
  9575. * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
  9576. * //执行命令,插入<b>CC</b>
  9577. * //插入后的效果 xxx<b>CC</b>xxx
  9578. * //<p>xx|xxx</p> 当前选区为闭合状态
  9579. * //插入<p>CC</p>
  9580. * //结果 <p>xx</p><p>CC</p><p>xxx</p>
  9581. * //<p>xxxx</p>|</p>xxx</p> 当前选区在两个p标签之间
  9582. * //插入 xxxx
  9583. * //结果 <p>xxxx</p><p>xxxx</p></p>xxx</p>
  9584. * ```
  9585. */
  9586. UE.commands['inserthtml'] = {
  9587. execCommand: function (command,html,notNeedFilter){
  9588. var me = this,
  9589. range,
  9590. div;
  9591. if(!html){
  9592. return;
  9593. }
  9594. if(me.fireEvent('beforeinserthtml',html) === true){
  9595. return;
  9596. }
  9597. range = me.selection.getRange();
  9598. div = range.document.createElement( 'div' );
  9599. div.style.display = 'inline';
  9600. if (!notNeedFilter) {
  9601. var root = UE.htmlparser(html);
  9602. //如果给了过滤规则就先进行过滤
  9603. if(me.options.filterRules){
  9604. UE.filterNode(root,me.options.filterRules);
  9605. }
  9606. //执行默认的处理
  9607. me.filterInputRule(root);
  9608. html = root.toHtml()
  9609. }
  9610. div.innerHTML = utils.trim( html );
  9611. if ( !range.collapsed ) {
  9612. var tmpNode = range.startContainer;
  9613. if(domUtils.isFillChar(tmpNode)){
  9614. range.setStartBefore(tmpNode)
  9615. }
  9616. tmpNode = range.endContainer;
  9617. if(domUtils.isFillChar(tmpNode)){
  9618. range.setEndAfter(tmpNode)
  9619. }
  9620. range.txtToElmBoundary();
  9621. //结束边界可能放到了br的前边,要把br包含进来
  9622. // x[xxx]<br/>
  9623. if(range.endContainer && range.endContainer.nodeType == 1){
  9624. tmpNode = range.endContainer.childNodes[range.endOffset];
  9625. if(tmpNode && domUtils.isBr(tmpNode)){
  9626. range.setEndAfter(tmpNode);
  9627. }
  9628. }
  9629. if(range.startOffset == 0){
  9630. tmpNode = range.startContainer;
  9631. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  9632. tmpNode = range.endContainer;
  9633. if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  9634. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  9635. range.setStart(me.body.firstChild,0).collapse(true)
  9636. }
  9637. }
  9638. }
  9639. !range.collapsed && range.deleteContents();
  9640. if(range.startContainer.nodeType == 1){
  9641. var child = range.startContainer.childNodes[range.startOffset],pre;
  9642. if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
  9643. range.setEnd(pre,pre.childNodes.length).collapse();
  9644. while(child.firstChild){
  9645. pre.appendChild(child.firstChild);
  9646. }
  9647. domUtils.remove(child);
  9648. }
  9649. }
  9650. }
  9651. var child,parent,pre,tmp,hadBreak = 0, nextNode;
  9652. //如果当前位置选中了fillchar要干掉,要不会产生空行
  9653. if(range.inFillChar()){
  9654. child = range.startContainer;
  9655. if(domUtils.isFillChar(child)){
  9656. range.setStartBefore(child).collapse(true);
  9657. domUtils.remove(child);
  9658. }else if(domUtils.isFillChar(child,true)){
  9659. child.nodeValue = child.nodeValue.replace(fillCharReg,'');
  9660. range.startOffset--;
  9661. range.collapsed && range.collapse(true)
  9662. }
  9663. }
  9664. //列表单独处理
  9665. var li = domUtils.findParentByTagName(range.startContainer,'li',true);
  9666. if(li){
  9667. var next,last;
  9668. while(child = div.firstChild){
  9669. //针对hr单独处理一下先
  9670. while(child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName=='HR' )){
  9671. next = child.nextSibling;
  9672. range.insertNode( child).collapse();
  9673. last = child;
  9674. child = next;
  9675. }
  9676. if(child){
  9677. if(/^(ol|ul)$/i.test(child.tagName)){
  9678. while(child.firstChild){
  9679. last = child.firstChild;
  9680. domUtils.insertAfter(li,child.firstChild);
  9681. li = li.nextSibling;
  9682. }
  9683. domUtils.remove(child)
  9684. }else{
  9685. var tmpLi;
  9686. next = child.nextSibling;
  9687. tmpLi = me.document.createElement('li');
  9688. domUtils.insertAfter(li,tmpLi);
  9689. tmpLi.appendChild(child);
  9690. last = child;
  9691. child = next;
  9692. li = tmpLi;
  9693. }
  9694. }
  9695. }
  9696. li = domUtils.findParentByTagName(range.startContainer,'li',true);
  9697. if(domUtils.isEmptyBlock(li)){
  9698. domUtils.remove(li)
  9699. }
  9700. if(last){
  9701. range.setStartAfter(last).collapse(true).select(true)
  9702. }
  9703. }else{
  9704. while ( child = div.firstChild ) {
  9705. if(hadBreak){
  9706. var p = me.document.createElement('p');
  9707. while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
  9708. nextNode = child.nextSibling;
  9709. p.appendChild(child);
  9710. child = nextNode;
  9711. }
  9712. if(p.firstChild){
  9713. child = p
  9714. }
  9715. }
  9716. range.insertNode( child );
  9717. nextNode = child.nextSibling;
  9718. if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
  9719. parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
  9720. if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
  9721. if(!dtd[parent.tagName][child.nodeName]){
  9722. pre = parent;
  9723. }else{
  9724. tmp = child.parentNode;
  9725. while (tmp !== parent){
  9726. pre = tmp;
  9727. tmp = tmp.parentNode;
  9728. }
  9729. }
  9730. domUtils.breakParent( child, pre || tmp );
  9731. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  9732. var pre = child.previousSibling;
  9733. domUtils.trimWhiteTextNode(pre);
  9734. if(!pre.childNodes.length){
  9735. domUtils.remove(pre);
  9736. }
  9737. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  9738. if(!browser.ie &&
  9739. (next = child.nextSibling) &&
  9740. domUtils.isBlockElm(next) &&
  9741. next.lastChild &&
  9742. !domUtils.isBr(next.lastChild)){
  9743. next.appendChild(me.document.createElement('br'));
  9744. }
  9745. hadBreak = 1;
  9746. }
  9747. }
  9748. var next = child.nextSibling;
  9749. if(!div.firstChild && next && domUtils.isBlockElm(next)){
  9750. range.setStart(next,0).collapse(true);
  9751. break;
  9752. }
  9753. range.setEndAfter( child ).collapse();
  9754. }
  9755. child = range.startContainer;
  9756. if(nextNode && domUtils.isBr(nextNode)){
  9757. domUtils.remove(nextNode)
  9758. }
  9759. //用chrome可能有空白展位符
  9760. if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
  9761. if(nextNode = child.nextSibling){
  9762. domUtils.remove(child);
  9763. if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
  9764. range.setStart(nextNode,0).collapse(true).shrinkBoundary()
  9765. }
  9766. }else{
  9767. try{
  9768. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  9769. }catch(e){
  9770. range.setStartBefore(child);
  9771. domUtils.remove(child)
  9772. }
  9773. }
  9774. }
  9775. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  9776. try{
  9777. range.select(true);
  9778. }catch(e){}
  9779. }
  9780. setTimeout(function(){
  9781. range = me.selection.getRange();
  9782. range.scrollToView(me.autoHeightEnabled,me.autoHeightEnabled ? domUtils.getXY(me.iframe).y:0);
  9783. me.fireEvent('afterinserthtml', html);
  9784. },200);
  9785. }
  9786. };
  9787. // plugins/image.js
  9788. /**
  9789. * 图片插入、排版插件
  9790. * @file
  9791. * @since 1.2.6.1
  9792. */
  9793. /**
  9794. * 图片对齐方式
  9795. * @command imagefloat
  9796. * @method execCommand
  9797. * @remind 值center为独占一行居中
  9798. * @param { String } cmd 命令字符串
  9799. * @param { String } align 对齐方式,可传left、right、none、center
  9800. * @remaind center表示图片独占一行
  9801. * @example
  9802. * ```javascript
  9803. * editor.execCommand( 'imagefloat', 'center' );
  9804. * ```
  9805. */
  9806. /**
  9807. * 如果选区所在位置是图片区域
  9808. * @command imagefloat
  9809. * @method queryCommandValue
  9810. * @param { String } cmd 命令字符串
  9811. * @return { String } 返回图片对齐方式
  9812. * @example
  9813. * ```javascript
  9814. * editor.queryCommandValue( 'imagefloat' );
  9815. * ```
  9816. */
  9817. UE.commands['imagefloat'] = {
  9818. execCommand:function (cmd, align) {
  9819. var me = this,
  9820. range = me.selection.getRange();
  9821. if (!range.collapsed) {
  9822. var img = range.getClosedNode();
  9823. if (img && img.tagName == 'IMG') {
  9824. switch (align) {
  9825. case 'left':
  9826. case 'right':
  9827. case 'none':
  9828. var pN = img.parentNode, tmpNode, pre, next;
  9829. while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
  9830. pN = pN.parentNode;
  9831. }
  9832. tmpNode = pN;
  9833. if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
  9834. if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) {
  9835. return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
  9836. }) == 1) {
  9837. pre = tmpNode.previousSibling;
  9838. next = tmpNode.nextSibling;
  9839. if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) {
  9840. pre.appendChild(tmpNode.firstChild);
  9841. while (next.firstChild) {
  9842. pre.appendChild(next.firstChild);
  9843. }
  9844. domUtils.remove(tmpNode);
  9845. domUtils.remove(next);
  9846. } else {
  9847. domUtils.setStyle(tmpNode, 'text-align', '');
  9848. }
  9849. }
  9850. range.selectNode(img).select();
  9851. }
  9852. domUtils.setStyle(img, 'float', align == 'none' ? '' : align);
  9853. if(align == 'none'){
  9854. domUtils.removeAttributes(img,'align');
  9855. }
  9856. break;
  9857. case 'center':
  9858. if (me.queryCommandValue('imagefloat') != 'center') {
  9859. pN = img.parentNode;
  9860. domUtils.setStyle(img, 'float', '');
  9861. domUtils.removeAttributes(img,'align');
  9862. tmpNode = img;
  9863. while (pN && domUtils.getChildCount(pN, function (node) {
  9864. return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
  9865. }) == 1
  9866. && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) {
  9867. tmpNode = pN;
  9868. pN = pN.parentNode;
  9869. }
  9870. range.setStartBefore(tmpNode).setCursor(false);
  9871. pN = me.document.createElement('div');
  9872. pN.appendChild(tmpNode);
  9873. domUtils.setStyle(tmpNode, 'float', '');
  9874. me.execCommand('insertHtml', '<p id="_img_parent_tmp" style="text-align:center">' + pN.innerHTML + '</p>');
  9875. tmpNode = me.document.getElementById('_img_parent_tmp');
  9876. tmpNode.removeAttribute('id');
  9877. tmpNode = tmpNode.firstChild;
  9878. range.selectNode(tmpNode).select();
  9879. //去掉后边多余的元素
  9880. next = tmpNode.parentNode.nextSibling;
  9881. if (next && domUtils.isEmptyNode(next)) {
  9882. domUtils.remove(next);
  9883. }
  9884. }
  9885. break;
  9886. }
  9887. }
  9888. }
  9889. },
  9890. queryCommandValue:function () {
  9891. var range = this.selection.getRange(),
  9892. startNode, floatStyle;
  9893. if (range.collapsed) {
  9894. return 'none';
  9895. }
  9896. startNode = range.getClosedNode();
  9897. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  9898. floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align');
  9899. if (floatStyle == 'none') {
  9900. floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle;
  9901. }
  9902. return {
  9903. left:1,
  9904. right:1,
  9905. center:1
  9906. }[floatStyle] ? floatStyle : 'none';
  9907. }
  9908. return 'none';
  9909. },
  9910. queryCommandState:function () {
  9911. var range = this.selection.getRange(),
  9912. startNode;
  9913. if (range.collapsed) return -1;
  9914. startNode = range.getClosedNode();
  9915. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  9916. return 0;
  9917. }
  9918. return -1;
  9919. }
  9920. };
  9921. /**
  9922. * 插入图片
  9923. * @command insertimage
  9924. * @method execCommand
  9925. * @param { String } cmd 命令字符串
  9926. * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
  9927. * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
  9928. * 此时数组的每一个元素都是一个Object类型的图片属性集合。
  9929. * @example
  9930. * ```javascript
  9931. * editor.execCommand( 'insertimage', {
  9932. * src:'a/b/c.jpg',
  9933. * width:'100',
  9934. * height:'100'
  9935. * } );
  9936. * ```
  9937. * @example
  9938. * ```javascript
  9939. * editor.execCommand( 'insertimage', [{
  9940. * src:'a/b/c.jpg',
  9941. * width:'100',
  9942. * height:'100'
  9943. * },{
  9944. * src:'a/b/d.jpg',
  9945. * width:'100',
  9946. * height:'100'
  9947. * }] );
  9948. * ```
  9949. */
  9950. UE.commands['insertimage'] = {
  9951. execCommand:function (cmd, opt) {
  9952. opt = utils.isArray(opt) ? opt : [opt];
  9953. if (!opt.length) {
  9954. return;
  9955. }
  9956. var me = this,
  9957. range = me.selection.getRange(),
  9958. img = range.getClosedNode();
  9959. if(me.fireEvent('beforeinsertimage', opt) === true){
  9960. return;
  9961. }
  9962. if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1) && !img.getAttribute("word_img")) {
  9963. var first = opt.shift();
  9964. var floatStyle = first['floatStyle'];
  9965. delete first['floatStyle'];
  9966. //// img.style.border = (first.border||0) +"px solid #000";
  9967. //// img.style.margin = (first.margin||0) +"px";
  9968. // img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
  9969. domUtils.setAttributes(img, first);
  9970. me.execCommand('imagefloat', floatStyle);
  9971. if (opt.length > 0) {
  9972. range.setStartAfter(img).setCursor(false, true);
  9973. me.execCommand('insertimage', opt);
  9974. }
  9975. } else {
  9976. var html = [], str = '', ci;
  9977. ci = opt[0];
  9978. if (opt.length == 1) {
  9979. str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  9980. (ci.width ? 'width="' + ci.width + '" ' : '') +
  9981. (ci.height ? ' height="' + ci.height + '" ' : '') +
  9982. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
  9983. (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
  9984. (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
  9985. (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
  9986. (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
  9987. (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
  9988. if (ci['floatStyle'] == 'center') {
  9989. str = '<p style="text-align: center">' + str + '</p>';
  9990. }
  9991. html.push(str);
  9992. } else {
  9993. for (var i = 0; ci = opt[i++];) {
  9994. str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
  9995. (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  9996. (ci.height ? ' height="' + ci.height + '" ' : '') +
  9997. ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
  9998. (ci.border || '') + '" ' +
  9999. (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
  10000. html.push(str);
  10001. }
  10002. }
  10003. me.execCommand('insertHtml', html.join(''));
  10004. }
  10005. me.fireEvent('afterinsertimage', opt)
  10006. }
  10007. };
  10008. // plugins/link.js
  10009. /**
  10010. * 超链接
  10011. * @file
  10012. * @since 1.2.6.1
  10013. */
  10014. /**
  10015. * 插入超链接
  10016. * @command link
  10017. * @method execCommand
  10018. * @param { String } cmd 命令字符串
  10019. * @param { Object } options 设置自定义属性,例如:url、title、target
  10020. * @example
  10021. * ```javascript
  10022. * editor.execCommand( 'link', '{
  10023. * url:'ueditor.baidu.com',
  10024. * title:'ueditor',
  10025. * target:'_blank'
  10026. * }' );
  10027. * ```
  10028. */
  10029. /**
  10030. * 返回当前选中的第一个超链接节点
  10031. * @command link
  10032. * @method queryCommandValue
  10033. * @param { String } cmd 命令字符串
  10034. * @return { Element } 超链接节点
  10035. * @example
  10036. * ```javascript
  10037. * editor.queryCommandValue( 'link' );
  10038. * ```
  10039. */
  10040. /**
  10041. * 取消超链接
  10042. * @command unlink
  10043. * @method execCommand
  10044. * @param { String } cmd 命令字符串
  10045. * @example
  10046. * ```javascript
  10047. * editor.execCommand( 'unlink');
  10048. * ```
  10049. */
  10050. UE.plugins['link'] = function(){
  10051. function optimize( range ) {
  10052. var start = range.startContainer,end = range.endContainer;
  10053. if ( start = domUtils.findParentByTagName( start, 'a', true ) ) {
  10054. range.setStartBefore( start );
  10055. }
  10056. if ( end = domUtils.findParentByTagName( end, 'a', true ) ) {
  10057. range.setEndAfter( end );
  10058. }
  10059. }
  10060. UE.commands['unlink'] = {
  10061. execCommand : function() {
  10062. var range = this.selection.getRange(),
  10063. bookmark;
  10064. if(range.collapsed && !domUtils.findParentByTagName( range.startContainer, 'a', true )){
  10065. return;
  10066. }
  10067. bookmark = range.createBookmark();
  10068. optimize( range );
  10069. range.removeInlineStyle( 'a' ).moveToBookmark( bookmark ).select();
  10070. },
  10071. queryCommandState : function(){
  10072. return !this.highlight && this.queryCommandValue('link') ? 0 : -1;
  10073. }
  10074. };
  10075. function doLink(range,opt,me){
  10076. var rngClone = range.cloneRange(),
  10077. link = me.queryCommandValue('link');
  10078. optimize( range = range.adjustmentBoundary() );
  10079. var start = range.startContainer;
  10080. if(start.nodeType == 1 && link){
  10081. start = start.childNodes[range.startOffset];
  10082. if(start && start.nodeType == 1 && start.tagName == 'A' && /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie?'innerText':'textContent'])){
  10083. start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue||opt.href);
  10084. }
  10085. }
  10086. if( !rngClone.collapsed || link){
  10087. range.removeInlineStyle( 'a' );
  10088. rngClone = range.cloneRange();
  10089. }
  10090. if ( rngClone.collapsed ) {
  10091. var a = range.document.createElement( 'a'),
  10092. text = '';
  10093. if(opt.textValue){
  10094. text = utils.html(opt.textValue);
  10095. delete opt.textValue;
  10096. }else{
  10097. text = utils.html(opt.href);
  10098. }
  10099. domUtils.setAttributes( a, opt );
  10100. start = domUtils.findParentByTagName( rngClone.startContainer, 'a', true );
  10101. if(start && domUtils.isInNodeEndBoundary(rngClone,start)){
  10102. range.setStartAfter(start).collapse(true);
  10103. }
  10104. a[browser.ie ? 'innerText' : 'textContent'] = text;
  10105. range.insertNode(a).selectNode( a );
  10106. } else {
  10107. range.applyInlineStyle( 'a', opt );
  10108. }
  10109. }
  10110. UE.commands['link'] = {
  10111. execCommand : function( cmdName, opt ) {
  10112. var range;
  10113. opt._href && (opt._href = utils.unhtml(opt._href,/[<">]/g));
  10114. opt.href && (opt.href = utils.unhtml(opt.href,/[<">]/g));
  10115. opt.textValue && (opt.textValue = utils.unhtml(opt.textValue,/[<">]/g));
  10116. doLink(range=this.selection.getRange(),opt,this);
  10117. //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
  10118. range.collapse().select(true);
  10119. },
  10120. queryCommandValue : function() {
  10121. var range = this.selection.getRange(),
  10122. node;
  10123. if ( range.collapsed ) {
  10124. // node = this.selection.getStart();
  10125. //在ie下getstart()取值偏上了
  10126. node = range.startContainer;
  10127. node = node.nodeType == 1 ? node : node.parentNode;
  10128. if ( node && (node = domUtils.findParentByTagName( node, 'a', true )) && ! domUtils.isInNodeEndBoundary(range,node)) {
  10129. return node;
  10130. }
  10131. } else {
  10132. //trace:1111 如果是<p><a>xx</a></p> startContainer是p就会找不到a
  10133. range.shrinkBoundary();
  10134. var start = range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset] ? range.startContainer : range.startContainer.childNodes[range.startOffset],
  10135. end = range.endContainer.nodeType == 3 || range.endOffset == 0 ? range.endContainer : range.endContainer.childNodes[range.endOffset-1],
  10136. common = range.getCommonAncestor();
  10137. node = domUtils.findParentByTagName( common, 'a', true );
  10138. if ( !node && common.nodeType == 1){
  10139. var as = common.getElementsByTagName( 'a' ),
  10140. ps,pe;
  10141. for ( var i = 0,ci; ci = as[i++]; ) {
  10142. ps = domUtils.getPosition( ci, start ),pe = domUtils.getPosition( ci,end);
  10143. if ( (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
  10144. &&
  10145. (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
  10146. ) {
  10147. node = ci;
  10148. break;
  10149. }
  10150. }
  10151. }
  10152. return node;
  10153. }
  10154. },
  10155. queryCommandState : function() {
  10156. //判断如果是视频的话连接不可用
  10157. //fix 853
  10158. var img = this.selection.getRange().getClosedNode(),
  10159. flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video")!=-1);
  10160. return flag ? -1 : 0;
  10161. }
  10162. };
  10163. };
  10164. // plugins/dragdrop.js
  10165. UE.plugins['dragdrop'] = function (){
  10166. var me = this;
  10167. me.ready(function(){
  10168. domUtils.on(this.body,'dragend',function(){
  10169. var rng = me.selection.getRange();
  10170. var node = rng.getClosedNode()||me.selection.getStart();
  10171. if(node && node.tagName == 'IMG'){
  10172. var pre = node.previousSibling,next;
  10173. while(next = node.nextSibling){
  10174. if(next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild){
  10175. domUtils.remove(next)
  10176. }else{
  10177. break;
  10178. }
  10179. }
  10180. if((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre) || !pre) && (!next || next && !domUtils.isEmptyBlock(next))){
  10181. if(pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)){
  10182. pre.appendChild(node);
  10183. domUtils.moveChild(next,pre);
  10184. domUtils.remove(next);
  10185. }else if(next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)){
  10186. next.insertBefore(node,next.firstChild);
  10187. }
  10188. if(pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)){
  10189. domUtils.remove(pre)
  10190. }
  10191. if(next && next.tagName == 'P' && domUtils.isEmptyBlock(next)){
  10192. domUtils.remove(next)
  10193. }
  10194. rng.selectNode(node).select();
  10195. me.fireEvent('saveScene');
  10196. }
  10197. }
  10198. })
  10199. });
  10200. me.addListener('keyup', function(type, evt) {
  10201. var keyCode = evt.keyCode || evt.which;
  10202. if (keyCode == 13) {
  10203. var rng = me.selection.getRange(),node;
  10204. if(node = domUtils.findParentByTagName(rng.startContainer,'p',true)){
  10205. if(domUtils.getComputedStyle(node,'text-align') == 'center'){
  10206. domUtils.removeStyle(node,'text-align')
  10207. }
  10208. }
  10209. }
  10210. })
  10211. };
  10212. // plugins/paste.js
  10213. ///import core
  10214. ///import plugins/inserthtml.js
  10215. ///import plugins/undo.js
  10216. ///import plugins/serialize.js
  10217. ///commands 粘贴
  10218. ///commandsName PastePlain
  10219. ///commandsTitle 纯文本粘贴模式
  10220. /**
  10221. * @description 粘贴
  10222. * @author zhanyi
  10223. */
  10224. UE.plugins['paste'] = function () {
  10225. function getClipboardData(callback) {
  10226. var doc = this.document;
  10227. if (doc.getElementById('baidu_pastebin')) {
  10228. return;
  10229. }
  10230. var range = this.selection.getRange(),
  10231. bk = range.createBookmark(),
  10232. //创建剪贴的容器div
  10233. pastebin = doc.createElement('div');
  10234. pastebin.id = 'baidu_pastebin';
  10235. // Safari 要求div必须有内容,才能粘贴内容进来
  10236. browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
  10237. doc.body.appendChild(pastebin);
  10238. //trace:717 隐藏的span不能得到top
  10239. //bk.start.innerHTML = '&nbsp;';
  10240. bk.start.style.display = '';
  10241. pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
  10242. //要在现在光标平行的位置加入,否则会出现跳动的问题
  10243. domUtils.getXY(bk.start).y + 'px';
  10244. range.selectNodeContents(pastebin).select(true);
  10245. setTimeout(function () {
  10246. if (browser.webkit) {
  10247. for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
  10248. if (domUtils.isEmptyNode(pi)) {
  10249. domUtils.remove(pi);
  10250. } else {
  10251. pastebin = pi;
  10252. break;
  10253. }
  10254. }
  10255. }
  10256. try {
  10257. pastebin.parentNode.removeChild(pastebin);
  10258. } catch (e) {
  10259. }
  10260. range.moveToBookmark(bk).select(true);
  10261. callback(pastebin);
  10262. }, 0);
  10263. }
  10264. var me = this;
  10265. me.setOpt({
  10266. retainOnlyLabelPasted : false
  10267. });
  10268. var txtContent, htmlContent, address;
  10269. function getPureHtml(html){
  10270. return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (a, b, tagName, attrs) {
  10271. tagName = tagName.toLowerCase();
  10272. if ({img: 1}[tagName]) {
  10273. return a;
  10274. }
  10275. attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function (str, atr, val) {
  10276. if ({
  10277. 'src': 1,
  10278. 'href': 1,
  10279. 'name': 1
  10280. }[atr.toLowerCase()]) {
  10281. return atr + '=' + val + ' '
  10282. }
  10283. return ''
  10284. });
  10285. if ({
  10286. 'span': 1,
  10287. 'div': 1
  10288. }[tagName]) {
  10289. return ''
  10290. } else {
  10291. return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
  10292. }
  10293. });
  10294. }
  10295. function filter(div) {
  10296. var html;
  10297. if (div.firstChild) {
  10298. //去掉cut中添加的边界值
  10299. var nodes = domUtils.getElementsByTagName(div, 'span');
  10300. for (var i = 0, ni; ni = nodes[i++];) {
  10301. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  10302. domUtils.remove(ni);
  10303. }
  10304. }
  10305. if (browser.webkit) {
  10306. var brs = div.querySelectorAll('div br');
  10307. for (var i = 0, bi; bi = brs[i++];) {
  10308. var pN = bi.parentNode;
  10309. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  10310. pN.innerHTML = '<p><br/></p>';
  10311. domUtils.remove(pN);
  10312. }
  10313. }
  10314. var divs = div.querySelectorAll('#baidu_pastebin');
  10315. for (var i = 0, di; di = divs[i++];) {
  10316. var tmpP = me.document.createElement('p');
  10317. di.parentNode.insertBefore(tmpP, di);
  10318. while (di.firstChild) {
  10319. tmpP.appendChild(di.firstChild);
  10320. }
  10321. domUtils.remove(di);
  10322. }
  10323. var metas = div.querySelectorAll('meta');
  10324. for (var i = 0, ci; ci = metas[i++];) {
  10325. domUtils.remove(ci);
  10326. }
  10327. var brs = div.querySelectorAll('br');
  10328. for (i = 0; ci = brs[i++];) {
  10329. if (/^apple-/i.test(ci.className)) {
  10330. domUtils.remove(ci);
  10331. }
  10332. }
  10333. }
  10334. if (browser.gecko) {
  10335. var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
  10336. for (i = 0; ci = dirtyNodes[i++];) {
  10337. ci.removeAttribute('_moz_dirty');
  10338. }
  10339. }
  10340. if (!browser.ie) {
  10341. var spans = div.querySelectorAll('span.Apple-style-span');
  10342. for (var i = 0, ci; ci = spans[i++];) {
  10343. domUtils.remove(ci, true);
  10344. }
  10345. }
  10346. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  10347. html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  10348. //过滤word粘贴过来的冗余属性
  10349. html = UE.filterWord(html);
  10350. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  10351. var root = UE.htmlparser(html);
  10352. //如果给了过滤规则就先进行过滤
  10353. if (me.options.filterRules) {
  10354. UE.filterNode(root, me.options.filterRules);
  10355. }
  10356. //执行默认的处理
  10357. me.filterInputRule(root);
  10358. //针对chrome的处理
  10359. if (browser.webkit) {
  10360. var br = root.lastChild();
  10361. if (br && br.type == 'element' && br.tagName == 'br') {
  10362. root.removeChild(br)
  10363. }
  10364. utils.each(me.body.querySelectorAll('div'), function (node) {
  10365. if (domUtils.isEmptyBlock(node)) {
  10366. domUtils.remove(node,true)
  10367. }
  10368. })
  10369. }
  10370. html = {'html': root.toHtml()};
  10371. me.fireEvent('beforepaste', html, root);
  10372. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  10373. if(!html.html){
  10374. return;
  10375. }
  10376. root = UE.htmlparser(html.html,true);
  10377. //如果开启了纯文本模式
  10378. if (me.queryCommandState('pasteplain') === 1) {
  10379. me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true);
  10380. } else {
  10381. //文本模式
  10382. UE.filterNode(root, me.options.filterTxtRules);
  10383. txtContent = root.toHtml();
  10384. //完全模式
  10385. htmlContent = html.html;
  10386. address = me.selection.getRange().createAddress(true);
  10387. me.execCommand('insertHtml', me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent, true);
  10388. }
  10389. me.fireEvent("afterpaste", html);
  10390. }
  10391. }
  10392. me.addListener('pasteTransfer', function (cmd, plainType) {
  10393. if (address && txtContent && htmlContent && txtContent != htmlContent) {
  10394. var range = me.selection.getRange();
  10395. range.moveToAddress(address, true);
  10396. if (!range.collapsed) {
  10397. while (!domUtils.isBody(range.startContainer)
  10398. ) {
  10399. var start = range.startContainer;
  10400. if(start.nodeType == 1){
  10401. start = start.childNodes[range.startOffset];
  10402. if(!start){
  10403. range.setStartBefore(range.startContainer);
  10404. continue;
  10405. }
  10406. var pre = start.previousSibling;
  10407. if(pre && pre.nodeType == 3 && new RegExp('^[\n\r\t '+domUtils.fillChar+']*$').test(pre.nodeValue)){
  10408. range.setStartBefore(pre)
  10409. }
  10410. }
  10411. if(range.startOffset == 0){
  10412. range.setStartBefore(range.startContainer);
  10413. }else{
  10414. break;
  10415. }
  10416. }
  10417. while (!domUtils.isBody(range.endContainer)
  10418. ) {
  10419. var end = range.endContainer;
  10420. if(end.nodeType == 1){
  10421. end = end.childNodes[range.endOffset];
  10422. if(!end){
  10423. range.setEndAfter(range.endContainer);
  10424. continue;
  10425. }
  10426. var next = end.nextSibling;
  10427. if(next && next.nodeType == 3 && new RegExp('^[\n\r\t'+domUtils.fillChar+']*$').test(next.nodeValue)){
  10428. range.setEndAfter(next)
  10429. }
  10430. }
  10431. if(range.endOffset == range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length){
  10432. range.setEndAfter(range.endContainer);
  10433. }else{
  10434. break;
  10435. }
  10436. }
  10437. }
  10438. range.deleteContents();
  10439. range.select(true);
  10440. me.__hasEnterExecCommand = true;
  10441. var html = htmlContent;
  10442. if (plainType === 2 ) {
  10443. html = getPureHtml(html);
  10444. } else if (plainType) {
  10445. html = txtContent;
  10446. }
  10447. me.execCommand('inserthtml', html, true);
  10448. me.__hasEnterExecCommand = false;
  10449. var rng = me.selection.getRange();
  10450. while (!domUtils.isBody(rng.startContainer) && !rng.startOffset &&
  10451. rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  10452. ) {
  10453. rng.setStartBefore(rng.startContainer);
  10454. }
  10455. var tmpAddress = rng.createAddress(true);
  10456. address.endAddress = tmpAddress.startAddress;
  10457. }
  10458. });
  10459. me.addListener('ready', function () {
  10460. domUtils.on(me.body, 'cut', function () {
  10461. var range = me.selection.getRange();
  10462. if (!range.collapsed && me.undoManger) {
  10463. me.undoManger.save();
  10464. }
  10465. });
  10466. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  10467. domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
  10468. if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
  10469. return;
  10470. }
  10471. getClipboardData.call(me, function (div) {
  10472. filter(div);
  10473. });
  10474. });
  10475. });
  10476. me.commands['paste'] = {
  10477. execCommand: function (cmd) {
  10478. if (browser.ie) {
  10479. getClipboardData.call(me, function (div) {
  10480. filter(div);
  10481. });
  10482. me.document.execCommand('paste');
  10483. } else {
  10484. alert(me.getLang('pastemsg'));
  10485. }
  10486. }
  10487. }
  10488. };
  10489. // plugins/puretxtpaste.js
  10490. /**
  10491. * 纯文本粘贴插件
  10492. * @file
  10493. * @since 1.2.6.1
  10494. */
  10495. UE.plugins['pasteplain'] = function(){
  10496. var me = this;
  10497. me.setOpt({
  10498. 'pasteplain':false,
  10499. 'filterTxtRules' : function(){
  10500. function transP(node){
  10501. node.tagName = 'p';
  10502. node.setStyle();
  10503. }
  10504. function removeNode(node){
  10505. node.parentNode.removeChild(node,true)
  10506. }
  10507. return {
  10508. //直接删除及其字节点内容
  10509. '-' : 'script style object iframe embed input select',
  10510. 'p': {$:{}},
  10511. 'br':{$:{}},
  10512. div: function (node) {
  10513. var tmpNode, p = UE.uNode.createElement('p');
  10514. while (tmpNode = node.firstChild()) {
  10515. if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
  10516. p.appendChild(tmpNode);
  10517. } else {
  10518. if (p.firstChild()) {
  10519. node.parentNode.insertBefore(p, node);
  10520. p = UE.uNode.createElement('p');
  10521. } else {
  10522. node.parentNode.insertBefore(tmpNode, node);
  10523. }
  10524. }
  10525. }
  10526. if (p.firstChild()) {
  10527. node.parentNode.insertBefore(p, node);
  10528. }
  10529. node.parentNode.removeChild(node);
  10530. },
  10531. ol: removeNode,
  10532. ul: removeNode,
  10533. dl:removeNode,
  10534. dt:removeNode,
  10535. dd:removeNode,
  10536. 'li':removeNode,
  10537. 'caption':transP,
  10538. 'th':transP,
  10539. 'tr':transP,
  10540. 'h1':transP,'h2':transP,'h3':transP,'h4':transP,'h5':transP,'h6':transP,
  10541. 'td':function(node){
  10542. //没有内容的td直接删掉
  10543. var txt = !!node.innerText();
  10544. if(txt){
  10545. node.parentNode.insertAfter(UE.uNode.createText(' &nbsp; &nbsp;'),node);
  10546. }
  10547. node.parentNode.removeChild(node,node.innerText())
  10548. }
  10549. }
  10550. }()
  10551. });
  10552. //暂时这里支持一下老版本的属性
  10553. var pasteplain = me.options.pasteplain;
  10554. /**
  10555. * 启用或取消纯文本粘贴模式
  10556. * @command pasteplain
  10557. * @method execCommand
  10558. * @param { String } cmd 命令字符串
  10559. * @example
  10560. * ```javascript
  10561. * editor.queryCommandState( 'pasteplain' );
  10562. * ```
  10563. */
  10564. /**
  10565. * 查询当前是否处于纯文本粘贴模式
  10566. * @command pasteplain
  10567. * @method queryCommandState
  10568. * @param { String } cmd 命令字符串
  10569. * @return { int } 如果处于纯文本模式,返回1,否则,返回0
  10570. * @example
  10571. * ```javascript
  10572. * editor.queryCommandState( 'pasteplain' );
  10573. * ```
  10574. */
  10575. me.commands['pasteplain'] = {
  10576. queryCommandState: function (){
  10577. return pasteplain ? 1 : 0;
  10578. },
  10579. execCommand: function (){
  10580. pasteplain = !pasteplain|0;
  10581. },
  10582. notNeedUndo : 1
  10583. };
  10584. };
  10585. // plugins/source.js
  10586. /**
  10587. * 源码编辑插件
  10588. * @file
  10589. * @since 1.2.6.1
  10590. */
  10591. (function (){
  10592. var sourceEditors = {
  10593. textarea: function (editor, holder){
  10594. var textarea = holder.ownerDocument.createElement('textarea');
  10595. textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;';
  10596. // todo: IE下只有onresize属性可用... 很纠结
  10597. if (browser.ie && browser.version < 8) {
  10598. textarea.style.width = holder.offsetWidth + 'px';
  10599. textarea.style.height = holder.offsetHeight + 'px';
  10600. holder.onresize = function (){
  10601. textarea.style.width = holder.offsetWidth + 'px';
  10602. textarea.style.height = holder.offsetHeight + 'px';
  10603. };
  10604. }
  10605. holder.appendChild(textarea);
  10606. return {
  10607. setContent: function (content){
  10608. textarea.value = content;
  10609. },
  10610. getContent: function (){
  10611. return textarea.value;
  10612. },
  10613. select: function (){
  10614. var range;
  10615. if (browser.ie) {
  10616. range = textarea.createTextRange();
  10617. range.collapse(true);
  10618. range.select();
  10619. } else {
  10620. //todo: chrome下无法设置焦点
  10621. textarea.setSelectionRange(0, 0);
  10622. textarea.focus();
  10623. }
  10624. },
  10625. dispose: function (){
  10626. holder.removeChild(textarea);
  10627. // todo
  10628. holder.onresize = null;
  10629. textarea = null;
  10630. holder = null;
  10631. }
  10632. };
  10633. },
  10634. codemirror: function (editor, holder){
  10635. var codeEditor = window.CodeMirror(holder, {
  10636. mode: "text/html",
  10637. tabMode: "indent",
  10638. lineNumbers: true,
  10639. lineWrapping:true
  10640. });
  10641. var dom = codeEditor.getWrapperElement();
  10642. dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
  10643. codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;';
  10644. codeEditor.refresh();
  10645. return {
  10646. getCodeMirror:function(){
  10647. return codeEditor;
  10648. },
  10649. setContent: function (content){
  10650. codeEditor.setValue(content);
  10651. },
  10652. getContent: function (){
  10653. return codeEditor.getValue();
  10654. },
  10655. select: function (){
  10656. codeEditor.focus();
  10657. },
  10658. dispose: function (){
  10659. holder.removeChild(dom);
  10660. dom = null;
  10661. codeEditor = null;
  10662. }
  10663. };
  10664. }
  10665. };
  10666. UE.plugins['source'] = function (){
  10667. var me = this;
  10668. var opt = this.options;
  10669. var sourceMode = false;
  10670. var sourceEditor;
  10671. var orgSetContent;
  10672. opt.sourceEditor = browser.ie ? 'textarea' : (opt.sourceEditor || 'codemirror');
  10673. me.setOpt({
  10674. sourceEditorFirst:false
  10675. });
  10676. function createSourceEditor(holder){
  10677. return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder);
  10678. }
  10679. var bakCssText;
  10680. //解决在源码模式下getContent不能得到最新的内容问题
  10681. var oldGetContent,
  10682. bakAddress;
  10683. /**
  10684. * 切换源码模式和编辑模式
  10685. * @command source
  10686. * @method execCommand
  10687. * @param { String } cmd 命令字符串
  10688. * @example
  10689. * ```javascript
  10690. * editor.execCommand( 'source');
  10691. * ```
  10692. */
  10693. /**
  10694. * 查询当前编辑区域的状态是源码模式还是可视化模式
  10695. * @command source
  10696. * @method queryCommandState
  10697. * @param { String } cmd 命令字符串
  10698. * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
  10699. * @example
  10700. * ```javascript
  10701. * editor.queryCommandState( 'source' );
  10702. * ```
  10703. */
  10704. me.commands['source'] = {
  10705. execCommand: function (){
  10706. sourceMode = !sourceMode;
  10707. if (sourceMode) {
  10708. bakAddress = me.selection.getRange().createAddress(false,true);
  10709. me.undoManger && me.undoManger.save(true);
  10710. if(browser.gecko){
  10711. me.body.contentEditable = false;
  10712. }
  10713. bakCssText = me.iframe.style.cssText;
  10714. me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;';
  10715. me.fireEvent('beforegetcontent');
  10716. var root = UE.htmlparser(me.body.innerHTML);
  10717. me.filterOutputRule(root);
  10718. root.traversal(function (node) {
  10719. if (node.type == 'element') {
  10720. switch (node.tagName) {
  10721. case 'td':
  10722. case 'th':
  10723. case 'caption':
  10724. if(node.children && node.children.length == 1){
  10725. if(node.firstChild().tagName == 'br' ){
  10726. node.removeChild(node.firstChild())
  10727. }
  10728. };
  10729. break;
  10730. case 'pre':
  10731. node.innerText(node.innerText().replace(/&nbsp;/g,' '))
  10732. }
  10733. }
  10734. });
  10735. me.fireEvent('aftergetcontent');
  10736. var content = root.toHtml(true);
  10737. sourceEditor = createSourceEditor(me.iframe.parentNode);
  10738. sourceEditor.setContent(content);
  10739. orgSetContent = me.setContent;
  10740. me.setContent = function(html){
  10741. //这里暂时不触发事件,防止报错
  10742. var root = UE.htmlparser(html);
  10743. me.filterInputRule(root);
  10744. html = root.toHtml();
  10745. sourceEditor.setContent(html);
  10746. };
  10747. setTimeout(function (){
  10748. sourceEditor.select();
  10749. me.addListener('fullscreenchanged', function(){
  10750. try{
  10751. sourceEditor.getCodeMirror().refresh()
  10752. }catch(e){}
  10753. });
  10754. });
  10755. //重置getContent,源码模式下取值也能是最新的数据
  10756. oldGetContent = me.getContent;
  10757. me.getContent = function (){
  10758. return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  10759. };
  10760. } else {
  10761. me.iframe.style.cssText = bakCssText;
  10762. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  10763. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  10764. cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
  10765. if(b && !dtd.$inlineWithA[b.toLowerCase()]){
  10766. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
  10767. }
  10768. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
  10769. });
  10770. me.setContent = orgSetContent;
  10771. me.setContent(cont);
  10772. sourceEditor.dispose();
  10773. sourceEditor = null;
  10774. //还原getContent方法
  10775. me.getContent = oldGetContent;
  10776. var first = me.body.firstChild;
  10777. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  10778. if(!first){
  10779. me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
  10780. first = me.body.firstChild;
  10781. }
  10782. //要在ifm为显示时ff才能取到selection,否则报错
  10783. //这里不能比较位置了
  10784. me.undoManger && me.undoManger.save(true);
  10785. if(browser.gecko){
  10786. var input = document.createElement('input');
  10787. input.style.cssText = 'position:absolute;left:0;top:-32768px';
  10788. document.body.appendChild(input);
  10789. me.body.contentEditable = false;
  10790. setTimeout(function(){
  10791. domUtils.setViewportOffset(input, { left: -32768, top: 0 });
  10792. input.focus();
  10793. setTimeout(function(){
  10794. me.body.contentEditable = true;
  10795. me.selection.getRange().moveToAddress(bakAddress).select(true);
  10796. domUtils.remove(input);
  10797. });
  10798. });
  10799. }else{
  10800. //ie下有可能报错,比如在代码顶头的情况
  10801. try{
  10802. me.selection.getRange().moveToAddress(bakAddress).select(true);
  10803. }catch(e){}
  10804. }
  10805. }
  10806. this.fireEvent('sourcemodechanged', sourceMode);
  10807. },
  10808. queryCommandState: function (){
  10809. return sourceMode|0;
  10810. },
  10811. notNeedUndo : 1
  10812. };
  10813. var oldQueryCommandState = me.queryCommandState;
  10814. me.queryCommandState = function (cmdName){
  10815. cmdName = cmdName.toLowerCase();
  10816. if (sourceMode) {
  10817. //源码模式下可以开启的命令
  10818. return cmdName in {
  10819. 'source' : 1,
  10820. 'fullscreen' : 1
  10821. } ? 1 : -1
  10822. }
  10823. return oldQueryCommandState.apply(this, arguments);
  10824. };
  10825. if(opt.sourceEditor == "codemirror"){
  10826. me.addListener("ready",function(){
  10827. utils.loadFile(document,{
  10828. src : opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
  10829. tag : "script",
  10830. type : "text/javascript",
  10831. defer : "defer"
  10832. },function(){
  10833. if(opt.sourceEditorFirst){
  10834. setTimeout(function(){
  10835. me.execCommand("source");
  10836. },0);
  10837. }
  10838. });
  10839. utils.loadFile(document,{
  10840. tag : "link",
  10841. rel : "stylesheet",
  10842. type : "text/css",
  10843. href : opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
  10844. });
  10845. });
  10846. }
  10847. };
  10848. })();
  10849. // plugins/enterkey.js
  10850. ///import core
  10851. ///import plugins/undo.js
  10852. ///commands 设置回车标签p或br
  10853. ///commandsName EnterKey
  10854. ///commandsTitle 设置回车标签p或br
  10855. /**
  10856. * @description 处理回车
  10857. * @author zhanyi
  10858. */
  10859. UE.plugins['enterkey'] = function() {
  10860. var hTag,
  10861. me = this,
  10862. tag = me.options.enterTag;
  10863. me.addListener('keyup', function(type, evt) {
  10864. var keyCode = evt.keyCode || evt.which;
  10865. if (keyCode == 13) {
  10866. var range = me.selection.getRange(),
  10867. start = range.startContainer,
  10868. doSave;
  10869. //修正在h1-h6里边回车后不能嵌套p的问题
  10870. if (!browser.ie) {
  10871. if (/h\d/i.test(hTag)) {
  10872. if (browser.gecko) {
  10873. var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
  10874. if (!h) {
  10875. me.document.execCommand('formatBlock', false, '<p>');
  10876. doSave = 1;
  10877. }
  10878. } else {
  10879. //chrome remove div
  10880. if (start.nodeType == 1) {
  10881. var tmp = me.document.createTextNode(''),div;
  10882. range.insertNode(tmp);
  10883. div = domUtils.findParentByTagName(tmp, 'div', true);
  10884. if (div) {
  10885. var p = me.document.createElement('p');
  10886. while (div.firstChild) {
  10887. p.appendChild(div.firstChild);
  10888. }
  10889. div.parentNode.insertBefore(p, div);
  10890. domUtils.remove(div);
  10891. range.setStartBefore(tmp).setCursor();
  10892. doSave = 1;
  10893. }
  10894. domUtils.remove(tmp);
  10895. }
  10896. }
  10897. if (me.undoManger && doSave) {
  10898. me.undoManger.save();
  10899. }
  10900. }
  10901. //没有站位符,会出现多行的问题
  10902. browser.opera && range.select();
  10903. }else{
  10904. me.fireEvent('saveScene',true,true)
  10905. }
  10906. }
  10907. });
  10908. me.addListener('keydown', function(type, evt) {
  10909. var keyCode = evt.keyCode || evt.which;
  10910. if (keyCode == 13) {//回车
  10911. if(me.fireEvent('beforeenterkeydown')){
  10912. domUtils.preventDefault(evt);
  10913. return;
  10914. }
  10915. me.fireEvent('saveScene',true,true);
  10916. hTag = '';
  10917. var range = me.selection.getRange();
  10918. if (!range.collapsed) {
  10919. //跨td不能删
  10920. var start = range.startContainer,
  10921. end = range.endContainer,
  10922. startTd = domUtils.findParentByTagName(start, 'td', true),
  10923. endTd = domUtils.findParentByTagName(end, 'td', true);
  10924. if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
  10925. evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
  10926. return;
  10927. }
  10928. }
  10929. if (tag == 'p') {
  10930. if (!browser.ie) {
  10931. start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
  10932. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  10933. //trace:2431
  10934. if (!start && !browser.opera) {
  10935. me.document.execCommand('formatBlock', false, '<p>');
  10936. if (browser.gecko) {
  10937. range = me.selection.getRange();
  10938. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  10939. start && domUtils.removeDirtyAttr(start);
  10940. }
  10941. } else {
  10942. hTag = start.tagName;
  10943. start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
  10944. }
  10945. }
  10946. } else {
  10947. evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
  10948. if (!range.collapsed) {
  10949. range.deleteContents();
  10950. start = range.startContainer;
  10951. if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
  10952. while (start.nodeType == 1) {
  10953. if (dtd.$empty[start.tagName]) {
  10954. range.setStartBefore(start).setCursor();
  10955. if (me.undoManger) {
  10956. me.undoManger.save();
  10957. }
  10958. return false;
  10959. }
  10960. if (!start.firstChild) {
  10961. var br = range.document.createElement('br');
  10962. start.appendChild(br);
  10963. range.setStart(start, 0).setCursor();
  10964. if (me.undoManger) {
  10965. me.undoManger.save();
  10966. }
  10967. return false;
  10968. }
  10969. start = start.firstChild;
  10970. }
  10971. if (start === range.startContainer.childNodes[range.startOffset]) {
  10972. br = range.document.createElement('br');
  10973. range.insertNode(br).setCursor();
  10974. } else {
  10975. range.setStart(start, 0).setCursor();
  10976. }
  10977. } else {
  10978. br = range.document.createElement('br');
  10979. range.insertNode(br).setStartAfter(br).setCursor();
  10980. }
  10981. } else {
  10982. br = range.document.createElement('br');
  10983. range.insertNode(br);
  10984. var parent = br.parentNode;
  10985. if (parent.lastChild === br) {
  10986. br.parentNode.insertBefore(br.cloneNode(true), br);
  10987. range.setStartBefore(br);
  10988. } else {
  10989. range.setStartAfter(br);
  10990. }
  10991. range.setCursor();
  10992. }
  10993. }
  10994. }
  10995. });
  10996. };
  10997. // plugins/keystrokes.js
  10998. /* 处理特殊键的兼容性问题 */
  10999. UE.plugins['keystrokes'] = function() {
  11000. var me = this;
  11001. var collapsed = true;
  11002. me.addListener('keydown', function(type, evt) {
  11003. var keyCode = evt.keyCode || evt.which,
  11004. rng = me.selection.getRange();
  11005. //处理全选的情况
  11006. if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
  11007. || keyCode >= 48 && keyCode <= 57 ||
  11008. keyCode >= 96 && keyCode <= 111 || {
  11009. 13:1,
  11010. 8:1,
  11011. 46:1
  11012. }[keyCode])
  11013. ){
  11014. var tmpNode = rng.startContainer;
  11015. if(domUtils.isFillChar(tmpNode)){
  11016. rng.setStartBefore(tmpNode)
  11017. }
  11018. tmpNode = rng.endContainer;
  11019. if(domUtils.isFillChar(tmpNode)){
  11020. rng.setEndAfter(tmpNode)
  11021. }
  11022. rng.txtToElmBoundary();
  11023. //结束边界可能放到了br的前边,要把br包含进来
  11024. // x[xxx]<br/>
  11025. if(rng.endContainer && rng.endContainer.nodeType == 1){
  11026. tmpNode = rng.endContainer.childNodes[rng.endOffset];
  11027. if(tmpNode && domUtils.isBr(tmpNode)){
  11028. rng.setEndAfter(tmpNode);
  11029. }
  11030. }
  11031. if(rng.startOffset == 0){
  11032. tmpNode = rng.startContainer;
  11033. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  11034. tmpNode = rng.endContainer;
  11035. if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  11036. me.fireEvent('saveScene');
  11037. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  11038. rng.setStart(me.body.firstChild,0).setCursor(false,true);
  11039. me._selectionChange();
  11040. return;
  11041. }
  11042. }
  11043. }
  11044. }
  11045. //处理backspace
  11046. if (keyCode == keymap.Backspace) {
  11047. rng = me.selection.getRange();
  11048. collapsed = rng.collapsed;
  11049. if(me.fireEvent('delkeydown',evt)){
  11050. return;
  11051. }
  11052. var start,end;
  11053. //避免按两次删除才能生效的问题
  11054. if(rng.collapsed && rng.inFillChar()){
  11055. start = rng.startContainer;
  11056. if(domUtils.isFillChar(start)){
  11057. rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
  11058. domUtils.remove(start)
  11059. }else{
  11060. start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
  11061. rng.startOffset--;
  11062. rng.collapse(true).select(true)
  11063. }
  11064. }
  11065. //解决选中control元素不能删除的问题
  11066. if (start = rng.getClosedNode()) {
  11067. me.fireEvent('saveScene');
  11068. rng.setStartBefore(start);
  11069. domUtils.remove(start);
  11070. rng.setCursor();
  11071. me.fireEvent('saveScene');
  11072. domUtils.preventDefault(evt);
  11073. return;
  11074. }
  11075. //阻止在table上的删除
  11076. if (!browser.ie) {
  11077. start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
  11078. end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
  11079. if (start && !end || !start && end || start !== end) {
  11080. evt.preventDefault();
  11081. return;
  11082. }
  11083. }
  11084. }
  11085. //处理tab键的逻辑
  11086. if (keyCode == keymap.Tab) {
  11087. //不处理以下标签
  11088. var excludeTagNameForTabKey = {
  11089. 'ol' : 1,
  11090. 'ul' : 1,
  11091. 'table':1
  11092. };
  11093. //处理组件里的tab按下事件
  11094. if(me.fireEvent('tabkeydown',evt)){
  11095. domUtils.preventDefault(evt);
  11096. return;
  11097. }
  11098. var range = me.selection.getRange();
  11099. me.fireEvent('saveScene');
  11100. for (var i = 0,txt = '',tabSize = me.options.tabSize|| 4,tabNode = me.options.tabNode || '&nbsp;'; i < tabSize; i++) {
  11101. txt += tabNode;
  11102. }
  11103. var span = me.document.createElement('span');
  11104. span.innerHTML = txt + domUtils.fillChar;
  11105. if (range.collapsed) {
  11106. range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
  11107. } else {
  11108. var filterFn = function(node) {
  11109. return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
  11110. };
  11111. //普通的情况
  11112. start = domUtils.findParent(range.startContainer, filterFn,true);
  11113. end = domUtils.findParent(range.endContainer, filterFn,true);
  11114. if (start && end && start === end) {
  11115. range.deleteContents();
  11116. range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
  11117. } else {
  11118. var bookmark = range.createBookmark();
  11119. range.enlarge(true);
  11120. var bookmark2 = range.createBookmark(),
  11121. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
  11122. while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
  11123. current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
  11124. current = domUtils.getNextDomNode(current, false, filterFn);
  11125. }
  11126. range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
  11127. }
  11128. }
  11129. domUtils.preventDefault(evt)
  11130. }
  11131. //trace:1634
  11132. //ff的del键在容器空的时候,也会删除
  11133. if(browser.gecko && keyCode == 46){
  11134. range = me.selection.getRange();
  11135. if(range.collapsed){
  11136. start = range.startContainer;
  11137. if(domUtils.isEmptyBlock(start)){
  11138. var parent = start.parentNode;
  11139. while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
  11140. start = parent;
  11141. parent = parent.parentNode;
  11142. }
  11143. if(start === parent.lastChild)
  11144. evt.preventDefault();
  11145. return;
  11146. }
  11147. }
  11148. }
  11149. });
  11150. me.addListener('keyup', function(type, evt) {
  11151. var keyCode = evt.keyCode || evt.which,
  11152. rng,me = this;
  11153. if(keyCode == keymap.Backspace){
  11154. if(me.fireEvent('delkeyup')){
  11155. return;
  11156. }
  11157. rng = me.selection.getRange();
  11158. if(rng.collapsed){
  11159. var tmpNode,
  11160. autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
  11161. if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
  11162. if(domUtils.isEmptyBlock(tmpNode)){
  11163. var pre = tmpNode.previousSibling;
  11164. if(pre && pre.nodeName != 'TABLE'){
  11165. domUtils.remove(tmpNode);
  11166. rng.setStartAtLast(pre).setCursor(false,true);
  11167. return;
  11168. }else{
  11169. var next = tmpNode.nextSibling;
  11170. if(next && next.nodeName != 'TABLE'){
  11171. domUtils.remove(tmpNode);
  11172. rng.setStartAtFirst(next).setCursor(false,true);
  11173. return;
  11174. }
  11175. }
  11176. }
  11177. }
  11178. //处理当删除到body时,要重新给p标签展位
  11179. if(domUtils.isBody(rng.startContainer)){
  11180. var tmpNode = domUtils.createElement(me.document,'p',{
  11181. 'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
  11182. });
  11183. rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
  11184. }
  11185. }
  11186. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  11187. if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
  11188. if(browser.ie){
  11189. var span = rng.document.createElement('span');
  11190. rng.insertNode(span).setStartBefore(span).collapse(true);
  11191. rng.select();
  11192. domUtils.remove(span)
  11193. }else{
  11194. rng.select()
  11195. }
  11196. }
  11197. }
  11198. })
  11199. };
  11200. // plugins/fiximgclick.js
  11201. ///import core
  11202. ///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
  11203. ///commandsName FixImgClick
  11204. ///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小
  11205. //修复chrome下图片不能点击的问题,出现八个角可改变大小
  11206. UE.plugins['fiximgclick'] = (function () {
  11207. var elementUpdated = false;
  11208. function Scale() {
  11209. this.editor = null;
  11210. this.resizer = null;
  11211. this.cover = null;
  11212. this.doc = document;
  11213. this.prePos = {x: 0, y: 0};
  11214. this.startPos = {x: 0, y: 0};
  11215. }
  11216. (function () {
  11217. var rect = [
  11218. //[left, top, width, height]
  11219. [0, 0, -1, -1],
  11220. [0, 0, 0, -1],
  11221. [0, 0, 1, -1],
  11222. [0, 0, -1, 0],
  11223. [0, 0, 1, 0],
  11224. [0, 0, -1, 1],
  11225. [0, 0, 0, 1],
  11226. [0, 0, 1, 1]
  11227. ];
  11228. Scale.prototype = {
  11229. init: function (editor) {
  11230. var me = this;
  11231. me.editor = editor;
  11232. me.startPos = this.prePos = {x: 0, y: 0};
  11233. me.dragId = -1;
  11234. var hands = [],
  11235. cover = me.cover = document.createElement('div'),
  11236. resizer = me.resizer = document.createElement('div');
  11237. cover.id = me.editor.ui.id + '_imagescale_cover';
  11238. cover.style.cssText = 'position:absolute;display:none;z-index:' + (me.editor.options.zIndex) + ';filter:alpha(opacity=0); opacity:0;background:#CCC;';
  11239. domUtils.on(cover, 'mousedown click', function () {
  11240. me.hide();
  11241. });
  11242. for (i = 0; i < 8; i++) {
  11243. hands.push('<span class="edui-editor-imagescale-hand' + i + '"></span>');
  11244. }
  11245. resizer.id = me.editor.ui.id + '_imagescale';
  11246. resizer.className = 'edui-editor-imagescale';
  11247. resizer.innerHTML = hands.join('');
  11248. resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + (me.editor.options.zIndex) + ';';
  11249. me.editor.ui.getDom().appendChild(cover);
  11250. me.editor.ui.getDom().appendChild(resizer);
  11251. me.initStyle();
  11252. me.initEvents();
  11253. },
  11254. initStyle: function () {
  11255. utils.cssRule('imagescale', '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' +
  11256. '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
  11257. + '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
  11258. + '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
  11259. + '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
  11260. + '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
  11261. + '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
  11262. + '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
  11263. + '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
  11264. + '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
  11265. },
  11266. initEvents: function () {
  11267. var me = this;
  11268. me.startPos.x = me.startPos.y = 0;
  11269. me.isDraging = false;
  11270. },
  11271. _eventHandler: function (e) {
  11272. var me = this;
  11273. switch (e.type) {
  11274. case 'mousedown':
  11275. var hand = e.target || e.srcElement, hand;
  11276. if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) {
  11277. me.dragId = hand.className.slice(-1);
  11278. me.startPos.x = me.prePos.x = e.clientX;
  11279. me.startPos.y = me.prePos.y = e.clientY;
  11280. domUtils.on(me.doc,'mousemove', me.proxy(me._eventHandler, me));
  11281. }
  11282. break;
  11283. case 'mousemove':
  11284. if (me.dragId != -1) {
  11285. me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
  11286. me.prePos.x = e.clientX;
  11287. me.prePos.y = e.clientY;
  11288. elementUpdated = true;
  11289. me.updateTargetElement();
  11290. }
  11291. break;
  11292. case 'mouseup':
  11293. if (me.dragId != -1) {
  11294. me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
  11295. me.updateTargetElement();
  11296. if (me.target.parentNode) me.attachTo(me.target);
  11297. me.dragId = -1;
  11298. }
  11299. domUtils.un(me.doc,'mousemove', me.proxy(me._eventHandler, me));
  11300. //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
  11301. if(elementUpdated){
  11302. elementUpdated = false;
  11303. me.editor.fireEvent('contentchange');
  11304. }
  11305. break;
  11306. default:
  11307. break;
  11308. }
  11309. },
  11310. updateTargetElement: function () {
  11311. var me = this;
  11312. domUtils.setStyles(me.target, {
  11313. 'width': me.resizer.style.width,
  11314. 'height': me.resizer.style.height
  11315. });
  11316. me.target.width = parseInt(me.resizer.style.width);
  11317. me.target.height = parseInt(me.resizer.style.height);
  11318. me.attachTo(me.target);
  11319. },
  11320. updateContainerStyle: function (dir, offset) {
  11321. var me = this,
  11322. dom = me.resizer, tmp;
  11323. if (rect[dir][0] != 0) {
  11324. tmp = parseInt(dom.style.left) + offset.x;
  11325. dom.style.left = me._validScaledProp('left', tmp) + 'px';
  11326. }
  11327. if (rect[dir][1] != 0) {
  11328. tmp = parseInt(dom.style.top) + offset.y;
  11329. dom.style.top = me._validScaledProp('top', tmp) + 'px';
  11330. }
  11331. if (rect[dir][2] != 0) {
  11332. tmp = dom.clientWidth + rect[dir][2] * offset.x;
  11333. dom.style.width = me._validScaledProp('width', tmp) + 'px';
  11334. }
  11335. if (rect[dir][3] != 0) {
  11336. tmp = dom.clientHeight + rect[dir][3] * offset.y;
  11337. dom.style.height = me._validScaledProp('height', tmp) + 'px';
  11338. }
  11339. },
  11340. _validScaledProp: function (prop, value) {
  11341. var ele = this.resizer,
  11342. wrap = document;
  11343. value = isNaN(value) ? 0 : value;
  11344. switch (prop) {
  11345. case 'left':
  11346. return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value;
  11347. case 'top':
  11348. return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value;
  11349. case 'width':
  11350. return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value;
  11351. case 'height':
  11352. return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value;
  11353. }
  11354. },
  11355. hideCover: function () {
  11356. this.cover.style.display = 'none';
  11357. },
  11358. showCover: function () {
  11359. var me = this,
  11360. editorPos = domUtils.getXY(me.editor.ui.getDom()),
  11361. iframePos = domUtils.getXY(me.editor.iframe);
  11362. domUtils.setStyles(me.cover, {
  11363. 'width': me.editor.iframe.offsetWidth + 'px',
  11364. 'height': me.editor.iframe.offsetHeight + 'px',
  11365. 'top': iframePos.y - editorPos.y + 'px',
  11366. 'left': iframePos.x - editorPos.x + 'px',
  11367. 'position': 'absolute',
  11368. 'display': ''
  11369. })
  11370. },
  11371. show: function (targetObj) {
  11372. var me = this;
  11373. me.resizer.style.display = 'block';
  11374. if(targetObj) me.attachTo(targetObj);
  11375. domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me));
  11376. domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
  11377. me.showCover();
  11378. me.editor.fireEvent('afterscaleshow', me);
  11379. me.editor.fireEvent('saveScene');
  11380. },
  11381. hide: function () {
  11382. var me = this;
  11383. me.hideCover();
  11384. me.resizer.style.display = 'none';
  11385. domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me));
  11386. domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
  11387. me.editor.fireEvent('afterscalehide', me);
  11388. },
  11389. proxy: function( fn, context ) {
  11390. return function(e) {
  11391. return fn.apply( context || this, arguments);
  11392. };
  11393. },
  11394. attachTo: function (targetObj) {
  11395. var me = this,
  11396. target = me.target = targetObj,
  11397. resizer = this.resizer,
  11398. imgPos = domUtils.getXY(target),
  11399. iframePos = domUtils.getXY(me.editor.iframe),
  11400. editorPos = domUtils.getXY(resizer.parentNode);
  11401. domUtils.setStyles(resizer, {
  11402. 'width': target.width + 'px',
  11403. 'height': target.height + 'px',
  11404. 'left': iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + 'px',
  11405. 'top': iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + 'px'
  11406. });
  11407. }
  11408. }
  11409. })();
  11410. return function () {
  11411. var me = this,
  11412. imageScale;
  11413. me.setOpt('imageScaleEnabled', true);
  11414. if ( !browser.ie && me.options.imageScaleEnabled) {
  11415. me.addListener('click', function (type, e) {
  11416. var range = me.selection.getRange(),
  11417. img = range.getClosedNode();
  11418. if (img && img.tagName == 'IMG' && me.body.contentEditable!="false") {
  11419. if (img.className.indexOf("edui-faked-music") != -1 ||
  11420. img.getAttribute("anchorname") ||
  11421. domUtils.hasClass(img, 'loadingclass') ||
  11422. domUtils.hasClass(img, 'loaderrorclass')) { return }
  11423. if (!imageScale) {
  11424. imageScale = new Scale();
  11425. imageScale.init(me);
  11426. me.ui.getDom().appendChild(imageScale.resizer);
  11427. var _keyDownHandler = function (e) {
  11428. imageScale.hide();
  11429. if(imageScale.target) me.selection.getRange().selectNode(imageScale.target).select();
  11430. }, _mouseDownHandler = function (e) {
  11431. var ele = e.target || e.srcElement;
  11432. if (ele && (ele.className===undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) {
  11433. _keyDownHandler(e);
  11434. }
  11435. }, timer;
  11436. me.addListener('afterscaleshow', function (e) {
  11437. me.addListener('beforekeydown', _keyDownHandler);
  11438. me.addListener('beforemousedown', _mouseDownHandler);
  11439. domUtils.on(document, 'keydown', _keyDownHandler);
  11440. domUtils.on(document,'mousedown', _mouseDownHandler);
  11441. me.selection.getNative().removeAllRanges();
  11442. });
  11443. me.addListener('afterscalehide', function (e) {
  11444. me.removeListener('beforekeydown', _keyDownHandler);
  11445. me.removeListener('beforemousedown', _mouseDownHandler);
  11446. domUtils.un(document, 'keydown', _keyDownHandler);
  11447. domUtils.un(document,'mousedown', _mouseDownHandler);
  11448. var target = imageScale.target;
  11449. if (target.parentNode) {
  11450. me.selection.getRange().selectNode(target).select();
  11451. }
  11452. });
  11453. //TODO 有iframe的情况,mousedown不能往下传。。
  11454. domUtils.on(imageScale.resizer, 'mousedown', function (e) {
  11455. me.selection.getNative().removeAllRanges();
  11456. var ele = e.target || e.srcElement;
  11457. if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
  11458. timer = setTimeout(function () {
  11459. imageScale.hide();
  11460. if(imageScale.target) me.selection.getRange().selectNode(ele).select();
  11461. }, 200);
  11462. }
  11463. });
  11464. domUtils.on(imageScale.resizer, 'mouseup', function (e) {
  11465. var ele = e.target || e.srcElement;
  11466. if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
  11467. clearTimeout(timer);
  11468. }
  11469. });
  11470. }
  11471. imageScale.show(img);
  11472. } else {
  11473. if (imageScale && imageScale.resizer.style.display != 'none') imageScale.hide();
  11474. }
  11475. });
  11476. }
  11477. if (browser.webkit) {
  11478. me.addListener('click', function (type, e) {
  11479. if (e.target.tagName == 'IMG' && me.body.contentEditable!="false") {
  11480. var range = new dom.Range(me.document);
  11481. range.selectNode(e.target).select();
  11482. }
  11483. });
  11484. }
  11485. }
  11486. })();
  11487. // plugins/autoheight.js
  11488. ///import core
  11489. ///commands 当输入内容超过编辑器高度时,编辑器自动增高
  11490. ///commandsName AutoHeight,autoHeightEnabled
  11491. ///commandsTitle 自动增高
  11492. /**
  11493. * @description 自动伸展
  11494. * @author zhanyi
  11495. */
  11496. UE.plugins['autoheight'] = function () {
  11497. var me = this;
  11498. //提供开关,就算加载也可以关闭
  11499. me.autoHeightEnabled = me.options.autoHeightEnabled !== false;
  11500. if (!me.autoHeightEnabled) {
  11501. return;
  11502. }
  11503. var bakOverflow,
  11504. lastHeight = 0,
  11505. options = me.options,
  11506. currentHeight,
  11507. timer;
  11508. function adjustHeight() {
  11509. var me = this;
  11510. clearTimeout(timer);
  11511. if(isFullscreen)return;
  11512. if (!me.queryCommandState || me.queryCommandState && me.queryCommandState('source') != 1) {
  11513. timer = setTimeout(function(){
  11514. var node = me.body.lastChild;
  11515. while(node && node.nodeType != 1){
  11516. node = node.previousSibling;
  11517. }
  11518. if(node && node.nodeType == 1){
  11519. node.style.clear = 'both';
  11520. currentHeight = Math.max(domUtils.getXY(node).y + node.offsetHeight + 25 ,Math.max(options.minFrameHeight, options.initialFrameHeight)) ;
  11521. if (currentHeight != lastHeight) {
  11522. if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
  11523. me.iframe.parentNode.style.height = currentHeight + 'px';
  11524. }
  11525. me.body.style.height = currentHeight + 'px';
  11526. lastHeight = currentHeight;
  11527. }
  11528. domUtils.removeStyle(node,'clear');
  11529. }
  11530. },50)
  11531. }
  11532. }
  11533. var isFullscreen;
  11534. me.addListener('fullscreenchanged',function(cmd,f){
  11535. isFullscreen = f
  11536. });
  11537. me.addListener('destroy', function () {
  11538. me.removeListener('contentchange afterinserthtml keyup mouseup',adjustHeight)
  11539. });
  11540. me.enableAutoHeight = function () {
  11541. var me = this;
  11542. if (!me.autoHeightEnabled) {
  11543. return;
  11544. }
  11545. var doc = me.document;
  11546. me.autoHeightEnabled = true;
  11547. bakOverflow = doc.body.style.overflowY;
  11548. doc.body.style.overflowY = 'hidden';
  11549. me.addListener('contentchange afterinserthtml keyup mouseup',adjustHeight);
  11550. //ff不给事件算得不对
  11551. setTimeout(function () {
  11552. adjustHeight.call(me);
  11553. }, browser.gecko ? 100 : 0);
  11554. me.fireEvent('autoheightchanged', me.autoHeightEnabled);
  11555. };
  11556. me.disableAutoHeight = function () {
  11557. me.body.style.overflowY = bakOverflow || '';
  11558. me.removeListener('contentchange', adjustHeight);
  11559. me.removeListener('keyup', adjustHeight);
  11560. me.removeListener('mouseup', adjustHeight);
  11561. me.autoHeightEnabled = false;
  11562. me.fireEvent('autoheightchanged', me.autoHeightEnabled);
  11563. };
  11564. me.on('setHeight',function(){
  11565. me.disableAutoHeight()
  11566. });
  11567. me.addListener('ready', function () {
  11568. me.enableAutoHeight();
  11569. //trace:1764
  11570. var timer;
  11571. domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function () {
  11572. clearTimeout(timer);
  11573. timer = setTimeout(function () {
  11574. //trace:3681
  11575. adjustHeight.call(me);
  11576. }, 100);
  11577. });
  11578. //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
  11579. var lastScrollY;
  11580. window.onscroll = function(){
  11581. if(lastScrollY === null){
  11582. lastScrollY = this.scrollY
  11583. }else if(this.scrollY == 0 && lastScrollY != 0){
  11584. me.window.scrollTo(0,0);
  11585. lastScrollY = null;
  11586. }
  11587. }
  11588. });
  11589. };
  11590. // plugins/basestyle.js
  11591. /**
  11592. * B、I、sub、super命令支持
  11593. * @file
  11594. * @since 1.2.6.1
  11595. */
  11596. UE.plugins['basestyle'] = function(){
  11597. /**
  11598. * 字体加粗
  11599. * @command bold
  11600. * @param { String } cmd 命令字符串
  11601. * @remind 对已加粗的文本内容执行该命令, 将取消加粗
  11602. * @method execCommand
  11603. * @example
  11604. * ```javascript
  11605. * //editor是编辑器实例
  11606. * //对当前选中的文本内容执行加粗操作
  11607. * //第一次执行, 文本内容加粗
  11608. * editor.execCommand( 'bold' );
  11609. *
  11610. * //第二次执行, 文本内容取消加粗
  11611. * editor.execCommand( 'bold' );
  11612. * ```
  11613. */
  11614. /**
  11615. * 字体倾斜
  11616. * @command italic
  11617. * @method execCommand
  11618. * @param { String } cmd 命令字符串
  11619. * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
  11620. * @example
  11621. * ```javascript
  11622. * //editor是编辑器实例
  11623. * //对当前选中的文本内容执行斜体操作
  11624. * //第一次操作, 文本内容将变成斜体
  11625. * editor.execCommand( 'italic' );
  11626. *
  11627. * //再次对同一文本内容执行, 则文本内容将恢复正常
  11628. * editor.execCommand( 'italic' );
  11629. * ```
  11630. */
  11631. /**
  11632. * 下标文本,与“superscript”命令互斥
  11633. * @command subscript
  11634. * @method execCommand
  11635. * @remind 把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
  11636. * @param { String } cmd 命令字符串
  11637. * @example
  11638. * ```javascript
  11639. * //editor是编辑器实例
  11640. * //对当前选中的文本内容执行下标操作
  11641. * //第一次操作, 文本内容将变成下标文本
  11642. * editor.execCommand( 'subscript' );
  11643. *
  11644. * //再次对同一文本内容执行, 则文本内容将恢复正常
  11645. * editor.execCommand( 'subscript' );
  11646. * ```
  11647. */
  11648. /**
  11649. * 上标文本,与“subscript”命令互斥
  11650. * @command superscript
  11651. * @method execCommand
  11652. * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
  11653. * @param { String } cmd 命令字符串
  11654. * @example
  11655. * ```javascript
  11656. * //editor是编辑器实例
  11657. * //对当前选中的文本内容执行上标操作
  11658. * //第一次操作, 文本内容将变成上标文本
  11659. * editor.execCommand( 'superscript' );
  11660. *
  11661. * //再次对同一文本内容执行, 则文本内容将恢复正常
  11662. * editor.execCommand( 'superscript' );
  11663. * ```
  11664. */
  11665. var basestyles = {
  11666. 'bold':['strong','b'],
  11667. 'italic':['em','i'],
  11668. 'subscript':['sub'],
  11669. 'superscript':['sup']
  11670. },
  11671. getObj = function(editor,tagNames){
  11672. return domUtils.filterNodeList(editor.selection.getStartElementPath(),tagNames);
  11673. },
  11674. me = this;
  11675. //添加快捷键
  11676. me.addshortcutkey({
  11677. "Bold" : "ctrl+66",//^B
  11678. "Italic" : "ctrl+73", //^I
  11679. "Underline" : "ctrl+85"//^U
  11680. });
  11681. me.addInputRule(function(root){
  11682. utils.each(root.getNodesByTagName('b i'),function(node){
  11683. switch (node.tagName){
  11684. case 'b':
  11685. node.tagName = 'strong';
  11686. break;
  11687. case 'i':
  11688. node.tagName = 'em';
  11689. }
  11690. });
  11691. });
  11692. for ( var style in basestyles ) {
  11693. (function( cmd, tagNames ) {
  11694. me.commands[cmd] = {
  11695. execCommand : function( cmdName ) {
  11696. var range = me.selection.getRange(),obj = getObj(this,tagNames);
  11697. if ( range.collapsed ) {
  11698. if ( obj ) {
  11699. var tmpText = me.document.createTextNode('');
  11700. range.insertNode( tmpText ).removeInlineStyle( tagNames );
  11701. range.setStartBefore(tmpText);
  11702. domUtils.remove(tmpText);
  11703. } else {
  11704. var tmpNode = range.document.createElement( tagNames[0] );
  11705. if(cmdName == 'superscript' || cmdName == 'subscript'){
  11706. tmpText = me.document.createTextNode('');
  11707. range.insertNode(tmpText)
  11708. .removeInlineStyle(['sub','sup'])
  11709. .setStartBefore(tmpText)
  11710. .collapse(true);
  11711. }
  11712. range.insertNode( tmpNode ).setStart( tmpNode, 0 );
  11713. }
  11714. range.collapse( true );
  11715. } else {
  11716. if(cmdName == 'superscript' || cmdName == 'subscript'){
  11717. if(!obj || obj.tagName.toLowerCase() != cmdName){
  11718. range.removeInlineStyle(['sub','sup']);
  11719. }
  11720. }
  11721. obj ? range.removeInlineStyle( tagNames ) : range.applyInlineStyle( tagNames[0] );
  11722. }
  11723. range.select();
  11724. },
  11725. queryCommandState : function() {
  11726. return getObj(this,tagNames) ? 1 : 0;
  11727. }
  11728. };
  11729. })( style, basestyles[style] );
  11730. }
  11731. };
  11732. // plugins/serverparam.js
  11733. /**
  11734. * 服务器提交的额外参数列表设置插件
  11735. * @file
  11736. * @since 1.2.6.1
  11737. */
  11738. UE.plugin.register('serverparam', function (){
  11739. var me = this,
  11740. serverParam = {};
  11741. return {
  11742. commands:{
  11743. /**
  11744. * 修改服务器提交的额外参数列表,清除所有项
  11745. * @command serverparam
  11746. * @method execCommand
  11747. * @param { String } cmd 命令字符串
  11748. * @example
  11749. * ```javascript
  11750. * editor.execCommand('serverparam');
  11751. * editor.queryCommandValue('serverparam'); //返回空
  11752. * ```
  11753. */
  11754. /**
  11755. * 修改服务器提交的额外参数列表,删除指定项
  11756. * @command serverparam
  11757. * @method execCommand
  11758. * @param { String } cmd 命令字符串
  11759. * @param { String } key 要清除的属性
  11760. * @example
  11761. * ```javascript
  11762. * editor.execCommand('serverparam', 'name'); //删除属性name
  11763. * ```
  11764. */
  11765. /**
  11766. * 修改服务器提交的额外参数列表,使用键值添加项
  11767. * @command serverparam
  11768. * @method execCommand
  11769. * @param { String } cmd 命令字符串
  11770. * @param { String } key 要添加的属性
  11771. * @param { String } value 要添加属性的值
  11772. * @example
  11773. * ```javascript
  11774. * editor.execCommand('serverparam', 'name', 'hello');
  11775. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  11776. * ```
  11777. */
  11778. /**
  11779. * 修改服务器提交的额外参数列表,传入键值对对象添加多项
  11780. * @command serverparam
  11781. * @method execCommand
  11782. * @param { String } cmd 命令字符串
  11783. * @param { Object } key 传入的键值对对象
  11784. * @example
  11785. * ```javascript
  11786. * editor.execCommand('serverparam', {'name': 'hello'});
  11787. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  11788. * ```
  11789. */
  11790. /**
  11791. * 修改服务器提交的额外参数列表,使用自定义函数添加多项
  11792. * @command serverparam
  11793. * @method execCommand
  11794. * @param { String } cmd 命令字符串
  11795. * @param { Function } key 自定义获取参数的函数
  11796. * @example
  11797. * ```javascript
  11798. * editor.execCommand('serverparam', function(editor){
  11799. * return {'key': 'value'};
  11800. * });
  11801. * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'}
  11802. * ```
  11803. */
  11804. /**
  11805. * 获取服务器提交的额外参数列表
  11806. * @command serverparam
  11807. * @method queryCommandValue
  11808. * @param { String } cmd 命令字符串
  11809. * @example
  11810. * ```javascript
  11811. * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'}
  11812. * ```
  11813. */
  11814. 'serverparam':{
  11815. execCommand:function (cmd, key, value) {
  11816. if (key === undefined || key === null) { //不传参数,清空列表
  11817. serverParam = {};
  11818. } else if (utils.isString(key)) { //传入键值
  11819. if(value === undefined || value === null) {
  11820. delete serverParam[key];
  11821. } else {
  11822. serverParam[key] = value;
  11823. }
  11824. } else if (utils.isObject(key)) { //传入对象,覆盖列表项
  11825. utils.extend(serverParam, key, true);
  11826. } else if (utils.isFunction(key)){ //传入函数,添加列表项
  11827. utils.extend(serverParam, key(), true);
  11828. }
  11829. },
  11830. queryCommandValue: function(){
  11831. return serverParam || {};
  11832. }
  11833. }
  11834. }
  11835. }
  11836. });
  11837. // ui/ui.js
  11838. var baidu = baidu || {};
  11839. baidu.editor = baidu.editor || {};
  11840. UE.ui = baidu.editor.ui = {};
  11841. // ui/uiutils.js
  11842. (function (){
  11843. var browser = baidu.editor.browser,
  11844. domUtils = baidu.editor.dom.domUtils;
  11845. var magic = '$EDITORUI';
  11846. var root = window[magic] = {};
  11847. var uidMagic = 'ID' + magic;
  11848. var uidCount = 0;
  11849. var uiUtils = baidu.editor.ui.uiUtils = {
  11850. uid: function (obj){
  11851. return (obj ? obj[uidMagic] || (obj[uidMagic] = ++ uidCount) : ++ uidCount);
  11852. },
  11853. hook: function ( fn, callback ) {
  11854. var dg;
  11855. if (fn && fn._callbacks) {
  11856. dg = fn;
  11857. } else {
  11858. dg = function (){
  11859. var q;
  11860. if (fn) {
  11861. q = fn.apply(this, arguments);
  11862. }
  11863. var callbacks = dg._callbacks;
  11864. var k = callbacks.length;
  11865. while (k --) {
  11866. var r = callbacks[k].apply(this, arguments);
  11867. if (q === undefined) {
  11868. q = r;
  11869. }
  11870. }
  11871. return q;
  11872. };
  11873. dg._callbacks = [];
  11874. }
  11875. dg._callbacks.push(callback);
  11876. return dg;
  11877. },
  11878. createElementByHtml: function (html){
  11879. var el = document.createElement('div');
  11880. el.innerHTML = html;
  11881. el = el.firstChild;
  11882. el.parentNode.removeChild(el);
  11883. return el;
  11884. },
  11885. getViewportElement: function (){
  11886. return (browser.ie && browser.quirks) ?
  11887. document.body : document.documentElement;
  11888. },
  11889. getClientRect: function (element){
  11890. var bcr;
  11891. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  11892. try{
  11893. bcr = element.getBoundingClientRect();
  11894. }catch(e){
  11895. bcr={left:0,top:0,height:0,width:0}
  11896. }
  11897. var rect = {
  11898. left: Math.round(bcr.left),
  11899. top: Math.round(bcr.top),
  11900. height: Math.round(bcr.bottom - bcr.top),
  11901. width: Math.round(bcr.right - bcr.left)
  11902. };
  11903. var doc;
  11904. while ((doc = element.ownerDocument) !== document &&
  11905. (element = domUtils.getWindow(doc).frameElement)) {
  11906. bcr = element.getBoundingClientRect();
  11907. rect.left += bcr.left;
  11908. rect.top += bcr.top;
  11909. }
  11910. rect.bottom = rect.top + rect.height;
  11911. rect.right = rect.left + rect.width;
  11912. return rect;
  11913. },
  11914. getViewportRect: function (){
  11915. var viewportEl = uiUtils.getViewportElement();
  11916. var width = (window.innerWidth || viewportEl.clientWidth) | 0;
  11917. var height = (window.innerHeight ||viewportEl.clientHeight) | 0;
  11918. return {
  11919. left: 0,
  11920. top: 0,
  11921. height: height,
  11922. width: width,
  11923. bottom: height,
  11924. right: width
  11925. };
  11926. },
  11927. setViewportOffset: function (element, offset){
  11928. var rect;
  11929. var fixedLayer = uiUtils.getFixedLayer();
  11930. if (element.parentNode === fixedLayer) {
  11931. element.style.left = offset.left + 'px';
  11932. element.style.top = offset.top + 'px';
  11933. } else {
  11934. domUtils.setViewportOffset(element, offset);
  11935. }
  11936. },
  11937. getEventOffset: function (evt){
  11938. var el = evt.target || evt.srcElement;
  11939. var rect = uiUtils.getClientRect(el);
  11940. var offset = uiUtils.getViewportOffsetByEvent(evt);
  11941. return {
  11942. left: offset.left - rect.left,
  11943. top: offset.top - rect.top
  11944. };
  11945. },
  11946. getViewportOffsetByEvent: function (evt){
  11947. var el = evt.target || evt.srcElement;
  11948. var frameEl = domUtils.getWindow(el).frameElement;
  11949. var offset = {
  11950. left: evt.clientX,
  11951. top: evt.clientY
  11952. };
  11953. if (frameEl && el.ownerDocument !== document) {
  11954. var rect = uiUtils.getClientRect(frameEl);
  11955. offset.left += rect.left;
  11956. offset.top += rect.top;
  11957. }
  11958. return offset;
  11959. },
  11960. setGlobal: function (id, obj){
  11961. root[id] = obj;
  11962. return magic + '["' + id + '"]';
  11963. },
  11964. unsetGlobal: function (id){
  11965. delete root[id];
  11966. },
  11967. copyAttributes: function (tgt, src){
  11968. var attributes = src.attributes;
  11969. var k = attributes.length;
  11970. while (k --) {
  11971. var attrNode = attributes[k];
  11972. if ( attrNode.nodeName != 'style' && attrNode.nodeName != 'class' && (!browser.ie || attrNode.specified) ) {
  11973. tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue);
  11974. }
  11975. }
  11976. if (src.className) {
  11977. domUtils.addClass(tgt,src.className);
  11978. }
  11979. if (src.style.cssText) {
  11980. tgt.style.cssText += ';' + src.style.cssText;
  11981. }
  11982. },
  11983. removeStyle: function (el, styleName){
  11984. if (el.style.removeProperty) {
  11985. el.style.removeProperty(styleName);
  11986. } else if (el.style.removeAttribute) {
  11987. el.style.removeAttribute(styleName);
  11988. } else throw '';
  11989. },
  11990. contains: function (elA, elB){
  11991. return elA && elB && (elA === elB ? false : (
  11992. elA.contains ? elA.contains(elB) :
  11993. elA.compareDocumentPosition(elB) & 16
  11994. ));
  11995. },
  11996. startDrag: function (evt, callbacks,doc){
  11997. var doc = doc || document;
  11998. var startX = evt.clientX;
  11999. var startY = evt.clientY;
  12000. function handleMouseMove(evt){
  12001. var x = evt.clientX - startX;
  12002. var y = evt.clientY - startY;
  12003. callbacks.ondragmove(x, y,evt);
  12004. if (evt.stopPropagation) {
  12005. evt.stopPropagation();
  12006. } else {
  12007. evt.cancelBubble = true;
  12008. }
  12009. }
  12010. if (doc.addEventListener) {
  12011. function handleMouseUp(evt){
  12012. doc.removeEventListener('mousemove', handleMouseMove, true);
  12013. doc.removeEventListener('mouseup', handleMouseUp, true);
  12014. window.removeEventListener('mouseup', handleMouseUp, true);
  12015. callbacks.ondragstop();
  12016. }
  12017. doc.addEventListener('mousemove', handleMouseMove, true);
  12018. doc.addEventListener('mouseup', handleMouseUp, true);
  12019. window.addEventListener('mouseup', handleMouseUp, true);
  12020. evt.preventDefault();
  12021. } else {
  12022. var elm = evt.srcElement;
  12023. elm.setCapture();
  12024. function releaseCaptrue(){
  12025. elm.releaseCapture();
  12026. elm.detachEvent('onmousemove', handleMouseMove);
  12027. elm.detachEvent('onmouseup', releaseCaptrue);
  12028. elm.detachEvent('onlosecaptrue', releaseCaptrue);
  12029. callbacks.ondragstop();
  12030. }
  12031. elm.attachEvent('onmousemove', handleMouseMove);
  12032. elm.attachEvent('onmouseup', releaseCaptrue);
  12033. elm.attachEvent('onlosecaptrue', releaseCaptrue);
  12034. evt.returnValue = false;
  12035. }
  12036. callbacks.ondragstart();
  12037. },
  12038. getFixedLayer: function (){
  12039. var layer = document.getElementById('edui_fixedlayer');
  12040. if (layer == null) {
  12041. layer = document.createElement('div');
  12042. layer.id = 'edui_fixedlayer';
  12043. document.body.appendChild(layer);
  12044. if (browser.ie && browser.version <= 8) {
  12045. layer.style.position = 'absolute';
  12046. bindFixedLayer();
  12047. setTimeout(updateFixedOffset);
  12048. } else {
  12049. layer.style.position = 'fixed';
  12050. }
  12051. layer.style.left = '0';
  12052. layer.style.top = '0';
  12053. layer.style.width = '0';
  12054. layer.style.height = '0';
  12055. }
  12056. return layer;
  12057. },
  12058. makeUnselectable: function (element){
  12059. if (browser.opera || (browser.ie && browser.version < 9)) {
  12060. element.unselectable = 'on';
  12061. if (element.hasChildNodes()) {
  12062. for (var i=0; i<element.childNodes.length; i++) {
  12063. if (element.childNodes[i].nodeType == 1) {
  12064. uiUtils.makeUnselectable(element.childNodes[i]);
  12065. }
  12066. }
  12067. }
  12068. } else {
  12069. if (element.style.MozUserSelect !== undefined) {
  12070. element.style.MozUserSelect = 'none';
  12071. } else if (element.style.WebkitUserSelect !== undefined) {
  12072. element.style.WebkitUserSelect = 'none';
  12073. } else if (element.style.KhtmlUserSelect !== undefined) {
  12074. element.style.KhtmlUserSelect = 'none';
  12075. }
  12076. }
  12077. }
  12078. };
  12079. function updateFixedOffset(){
  12080. var layer = document.getElementById('edui_fixedlayer');
  12081. uiUtils.setViewportOffset(layer, {
  12082. left: 0,
  12083. top: 0
  12084. });
  12085. // layer.style.display = 'none';
  12086. // layer.style.display = 'block';
  12087. //#trace: 1354
  12088. // setTimeout(updateFixedOffset);
  12089. }
  12090. function bindFixedLayer(adjOffset){
  12091. domUtils.on(window, 'scroll', updateFixedOffset);
  12092. domUtils.on(window, 'resize', baidu.editor.utils.defer(updateFixedOffset, 0, true));
  12093. }
  12094. })();
  12095. // ui/uibase.js
  12096. (function () {
  12097. var utils = baidu.editor.utils,
  12098. uiUtils = baidu.editor.ui.uiUtils,
  12099. EventBase = baidu.editor.EventBase,
  12100. UIBase = baidu.editor.ui.UIBase = function () {
  12101. };
  12102. UIBase.prototype = {
  12103. className:'',
  12104. uiName:'',
  12105. initOptions:function (options) {
  12106. var me = this;
  12107. for (var k in options) {
  12108. me[k] = options[k];
  12109. }
  12110. this.id = this.id || 'edui' + uiUtils.uid();
  12111. },
  12112. initUIBase:function () {
  12113. this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this));
  12114. },
  12115. render:function (holder) {
  12116. var html = this.renderHtml();
  12117. var el = uiUtils.createElementByHtml(html);
  12118. //by xuheng 给每个node添加class
  12119. var list = domUtils.getElementsByTagName(el, "*");
  12120. var theme = "edui-" + (this.theme || this.editor.options.theme);
  12121. var layer = document.getElementById('edui_fixedlayer');
  12122. for (var i = 0, node; node = list[i++];) {
  12123. domUtils.addClass(node, theme);
  12124. }
  12125. domUtils.addClass(el, theme);
  12126. if(layer){
  12127. layer.className="";
  12128. domUtils.addClass(layer,theme);
  12129. }
  12130. var seatEl = this.getDom();
  12131. if (seatEl != null) {
  12132. seatEl.parentNode.replaceChild(el, seatEl);
  12133. uiUtils.copyAttributes(el, seatEl);
  12134. } else {
  12135. if (typeof holder == 'string') {
  12136. holder = document.getElementById(holder);
  12137. }
  12138. holder = holder || uiUtils.getFixedLayer();
  12139. domUtils.addClass(holder, theme);
  12140. holder.appendChild(el);
  12141. }
  12142. this.postRender();
  12143. },
  12144. getDom:function (name) {
  12145. if (!name) {
  12146. return document.getElementById(this.id);
  12147. } else {
  12148. return document.getElementById(this.id + '_' + name);
  12149. }
  12150. },
  12151. postRender:function () {
  12152. this.fireEvent('postrender');
  12153. },
  12154. getHtmlTpl:function () {
  12155. return '';
  12156. },
  12157. formatHtml:function (tpl) {
  12158. var prefix = 'edui-' + this.uiName;
  12159. return (tpl
  12160. .replace(/##/g, this.id)
  12161. .replace(/%%-/g, this.uiName ? prefix + '-' : '')
  12162. .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
  12163. .replace(/\$\$/g, this._globalKey));
  12164. },
  12165. renderHtml:function () {
  12166. return this.formatHtml(this.getHtmlTpl());
  12167. },
  12168. dispose:function () {
  12169. var box = this.getDom();
  12170. if (box) baidu.editor.dom.domUtils.remove(box);
  12171. uiUtils.unsetGlobal(this.id);
  12172. }
  12173. };
  12174. utils.inherits(UIBase, EventBase);
  12175. })();
  12176. // ui/separator.js
  12177. (function (){
  12178. var utils = baidu.editor.utils,
  12179. UIBase = baidu.editor.ui.UIBase,
  12180. Separator = baidu.editor.ui.Separator = function (options){
  12181. this.initOptions(options);
  12182. this.initSeparator();
  12183. };
  12184. Separator.prototype = {
  12185. uiName: 'separator',
  12186. initSeparator: function (){
  12187. this.initUIBase();
  12188. },
  12189. getHtmlTpl: function (){
  12190. return '<div id="##" class="edui-box %%"></div>';
  12191. }
  12192. };
  12193. utils.inherits(Separator, UIBase);
  12194. })();
  12195. // ui/mask.js
  12196. ///import core
  12197. ///import uicore
  12198. (function (){
  12199. var utils = baidu.editor.utils,
  12200. domUtils = baidu.editor.dom.domUtils,
  12201. UIBase = baidu.editor.ui.UIBase,
  12202. uiUtils = baidu.editor.ui.uiUtils;
  12203. var Mask = baidu.editor.ui.Mask = function (options){
  12204. this.initOptions(options);
  12205. this.initUIBase();
  12206. };
  12207. Mask.prototype = {
  12208. getHtmlTpl: function (){
  12209. return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>';
  12210. },
  12211. postRender: function (){
  12212. var me = this;
  12213. domUtils.on(window, 'resize', function (){
  12214. setTimeout(function (){
  12215. if (!me.isHidden()) {
  12216. me._fill();
  12217. }
  12218. });
  12219. });
  12220. },
  12221. show: function (zIndex){
  12222. this._fill();
  12223. this.getDom().style.display = '';
  12224. this.getDom().style.zIndex = zIndex;
  12225. },
  12226. hide: function (){
  12227. this.getDom().style.display = 'none';
  12228. this.getDom().style.zIndex = '';
  12229. },
  12230. isHidden: function (){
  12231. return this.getDom().style.display == 'none';
  12232. },
  12233. _onMouseDown: function (){
  12234. return false;
  12235. },
  12236. _onClick: function (e, target){
  12237. this.fireEvent('click', e, target);
  12238. },
  12239. _fill: function (){
  12240. var el = this.getDom();
  12241. var vpRect = uiUtils.getViewportRect();
  12242. el.style.width = vpRect.width + 'px';
  12243. el.style.height = vpRect.height + 'px';
  12244. }
  12245. };
  12246. utils.inherits(Mask, UIBase);
  12247. })();
  12248. // ui/popup.js
  12249. ///import core
  12250. ///import uicore
  12251. (function () {
  12252. var utils = baidu.editor.utils,
  12253. uiUtils = baidu.editor.ui.uiUtils,
  12254. domUtils = baidu.editor.dom.domUtils,
  12255. UIBase = baidu.editor.ui.UIBase,
  12256. Popup = baidu.editor.ui.Popup = function (options){
  12257. this.initOptions(options);
  12258. this.initPopup();
  12259. };
  12260. var allPopups = [];
  12261. function closeAllPopup( evt,el ){
  12262. for ( var i = 0; i < allPopups.length; i++ ) {
  12263. var pop = allPopups[i];
  12264. if (!pop.isHidden()) {
  12265. if (pop.queryAutoHide(el) !== false) {
  12266. if(evt&&/scroll/ig.test(evt.type)&&pop.className=="edui-wordpastepop") return;
  12267. pop.hide();
  12268. }
  12269. }
  12270. }
  12271. if(allPopups.length)
  12272. pop.editor.fireEvent("afterhidepop");
  12273. }
  12274. Popup.postHide = closeAllPopup;
  12275. var ANCHOR_CLASSES = ['edui-anchor-topleft','edui-anchor-topright',
  12276. 'edui-anchor-bottomleft','edui-anchor-bottomright'];
  12277. Popup.prototype = {
  12278. SHADOW_RADIUS: 5,
  12279. content: null,
  12280. _hidden: false,
  12281. autoRender: true,
  12282. canSideLeft: true,
  12283. canSideUp: true,
  12284. initPopup: function (){
  12285. this.initUIBase();
  12286. allPopups.push( this );
  12287. },
  12288. getHtmlTpl: function (){
  12289. return '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
  12290. ' <div id="##_body" class="edui-popup-body">' +
  12291. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  12292. ' <div class="edui-shadow"></div>' +
  12293. ' <div id="##_content" class="edui-popup-content">' +
  12294. this.getContentHtmlTpl() +
  12295. ' </div>' +
  12296. ' </div>' +
  12297. '</div>';
  12298. },
  12299. getContentHtmlTpl: function (){
  12300. if(this.content){
  12301. if (typeof this.content == 'string') {
  12302. return this.content;
  12303. }
  12304. return this.content.renderHtml();
  12305. }else{
  12306. return ''
  12307. }
  12308. },
  12309. _UIBase_postRender: UIBase.prototype.postRender,
  12310. postRender: function (){
  12311. if (this.content instanceof UIBase) {
  12312. this.content.postRender();
  12313. }
  12314. //捕获鼠标滚轮
  12315. if( this.captureWheel && !this.captured ) {
  12316. this.captured = true;
  12317. var winHeight = ( document.documentElement.clientHeight || document.body.clientHeight ) - 80,
  12318. _height = this.getDom().offsetHeight,
  12319. _top = uiUtils.getClientRect( this.combox.getDom() ).top,
  12320. content = this.getDom('content'),
  12321. ifr = this.getDom('body').getElementsByTagName('iframe'),
  12322. me = this;
  12323. ifr.length && ( ifr = ifr[0] );
  12324. while( _top + _height > winHeight ) {
  12325. _height -= 30;
  12326. }
  12327. content.style.height = _height + 'px';
  12328. //同步更改iframe高度
  12329. ifr && ( ifr.style.height = _height + 'px' );
  12330. //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
  12331. if( window.XMLHttpRequest ) {
  12332. domUtils.on( content, ( 'onmousewheel' in document.body ) ? 'mousewheel' :'DOMMouseScroll' , function(e){
  12333. if(e.preventDefault) {
  12334. e.preventDefault();
  12335. } else {
  12336. e.returnValue = false;
  12337. }
  12338. if( e.wheelDelta ) {
  12339. content.scrollTop -= ( e.wheelDelta / 120 )*60;
  12340. } else {
  12341. content.scrollTop -= ( e.detail / -3 )*60;
  12342. }
  12343. });
  12344. } else {
  12345. //ie6
  12346. domUtils.on( this.getDom(), 'mousewheel' , function(e){
  12347. e.returnValue = false;
  12348. me.getDom('content').scrollTop -= ( e.wheelDelta / 120 )*60;
  12349. });
  12350. }
  12351. }
  12352. this.fireEvent('postRenderAfter');
  12353. this.hide(true);
  12354. this._UIBase_postRender();
  12355. },
  12356. _doAutoRender: function (){
  12357. if (!this.getDom() && this.autoRender) {
  12358. this.render();
  12359. }
  12360. },
  12361. mesureSize: function (){
  12362. var box = this.getDom('content');
  12363. return uiUtils.getClientRect(box);
  12364. },
  12365. fitSize: function (){
  12366. if( this.captureWheel && this.sized ) {
  12367. return this.__size;
  12368. }
  12369. this.sized = true;
  12370. var popBodyEl = this.getDom('body');
  12371. popBodyEl.style.width = '';
  12372. popBodyEl.style.height = '';
  12373. var size = this.mesureSize();
  12374. if( this.captureWheel ) {
  12375. popBodyEl.style.width = -(-20 -size.width) + 'px';
  12376. var height = parseInt( this.getDom('content').style.height, 10 );
  12377. !window.isNaN( height ) && ( size.height = height );
  12378. } else {
  12379. popBodyEl.style.width = size.width + 'px';
  12380. }
  12381. popBodyEl.style.height = size.height + 'px';
  12382. this.__size = size;
  12383. this.captureWheel && (this.getDom('content').style.overflow = 'auto');
  12384. return size;
  12385. },
  12386. showAnchor: function ( element, hoz ){
  12387. this.showAnchorRect( uiUtils.getClientRect( element ), hoz );
  12388. },
  12389. showAnchorRect: function ( rect, hoz, adj ){
  12390. this._doAutoRender();
  12391. var vpRect = uiUtils.getViewportRect();
  12392. this.getDom().style.visibility = 'hidden';
  12393. this._show();
  12394. var popSize = this.fitSize();
  12395. var sideLeft, sideUp, left, top;
  12396. if (hoz) {
  12397. sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
  12398. sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
  12399. left = (sideLeft ? rect.left - popSize.width : rect.right);
  12400. top = (sideUp ? rect.bottom - popSize.height : rect.top);
  12401. } else {
  12402. sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
  12403. sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
  12404. left = (sideLeft ? rect.right - popSize.width : rect.left);
  12405. top = (sideUp ? rect.top - popSize.height : rect.bottom);
  12406. }
  12407. var popEl = this.getDom();
  12408. uiUtils.setViewportOffset(popEl, {
  12409. left: left,
  12410. top: top
  12411. });
  12412. domUtils.removeClasses(popEl, ANCHOR_CLASSES);
  12413. popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)];
  12414. if(this.editor){
  12415. popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10;
  12416. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1;
  12417. }
  12418. this.getDom().style.visibility = 'visible';
  12419. },
  12420. showAt: function (offset) {
  12421. var left = offset.left;
  12422. var top = offset.top;
  12423. var rect = {
  12424. left: left,
  12425. top: top,
  12426. right: left,
  12427. bottom: top,
  12428. height: 0,
  12429. width: 0
  12430. };
  12431. this.showAnchorRect(rect, false, true);
  12432. },
  12433. _show: function (){
  12434. if (this._hidden) {
  12435. var box = this.getDom();
  12436. box.style.display = '';
  12437. this._hidden = false;
  12438. // if (box.setActive) {
  12439. // box.setActive();
  12440. // }
  12441. this.fireEvent('show');
  12442. }
  12443. },
  12444. isHidden: function (){
  12445. return this._hidden;
  12446. },
  12447. show: function (){
  12448. this._doAutoRender();
  12449. this._show();
  12450. },
  12451. hide: function (notNofity){
  12452. if (!this._hidden && this.getDom()) {
  12453. this.getDom().style.display = 'none';
  12454. this._hidden = true;
  12455. if (!notNofity) {
  12456. this.fireEvent('hide');
  12457. }
  12458. }
  12459. },
  12460. queryAutoHide: function (el){
  12461. return !el || !uiUtils.contains(this.getDom(), el);
  12462. }
  12463. };
  12464. utils.inherits(Popup, UIBase);
  12465. domUtils.on( document, 'mousedown', function ( evt ) {
  12466. var el = evt.target || evt.srcElement;
  12467. closeAllPopup( evt,el );
  12468. } );
  12469. domUtils.on( window, 'scroll', function (evt,el) {
  12470. closeAllPopup( evt,el );
  12471. } );
  12472. })();
  12473. // ui/colorpicker.js
  12474. ///import core
  12475. ///import uicore
  12476. (function (){
  12477. var utils = baidu.editor.utils,
  12478. UIBase = baidu.editor.ui.UIBase,
  12479. ColorPicker = baidu.editor.ui.ColorPicker = function (options){
  12480. this.initOptions(options);
  12481. this.noColorText = this.noColorText || this.editor.getLang("clearColor");
  12482. this.initUIBase();
  12483. };
  12484. ColorPicker.prototype = {
  12485. getHtmlTpl: function (){
  12486. return genColorPicker(this.noColorText,this.editor);
  12487. },
  12488. _onTableClick: function (evt){
  12489. var tgt = evt.target || evt.srcElement;
  12490. var color = tgt.getAttribute('data-color');
  12491. if (color) {
  12492. this.fireEvent('pickcolor', color);
  12493. }
  12494. },
  12495. _onTableOver: function (evt){
  12496. var tgt = evt.target || evt.srcElement;
  12497. var color = tgt.getAttribute('data-color');
  12498. if (color) {
  12499. this.getDom('preview').style.backgroundColor = color;
  12500. }
  12501. },
  12502. _onTableOut: function (){
  12503. this.getDom('preview').style.backgroundColor = '';
  12504. },
  12505. _onPickNoColor: function (){
  12506. this.fireEvent('picknocolor');
  12507. }
  12508. };
  12509. utils.inherits(ColorPicker, UIBase);
  12510. var COLORS = (
  12511. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  12512. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  12513. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  12514. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  12515. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  12516. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  12517. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  12518. function genColorPicker(noColorText,editor){
  12519. var html = '<div id="##" class="edui-colorpicker %%">' +
  12520. '<div class="edui-colorpicker-topbar edui-clearfix">' +
  12521. '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
  12522. '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">'+ noColorText +'</div>' +
  12523. '</div>' +
  12524. '<table class="edui-box" style="border-collapse: collapse;" onmouseover="$$._onTableOver(event, this);" onmouseout="$$._onTableOut(event, this);" onclick="return $$._onTableClick(event, this);" cellspacing="0" cellpadding="0">' +
  12525. '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;padding-top: 2px"><td colspan="10">'+editor.getLang("themeColor")+'</td> </tr>'+
  12526. '<tr class="edui-colorpicker-tablefirstrow" >';
  12527. for (var i=0; i<COLORS.length; i++) {
  12528. if (i && i%10 === 0) {
  12529. html += '</tr>'+(i==60?'<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;"><td colspan="10">'+editor.getLang("standardColor")+'</td></tr>':'')+'<tr'+(i==60?' class="edui-colorpicker-tablefirstrow"':'')+'>';
  12530. }
  12531. html += i<70 ? '<td style="padding: 0 2px;"><a hidefocus title="'+COLORS[i]+'" onclick="return false;" href="javascript:" unselectable="on" class="edui-box edui-colorpicker-colorcell"' +
  12532. ' data-color="#'+ COLORS[i] +'"'+
  12533. ' style="background-color:#'+ COLORS[i] +';border:solid #ccc;'+
  12534. (i<10 || i>=60?'border-width:1px;':
  12535. i>=10&&i<20?'border-width:1px 1px 0 1px;':
  12536. 'border-width:0 1px 0 1px;')+
  12537. '"' +
  12538. '></a></td>':'';
  12539. }
  12540. html += '</tr></table></div>';
  12541. return html;
  12542. }
  12543. })();
  12544. // ui/tablepicker.js
  12545. ///import core
  12546. ///import uicore
  12547. (function (){
  12548. var utils = baidu.editor.utils,
  12549. uiUtils = baidu.editor.ui.uiUtils,
  12550. UIBase = baidu.editor.ui.UIBase;
  12551. var TablePicker = baidu.editor.ui.TablePicker = function (options){
  12552. this.initOptions(options);
  12553. this.initTablePicker();
  12554. };
  12555. TablePicker.prototype = {
  12556. defaultNumRows: 10,
  12557. defaultNumCols: 10,
  12558. maxNumRows: 20,
  12559. maxNumCols: 20,
  12560. numRows: 10,
  12561. numCols: 10,
  12562. lengthOfCellSide: 22,
  12563. initTablePicker: function (){
  12564. this.initUIBase();
  12565. },
  12566. getHtmlTpl: function (){
  12567. var me = this;
  12568. return '<div id="##" class="edui-tablepicker %%">' +
  12569. '<div class="edui-tablepicker-body">' +
  12570. '<div class="edui-infoarea">' +
  12571. '<span id="##_label" class="edui-label"></span>' +
  12572. '</div>' +
  12573. '<div class="edui-pickarea"' +
  12574. ' onmousemove="$$._onMouseMove(event, this);"' +
  12575. ' onmouseover="$$._onMouseOver(event, this);"' +
  12576. ' onmouseout="$$._onMouseOut(event, this);"' +
  12577. ' onclick="$$._onClick(event, this);"' +
  12578. '>' +
  12579. '<div id="##_overlay" class="edui-overlay"></div>' +
  12580. '</div>' +
  12581. '</div>' +
  12582. '</div>';
  12583. },
  12584. _UIBase_render: UIBase.prototype.render,
  12585. render: function (holder){
  12586. this._UIBase_render(holder);
  12587. this.getDom('label').innerHTML = '0'+this.editor.getLang("t_row")+' x 0'+this.editor.getLang("t_col");
  12588. },
  12589. _track: function (numCols, numRows){
  12590. var style = this.getDom('overlay').style;
  12591. var sideLen = this.lengthOfCellSide;
  12592. style.width = numCols * sideLen + 'px';
  12593. style.height = numRows * sideLen + 'px';
  12594. var label = this.getDom('label');
  12595. label.innerHTML = numCols +this.editor.getLang("t_col")+' x ' + numRows + this.editor.getLang("t_row");
  12596. this.numCols = numCols;
  12597. this.numRows = numRows;
  12598. },
  12599. _onMouseOver: function (evt, el){
  12600. var rel = evt.relatedTarget || evt.fromElement;
  12601. if (!uiUtils.contains(el, rel) && el !== rel) {
  12602. this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row");
  12603. this.getDom('overlay').style.visibility = '';
  12604. }
  12605. },
  12606. _onMouseOut: function (evt, el){
  12607. var rel = evt.relatedTarget || evt.toElement;
  12608. if (!uiUtils.contains(el, rel) && el !== rel) {
  12609. this.getDom('label').innerHTML = '0'+this.editor.getLang("t_col")+' x 0'+this.editor.getLang("t_row");
  12610. this.getDom('overlay').style.visibility = 'hidden';
  12611. }
  12612. },
  12613. _onMouseMove: function (evt, el){
  12614. var style = this.getDom('overlay').style;
  12615. var offset = uiUtils.getEventOffset(evt);
  12616. var sideLen = this.lengthOfCellSide;
  12617. var numCols = Math.ceil(offset.left / sideLen);
  12618. var numRows = Math.ceil(offset.top / sideLen);
  12619. this._track(numCols, numRows);
  12620. },
  12621. _onClick: function (){
  12622. this.fireEvent('picktable', this.numCols, this.numRows);
  12623. }
  12624. };
  12625. utils.inherits(TablePicker, UIBase);
  12626. })();
  12627. // ui/stateful.js
  12628. (function (){
  12629. var browser = baidu.editor.browser,
  12630. domUtils = baidu.editor.dom.domUtils,
  12631. uiUtils = baidu.editor.ui.uiUtils;
  12632. var TPL_STATEFUL = 'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
  12633. ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
  12634. ( browser.ie ? (
  12635. ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
  12636. ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"' )
  12637. : (
  12638. ' onmouseover="$$.Stateful_onMouseOver(event, this);"' +
  12639. ' onmouseout="$$.Stateful_onMouseOut(event, this);"' ));
  12640. baidu.editor.ui.Stateful = {
  12641. alwalysHoverable: false,
  12642. target:null,//目标元素和this指向dom不一样
  12643. Stateful_init: function (){
  12644. this._Stateful_dGetHtmlTpl = this.getHtmlTpl;
  12645. this.getHtmlTpl = this.Stateful_getHtmlTpl;
  12646. },
  12647. Stateful_getHtmlTpl: function (){
  12648. var tpl = this._Stateful_dGetHtmlTpl();
  12649. // 使用function避免$转义
  12650. return tpl.replace(/stateful/g, function (){ return TPL_STATEFUL; });
  12651. },
  12652. Stateful_onMouseEnter: function (evt, el){
  12653. this.target=el;
  12654. if (!this.isDisabled() || this.alwalysHoverable) {
  12655. this.addState('hover');
  12656. this.fireEvent('over');
  12657. }
  12658. },
  12659. Stateful_onMouseLeave: function (evt, el){
  12660. if (!this.isDisabled() || this.alwalysHoverable) {
  12661. this.removeState('hover');
  12662. this.removeState('active');
  12663. this.fireEvent('out');
  12664. }
  12665. },
  12666. Stateful_onMouseOver: function (evt, el){
  12667. var rel = evt.relatedTarget;
  12668. if (!uiUtils.contains(el, rel) && el !== rel) {
  12669. this.Stateful_onMouseEnter(evt, el);
  12670. }
  12671. },
  12672. Stateful_onMouseOut: function (evt, el){
  12673. var rel = evt.relatedTarget;
  12674. if (!uiUtils.contains(el, rel) && el !== rel) {
  12675. this.Stateful_onMouseLeave(evt, el);
  12676. }
  12677. },
  12678. Stateful_onMouseDown: function (evt, el){
  12679. if (!this.isDisabled()) {
  12680. this.addState('active');
  12681. }
  12682. },
  12683. Stateful_onMouseUp: function (evt, el){
  12684. if (!this.isDisabled()) {
  12685. this.removeState('active');
  12686. }
  12687. },
  12688. Stateful_postRender: function (){
  12689. if (this.disabled && !this.hasState('disabled')) {
  12690. this.addState('disabled');
  12691. }
  12692. },
  12693. hasState: function (state){
  12694. return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state);
  12695. },
  12696. addState: function (state){
  12697. if (!this.hasState(state)) {
  12698. this.getStateDom().className += ' edui-state-' + state;
  12699. }
  12700. },
  12701. removeState: function (state){
  12702. if (this.hasState(state)) {
  12703. domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state]);
  12704. }
  12705. },
  12706. getStateDom: function (){
  12707. return this.getDom('state');
  12708. },
  12709. isChecked: function (){
  12710. return this.hasState('checked');
  12711. },
  12712. setChecked: function (checked){
  12713. if (!this.isDisabled() && checked) {
  12714. this.addState('checked');
  12715. } else {
  12716. this.removeState('checked');
  12717. }
  12718. },
  12719. isDisabled: function (){
  12720. return this.hasState('disabled');
  12721. },
  12722. setDisabled: function (disabled){
  12723. if (disabled) {
  12724. this.removeState('hover');
  12725. this.removeState('checked');
  12726. this.removeState('active');
  12727. this.addState('disabled');
  12728. } else {
  12729. this.removeState('disabled');
  12730. }
  12731. }
  12732. };
  12733. })();
  12734. // ui/button.js
  12735. ///import core
  12736. ///import uicore
  12737. ///import ui/stateful.js
  12738. (function (){
  12739. var utils = baidu.editor.utils,
  12740. UIBase = baidu.editor.ui.UIBase,
  12741. Stateful = baidu.editor.ui.Stateful,
  12742. Button = baidu.editor.ui.Button = function (options){
  12743. if(options.name){
  12744. var btnName = options.name;
  12745. var cssRules = options.cssRules;
  12746. if(!options.className){
  12747. options.className = 'edui-for-' + btnName;
  12748. }
  12749. options.cssRules = '.edui-default .edui-for-'+ btnName +' .edui-icon {'+ cssRules +'}'
  12750. }
  12751. this.initOptions(options);
  12752. this.initButton();
  12753. };
  12754. Button.prototype = {
  12755. uiName: 'button',
  12756. label: '',
  12757. title: '',
  12758. showIcon: true,
  12759. showText: true,
  12760. cssRules:'',
  12761. initButton: function (){
  12762. this.initUIBase();
  12763. this.Stateful_init();
  12764. if(this.cssRules){
  12765. utils.cssRule('edui-customize-'+this.name+'-style',this.cssRules);
  12766. }
  12767. },
  12768. getHtmlTpl: function (){
  12769. return '<div id="##" class="edui-box %%">' +
  12770. '<div id="##_state" stateful>' +
  12771. '<div class="%%-wrap"><div id="##_body" unselectable="on" ' + (this.title ? 'title="' + this.title + '"' : '') +
  12772. ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
  12773. (this.showIcon ? '<div class="edui-box edui-icon"></div>' : '') +
  12774. (this.showText ? '<div class="edui-box edui-label">' + this.label + '</div>' : '') +
  12775. '</div>' +
  12776. '</div>' +
  12777. '</div></div>';
  12778. },
  12779. postRender: function (){
  12780. this.Stateful_postRender();
  12781. this.setDisabled(this.disabled)
  12782. },
  12783. _onMouseDown: function (e){
  12784. var target = e.target || e.srcElement,
  12785. tagName = target && target.tagName && target.tagName.toLowerCase();
  12786. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  12787. return false;
  12788. }
  12789. },
  12790. _onClick: function (){
  12791. if (!this.isDisabled()) {
  12792. this.fireEvent('click');
  12793. }
  12794. },
  12795. setTitle: function(text){
  12796. var label = this.getDom('label');
  12797. label.innerHTML = text;
  12798. }
  12799. };
  12800. utils.inherits(Button, UIBase);
  12801. utils.extend(Button.prototype, Stateful);
  12802. })();
  12803. // ui/splitbutton.js
  12804. ///import core
  12805. ///import uicore
  12806. ///import ui/stateful.js
  12807. (function (){
  12808. var utils = baidu.editor.utils,
  12809. uiUtils = baidu.editor.ui.uiUtils,
  12810. domUtils = baidu.editor.dom.domUtils,
  12811. UIBase = baidu.editor.ui.UIBase,
  12812. Stateful = baidu.editor.ui.Stateful,
  12813. SplitButton = baidu.editor.ui.SplitButton = function (options){
  12814. this.initOptions(options);
  12815. this.initSplitButton();
  12816. };
  12817. SplitButton.prototype = {
  12818. popup: null,
  12819. uiName: 'splitbutton',
  12820. title: '',
  12821. initSplitButton: function (){
  12822. this.initUIBase();
  12823. this.Stateful_init();
  12824. var me = this;
  12825. if (this.popup != null) {
  12826. var popup = this.popup;
  12827. this.popup = null;
  12828. this.setPopup(popup);
  12829. }
  12830. },
  12831. _UIBase_postRender: UIBase.prototype.postRender,
  12832. postRender: function (){
  12833. this.Stateful_postRender();
  12834. this._UIBase_postRender();
  12835. },
  12836. setPopup: function (popup){
  12837. if (this.popup === popup) return;
  12838. if (this.popup != null) {
  12839. this.popup.dispose();
  12840. }
  12841. popup.addListener('show', utils.bind(this._onPopupShow, this));
  12842. popup.addListener('hide', utils.bind(this._onPopupHide, this));
  12843. popup.addListener('postrender', utils.bind(function (){
  12844. popup.getDom('body').appendChild(
  12845. uiUtils.createElementByHtml('<div id="' +
  12846. this.popup.id + '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
  12847. (uiUtils.getClientRect(this.getDom()).width + 20) + 'px"></div>')
  12848. );
  12849. popup.getDom().className += ' ' + this.className;
  12850. }, this));
  12851. this.popup = popup;
  12852. },
  12853. _onPopupShow: function (){
  12854. this.addState('opened');
  12855. },
  12856. _onPopupHide: function (){
  12857. this.removeState('opened');
  12858. },
  12859. getHtmlTpl: function (){
  12860. return '<div id="##" class="edui-box %%">' +
  12861. '<div '+ (this.title ? 'title="' + this.title + '"' : '') +' id="##_state" stateful><div class="%%-body">' +
  12862. '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
  12863. '<div class="edui-box edui-icon"></div>' +
  12864. '</div>' +
  12865. '<div class="edui-box edui-splitborder"></div>' +
  12866. '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
  12867. '</div></div></div>';
  12868. },
  12869. showPopup: function (){
  12870. // 当popup往上弹出的时候,做特殊处理
  12871. var rect = uiUtils.getClientRect(this.getDom());
  12872. rect.top -= this.popup.SHADOW_RADIUS;
  12873. rect.height += this.popup.SHADOW_RADIUS;
  12874. this.popup.showAnchorRect(rect);
  12875. },
  12876. _onArrowClick: function (event, el){
  12877. if (!this.isDisabled()) {
  12878. this.showPopup();
  12879. }
  12880. },
  12881. _onButtonClick: function (){
  12882. if (!this.isDisabled()) {
  12883. this.fireEvent('buttonclick');
  12884. }
  12885. }
  12886. };
  12887. utils.inherits(SplitButton, UIBase);
  12888. utils.extend(SplitButton.prototype, Stateful, true);
  12889. })();
  12890. // ui/colorbutton.js
  12891. ///import core
  12892. ///import uicore
  12893. ///import ui/colorpicker.js
  12894. ///import ui/popup.js
  12895. ///import ui/splitbutton.js
  12896. (function (){
  12897. var utils = baidu.editor.utils,
  12898. uiUtils = baidu.editor.ui.uiUtils,
  12899. ColorPicker = baidu.editor.ui.ColorPicker,
  12900. Popup = baidu.editor.ui.Popup,
  12901. SplitButton = baidu.editor.ui.SplitButton,
  12902. ColorButton = baidu.editor.ui.ColorButton = function (options){
  12903. this.initOptions(options);
  12904. this.initColorButton();
  12905. };
  12906. ColorButton.prototype = {
  12907. initColorButton: function (){
  12908. var me = this;
  12909. this.popup = new Popup({
  12910. content: new ColorPicker({
  12911. noColorText: me.editor.getLang("clearColor"),
  12912. editor:me.editor,
  12913. onpickcolor: function (t, color){
  12914. me._onPickColor(color);
  12915. },
  12916. onpicknocolor: function (t, color){
  12917. me._onPickNoColor(color);
  12918. }
  12919. }),
  12920. editor:me.editor
  12921. });
  12922. this.initSplitButton();
  12923. },
  12924. _SplitButton_postRender: SplitButton.prototype.postRender,
  12925. postRender: function (){
  12926. this._SplitButton_postRender();
  12927. this.getDom('button_body').appendChild(
  12928. uiUtils.createElementByHtml('<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>')
  12929. );
  12930. this.getDom().className += ' edui-colorbutton';
  12931. },
  12932. setColor: function (color){
  12933. this.getDom('colorlump').style.backgroundColor = color;
  12934. this.color = color;
  12935. },
  12936. _onPickColor: function (color){
  12937. if (this.fireEvent('pickcolor', color) !== false) {
  12938. this.setColor(color);
  12939. this.popup.hide();
  12940. }
  12941. },
  12942. _onPickNoColor: function (color){
  12943. if (this.fireEvent('picknocolor') !== false) {
  12944. this.popup.hide();
  12945. }
  12946. }
  12947. };
  12948. utils.inherits(ColorButton, SplitButton);
  12949. })();
  12950. // ui/tablebutton.js
  12951. ///import core
  12952. ///import uicore
  12953. ///import ui/popup.js
  12954. ///import ui/tablepicker.js
  12955. ///import ui/splitbutton.js
  12956. (function (){
  12957. var utils = baidu.editor.utils,
  12958. Popup = baidu.editor.ui.Popup,
  12959. TablePicker = baidu.editor.ui.TablePicker,
  12960. SplitButton = baidu.editor.ui.SplitButton,
  12961. TableButton = baidu.editor.ui.TableButton = function (options){
  12962. this.initOptions(options);
  12963. this.initTableButton();
  12964. };
  12965. TableButton.prototype = {
  12966. initTableButton: function (){
  12967. var me = this;
  12968. this.popup = new Popup({
  12969. content: new TablePicker({
  12970. editor:me.editor,
  12971. onpicktable: function (t, numCols, numRows){
  12972. me._onPickTable(numCols, numRows);
  12973. }
  12974. }),
  12975. 'editor':me.editor
  12976. });
  12977. this.initSplitButton();
  12978. },
  12979. _onPickTable: function (numCols, numRows){
  12980. if (this.fireEvent('picktable', numCols, numRows) !== false) {
  12981. this.popup.hide();
  12982. }
  12983. }
  12984. };
  12985. utils.inherits(TableButton, SplitButton);
  12986. })();
  12987. // ui/autotypesetpicker.js
  12988. ///import core
  12989. ///import uicore
  12990. (function () {
  12991. var utils = baidu.editor.utils,
  12992. UIBase = baidu.editor.ui.UIBase;
  12993. var AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker = function (options) {
  12994. this.initOptions(options);
  12995. this.initAutoTypeSetPicker();
  12996. };
  12997. AutoTypeSetPicker.prototype = {
  12998. initAutoTypeSetPicker:function () {
  12999. this.initUIBase();
  13000. },
  13001. getHtmlTpl:function () {
  13002. var me = this.editor,
  13003. opt = me.options.autotypeset,
  13004. lang = me.getLang("autoTypeSet");
  13005. var textAlignInputName = 'textAlignValue' + me.uid,
  13006. imageBlockInputName = 'imageBlockLineValue' + me.uid,
  13007. symbolConverInputName = 'symbolConverValue' + me.uid;
  13008. return '<div id="##" class="edui-autotypesetpicker %%">' +
  13009. '<div class="edui-autotypesetpicker-body">' +
  13010. '<table >' +
  13011. '<tr><td nowrap><input type="checkbox" name="mergeEmptyline" ' + (opt["mergeEmptyline"] ? "checked" : "" ) + '>' + lang.mergeLine + '</td><td colspan="2"><input type="checkbox" name="removeEmptyline" ' + (opt["removeEmptyline"] ? "checked" : "" ) + '>' + lang.delLine + '</td></tr>' +
  13012. '<tr><td nowrap><input type="checkbox" name="removeClass" ' + (opt["removeClass"] ? "checked" : "" ) + '>' + lang.removeFormat + '</td><td colspan="2"><input type="checkbox" name="indent" ' + (opt["indent"] ? "checked" : "" ) + '>' + lang.indent + '</td></tr>' +
  13013. '<tr>' +
  13014. '<td nowrap><input type="checkbox" name="textAlign" ' + (opt["textAlign"] ? "checked" : "" ) + '>' + lang.alignment + '</td>' +
  13015. '<td colspan="2" id="' + textAlignInputName + '">' +
  13016. '<input type="radio" name="'+ textAlignInputName +'" value="left" ' + ((opt["textAlign"] && opt["textAlign"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
  13017. '<input type="radio" name="'+ textAlignInputName +'" value="center" ' + ((opt["textAlign"] && opt["textAlign"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
  13018. '<input type="radio" name="'+ textAlignInputName +'" value="right" ' + ((opt["textAlign"] && opt["textAlign"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
  13019. '</td>' +
  13020. '</tr>' +
  13021. '<tr>' +
  13022. '<td nowrap><input type="checkbox" name="imageBlockLine" ' + (opt["imageBlockLine"] ? "checked" : "" ) + '>' + lang.imageFloat + '</td>' +
  13023. '<td nowrap id="'+ imageBlockInputName +'">' +
  13024. '<input type="radio" name="'+ imageBlockInputName +'" value="none" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "none") ? "checked" : "") + '>' + me.getLang("default") +
  13025. '<input type="radio" name="'+ imageBlockInputName +'" value="left" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
  13026. '<input type="radio" name="'+ imageBlockInputName +'" value="center" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
  13027. '<input type="radio" name="'+ imageBlockInputName +'" value="right" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
  13028. '</td>' +
  13029. '</tr>' +
  13030. '<tr><td nowrap><input type="checkbox" name="clearFontSize" ' + (opt["clearFontSize"] ? "checked" : "" ) + '>' + lang.removeFontsize + '</td><td colspan="2"><input type="checkbox" name="clearFontFamily" ' + (opt["clearFontFamily"] ? "checked" : "" ) + '>' + lang.removeFontFamily + '</td></tr>' +
  13031. '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' + (opt["removeEmptyNode"] ? "checked" : "" ) + '>' + lang.removeHtml + '</td></tr>' +
  13032. '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' + (opt["pasteFilter"] ? "checked" : "" ) + '>' + lang.pasteFilter + '</td></tr>' +
  13033. '<tr>' +
  13034. '<td nowrap><input type="checkbox" name="symbolConver" ' + (opt["bdc2sb"] || opt["tobdc"] ? "checked" : "" ) + '>' + lang.symbol + '</td>' +
  13035. '<td id="' + symbolConverInputName + '">' +
  13036. '<input type="radio" name="bdc" value="bdc2sb" ' + (opt["bdc2sb"] ? "checked" : "" ) + '>' + lang.bdc2sb +
  13037. '<input type="radio" name="bdc" value="tobdc" ' + (opt["tobdc"] ? "checked" : "" ) + '>' + lang.tobdc + '' +
  13038. '</td>' +
  13039. '<td nowrap align="right"><button >' + lang.run + '</button></td>' +
  13040. '</tr>' +
  13041. '</table>' +
  13042. '</div>' +
  13043. '</div>';
  13044. },
  13045. _UIBase_render:UIBase.prototype.render
  13046. };
  13047. utils.inherits(AutoTypeSetPicker, UIBase);
  13048. })();
  13049. // ui/autotypesetbutton.js
  13050. ///import core
  13051. ///import uicore
  13052. ///import ui/popup.js
  13053. ///import ui/autotypesetpicker.js
  13054. ///import ui/splitbutton.js
  13055. (function (){
  13056. var utils = baidu.editor.utils,
  13057. Popup = baidu.editor.ui.Popup,
  13058. AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
  13059. SplitButton = baidu.editor.ui.SplitButton,
  13060. AutoTypeSetButton = baidu.editor.ui.AutoTypeSetButton = function (options){
  13061. this.initOptions(options);
  13062. this.initAutoTypeSetButton();
  13063. };
  13064. function getPara(me){
  13065. var opt = {},
  13066. cont = me.getDom(),
  13067. editorId = me.editor.uid,
  13068. inputType = null,
  13069. attrName = null,
  13070. ipts = domUtils.getElementsByTagName(cont,"input");
  13071. for(var i=ipts.length-1,ipt;ipt=ipts[i--];){
  13072. inputType = ipt.getAttribute("type");
  13073. if(inputType=="checkbox"){
  13074. attrName = ipt.getAttribute("name");
  13075. opt[attrName] && delete opt[attrName];
  13076. if(ipt.checked){
  13077. var attrValue = document.getElementById( attrName + "Value" + editorId );
  13078. if(attrValue){
  13079. if(/input/ig.test(attrValue.tagName)){
  13080. opt[attrName] = attrValue.value;
  13081. } else {
  13082. var iptChilds = attrValue.getElementsByTagName("input");
  13083. for(var j=iptChilds.length-1,iptchild;iptchild=iptChilds[j--];){
  13084. if(iptchild.checked){
  13085. opt[attrName] = iptchild.value;
  13086. break;
  13087. }
  13088. }
  13089. }
  13090. } else {
  13091. opt[attrName] = true;
  13092. }
  13093. } else {
  13094. opt[attrName] = false;
  13095. }
  13096. } else {
  13097. opt[ipt.getAttribute("value")] = ipt.checked;
  13098. }
  13099. }
  13100. var selects = domUtils.getElementsByTagName(cont,"select");
  13101. for(var i=0,si;si=selects[i++];){
  13102. var attr = si.getAttribute('name');
  13103. opt[attr] = opt[attr] ? si.value : '';
  13104. }
  13105. utils.extend(me.editor.options.autotypeset,opt);
  13106. me.editor.setPreferences('autotypeset', opt);
  13107. }
  13108. AutoTypeSetButton.prototype = {
  13109. initAutoTypeSetButton: function (){
  13110. var me = this;
  13111. this.popup = new Popup({
  13112. //传入配置参数
  13113. content: new AutoTypeSetPicker({editor:me.editor}),
  13114. 'editor':me.editor,
  13115. hide : function(){
  13116. if (!this._hidden && this.getDom()) {
  13117. getPara(this);
  13118. this.getDom().style.display = 'none';
  13119. this._hidden = true;
  13120. this.fireEvent('hide');
  13121. }
  13122. }
  13123. });
  13124. var flag = 0;
  13125. this.popup.addListener('postRenderAfter',function(){
  13126. var popupUI = this;
  13127. if(flag)return;
  13128. var cont = this.getDom(),
  13129. btn = cont.getElementsByTagName('button')[0];
  13130. btn.onclick = function(){
  13131. getPara(popupUI);
  13132. me.editor.execCommand('autotypeset');
  13133. popupUI.hide()
  13134. };
  13135. domUtils.on(cont, 'click', function(e) {
  13136. var target = e.target || e.srcElement,
  13137. editorId = me.editor.uid;
  13138. if (target && target.tagName == 'INPUT') {
  13139. // 点击图片浮动的checkbox,去除对应的radio
  13140. if (target.name == 'imageBlockLine' || target.name == 'textAlign' || target.name == 'symbolConver') {
  13141. var checked = target.checked,
  13142. radioTd = document.getElementById( target.name + 'Value' + editorId),
  13143. radios = radioTd.getElementsByTagName('input'),
  13144. defalutSelect = {
  13145. 'imageBlockLine': 'none',
  13146. 'textAlign': 'left',
  13147. 'symbolConver': 'tobdc'
  13148. };
  13149. for (var i = 0; i < radios.length; i++) {
  13150. if (checked) {
  13151. if (radios[i].value == defalutSelect[target.name]) {
  13152. radios[i].checked = 'checked';
  13153. }
  13154. } else {
  13155. radios[i].checked = false;
  13156. }
  13157. }
  13158. }
  13159. // 点击radio,选中对应的checkbox
  13160. if (target.name == ('imageBlockLineValue' + editorId) || target.name == ('textAlignValue' + editorId) || target.name == 'bdc') {
  13161. var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input');
  13162. checkboxs && (checkboxs[0].checked = true);
  13163. }
  13164. getPara(popupUI);
  13165. }
  13166. });
  13167. flag = 1;
  13168. });
  13169. this.initSplitButton();
  13170. }
  13171. };
  13172. utils.inherits(AutoTypeSetButton, SplitButton);
  13173. })();
  13174. // ui/cellalignpicker.js
  13175. ///import core
  13176. ///import uicore
  13177. (function () {
  13178. var utils = baidu.editor.utils,
  13179. Popup = baidu.editor.ui.Popup,
  13180. Stateful = baidu.editor.ui.Stateful,
  13181. UIBase = baidu.editor.ui.UIBase;
  13182. /**
  13183. * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
  13184. * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
  13185. * @update 2013/4/2 hancong03@baidu.com
  13186. */
  13187. var CellAlignPicker = baidu.editor.ui.CellAlignPicker = function (options) {
  13188. this.initOptions(options);
  13189. this.initSelected();
  13190. this.initCellAlignPicker();
  13191. };
  13192. CellAlignPicker.prototype = {
  13193. //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
  13194. initSelected: function(){
  13195. var status = {
  13196. valign: {
  13197. top: 0,
  13198. middle: 1,
  13199. bottom: 2
  13200. },
  13201. align: {
  13202. left: 0,
  13203. center: 1,
  13204. right: 2
  13205. },
  13206. count: 3
  13207. },
  13208. result = -1;
  13209. if( this.selected ) {
  13210. this.selectedIndex = status.valign[ this.selected.valign ] * status.count + status.align[ this.selected.align ];
  13211. }
  13212. },
  13213. initCellAlignPicker:function () {
  13214. this.initUIBase();
  13215. this.Stateful_init();
  13216. },
  13217. getHtmlTpl:function () {
  13218. var alignType = [ 'left', 'center', 'right' ],
  13219. COUNT = 9,
  13220. tempClassName = null,
  13221. tempIndex = -1,
  13222. tmpl = [];
  13223. for( var i= 0; i<COUNT; i++ ) {
  13224. tempClassName = this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : '';
  13225. tempIndex = i % 3;
  13226. tempIndex === 0 && tmpl.push('<tr>');
  13227. tmpl.push( '<td index="'+ i +'" ' + tempClassName + ' stateful><div class="edui-icon edui-'+ alignType[ tempIndex ] +'"></div></td>' );
  13228. tempIndex === 2 && tmpl.push('</tr>');
  13229. }
  13230. return '<div id="##" class="edui-cellalignpicker %%">' +
  13231. '<div class="edui-cellalignpicker-body">' +
  13232. '<table onclick="$$._onClick(event);">' +
  13233. tmpl.join('') +
  13234. '</table>' +
  13235. '</div>' +
  13236. '</div>';
  13237. },
  13238. getStateDom: function (){
  13239. return this.target;
  13240. },
  13241. _onClick: function (evt){
  13242. var target= evt.target || evt.srcElement;
  13243. if(/icon/.test(target.className)){
  13244. this.items[target.parentNode.getAttribute("index")].onclick();
  13245. Popup.postHide(evt);
  13246. }
  13247. },
  13248. _UIBase_render:UIBase.prototype.render
  13249. };
  13250. utils.inherits(CellAlignPicker, UIBase);
  13251. utils.extend(CellAlignPicker.prototype, Stateful,true);
  13252. })();
  13253. // ui/pastepicker.js
  13254. ///import core
  13255. ///import uicore
  13256. (function () {
  13257. var utils = baidu.editor.utils,
  13258. Stateful = baidu.editor.ui.Stateful,
  13259. uiUtils = baidu.editor.ui.uiUtils,
  13260. UIBase = baidu.editor.ui.UIBase;
  13261. var PastePicker = baidu.editor.ui.PastePicker = function (options) {
  13262. this.initOptions(options);
  13263. this.initPastePicker();
  13264. };
  13265. PastePicker.prototype = {
  13266. initPastePicker:function () {
  13267. this.initUIBase();
  13268. this.Stateful_init();
  13269. },
  13270. getHtmlTpl:function () {
  13271. return '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
  13272. '<div class="edui-pastecontainer">' +
  13273. '<div class="edui-title">' + this.editor.getLang("pasteOpt") + '</div>' +
  13274. '<div class="edui-button">' +
  13275. '<div title="' + this.editor.getLang("pasteSourceFormat") + '" onclick="$$.format(false)" stateful>' +
  13276. '<div class="edui-richtxticon"></div></div>' +
  13277. '<div title="' + this.editor.getLang("tagFormat") + '" onclick="$$.format(2)" stateful>' +
  13278. '<div class="edui-tagicon"></div></div>' +
  13279. '<div title="' + this.editor.getLang("pasteTextFormat") + '" onclick="$$.format(true)" stateful>' +
  13280. '<div class="edui-plaintxticon"></div></div>' +
  13281. '</div>' +
  13282. '</div>' +
  13283. '</div>'
  13284. },
  13285. getStateDom:function () {
  13286. return this.target;
  13287. },
  13288. format:function (param) {
  13289. this.editor.ui._isTransfer = true;
  13290. this.editor.fireEvent('pasteTransfer', param);
  13291. },
  13292. _onClick:function (cur) {
  13293. var node = domUtils.getNextDomNode(cur),
  13294. screenHt = uiUtils.getViewportRect().height,
  13295. subPop = uiUtils.getClientRect(node);
  13296. if ((subPop.top + subPop.height) > screenHt)
  13297. node.style.top = (-subPop.height - cur.offsetHeight) + "px";
  13298. else
  13299. node.style.top = "";
  13300. if (/hidden/ig.test(domUtils.getComputedStyle(node, "visibility"))) {
  13301. node.style.visibility = "visible";
  13302. domUtils.addClass(cur, "edui-state-opened");
  13303. } else {
  13304. node.style.visibility = "hidden";
  13305. domUtils.removeClasses(cur, "edui-state-opened")
  13306. }
  13307. },
  13308. _UIBase_render:UIBase.prototype.render
  13309. };
  13310. utils.inherits(PastePicker, UIBase);
  13311. utils.extend(PastePicker.prototype, Stateful, true);
  13312. })();
  13313. // ui/toolbar.js
  13314. (function (){
  13315. var utils = baidu.editor.utils,
  13316. uiUtils = baidu.editor.ui.uiUtils,
  13317. UIBase = baidu.editor.ui.UIBase,
  13318. Toolbar = baidu.editor.ui.Toolbar = function (options){
  13319. this.initOptions(options);
  13320. this.initToolbar();
  13321. };
  13322. Toolbar.prototype = {
  13323. items: null,
  13324. initToolbar: function (){
  13325. this.items = this.items || [];
  13326. this.initUIBase();
  13327. },
  13328. add: function (item,index){
  13329. if(index === undefined){
  13330. this.items.push(item);
  13331. }else{
  13332. this.items.splice(index,0,item)
  13333. }
  13334. },
  13335. getHtmlTpl: function (){
  13336. var buff = [];
  13337. for (var i=0; i<this.items.length; i++) {
  13338. buff[i] = this.items[i].renderHtml();
  13339. }
  13340. return '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
  13341. buff.join('') +
  13342. '</div>'
  13343. },
  13344. postRender: function (){
  13345. var box = this.getDom();
  13346. for (var i=0; i<this.items.length; i++) {
  13347. this.items[i].postRender();
  13348. }
  13349. uiUtils.makeUnselectable(box);
  13350. },
  13351. _onMouseDown: function (e){
  13352. var target = e.target || e.srcElement,
  13353. tagName = target && target.tagName && target.tagName.toLowerCase();
  13354. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  13355. return false;
  13356. }
  13357. }
  13358. };
  13359. utils.inherits(Toolbar, UIBase);
  13360. })();
  13361. // ui/menu.js
  13362. ///import core
  13363. ///import uicore
  13364. ///import ui\popup.js
  13365. ///import ui\stateful.js
  13366. (function () {
  13367. var utils = baidu.editor.utils,
  13368. domUtils = baidu.editor.dom.domUtils,
  13369. uiUtils = baidu.editor.ui.uiUtils,
  13370. UIBase = baidu.editor.ui.UIBase,
  13371. Popup = baidu.editor.ui.Popup,
  13372. Stateful = baidu.editor.ui.Stateful,
  13373. CellAlignPicker = baidu.editor.ui.CellAlignPicker,
  13374. Menu = baidu.editor.ui.Menu = function (options) {
  13375. this.initOptions(options);
  13376. this.initMenu();
  13377. };
  13378. var menuSeparator = {
  13379. renderHtml:function () {
  13380. return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>';
  13381. },
  13382. postRender:function () {
  13383. },
  13384. queryAutoHide:function () {
  13385. return true;
  13386. }
  13387. };
  13388. Menu.prototype = {
  13389. items:null,
  13390. uiName:'menu',
  13391. initMenu:function () {
  13392. this.items = this.items || [];
  13393. this.initPopup();
  13394. this.initItems();
  13395. },
  13396. initItems:function () {
  13397. for (var i = 0; i < this.items.length; i++) {
  13398. var item = this.items[i];
  13399. if (item == '-') {
  13400. this.items[i] = this.getSeparator();
  13401. } else if (!(item instanceof MenuItem)) {
  13402. item.editor = this.editor;
  13403. item.theme = this.editor.options.theme;
  13404. this.items[i] = this.createItem(item);
  13405. }
  13406. }
  13407. },
  13408. getSeparator:function () {
  13409. return menuSeparator;
  13410. },
  13411. createItem:function (item) {
  13412. //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  13413. item.menu = this;
  13414. return new MenuItem(item);
  13415. },
  13416. _Popup_getContentHtmlTpl:Popup.prototype.getContentHtmlTpl,
  13417. getContentHtmlTpl:function () {
  13418. if (this.items.length == 0) {
  13419. return this._Popup_getContentHtmlTpl();
  13420. }
  13421. var buff = [];
  13422. for (var i = 0; i < this.items.length; i++) {
  13423. var item = this.items[i];
  13424. buff[i] = item.renderHtml();
  13425. }
  13426. return ('<div class="%%-body">' + buff.join('') + '</div>');
  13427. },
  13428. _Popup_postRender:Popup.prototype.postRender,
  13429. postRender:function () {
  13430. var me = this;
  13431. for (var i = 0; i < this.items.length; i++) {
  13432. var item = this.items[i];
  13433. item.ownerMenu = this;
  13434. item.postRender();
  13435. }
  13436. domUtils.on(this.getDom(), 'mouseover', function (evt) {
  13437. evt = evt || event;
  13438. var rel = evt.relatedTarget || evt.fromElement;
  13439. var el = me.getDom();
  13440. if (!uiUtils.contains(el, rel) && el !== rel) {
  13441. me.fireEvent('over');
  13442. }
  13443. });
  13444. this._Popup_postRender();
  13445. },
  13446. queryAutoHide:function (el) {
  13447. if (el) {
  13448. if (uiUtils.contains(this.getDom(), el)) {
  13449. return false;
  13450. }
  13451. for (var i = 0; i < this.items.length; i++) {
  13452. var item = this.items[i];
  13453. if (item.queryAutoHide(el) === false) {
  13454. return false;
  13455. }
  13456. }
  13457. }
  13458. },
  13459. clearItems:function () {
  13460. for (var i = 0; i < this.items.length; i++) {
  13461. var item = this.items[i];
  13462. clearTimeout(item._showingTimer);
  13463. clearTimeout(item._closingTimer);
  13464. if (item.subMenu) {
  13465. item.subMenu.destroy();
  13466. }
  13467. }
  13468. this.items = [];
  13469. },
  13470. destroy:function () {
  13471. if (this.getDom()) {
  13472. domUtils.remove(this.getDom());
  13473. }
  13474. this.clearItems();
  13475. },
  13476. dispose:function () {
  13477. this.destroy();
  13478. }
  13479. };
  13480. utils.inherits(Menu, Popup);
  13481. /**
  13482. * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  13483. * @type {Function}
  13484. */
  13485. var MenuItem = baidu.editor.ui.MenuItem = function (options) {
  13486. this.initOptions(options);
  13487. this.initUIBase();
  13488. this.Stateful_init();
  13489. if (this.subMenu && !(this.subMenu instanceof Menu)) {
  13490. if (options.className && options.className.indexOf("aligntd") != -1) {
  13491. var me = this;
  13492. //获取单元格对齐初始状态
  13493. this.subMenu.selected = this.editor.queryCommandValue( 'cellalignment' );
  13494. this.subMenu = new Popup({
  13495. content:new CellAlignPicker(this.subMenu),
  13496. parentMenu:me,
  13497. editor:me.editor,
  13498. destroy:function () {
  13499. if (this.getDom()) {
  13500. domUtils.remove(this.getDom());
  13501. }
  13502. }
  13503. });
  13504. this.subMenu.addListener("postRenderAfter", function () {
  13505. domUtils.on(this.getDom(), "mouseover", function () {
  13506. me.addState('opened');
  13507. });
  13508. });
  13509. } else {
  13510. this.subMenu = new Menu(this.subMenu);
  13511. }
  13512. }
  13513. };
  13514. MenuItem.prototype = {
  13515. label:'',
  13516. subMenu:null,
  13517. ownerMenu:null,
  13518. uiName:'menuitem',
  13519. alwalysHoverable:true,
  13520. getHtmlTpl:function () {
  13521. return '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
  13522. '<div class="%%-body">' +
  13523. this.renderLabelHtml() +
  13524. '</div>' +
  13525. '</div>';
  13526. },
  13527. postRender:function () {
  13528. var me = this;
  13529. this.addListener('over', function () {
  13530. me.ownerMenu.fireEvent('submenuover', me);
  13531. if (me.subMenu) {
  13532. me.delayShowSubMenu();
  13533. }
  13534. });
  13535. if (this.subMenu) {
  13536. this.getDom().className += ' edui-hassubmenu';
  13537. this.subMenu.render();
  13538. this.addListener('out', function () {
  13539. me.delayHideSubMenu();
  13540. });
  13541. this.subMenu.addListener('over', function () {
  13542. clearTimeout(me._closingTimer);
  13543. me._closingTimer = null;
  13544. me.addState('opened');
  13545. });
  13546. this.ownerMenu.addListener('hide', function () {
  13547. me.hideSubMenu();
  13548. });
  13549. this.ownerMenu.addListener('submenuover', function (t, subMenu) {
  13550. if (subMenu !== me) {
  13551. me.delayHideSubMenu();
  13552. }
  13553. });
  13554. this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide;
  13555. this.subMenu.queryAutoHide = function (el) {
  13556. if (el && uiUtils.contains(me.getDom(), el)) {
  13557. return false;
  13558. }
  13559. return this._bakQueryAutoHide(el);
  13560. };
  13561. }
  13562. this.getDom().style.tabIndex = '-1';
  13563. uiUtils.makeUnselectable(this.getDom());
  13564. this.Stateful_postRender();
  13565. },
  13566. delayShowSubMenu:function () {
  13567. var me = this;
  13568. if (!me.isDisabled()) {
  13569. me.addState('opened');
  13570. clearTimeout(me._showingTimer);
  13571. clearTimeout(me._closingTimer);
  13572. me._closingTimer = null;
  13573. me._showingTimer = setTimeout(function () {
  13574. me.showSubMenu();
  13575. }, 250);
  13576. }
  13577. },
  13578. delayHideSubMenu:function () {
  13579. var me = this;
  13580. if (!me.isDisabled()) {
  13581. me.removeState('opened');
  13582. clearTimeout(me._showingTimer);
  13583. if (!me._closingTimer) {
  13584. me._closingTimer = setTimeout(function () {
  13585. if (!me.hasState('opened')) {
  13586. me.hideSubMenu();
  13587. }
  13588. me._closingTimer = null;
  13589. }, 400);
  13590. }
  13591. }
  13592. },
  13593. renderLabelHtml:function () {
  13594. return '<div class="edui-arrow"></div>' +
  13595. '<div class="edui-box edui-icon"></div>' +
  13596. '<div class="edui-box edui-label %%-label">' + (this.label || '') + '</div>';
  13597. },
  13598. getStateDom:function () {
  13599. return this.getDom();
  13600. },
  13601. queryAutoHide:function (el) {
  13602. if (this.subMenu && this.hasState('opened')) {
  13603. return this.subMenu.queryAutoHide(el);
  13604. }
  13605. },
  13606. _onClick:function (event, this_) {
  13607. if (this.hasState('disabled')) return;
  13608. if (this.fireEvent('click', event, this_) !== false) {
  13609. if (this.subMenu) {
  13610. this.showSubMenu();
  13611. } else {
  13612. Popup.postHide(event);
  13613. }
  13614. }
  13615. },
  13616. showSubMenu:function () {
  13617. var rect = uiUtils.getClientRect(this.getDom());
  13618. rect.right -= 5;
  13619. rect.left += 2;
  13620. rect.width -= 7;
  13621. rect.top -= 4;
  13622. rect.bottom += 4;
  13623. rect.height += 8;
  13624. this.subMenu.showAnchorRect(rect, true, true);
  13625. },
  13626. hideSubMenu:function () {
  13627. this.subMenu.hide();
  13628. }
  13629. };
  13630. utils.inherits(MenuItem, UIBase);
  13631. utils.extend(MenuItem.prototype, Stateful, true);
  13632. })();
  13633. // ui/combox.js
  13634. ///import core
  13635. ///import uicore
  13636. ///import ui/menu.js
  13637. ///import ui/splitbutton.js
  13638. (function (){
  13639. // todo: menu和item提成通用list
  13640. var utils = baidu.editor.utils,
  13641. uiUtils = baidu.editor.ui.uiUtils,
  13642. Menu = baidu.editor.ui.Menu,
  13643. SplitButton = baidu.editor.ui.SplitButton,
  13644. Combox = baidu.editor.ui.Combox = function (options){
  13645. this.initOptions(options);
  13646. this.initCombox();
  13647. };
  13648. Combox.prototype = {
  13649. uiName: 'combox',
  13650. onbuttonclick:function () {
  13651. this.showPopup();
  13652. },
  13653. initCombox: function (){
  13654. var me = this;
  13655. this.items = this.items || [];
  13656. for (var i=0; i<this.items.length; i++) {
  13657. var item = this.items[i];
  13658. item.uiName = 'listitem';
  13659. item.index = i;
  13660. item.onclick = function (){
  13661. me.selectByIndex(this.index);
  13662. };
  13663. }
  13664. this.popup = new Menu({
  13665. items: this.items,
  13666. uiName: 'list',
  13667. editor:this.editor,
  13668. captureWheel: true,
  13669. combox: this
  13670. });
  13671. this.initSplitButton();
  13672. },
  13673. _SplitButton_postRender: SplitButton.prototype.postRender,
  13674. postRender: function (){
  13675. this._SplitButton_postRender();
  13676. this.setLabel(this.label || '');
  13677. this.setValue(this.initValue || '');
  13678. },
  13679. showPopup: function (){
  13680. var rect = uiUtils.getClientRect(this.getDom());
  13681. rect.top += 1;
  13682. rect.bottom -= 1;
  13683. rect.height -= 2;
  13684. this.popup.showAnchorRect(rect);
  13685. },
  13686. getValue: function (){
  13687. return this.value;
  13688. },
  13689. setValue: function (value){
  13690. var index = this.indexByValue(value);
  13691. if (index != -1) {
  13692. this.selectedIndex = index;
  13693. this.setLabel(this.items[index].label);
  13694. this.value = this.items[index].value;
  13695. } else {
  13696. this.selectedIndex = -1;
  13697. this.setLabel(this.getLabelForUnknowValue(value));
  13698. this.value = value;
  13699. }
  13700. },
  13701. setLabel: function (label){
  13702. this.getDom('button_body').innerHTML = label;
  13703. this.label = label;
  13704. },
  13705. getLabelForUnknowValue: function (value){
  13706. return value;
  13707. },
  13708. indexByValue: function (value){
  13709. for (var i=0; i<this.items.length; i++) {
  13710. if (value == this.items[i].value) {
  13711. return i;
  13712. }
  13713. }
  13714. return -1;
  13715. },
  13716. getItem: function (index){
  13717. return this.items[index];
  13718. },
  13719. selectByIndex: function (index){
  13720. if (index < this.items.length && this.fireEvent('select', index) !== false) {
  13721. this.selectedIndex = index;
  13722. this.value = this.items[index].value;
  13723. this.setLabel(this.items[index].label);
  13724. }
  13725. }
  13726. };
  13727. utils.inherits(Combox, SplitButton);
  13728. })();
  13729. // ui/dialog.js
  13730. ///import core
  13731. ///import uicore
  13732. ///import ui/mask.js
  13733. ///import ui/button.js
  13734. (function (){
  13735. var utils = baidu.editor.utils,
  13736. domUtils = baidu.editor.dom.domUtils,
  13737. uiUtils = baidu.editor.ui.uiUtils,
  13738. Mask = baidu.editor.ui.Mask,
  13739. UIBase = baidu.editor.ui.UIBase,
  13740. Button = baidu.editor.ui.Button,
  13741. Dialog = baidu.editor.ui.Dialog = function (options){
  13742. if(options.name){
  13743. var name = options.name;
  13744. var cssRules = options.cssRules;
  13745. if(!options.className){
  13746. options.className = 'edui-for-' + name;
  13747. }
  13748. if(cssRules){
  13749. options.cssRules = '.edui-default .edui-for-'+ name +' .edui-dialog-content {'+ cssRules +'}'
  13750. }
  13751. }
  13752. this.initOptions(utils.extend({
  13753. autoReset: true,
  13754. draggable: true,
  13755. onok: function (){},
  13756. oncancel: function (){},
  13757. onclose: function (t, ok){
  13758. return ok ? this.onok() : this.oncancel();
  13759. },
  13760. //是否控制dialog中的scroll事件, 默认为不阻止
  13761. holdScroll: false
  13762. },options));
  13763. this.initDialog();
  13764. };
  13765. var modalMask;
  13766. var dragMask;
  13767. var activeDialog;
  13768. Dialog.prototype = {
  13769. draggable: false,
  13770. uiName: 'dialog',
  13771. initDialog: function (){
  13772. var me = this,
  13773. theme=this.editor.options.theme;
  13774. if(this.cssRules){
  13775. utils.cssRule('edui-customize-'+this.name+'-style',this.cssRules);
  13776. }
  13777. this.initUIBase();
  13778. this.modalMask = (modalMask || (modalMask = new Mask({
  13779. className: 'edui-dialog-modalmask',
  13780. theme:theme,
  13781. onclick: function (){
  13782. activeDialog && activeDialog.close(false);
  13783. }
  13784. })));
  13785. this.dragMask = (dragMask || (dragMask = new Mask({
  13786. className: 'edui-dialog-dragmask',
  13787. theme:theme
  13788. })));
  13789. this.closeButton = new Button({
  13790. className: 'edui-dialog-closebutton',
  13791. title: me.closeDialog,
  13792. theme:theme,
  13793. onclick: function (){
  13794. me.close(false);
  13795. }
  13796. });
  13797. this.fullscreen && this.initResizeEvent();
  13798. if (this.buttons) {
  13799. for (var i=0; i<this.buttons.length; i++) {
  13800. if (!(this.buttons[i] instanceof Button)) {
  13801. this.buttons[i] = new Button(utils.extend(this.buttons[i],{
  13802. editor : this.editor
  13803. },true));
  13804. }
  13805. }
  13806. }
  13807. },
  13808. initResizeEvent: function () {
  13809. var me = this;
  13810. domUtils.on( window, "resize", function () {
  13811. if ( me._hidden || me._hidden === undefined ) {
  13812. return;
  13813. }
  13814. if ( me.__resizeTimer ) {
  13815. window.clearTimeout( me.__resizeTimer );
  13816. }
  13817. me.__resizeTimer = window.setTimeout( function () {
  13818. me.__resizeTimer = null;
  13819. var dialogWrapNode = me.getDom(),
  13820. contentNode = me.getDom('content'),
  13821. wrapRect = UE.ui.uiUtils.getClientRect( dialogWrapNode ),
  13822. contentRect = UE.ui.uiUtils.getClientRect( contentNode ),
  13823. vpRect = uiUtils.getViewportRect();
  13824. contentNode.style.width = ( vpRect.width - wrapRect.width + contentRect.width ) + "px";
  13825. contentNode.style.height = ( vpRect.height - wrapRect.height + contentRect.height ) + "px";
  13826. dialogWrapNode.style.width = vpRect.width + "px";
  13827. dialogWrapNode.style.height = vpRect.height + "px";
  13828. me.fireEvent( "resize" );
  13829. }, 100 );
  13830. } );
  13831. },
  13832. fitSize: function (){
  13833. var popBodyEl = this.getDom('body');
  13834. // if (!(baidu.editor.browser.ie && baidu.editor.browser.version == 7)) {
  13835. // uiUtils.removeStyle(popBodyEl, 'width');
  13836. // uiUtils.removeStyle(popBodyEl, 'height');
  13837. // }
  13838. var size = this.mesureSize();
  13839. popBodyEl.style.width = size.width + 'px';
  13840. popBodyEl.style.height = size.height + 'px';
  13841. return size;
  13842. },
  13843. safeSetOffset: function (offset){
  13844. var me = this;
  13845. var el = me.getDom();
  13846. var vpRect = uiUtils.getViewportRect();
  13847. var rect = uiUtils.getClientRect(el);
  13848. var left = offset.left;
  13849. if (left + rect.width > vpRect.right) {
  13850. left = vpRect.right - rect.width;
  13851. }
  13852. var top = offset.top;
  13853. if (top + rect.height > vpRect.bottom) {
  13854. top = vpRect.bottom - rect.height;
  13855. }
  13856. el.style.left = Math.max(left, 0) + 'px';
  13857. el.style.top = Math.max(top, 0) + 'px';
  13858. },
  13859. showAtCenter: function (){
  13860. var vpRect = uiUtils.getViewportRect();
  13861. if ( !this.fullscreen ) {
  13862. this.getDom().style.display = '';
  13863. var popSize = this.fitSize();
  13864. var titleHeight = this.getDom('titlebar').offsetHeight | 0;
  13865. var left = vpRect.width / 2 - popSize.width / 2;
  13866. var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight;
  13867. var popEl = this.getDom();
  13868. this.safeSetOffset({
  13869. left: Math.max(left | 0, 0),
  13870. top: Math.max(top | 0, 0)
  13871. });
  13872. if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
  13873. popEl.className += ' edui-state-centered';
  13874. }
  13875. } else {
  13876. var dialogWrapNode = this.getDom(),
  13877. contentNode = this.getDom('content');
  13878. dialogWrapNode.style.display = "block";
  13879. var wrapRect = UE.ui.uiUtils.getClientRect( dialogWrapNode ),
  13880. contentRect = UE.ui.uiUtils.getClientRect( contentNode );
  13881. dialogWrapNode.style.left = "-100000px";
  13882. contentNode.style.width = ( vpRect.width - wrapRect.width + contentRect.width ) + "px";
  13883. contentNode.style.height = ( vpRect.height - wrapRect.height + contentRect.height ) + "px";
  13884. dialogWrapNode.style.width = vpRect.width + "px";
  13885. dialogWrapNode.style.height = vpRect.height + "px";
  13886. dialogWrapNode.style.left = 0;
  13887. //保存环境的overflow值
  13888. this._originalContext = {
  13889. html: {
  13890. overflowX: document.documentElement.style.overflowX,
  13891. overflowY: document.documentElement.style.overflowY
  13892. },
  13893. body: {
  13894. overflowX: document.body.style.overflowX,
  13895. overflowY: document.body.style.overflowY
  13896. }
  13897. };
  13898. document.documentElement.style.overflowX = 'hidden';
  13899. document.documentElement.style.overflowY = 'hidden';
  13900. document.body.style.overflowX = 'hidden';
  13901. document.body.style.overflowY = 'hidden';
  13902. }
  13903. this._show();
  13904. },
  13905. getContentHtml: function (){
  13906. var contentHtml = '';
  13907. if (typeof this.content == 'string') {
  13908. contentHtml = this.content;
  13909. } else if (this.iframeUrl) {
  13910. contentHtml = '<span id="'+ this.id +'_contmask" class="dialogcontmask"></span><iframe id="'+ this.id +
  13911. '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="'+ this.iframeUrl +'"></iframe>';
  13912. }
  13913. return contentHtml;
  13914. },
  13915. getHtmlTpl: function (){
  13916. var footHtml = '';
  13917. if (this.buttons) {
  13918. var buff = [];
  13919. for (var i=0; i<this.buttons.length; i++) {
  13920. buff[i] = this.buttons[i].renderHtml();
  13921. }
  13922. footHtml = '<div class="%%-foot">' +
  13923. '<div id="##_buttons" class="%%-buttons">' + buff.join('') + '</div>' +
  13924. '</div>';
  13925. }
  13926. return '<div id="##" class="%%"><div '+ ( !this.fullscreen ? 'class="%%"' : 'class="%%-wrap edui-dialog-fullscreen-flag"' ) +'><div id="##_body" class="%%-body">' +
  13927. '<div class="%%-shadow"></div>' +
  13928. '<div id="##_titlebar" class="%%-titlebar">' +
  13929. '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
  13930. '<span class="%%-caption">' + (this.title || '') + '</span>' +
  13931. '</div>' +
  13932. this.closeButton.renderHtml() +
  13933. '</div>' +
  13934. '<div id="##_content" class="%%-content">'+ ( this.autoReset ? '' : this.getContentHtml()) +'</div>' +
  13935. footHtml +
  13936. '</div></div></div>';
  13937. },
  13938. postRender: function (){
  13939. // todo: 保持居中/记住上次关闭位置选项
  13940. if (!this.modalMask.getDom()) {
  13941. this.modalMask.render();
  13942. this.modalMask.hide();
  13943. }
  13944. if (!this.dragMask.getDom()) {
  13945. this.dragMask.render();
  13946. this.dragMask.hide();
  13947. }
  13948. var me = this;
  13949. this.addListener('show', function (){
  13950. me.modalMask.show(this.getDom().style.zIndex - 2);
  13951. });
  13952. this.addListener('hide', function (){
  13953. me.modalMask.hide();
  13954. });
  13955. if (this.buttons) {
  13956. for (var i=0; i<this.buttons.length; i++) {
  13957. this.buttons[i].postRender();
  13958. }
  13959. }
  13960. domUtils.on(window, 'resize', function (){
  13961. setTimeout(function (){
  13962. if (!me.isHidden()) {
  13963. me.safeSetOffset(uiUtils.getClientRect(me.getDom()));
  13964. }
  13965. });
  13966. });
  13967. //hold住scroll事件,防止dialog的滚动影响页面
  13968. // if( this.holdScroll ) {
  13969. //
  13970. // if( !me.iframeUrl ) {
  13971. // domUtils.on( document.getElementById( me.id + "_iframe"), !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  13972. // domUtils.preventDefault(e);
  13973. // } );
  13974. // } else {
  13975. // me.addListener('dialogafterreset', function(){
  13976. // window.setTimeout(function(){
  13977. // var iframeWindow = document.getElementById( me.id + "_iframe").contentWindow;
  13978. //
  13979. // if( browser.ie ) {
  13980. //
  13981. // var timer = window.setInterval(function(){
  13982. //
  13983. // if( iframeWindow.document && iframeWindow.document.body ) {
  13984. // window.clearInterval( timer );
  13985. // timer = null;
  13986. // domUtils.on( iframeWindow.document.body, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  13987. // domUtils.preventDefault(e);
  13988. // } );
  13989. // }
  13990. //
  13991. // }, 100);
  13992. //
  13993. // } else {
  13994. // domUtils.on( iframeWindow, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  13995. // domUtils.preventDefault(e);
  13996. // } );
  13997. // }
  13998. //
  13999. // }, 1);
  14000. // });
  14001. // }
  14002. //
  14003. // }
  14004. this._hide();
  14005. },
  14006. mesureSize: function (){
  14007. var body = this.getDom('body');
  14008. var width = uiUtils.getClientRect(this.getDom('content')).width;
  14009. var dialogBodyStyle = body.style;
  14010. dialogBodyStyle.width = width;
  14011. return uiUtils.getClientRect(body);
  14012. },
  14013. _onTitlebarMouseDown: function (evt, el){
  14014. if (this.draggable) {
  14015. var rect;
  14016. var vpRect = uiUtils.getViewportRect();
  14017. var me = this;
  14018. uiUtils.startDrag(evt, {
  14019. ondragstart: function (){
  14020. rect = uiUtils.getClientRect(me.getDom());
  14021. me.getDom('contmask').style.visibility = 'visible';
  14022. me.dragMask.show(me.getDom().style.zIndex - 1);
  14023. },
  14024. ondragmove: function (x, y){
  14025. var left = rect.left + x;
  14026. var top = rect.top + y;
  14027. me.safeSetOffset({
  14028. left: left,
  14029. top: top
  14030. });
  14031. },
  14032. ondragstop: function (){
  14033. me.getDom('contmask').style.visibility = 'hidden';
  14034. domUtils.removeClasses(me.getDom(), ['edui-state-centered']);
  14035. me.dragMask.hide();
  14036. }
  14037. });
  14038. }
  14039. },
  14040. reset: function (){
  14041. this.getDom('content').innerHTML = this.getContentHtml();
  14042. this.fireEvent('dialogafterreset');
  14043. },
  14044. _show: function (){
  14045. if (this._hidden) {
  14046. this.getDom().style.display = '';
  14047. //要高过编辑器的zindxe
  14048. this.editor.container.style.zIndex && (this.getDom().style.zIndex = this.editor.container.style.zIndex * 1 + 10);
  14049. this._hidden = false;
  14050. this.fireEvent('show');
  14051. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = this.getDom().style.zIndex - 4;
  14052. }
  14053. },
  14054. isHidden: function (){
  14055. return this._hidden;
  14056. },
  14057. _hide: function (){
  14058. if (!this._hidden) {
  14059. var wrapNode = this.getDom();
  14060. wrapNode.style.display = 'none';
  14061. wrapNode.style.zIndex = '';
  14062. wrapNode.style.width = '';
  14063. wrapNode.style.height = '';
  14064. this._hidden = true;
  14065. this.fireEvent('hide');
  14066. }
  14067. },
  14068. open: function (){
  14069. if (this.autoReset) {
  14070. //有可能还没有渲染
  14071. try{
  14072. this.reset();
  14073. }catch(e){
  14074. this.render();
  14075. this.open()
  14076. }
  14077. }
  14078. this.showAtCenter();
  14079. if (this.iframeUrl) {
  14080. try {
  14081. this.getDom('iframe').focus();
  14082. } catch(ex){}
  14083. }
  14084. activeDialog = this;
  14085. },
  14086. _onCloseButtonClick: function (evt, el){
  14087. this.close(false);
  14088. },
  14089. close: function (ok){
  14090. if (this.fireEvent('close', ok) !== false) {
  14091. //还原环境
  14092. if ( this.fullscreen ) {
  14093. document.documentElement.style.overflowX = this._originalContext.html.overflowX;
  14094. document.documentElement.style.overflowY = this._originalContext.html.overflowY;
  14095. document.body.style.overflowX = this._originalContext.body.overflowX;
  14096. document.body.style.overflowY = this._originalContext.body.overflowY;
  14097. delete this._originalContext;
  14098. }
  14099. this._hide();
  14100. //销毁content
  14101. var content = this.getDom('content');
  14102. var iframe = this.getDom('iframe');
  14103. if (content && iframe) {
  14104. var doc = iframe.contentDocument || iframe.contentWindow.document;
  14105. doc && (doc.body.innerHTML = '');
  14106. domUtils.remove(content);
  14107. }
  14108. }
  14109. }
  14110. };
  14111. utils.inherits(Dialog, UIBase);
  14112. })();
  14113. // ui/menubutton.js
  14114. ///import core
  14115. ///import uicore
  14116. ///import ui/menu.js
  14117. ///import ui/splitbutton.js
  14118. (function (){
  14119. var utils = baidu.editor.utils,
  14120. Menu = baidu.editor.ui.Menu,
  14121. SplitButton = baidu.editor.ui.SplitButton,
  14122. MenuButton = baidu.editor.ui.MenuButton = function (options){
  14123. this.initOptions(options);
  14124. this.initMenuButton();
  14125. };
  14126. MenuButton.prototype = {
  14127. initMenuButton: function (){
  14128. var me = this;
  14129. this.uiName = "menubutton";
  14130. this.popup = new Menu({
  14131. items: me.items,
  14132. className: me.className,
  14133. editor:me.editor
  14134. });
  14135. this.popup.addListener('show', function (){
  14136. var list = this;
  14137. for (var i=0; i<list.items.length; i++) {
  14138. list.items[i].removeState('checked');
  14139. if (list.items[i].value == me._value) {
  14140. list.items[i].addState('checked');
  14141. this.value = me._value;
  14142. }
  14143. }
  14144. });
  14145. this.initSplitButton();
  14146. },
  14147. setValue : function(value){
  14148. this._value = value;
  14149. }
  14150. };
  14151. utils.inherits(MenuButton, SplitButton);
  14152. })();
  14153. // ui/multiMenu.js
  14154. ///import core
  14155. ///import uicore
  14156. ///commands 表情
  14157. (function(){
  14158. var utils = baidu.editor.utils,
  14159. Popup = baidu.editor.ui.Popup,
  14160. SplitButton = baidu.editor.ui.SplitButton,
  14161. MultiMenuPop = baidu.editor.ui.MultiMenuPop = function(options){
  14162. this.initOptions(options);
  14163. this.initMultiMenu();
  14164. };
  14165. MultiMenuPop.prototype = {
  14166. initMultiMenu: function (){
  14167. var me = this;
  14168. this.popup = new Popup({
  14169. content: '',
  14170. editor : me.editor,
  14171. iframe_rendered: false,
  14172. onshow: function (){
  14173. if (!this.iframe_rendered) {
  14174. this.iframe_rendered = true;
  14175. this.getDom('content').innerHTML = '<iframe id="'+me.id+'_iframe" src="'+ me.iframeUrl +'" frameborder="0"></iframe>';
  14176. me.editor.container.style.zIndex && (this.getDom().style.zIndex = me.editor.container.style.zIndex * 1 + 1);
  14177. }
  14178. }
  14179. // canSideUp:false,
  14180. // canSideLeft:false
  14181. });
  14182. this.onbuttonclick = function(){
  14183. this.showPopup();
  14184. };
  14185. this.initSplitButton();
  14186. }
  14187. };
  14188. utils.inherits(MultiMenuPop, SplitButton);
  14189. })();
  14190. // ui/shortcutmenu.js
  14191. (function () {
  14192. var UI = baidu.editor.ui,
  14193. UIBase = UI.UIBase,
  14194. uiUtils = UI.uiUtils,
  14195. utils = baidu.editor.utils,
  14196. domUtils = baidu.editor.dom.domUtils;
  14197. var allMenus = [],//存储所有快捷菜单
  14198. timeID,
  14199. isSubMenuShow = false;//是否有子pop显示
  14200. var ShortCutMenu = UI.ShortCutMenu = function (options) {
  14201. this.initOptions (options);
  14202. this.initShortCutMenu ();
  14203. };
  14204. ShortCutMenu.postHide = hideAllMenu;
  14205. ShortCutMenu.prototype = {
  14206. isHidden : true ,
  14207. SPACE : 5 ,
  14208. initShortCutMenu : function () {
  14209. this.items = this.items || [];
  14210. this.initUIBase ();
  14211. this.initItems ();
  14212. this.initEvent ();
  14213. allMenus.push (this);
  14214. } ,
  14215. initEvent : function () {
  14216. var me = this,
  14217. doc = me.editor.document;
  14218. domUtils.on (doc , "mousemove" , function (e) {
  14219. if (me.isHidden === false) {
  14220. //有pop显示就不隐藏快捷菜单
  14221. if (me.getSubMenuMark () || me.eventType == "contextmenu") return;
  14222. var flag = true,
  14223. el = me.getDom (),
  14224. wt = el.offsetWidth,
  14225. ht = el.offsetHeight,
  14226. distanceX = wt / 2 + me.SPACE,//距离中心X标准
  14227. distanceY = ht / 2,//距离中心Y标准
  14228. x = Math.abs (e.screenX - me.left),//离中心距离横坐标
  14229. y = Math.abs (e.screenY - me.top);//离中心距离纵坐标
  14230. clearTimeout (timeID);
  14231. timeID = setTimeout (function () {
  14232. if (y > 0 && y < distanceY) {
  14233. me.setOpacity (el , "1");
  14234. } else if (y > distanceY && y < distanceY + 70) {
  14235. me.setOpacity (el , "0.5");
  14236. flag = false;
  14237. } else if (y > distanceY + 70 && y < distanceY + 140) {
  14238. me.hide ();
  14239. }
  14240. if (flag && x > 0 && x < distanceX) {
  14241. me.setOpacity (el , "1")
  14242. } else if (x > distanceX && x < distanceX + 70) {
  14243. me.setOpacity (el , "0.5")
  14244. } else if (x > distanceX + 70 && x < distanceX + 140) {
  14245. me.hide ();
  14246. }
  14247. });
  14248. }
  14249. });
  14250. //ie\ff下 mouseout不准
  14251. if (browser.chrome) {
  14252. domUtils.on (doc , "mouseout" , function (e) {
  14253. var relatedTgt = e.relatedTarget || e.toElement;
  14254. if (relatedTgt == null || relatedTgt.tagName == "HTML") {
  14255. me.hide ();
  14256. }
  14257. });
  14258. }
  14259. me.editor.addListener ("afterhidepop" , function () {
  14260. if (!me.isHidden) {
  14261. isSubMenuShow = true;
  14262. }
  14263. });
  14264. } ,
  14265. initItems : function () {
  14266. if (utils.isArray (this.items)) {
  14267. for (var i = 0, len = this.items.length ; i < len ; i++) {
  14268. var item = this.items[i].toLowerCase ();
  14269. if (UI[item]) {
  14270. this.items[i] = new UI[item] (this.editor);
  14271. this.items[i].className += " edui-shortcutsubmenu ";
  14272. }
  14273. }
  14274. }
  14275. } ,
  14276. setOpacity : function (el , value) {
  14277. if (browser.ie && browser.version < 9) {
  14278. el.style.filter = "alpha(opacity = " + parseFloat (value) * 100 + ");"
  14279. } else {
  14280. el.style.opacity = value;
  14281. }
  14282. } ,
  14283. getSubMenuMark : function () {
  14284. isSubMenuShow = false;
  14285. var layerEle = uiUtils.getFixedLayer ();
  14286. var list = domUtils.getElementsByTagName (layerEle , "div" , function (node) {
  14287. return domUtils.hasClass (node , "edui-shortcutsubmenu edui-popup")
  14288. });
  14289. for (var i = 0, node ; node = list[i++] ;) {
  14290. if (node.style.display != "none") {
  14291. isSubMenuShow = true;
  14292. }
  14293. }
  14294. return isSubMenuShow;
  14295. } ,
  14296. show : function (e , hasContextmenu) {
  14297. var me = this,
  14298. offset = {},
  14299. el = this.getDom (),
  14300. fixedlayer = uiUtils.getFixedLayer ();
  14301. function setPos (offset) {
  14302. if (offset.left < 0) {
  14303. offset.left = 0;
  14304. }
  14305. if (offset.top < 0) {
  14306. offset.top = 0;
  14307. }
  14308. el.style.cssText = "position:absolute;left:" + offset.left + "px;top:" + offset.top + "px;";
  14309. }
  14310. function setPosByCxtMenu (menu) {
  14311. if (!menu.tagName) {
  14312. menu = menu.getDom ();
  14313. }
  14314. offset.left = parseInt (menu.style.left);
  14315. offset.top = parseInt (menu.style.top);
  14316. offset.top -= el.offsetHeight + 15;
  14317. setPos (offset);
  14318. }
  14319. me.eventType = e.type;
  14320. el.style.cssText = "display:block;left:-9999px";
  14321. if (e.type == "contextmenu" && hasContextmenu) {
  14322. var menu = domUtils.getElementsByTagName (fixedlayer , "div" , "edui-contextmenu")[0];
  14323. if (menu) {
  14324. setPosByCxtMenu (menu)
  14325. } else {
  14326. me.editor.addListener ("aftershowcontextmenu" , function (type , menu) {
  14327. setPosByCxtMenu (menu);
  14328. });
  14329. }
  14330. } else {
  14331. offset = uiUtils.getViewportOffsetByEvent (e);
  14332. offset.top -= el.offsetHeight + me.SPACE;
  14333. offset.left += me.SPACE + 20;
  14334. setPos (offset);
  14335. me.setOpacity (el , 0.2);
  14336. }
  14337. me.isHidden = false;
  14338. me.left = e.screenX + el.offsetWidth / 2 - me.SPACE;
  14339. me.top = e.screenY - (el.offsetHeight / 2) - me.SPACE;
  14340. if (me.editor) {
  14341. el.style.zIndex = me.editor.container.style.zIndex * 1 + 10;
  14342. fixedlayer.style.zIndex = el.style.zIndex - 1;
  14343. }
  14344. } ,
  14345. hide : function () {
  14346. if (this.getDom ()) {
  14347. this.getDom ().style.display = "none";
  14348. }
  14349. this.isHidden = true;
  14350. } ,
  14351. postRender : function () {
  14352. if (utils.isArray (this.items)) {
  14353. for (var i = 0, item ; item = this.items[i++] ;) {
  14354. item.postRender ();
  14355. }
  14356. }
  14357. } ,
  14358. getHtmlTpl : function () {
  14359. var buff;
  14360. if (utils.isArray (this.items)) {
  14361. buff = [];
  14362. for (var i = 0 ; i < this.items.length ; i++) {
  14363. buff[i] = this.items[i].renderHtml ();
  14364. }
  14365. buff = buff.join ("");
  14366. } else {
  14367. buff = this.items;
  14368. }
  14369. return '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
  14370. buff +
  14371. '</div>';
  14372. }
  14373. };
  14374. utils.inherits (ShortCutMenu , UIBase);
  14375. function hideAllMenu (e) {
  14376. var tgt = e.target || e.srcElement,
  14377. cur = domUtils.findParent (tgt , function (node) {
  14378. return domUtils.hasClass (node , "edui-shortcutmenu") || domUtils.hasClass (node , "edui-popup");
  14379. } , true);
  14380. if (!cur) {
  14381. for (var i = 0, menu ; menu = allMenus[i++] ;) {
  14382. menu.hide ()
  14383. }
  14384. }
  14385. }
  14386. domUtils.on (document , 'mousedown' , function (e) {
  14387. hideAllMenu (e);
  14388. });
  14389. domUtils.on (window , 'scroll' , function (e) {
  14390. hideAllMenu (e);
  14391. });
  14392. }) ();
  14393. // ui/breakline.js
  14394. (function (){
  14395. var utils = baidu.editor.utils,
  14396. UIBase = baidu.editor.ui.UIBase,
  14397. Breakline = baidu.editor.ui.Breakline = function (options){
  14398. this.initOptions(options);
  14399. this.initSeparator();
  14400. };
  14401. Breakline.prototype = {
  14402. uiName: 'Breakline',
  14403. initSeparator: function (){
  14404. this.initUIBase();
  14405. },
  14406. getHtmlTpl: function (){
  14407. return '<br/>';
  14408. }
  14409. };
  14410. utils.inherits(Breakline, UIBase);
  14411. })();
  14412. // ui/message.js
  14413. ///import core
  14414. ///import uicore
  14415. (function () {
  14416. var utils = baidu.editor.utils,
  14417. domUtils = baidu.editor.dom.domUtils,
  14418. UIBase = baidu.editor.ui.UIBase,
  14419. Message = baidu.editor.ui.Message = function (options){
  14420. this.initOptions(options);
  14421. this.initMessage();
  14422. };
  14423. Message.prototype = {
  14424. initMessage: function (){
  14425. this.initUIBase();
  14426. },
  14427. getHtmlTpl: function (){
  14428. return '<div id="##" class="edui-message %%">' +
  14429. ' <div id="##_closer" class="edui-message-closer">×</div>' +
  14430. ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
  14431. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  14432. ' <div class="edui-shadow"></div>' +
  14433. ' <div id="##_content" class="edui-message-content">' +
  14434. ' </div>' +
  14435. ' </div>' +
  14436. '</div>';
  14437. },
  14438. reset: function(opt){
  14439. var me = this;
  14440. if (!opt.keepshow) {
  14441. clearTimeout(this.timer);
  14442. me.timer = setTimeout(function(){
  14443. me.hide();
  14444. }, opt.timeout || 4000);
  14445. }
  14446. opt.content !== undefined && me.setContent(opt.content);
  14447. opt.type !== undefined && me.setType(opt.type);
  14448. me.show();
  14449. },
  14450. postRender: function(){
  14451. var me = this,
  14452. closer = this.getDom('closer');
  14453. closer && domUtils.on(closer, 'click', function(){
  14454. me.hide();
  14455. });
  14456. },
  14457. setContent: function(content){
  14458. this.getDom('content').innerHTML = content;
  14459. },
  14460. setType: function(type){
  14461. type = type || 'info';
  14462. var body = this.getDom('body');
  14463. body.className = body.className.replace(/edui-message-type-[\w-]+/, 'edui-message-type-' + type);
  14464. },
  14465. getContent: function(){
  14466. return this.getDom('content').innerHTML;
  14467. },
  14468. getType: function(){
  14469. var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/);
  14470. return arr ? arr[1]:'';
  14471. },
  14472. show: function (){
  14473. this.getDom().style.display = 'block';
  14474. },
  14475. hide: function (){
  14476. var dom = this.getDom();
  14477. if (dom) {
  14478. dom.style.display = 'none';
  14479. dom.parentNode && dom.parentNode.removeChild(dom);
  14480. }
  14481. }
  14482. };
  14483. utils.inherits(Message, UIBase);
  14484. })();
  14485. // adapter/editorui.js
  14486. //ui跟编辑器的适配層
  14487. //那个按钮弹出是dialog,是下拉筐等都是在这个js中配置
  14488. //自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化
  14489. (function () {
  14490. var utils = baidu.editor.utils;
  14491. var editorui = baidu.editor.ui;
  14492. var _Dialog = editorui.Dialog;
  14493. editorui.buttons = {};
  14494. editorui.Dialog = function (options) {
  14495. var dialog = new _Dialog(options);
  14496. dialog.addListener('hide', function () {
  14497. if (dialog.editor) {
  14498. var editor = dialog.editor;
  14499. try {
  14500. if (browser.gecko) {
  14501. var y = editor.window.scrollY,
  14502. x = editor.window.scrollX;
  14503. editor.body.focus();
  14504. editor.window.scrollTo(x, y);
  14505. } else {
  14506. editor.focus();
  14507. }
  14508. } catch (ex) {
  14509. }
  14510. }
  14511. });
  14512. return dialog;
  14513. };
  14514. var iframeUrlMap = {
  14515. 'anchor':'~/dialogs/anchor/anchor.html',
  14516. 'insertimage':'~/dialogs/image/image.html',
  14517. 'link':'~/dialogs/link/link.html',
  14518. 'spechars':'~/dialogs/spechars/spechars.html',
  14519. 'searchreplace':'~/dialogs/searchreplace/searchreplace.html',
  14520. 'map':'~/dialogs/map/map.html',
  14521. 'gmap':'~/dialogs/gmap/gmap.html',
  14522. 'insertvideo':'~/dialogs/video/video.html',
  14523. 'help':'~/dialogs/help/help.html',
  14524. 'preview':'~/dialogs/preview/preview.html',
  14525. 'emotion':'~/dialogs/emotion/emotion.html',
  14526. 'wordimage':'~/dialogs/wordimage/wordimage.html',
  14527. 'attachment':'~/dialogs/attachment/attachment.html',
  14528. 'insertframe':'~/dialogs/insertframe/insertframe.html',
  14529. 'edittip':'~/dialogs/table/edittip.html',
  14530. 'edittable':'~/dialogs/table/edittable.html',
  14531. 'edittd':'~/dialogs/table/edittd.html',
  14532. 'webapp':'~/dialogs/webapp/webapp.html',
  14533. 'snapscreen':'~/dialogs/snapscreen/snapscreen.html',
  14534. 'scrawl':'~/dialogs/scrawl/scrawl.html',
  14535. 'music':'~/dialogs/music/music.html',
  14536. 'template':'~/dialogs/template/template.html',
  14537. 'background':'~/dialogs/background/background.html',
  14538. 'charts': '~/dialogs/charts/charts.html'
  14539. };
  14540. //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起
  14541. var btnCmds = ['undo', 'redo', 'formatmatch',
  14542. 'bold', 'italic', 'underline', 'fontborder', 'touppercase', 'tolowercase',
  14543. 'strikethrough', 'subscript', 'superscript', 'source', 'indent', 'outdent',
  14544. 'blockquote', 'pasteplain', 'pagebreak',
  14545. 'selectall', 'print','horizontal', 'removeformat', 'time', 'date', 'unlink',
  14546. 'insertparagraphbeforetable', 'insertrow', 'insertcol', 'mergeright', 'mergedown', 'deleterow',
  14547. 'deletecol', 'splittorows', 'splittocols', 'splittocells', 'mergecells', 'deletetable', 'drafts'];
  14548. for (var i = 0, ci; ci = btnCmds[i++];) {
  14549. ci = ci.toLowerCase();
  14550. editorui[ci] = function (cmd) {
  14551. return function (editor) {
  14552. var ui = new editorui.Button({
  14553. className:'edui-for-' + cmd,
  14554. title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
  14555. onclick:function () {
  14556. editor.execCommand(cmd);
  14557. },
  14558. theme:editor.options.theme,
  14559. showText:false
  14560. });
  14561. editorui.buttons[cmd] = ui;
  14562. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14563. var state = editor.queryCommandState(cmd);
  14564. if (state == -1) {
  14565. ui.setDisabled(true);
  14566. ui.setChecked(false);
  14567. } else {
  14568. if (!uiReady) {
  14569. ui.setDisabled(false);
  14570. ui.setChecked(state);
  14571. }
  14572. }
  14573. });
  14574. return ui;
  14575. };
  14576. }(ci);
  14577. }
  14578. //清除文档
  14579. editorui.cleardoc = function (editor) {
  14580. var ui = new editorui.Button({
  14581. className:'edui-for-cleardoc',
  14582. title:editor.options.labelMap.cleardoc || editor.getLang("labelMap.cleardoc") || '',
  14583. theme:editor.options.theme,
  14584. onclick:function () {
  14585. if (confirm(editor.getLang("confirmClear"))) {
  14586. editor.execCommand('cleardoc');
  14587. }
  14588. }
  14589. });
  14590. editorui.buttons["cleardoc"] = ui;
  14591. editor.addListener('selectionchange', function () {
  14592. ui.setDisabled(editor.queryCommandState('cleardoc') == -1);
  14593. });
  14594. return ui;
  14595. };
  14596. //排版,图片排版,文字方向
  14597. var typeset = {
  14598. 'justify':['left', 'right', 'center', 'justify'],
  14599. 'imagefloat':['none', 'left', 'center', 'right'],
  14600. 'directionality':['ltr', 'rtl']
  14601. };
  14602. for (var p in typeset) {
  14603. (function (cmd, val) {
  14604. for (var i = 0, ci; ci = val[i++];) {
  14605. (function (cmd2) {
  14606. editorui[cmd.replace('float', '') + cmd2] = function (editor) {
  14607. var ui = new editorui.Button({
  14608. className:'edui-for-' + cmd.replace('float', '') + cmd2,
  14609. title:editor.options.labelMap[cmd.replace('float', '') + cmd2] || editor.getLang("labelMap." + cmd.replace('float', '') + cmd2) || '',
  14610. theme:editor.options.theme,
  14611. onclick:function () {
  14612. editor.execCommand(cmd, cmd2);
  14613. }
  14614. });
  14615. editorui.buttons[cmd] = ui;
  14616. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14617. ui.setDisabled(editor.queryCommandState(cmd) == -1);
  14618. ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady);
  14619. });
  14620. return ui;
  14621. };
  14622. })(ci)
  14623. }
  14624. })(p, typeset[p])
  14625. }
  14626. //字体颜色和背景颜色
  14627. for (var i = 0, ci; ci = ['backcolor', 'forecolor'][i++];) {
  14628. editorui[ci] = function (cmd) {
  14629. return function (editor) {
  14630. var ui = new editorui.ColorButton({
  14631. className:'edui-for-' + cmd,
  14632. color:'default',
  14633. title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
  14634. editor:editor,
  14635. onpickcolor:function (t, color) {
  14636. editor.execCommand(cmd, color);
  14637. },
  14638. onpicknocolor:function () {
  14639. editor.execCommand(cmd, 'default');
  14640. this.setColor('transparent');
  14641. this.color = 'default';
  14642. },
  14643. onbuttonclick:function () {
  14644. editor.execCommand(cmd, this.color);
  14645. }
  14646. });
  14647. editorui.buttons[cmd] = ui;
  14648. editor.addListener('selectionchange', function () {
  14649. ui.setDisabled(editor.queryCommandState(cmd) == -1);
  14650. });
  14651. return ui;
  14652. };
  14653. }(ci);
  14654. }
  14655. var dialogBtns = {
  14656. noOk:['searchreplace', 'help', 'spechars', 'webapp','preview'],
  14657. ok:['attachment', 'anchor', 'link', 'insertimage', 'map', 'gmap', 'insertframe', 'wordimage',
  14658. 'insertvideo', 'insertframe', 'edittip', 'edittable', 'edittd', 'scrawl', 'template', 'music', 'background', 'charts']
  14659. };
  14660. for (var p in dialogBtns) {
  14661. (function (type, vals) {
  14662. for (var i = 0, ci; ci = vals[i++];) {
  14663. //todo opera下存在问题
  14664. if (browser.opera && ci === "searchreplace") {
  14665. continue;
  14666. }
  14667. (function (cmd) {
  14668. editorui[cmd] = function (editor, iframeUrl, title) {
  14669. iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd];
  14670. title = editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '';
  14671. var dialog;
  14672. //没有iframeUrl不创建dialog
  14673. if (iframeUrl) {
  14674. dialog = new editorui.Dialog(utils.extend({
  14675. iframeUrl:editor.ui.mapUrl(iframeUrl),
  14676. editor:editor,
  14677. className:'edui-for-' + cmd,
  14678. title:title,
  14679. holdScroll: cmd === 'insertimage',
  14680. fullscreen: /charts|preview/.test(cmd),
  14681. closeDialog:editor.getLang("closeDialog")
  14682. }, type == 'ok' ? {
  14683. buttons:[
  14684. {
  14685. className:'edui-okbutton',
  14686. label:editor.getLang("ok"),
  14687. editor:editor,
  14688. onclick:function () {
  14689. dialog.close(true);
  14690. }
  14691. },
  14692. {
  14693. className:'edui-cancelbutton',
  14694. label:editor.getLang("cancel"),
  14695. editor:editor,
  14696. onclick:function () {
  14697. dialog.close(false);
  14698. }
  14699. }
  14700. ]
  14701. } : {}));
  14702. editor.ui._dialogs[cmd + "Dialog"] = dialog;
  14703. }
  14704. var ui = new editorui.Button({
  14705. className:'edui-for-' + cmd,
  14706. title:title,
  14707. onclick:function () {
  14708. if (dialog) {
  14709. switch (cmd) {
  14710. case "wordimage":
  14711. var images = editor.execCommand("wordimage");
  14712. if (images && images.length) {
  14713. dialog.render();
  14714. dialog.open();
  14715. }
  14716. break;
  14717. case "scrawl":
  14718. if (editor.queryCommandState("scrawl") != -1) {
  14719. dialog.render();
  14720. dialog.open();
  14721. }
  14722. break;
  14723. default:
  14724. dialog.render();
  14725. dialog.open();
  14726. }
  14727. }
  14728. },
  14729. theme:editor.options.theme,
  14730. disabled:(cmd == 'scrawl' && editor.queryCommandState("scrawl") == -1) || ( cmd == 'charts' )
  14731. });
  14732. editorui.buttons[cmd] = ui;
  14733. editor.addListener('selectionchange', function () {
  14734. //只存在于右键菜单而无工具栏按钮的ui不需要检测状态
  14735. var unNeedCheckState = {'edittable':1};
  14736. if (cmd in unNeedCheckState)return;
  14737. var state = editor.queryCommandState(cmd);
  14738. if (ui.getDom()) {
  14739. ui.setDisabled(state == -1);
  14740. ui.setChecked(state);
  14741. }
  14742. });
  14743. return ui;
  14744. };
  14745. })(ci.toLowerCase())
  14746. }
  14747. })(p, dialogBtns[p]);
  14748. }
  14749. editorui.snapscreen = function (editor, iframeUrl, title) {
  14750. title = editor.options.labelMap['snapscreen'] || editor.getLang("labelMap.snapscreen") || '';
  14751. var ui = new editorui.Button({
  14752. className:'edui-for-snapscreen',
  14753. title:title,
  14754. onclick:function () {
  14755. editor.execCommand("snapscreen");
  14756. },
  14757. theme:editor.options.theme
  14758. });
  14759. editorui.buttons['snapscreen'] = ui;
  14760. iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})["snapscreen"] || iframeUrlMap["snapscreen"];
  14761. if (iframeUrl) {
  14762. var dialog = new editorui.Dialog({
  14763. iframeUrl:editor.ui.mapUrl(iframeUrl),
  14764. editor:editor,
  14765. className:'edui-for-snapscreen',
  14766. title:title,
  14767. buttons:[
  14768. {
  14769. className:'edui-okbutton',
  14770. label:editor.getLang("ok"),
  14771. editor:editor,
  14772. onclick:function () {
  14773. dialog.close(true);
  14774. }
  14775. },
  14776. {
  14777. className:'edui-cancelbutton',
  14778. label:editor.getLang("cancel"),
  14779. editor:editor,
  14780. onclick:function () {
  14781. dialog.close(false);
  14782. }
  14783. }
  14784. ]
  14785. });
  14786. dialog.render();
  14787. editor.ui._dialogs["snapscreenDialog"] = dialog;
  14788. }
  14789. editor.addListener('selectionchange', function () {
  14790. ui.setDisabled(editor.queryCommandState('snapscreen') == -1);
  14791. });
  14792. return ui;
  14793. };
  14794. editorui.insertcode = function (editor, list, title) {
  14795. list = editor.options['insertcode'] || [];
  14796. title = editor.options.labelMap['insertcode'] || editor.getLang("labelMap.insertcode") || '';
  14797. // if (!list.length) return;
  14798. var items = [];
  14799. utils.each(list,function(key,val){
  14800. items.push({
  14801. label:key,
  14802. value:val,
  14803. theme:editor.options.theme,
  14804. renderLabelHtml:function () {
  14805. return '<div class="edui-label %%-label" >' + (this.label || '') + '</div>';
  14806. }
  14807. });
  14808. });
  14809. var ui = new editorui.Combox({
  14810. editor:editor,
  14811. items:items,
  14812. onselect:function (t, index) {
  14813. editor.execCommand('insertcode', this.items[index].value);
  14814. },
  14815. onbuttonclick:function () {
  14816. this.showPopup();
  14817. },
  14818. title:title,
  14819. initValue:title,
  14820. className:'edui-for-insertcode',
  14821. indexByValue:function (value) {
  14822. if (value) {
  14823. for (var i = 0, ci; ci = this.items[i]; i++) {
  14824. if (ci.value.indexOf(value) != -1)
  14825. return i;
  14826. }
  14827. }
  14828. return -1;
  14829. }
  14830. });
  14831. editorui.buttons['insertcode'] = ui;
  14832. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14833. if (!uiReady) {
  14834. var state = editor.queryCommandState('insertcode');
  14835. if (state == -1) {
  14836. ui.setDisabled(true);
  14837. } else {
  14838. ui.setDisabled(false);
  14839. var value = editor.queryCommandValue('insertcode');
  14840. if(!value){
  14841. ui.setValue(title);
  14842. return;
  14843. }
  14844. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  14845. value && (value = value.replace(/['"]/g, '').split(',')[0]);
  14846. ui.setValue(value);
  14847. }
  14848. }
  14849. });
  14850. return ui;
  14851. };
  14852. editorui.fontfamily = function (editor, list, title) {
  14853. list = editor.options['fontfamily'] || [];
  14854. title = editor.options.labelMap['fontfamily'] || editor.getLang("labelMap.fontfamily") || '';
  14855. if (!list.length) return;
  14856. for (var i = 0, ci, items = []; ci = list[i]; i++) {
  14857. var langLabel = editor.getLang('fontfamily')[ci.name] || "";
  14858. (function (key, val) {
  14859. items.push({
  14860. label:key,
  14861. value:val,
  14862. theme:editor.options.theme,
  14863. renderLabelHtml:function () {
  14864. return '<div class="edui-label %%-label" style="font-family:' +
  14865. utils.unhtml(this.value) + '">' + (this.label || '') + '</div>';
  14866. }
  14867. });
  14868. })(ci.label || langLabel, ci.val)
  14869. }
  14870. var ui = new editorui.Combox({
  14871. editor:editor,
  14872. items:items,
  14873. onselect:function (t, index) {
  14874. editor.execCommand('FontFamily', this.items[index].value);
  14875. },
  14876. onbuttonclick:function () {
  14877. this.showPopup();
  14878. },
  14879. title:title,
  14880. initValue:title,
  14881. className:'edui-for-fontfamily',
  14882. indexByValue:function (value) {
  14883. if (value) {
  14884. for (var i = 0, ci; ci = this.items[i]; i++) {
  14885. if (ci.value.indexOf(value) != -1)
  14886. return i;
  14887. }
  14888. }
  14889. return -1;
  14890. }
  14891. });
  14892. editorui.buttons['fontfamily'] = ui;
  14893. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14894. if (!uiReady) {
  14895. var state = editor.queryCommandState('FontFamily');
  14896. if (state == -1) {
  14897. ui.setDisabled(true);
  14898. } else {
  14899. ui.setDisabled(false);
  14900. var value = editor.queryCommandValue('FontFamily');
  14901. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  14902. value && (value = value.replace(/['"]/g, '').split(',')[0]);
  14903. ui.setValue(value);
  14904. }
  14905. }
  14906. });
  14907. return ui;
  14908. };
  14909. editorui.fontsize = function (editor, list, title) {
  14910. title = editor.options.labelMap['fontsize'] || editor.getLang("labelMap.fontsize") || '';
  14911. list = list || editor.options['fontsize'] || [];
  14912. if (!list.length) return;
  14913. var items = [];
  14914. for (var i = 0; i < list.length; i++) {
  14915. var size = list[i] + 'px';
  14916. items.push({
  14917. label:size,
  14918. value:size,
  14919. theme:editor.options.theme,
  14920. renderLabelHtml:function () {
  14921. return '<div class="edui-label %%-label" style="line-height:1;font-size:' +
  14922. this.value + '">' + (this.label || '') + '</div>';
  14923. }
  14924. });
  14925. }
  14926. var ui = new editorui.Combox({
  14927. editor:editor,
  14928. items:items,
  14929. title:title,
  14930. initValue:title,
  14931. onselect:function (t, index) {
  14932. editor.execCommand('FontSize', this.items[index].value);
  14933. },
  14934. onbuttonclick:function () {
  14935. this.showPopup();
  14936. },
  14937. className:'edui-for-fontsize'
  14938. });
  14939. editorui.buttons['fontsize'] = ui;
  14940. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14941. if (!uiReady) {
  14942. var state = editor.queryCommandState('FontSize');
  14943. if (state == -1) {
  14944. ui.setDisabled(true);
  14945. } else {
  14946. ui.setDisabled(false);
  14947. ui.setValue(editor.queryCommandValue('FontSize'));
  14948. }
  14949. }
  14950. });
  14951. return ui;
  14952. };
  14953. editorui.paragraph = function (editor, list, title) {
  14954. title = editor.options.labelMap['paragraph'] || editor.getLang("labelMap.paragraph") || '';
  14955. list = editor.options['paragraph'] || [];
  14956. if (utils.isEmptyObject(list)) return;
  14957. var items = [];
  14958. for (var i in list) {
  14959. items.push({
  14960. value:i,
  14961. label:list[i] || editor.getLang("paragraph")[i],
  14962. theme:editor.options.theme,
  14963. renderLabelHtml:function () {
  14964. return '<div class="edui-label %%-label"><span class="edui-for-' + this.value + '">' + (this.label || '') + '</span></div>';
  14965. }
  14966. })
  14967. }
  14968. var ui = new editorui.Combox({
  14969. editor:editor,
  14970. items:items,
  14971. title:title,
  14972. initValue:title,
  14973. className:'edui-for-paragraph',
  14974. onselect:function (t, index) {
  14975. editor.execCommand('Paragraph', this.items[index].value);
  14976. },
  14977. onbuttonclick:function () {
  14978. this.showPopup();
  14979. }
  14980. });
  14981. editorui.buttons['paragraph'] = ui;
  14982. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  14983. if (!uiReady) {
  14984. var state = editor.queryCommandState('Paragraph');
  14985. if (state == -1) {
  14986. ui.setDisabled(true);
  14987. } else {
  14988. ui.setDisabled(false);
  14989. var value = editor.queryCommandValue('Paragraph');
  14990. var index = ui.indexByValue(value);
  14991. if (index != -1) {
  14992. ui.setValue(value);
  14993. } else {
  14994. ui.setValue(ui.initValue);
  14995. }
  14996. }
  14997. }
  14998. });
  14999. return ui;
  15000. };
  15001. //自定义标题
  15002. editorui.customstyle = function (editor) {
  15003. var list = editor.options['customstyle'] || [],
  15004. title = editor.options.labelMap['customstyle'] || editor.getLang("labelMap.customstyle") || '';
  15005. if (!list.length)return;
  15006. var langCs = editor.getLang('customstyle');
  15007. for (var i = 0, items = [], t; t = list[i++];) {
  15008. (function (t) {
  15009. var ck = {};
  15010. ck.label = t.label ? t.label : langCs[t.name];
  15011. ck.style = t.style;
  15012. ck.className = t.className;
  15013. ck.tag = t.tag;
  15014. items.push({
  15015. label:ck.label,
  15016. value:ck,
  15017. theme:editor.options.theme,
  15018. renderLabelHtml:function () {
  15019. return '<div class="edui-label %%-label">' + '<' + ck.tag + ' ' + (ck.className ? ' class="' + ck.className + '"' : "")
  15020. + (ck.style ? ' style="' + ck.style + '"' : "") + '>' + ck.label + "<\/" + ck.tag + ">"
  15021. + '</div>';
  15022. }
  15023. });
  15024. })(t);
  15025. }
  15026. var ui = new editorui.Combox({
  15027. editor:editor,
  15028. items:items,
  15029. title:title,
  15030. initValue:title,
  15031. className:'edui-for-customstyle',
  15032. onselect:function (t, index) {
  15033. editor.execCommand('customstyle', this.items[index].value);
  15034. },
  15035. onbuttonclick:function () {
  15036. this.showPopup();
  15037. },
  15038. indexByValue:function (value) {
  15039. for (var i = 0, ti; ti = this.items[i++];) {
  15040. if (ti.label == value) {
  15041. return i - 1
  15042. }
  15043. }
  15044. return -1;
  15045. }
  15046. });
  15047. editorui.buttons['customstyle'] = ui;
  15048. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  15049. if (!uiReady) {
  15050. var state = editor.queryCommandState('customstyle');
  15051. if (state == -1) {
  15052. ui.setDisabled(true);
  15053. } else {
  15054. ui.setDisabled(false);
  15055. var value = editor.queryCommandValue('customstyle');
  15056. var index = ui.indexByValue(value);
  15057. if (index != -1) {
  15058. ui.setValue(value);
  15059. } else {
  15060. ui.setValue(ui.initValue);
  15061. }
  15062. }
  15063. }
  15064. });
  15065. return ui;
  15066. };
  15067. editorui.inserttable = function (editor, iframeUrl, title) {
  15068. title = editor.options.labelMap['inserttable'] || editor.getLang("labelMap.inserttable") || '';
  15069. var ui = new editorui.TableButton({
  15070. editor:editor,
  15071. title:title,
  15072. className:'edui-for-inserttable',
  15073. onpicktable:function (t, numCols, numRows) {
  15074. editor.execCommand('InsertTable', {numRows:numRows, numCols:numCols, border:1});
  15075. },
  15076. onbuttonclick:function () {
  15077. this.showPopup();
  15078. }
  15079. });
  15080. editorui.buttons['inserttable'] = ui;
  15081. editor.addListener('selectionchange', function () {
  15082. ui.setDisabled(editor.queryCommandState('inserttable') == -1);
  15083. });
  15084. return ui;
  15085. };
  15086. editorui.lineheight = function (editor) {
  15087. var val = editor.options.lineheight || [];
  15088. if (!val.length)return;
  15089. for (var i = 0, ci, items = []; ci = val[i++];) {
  15090. items.push({
  15091. //todo:写死了
  15092. label:ci,
  15093. value:ci,
  15094. theme:editor.options.theme,
  15095. onclick:function () {
  15096. editor.execCommand("lineheight", this.value);
  15097. }
  15098. })
  15099. }
  15100. var ui = new editorui.MenuButton({
  15101. editor:editor,
  15102. className:'edui-for-lineheight',
  15103. title:editor.options.labelMap['lineheight'] || editor.getLang("labelMap.lineheight") || '',
  15104. items:items,
  15105. onbuttonclick:function () {
  15106. var value = editor.queryCommandValue('LineHeight') || this.value;
  15107. editor.execCommand("LineHeight", value);
  15108. }
  15109. });
  15110. editorui.buttons['lineheight'] = ui;
  15111. editor.addListener('selectionchange', function () {
  15112. var state = editor.queryCommandState('LineHeight');
  15113. if (state == -1) {
  15114. ui.setDisabled(true);
  15115. } else {
  15116. ui.setDisabled(false);
  15117. var value = editor.queryCommandValue('LineHeight');
  15118. value && ui.setValue((value + '').replace(/cm/, ''));
  15119. ui.setChecked(state)
  15120. }
  15121. });
  15122. return ui;
  15123. };
  15124. var rowspacings = ['top', 'bottom'];
  15125. for (var r = 0, ri; ri = rowspacings[r++];) {
  15126. (function (cmd) {
  15127. editorui['rowspacing' + cmd] = function (editor) {
  15128. var val = editor.options['rowspacing' + cmd] || [];
  15129. if (!val.length) return null;
  15130. for (var i = 0, ci, items = []; ci = val[i++];) {
  15131. items.push({
  15132. label:ci,
  15133. value:ci,
  15134. theme:editor.options.theme,
  15135. onclick:function () {
  15136. editor.execCommand("rowspacing", this.value, cmd);
  15137. }
  15138. })
  15139. }
  15140. var ui = new editorui.MenuButton({
  15141. editor:editor,
  15142. className:'edui-for-rowspacing' + cmd,
  15143. title:editor.options.labelMap['rowspacing' + cmd] || editor.getLang("labelMap.rowspacing" + cmd) || '',
  15144. items:items,
  15145. onbuttonclick:function () {
  15146. var value = editor.queryCommandValue('rowspacing', cmd) || this.value;
  15147. editor.execCommand("rowspacing", value, cmd);
  15148. }
  15149. });
  15150. editorui.buttons[cmd] = ui;
  15151. editor.addListener('selectionchange', function () {
  15152. var state = editor.queryCommandState('rowspacing', cmd);
  15153. if (state == -1) {
  15154. ui.setDisabled(true);
  15155. } else {
  15156. ui.setDisabled(false);
  15157. var value = editor.queryCommandValue('rowspacing', cmd);
  15158. value && ui.setValue((value + '').replace(/%/, ''));
  15159. ui.setChecked(state)
  15160. }
  15161. });
  15162. return ui;
  15163. }
  15164. })(ri)
  15165. }
  15166. //有序,无序列表
  15167. var lists = ['insertorderedlist', 'insertunorderedlist'];
  15168. for (var l = 0, cl; cl = lists[l++];) {
  15169. (function (cmd) {
  15170. editorui[cmd] = function (editor) {
  15171. var vals = editor.options[cmd],
  15172. _onMenuClick = function () {
  15173. editor.execCommand(cmd, this.value);
  15174. }, items = [];
  15175. for (var i in vals) {
  15176. items.push({
  15177. label:vals[i] || editor.getLang()[cmd][i] || "",
  15178. value:i,
  15179. theme:editor.options.theme,
  15180. onclick:_onMenuClick
  15181. })
  15182. }
  15183. var ui = new editorui.MenuButton({
  15184. editor:editor,
  15185. className:'edui-for-' + cmd,
  15186. title:editor.getLang("labelMap." + cmd) || '',
  15187. 'items':items,
  15188. onbuttonclick:function () {
  15189. var value = editor.queryCommandValue(cmd) || this.value;
  15190. editor.execCommand(cmd, value);
  15191. }
  15192. });
  15193. editorui.buttons[cmd] = ui;
  15194. editor.addListener('selectionchange', function () {
  15195. var state = editor.queryCommandState(cmd);
  15196. if (state == -1) {
  15197. ui.setDisabled(true);
  15198. } else {
  15199. ui.setDisabled(false);
  15200. var value = editor.queryCommandValue(cmd);
  15201. ui.setValue(value);
  15202. ui.setChecked(state)
  15203. }
  15204. });
  15205. return ui;
  15206. };
  15207. })(cl)
  15208. }
  15209. editorui.fullscreen = function (editor, title) {
  15210. title = editor.options.labelMap['fullscreen'] || editor.getLang("labelMap.fullscreen") || '';
  15211. var ui = new editorui.Button({
  15212. className:'edui-for-fullscreen',
  15213. title:title,
  15214. theme:editor.options.theme,
  15215. onclick:function () {
  15216. if (editor.ui) {
  15217. editor.ui.setFullScreen(!editor.ui.isFullScreen());
  15218. }
  15219. this.setChecked(editor.ui.isFullScreen());
  15220. }
  15221. });
  15222. editorui.buttons['fullscreen'] = ui;
  15223. editor.addListener('selectionchange', function () {
  15224. var state = editor.queryCommandState('fullscreen');
  15225. ui.setDisabled(state == -1);
  15226. ui.setChecked(editor.ui.isFullScreen());
  15227. });
  15228. return ui;
  15229. };
  15230. // 表情
  15231. editorui["emotion"] = function (editor, iframeUrl) {
  15232. var cmd = "emotion";
  15233. var ui = new editorui.MultiMenuPop({
  15234. title:editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd + "") || '',
  15235. editor:editor,
  15236. className:'edui-for-' + cmd,
  15237. iframeUrl:editor.ui.mapUrl(iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd])
  15238. });
  15239. editorui.buttons[cmd] = ui;
  15240. editor.addListener('selectionchange', function () {
  15241. ui.setDisabled(editor.queryCommandState(cmd) == -1)
  15242. });
  15243. return ui;
  15244. };
  15245. editorui.autotypeset = function (editor) {
  15246. var ui = new editorui.AutoTypeSetButton({
  15247. editor:editor,
  15248. title:editor.options.labelMap['autotypeset'] || editor.getLang("labelMap.autotypeset") || '',
  15249. className:'edui-for-autotypeset',
  15250. onbuttonclick:function () {
  15251. editor.execCommand('autotypeset')
  15252. }
  15253. });
  15254. editorui.buttons['autotypeset'] = ui;
  15255. editor.addListener('selectionchange', function () {
  15256. ui.setDisabled(editor.queryCommandState('autotypeset') == -1);
  15257. });
  15258. return ui;
  15259. };
  15260. /* 简单上传插件 */
  15261. editorui["simpleupload"] = function (editor) {
  15262. var name = 'simpleupload',
  15263. ui = new editorui.Button({
  15264. className:'edui-for-' + name,
  15265. title:editor.options.labelMap[name] || editor.getLang("labelMap." + name) || '',
  15266. onclick:function () {},
  15267. theme:editor.options.theme,
  15268. showText:false
  15269. });
  15270. editorui.buttons[name] = ui;
  15271. editor.addListener('ready', function() {
  15272. var b = ui.getDom('body'),
  15273. iconSpan = b.children[0];
  15274. editor.fireEvent('simpleuploadbtnready', iconSpan);
  15275. });
  15276. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  15277. var state = editor.queryCommandState(name);
  15278. if (state == -1) {
  15279. ui.setDisabled(true);
  15280. ui.setChecked(false);
  15281. } else {
  15282. if (!uiReady) {
  15283. ui.setDisabled(false);
  15284. ui.setChecked(state);
  15285. }
  15286. }
  15287. });
  15288. return ui;
  15289. };
  15290. })();
  15291. // adapter/editor.js
  15292. ///import core
  15293. ///commands 全屏
  15294. ///commandsName FullScreen
  15295. ///commandsTitle 全屏
  15296. (function () {
  15297. var utils = baidu.editor.utils,
  15298. uiUtils = baidu.editor.ui.uiUtils,
  15299. UIBase = baidu.editor.ui.UIBase,
  15300. domUtils = baidu.editor.dom.domUtils;
  15301. var nodeStack = [];
  15302. function EditorUI(options) {
  15303. this.initOptions(options);
  15304. this.initEditorUI();
  15305. }
  15306. EditorUI.prototype = {
  15307. uiName:'editor',
  15308. initEditorUI:function () {
  15309. this.editor.ui = this;
  15310. this._dialogs = {};
  15311. this.initUIBase();
  15312. this._initToolbars();
  15313. var editor = this.editor,
  15314. me = this;
  15315. editor.addListener('ready', function () {
  15316. //提供getDialog方法
  15317. editor.getDialog = function (name) {
  15318. return editor.ui._dialogs[name + "Dialog"];
  15319. };
  15320. domUtils.on(editor.window, 'scroll', function (evt) {
  15321. baidu.editor.ui.Popup.postHide(evt);
  15322. });
  15323. //提供编辑器实时宽高(全屏时宽高不变化)
  15324. editor.ui._actualFrameWidth = editor.options.initialFrameWidth;
  15325. UE.browser.ie && UE.browser.version === 6 && editor.container.ownerDocument.execCommand("BackgroundImageCache", false, true);
  15326. //display bottom-bar label based on config
  15327. if (editor.options.elementPathEnabled) {
  15328. editor.ui.getDom('elementpath').innerHTML = '<div class="edui-editor-breadcrumb">' + editor.getLang("elementPathTip") + ':</div>';
  15329. }
  15330. if (editor.options.wordCount) {
  15331. function countFn() {
  15332. setCount(editor,me);
  15333. domUtils.un(editor.document, "click", arguments.callee);
  15334. }
  15335. domUtils.on(editor.document, "click", countFn);
  15336. editor.ui.getDom('wordcount').innerHTML = editor.getLang("wordCountTip");
  15337. }
  15338. editor.ui._scale();
  15339. if (editor.options.scaleEnabled) {
  15340. if (editor.autoHeightEnabled) {
  15341. editor.disableAutoHeight();
  15342. }
  15343. me.enableScale();
  15344. } else {
  15345. me.disableScale();
  15346. }
  15347. if (!editor.options.elementPathEnabled && !editor.options.wordCount && !editor.options.scaleEnabled) {
  15348. editor.ui.getDom('elementpath').style.display = "none";
  15349. editor.ui.getDom('wordcount').style.display = "none";
  15350. editor.ui.getDom('scale').style.display = "none";
  15351. }
  15352. if (!editor.selection.isFocus())return;
  15353. editor.fireEvent('selectionchange', false, true);
  15354. });
  15355. editor.addListener('mousedown', function (t, evt) {
  15356. var el = evt.target || evt.srcElement;
  15357. baidu.editor.ui.Popup.postHide(evt, el);
  15358. baidu.editor.ui.ShortCutMenu.postHide(evt);
  15359. });
  15360. editor.addListener("delcells", function () {
  15361. if (UE.ui['edittip']) {
  15362. new UE.ui['edittip'](editor);
  15363. }
  15364. editor.getDialog('edittip').open();
  15365. });
  15366. var pastePop, isPaste = false, timer;
  15367. editor.addListener("afterpaste", function () {
  15368. if(editor.queryCommandState('pasteplain'))
  15369. return;
  15370. if(baidu.editor.ui.PastePicker){
  15371. pastePop = new baidu.editor.ui.Popup({
  15372. content:new baidu.editor.ui.PastePicker({editor:editor}),
  15373. editor:editor,
  15374. className:'edui-wordpastepop'
  15375. });
  15376. pastePop.render();
  15377. }
  15378. isPaste = true;
  15379. });
  15380. editor.addListener("afterinserthtml", function () {
  15381. clearTimeout(timer);
  15382. timer = setTimeout(function () {
  15383. if (pastePop && (isPaste || editor.ui._isTransfer)) {
  15384. if(pastePop.isHidden()){
  15385. var span = domUtils.createElement(editor.document, 'span', {
  15386. 'style':"line-height:0px;",
  15387. 'innerHTML':'\ufeff'
  15388. }),
  15389. range = editor.selection.getRange();
  15390. range.insertNode(span);
  15391. var tmp= getDomNode(span, 'firstChild', 'previousSibling');
  15392. tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp);
  15393. domUtils.remove(span);
  15394. }else{
  15395. pastePop.show();
  15396. }
  15397. delete editor.ui._isTransfer;
  15398. isPaste = false;
  15399. }
  15400. }, 200)
  15401. });
  15402. editor.addListener('contextmenu', function (t, evt) {
  15403. baidu.editor.ui.Popup.postHide(evt);
  15404. });
  15405. editor.addListener('keydown', function (t, evt) {
  15406. if (pastePop) pastePop.dispose(evt);
  15407. var keyCode = evt.keyCode || evt.which;
  15408. if(evt.altKey&&keyCode==90){
  15409. UE.ui.buttons['fullscreen'].onclick();
  15410. }
  15411. });
  15412. editor.addListener('wordcount', function (type) {
  15413. setCount(this,me);
  15414. });
  15415. function setCount(editor,ui) {
  15416. editor.setOpt({
  15417. wordCount:true,
  15418. maximumWords:10000,
  15419. wordCountMsg:editor.options.wordCountMsg || editor.getLang("wordCountMsg"),
  15420. wordOverFlowMsg:editor.options.wordOverFlowMsg || editor.getLang("wordOverFlowMsg")
  15421. });
  15422. var opt = editor.options,
  15423. max = opt.maximumWords,
  15424. msg = opt.wordCountMsg ,
  15425. errMsg = opt.wordOverFlowMsg,
  15426. countDom = ui.getDom('wordcount');
  15427. if (!opt.wordCount) {
  15428. return;
  15429. }
  15430. var count = editor.getContentLength(true);
  15431. if (count > max) {
  15432. countDom.innerHTML = errMsg;
  15433. editor.fireEvent("wordcountoverflow");
  15434. } else {
  15435. countDom.innerHTML = msg.replace("{#leave}", max - count).replace("{#count}", count);
  15436. }
  15437. }
  15438. editor.addListener('selectionchange', function () {
  15439. if (editor.options.elementPathEnabled) {
  15440. me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']()
  15441. }
  15442. if (editor.options.scaleEnabled) {
  15443. me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale']();
  15444. }
  15445. });
  15446. var popup = new baidu.editor.ui.Popup({
  15447. editor:editor,
  15448. content:'',
  15449. className:'edui-bubble',
  15450. _onEditButtonClick:function () {
  15451. this.hide();
  15452. editor.ui._dialogs.linkDialog.open();
  15453. },
  15454. _onImgEditButtonClick:function (name) {
  15455. this.hide();
  15456. editor.ui._dialogs[name] && editor.ui._dialogs[name].open();
  15457. },
  15458. _onImgSetFloat:function (value) {
  15459. this.hide();
  15460. editor.execCommand("imagefloat", value);
  15461. },
  15462. _setIframeAlign:function (value) {
  15463. var frame = popup.anchorEl;
  15464. var newFrame = frame.cloneNode(true);
  15465. switch (value) {
  15466. case -2:
  15467. newFrame.setAttribute("align", "");
  15468. break;
  15469. case -1:
  15470. newFrame.setAttribute("align", "left");
  15471. break;
  15472. case 1:
  15473. newFrame.setAttribute("align", "right");
  15474. break;
  15475. }
  15476. frame.parentNode.insertBefore(newFrame, frame);
  15477. domUtils.remove(frame);
  15478. popup.anchorEl = newFrame;
  15479. popup.showAnchor(popup.anchorEl);
  15480. },
  15481. _updateIframe:function () {
  15482. var frame = editor._iframe = popup.anchorEl;
  15483. if(domUtils.hasClass(frame, 'ueditor_baidumap')) {
  15484. editor.selection.getRange().selectNode(frame).select();
  15485. editor.ui._dialogs.mapDialog.open();
  15486. popup.hide();
  15487. } else {
  15488. editor.ui._dialogs.insertframeDialog.open();
  15489. popup.hide();
  15490. }
  15491. },
  15492. _onRemoveButtonClick:function (cmdName) {
  15493. editor.execCommand(cmdName);
  15494. this.hide();
  15495. },
  15496. queryAutoHide:function (el) {
  15497. if (el && el.ownerDocument == editor.document) {
  15498. if (el.tagName.toLowerCase() == 'img' || domUtils.findParentByTagName(el, 'a', true)) {
  15499. return el !== popup.anchorEl;
  15500. }
  15501. }
  15502. return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el);
  15503. }
  15504. });
  15505. popup.render();
  15506. if (editor.options.imagePopup) {
  15507. editor.addListener('mouseover', function (t, evt) {
  15508. evt = evt || window.event;
  15509. var el = evt.target || evt.srcElement;
  15510. if (editor.ui._dialogs.insertframeDialog && /iframe/ig.test(el.tagName)) {
  15511. var html = popup.formatHtml(
  15512. '<nobr>' + editor.getLang("property") + ': <span onclick=$$._setIframeAlign(-2) class="edui-clickable">' + editor.getLang("default") + '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(-1) class="edui-clickable">' + editor.getLang("justifyleft") + '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(1) class="edui-clickable">' + editor.getLang("justifyright") + '</span>&nbsp;&nbsp;' +
  15513. ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>');
  15514. if (html) {
  15515. popup.getDom('content').innerHTML = html;
  15516. popup.anchorEl = el;
  15517. popup.showAnchor(popup.anchorEl);
  15518. } else {
  15519. popup.hide();
  15520. }
  15521. }
  15522. });
  15523. editor.addListener('selectionchange', function (t, causeByUi) {
  15524. if (!causeByUi) return;
  15525. var html = '', str = "",
  15526. img = editor.selection.getRange().getClosedNode(),
  15527. dialogs = editor.ui._dialogs;
  15528. if (img && img.tagName == 'IMG') {
  15529. var dialogName = 'insertimageDialog';
  15530. if (img.className.indexOf("edui-faked-video") != -1 || img.className.indexOf("edui-upload-video") != -1) {
  15531. dialogName = "insertvideoDialog"
  15532. }
  15533. if (img.className.indexOf("edui-faked-webapp") != -1) {
  15534. dialogName = "webappDialog"
  15535. }
  15536. if (img.src.indexOf("http://api.map.baidu.com") != -1) {
  15537. dialogName = "mapDialog"
  15538. }
  15539. if (img.className.indexOf("edui-faked-music") != -1) {
  15540. dialogName = "musicDialog"
  15541. }
  15542. if (img.src.indexOf("http://maps.google.com/maps/api/staticmap") != -1) {
  15543. dialogName = "gmapDialog"
  15544. }
  15545. if (img.getAttribute("anchorname")) {
  15546. dialogName = "anchorDialog";
  15547. html = popup.formatHtml(
  15548. '<nobr>' + editor.getLang("property") + ': <span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' + editor.getLang("modify") + '</span>&nbsp;&nbsp;' +
  15549. '<span onclick=$$._onRemoveButtonClick(\'anchor\') class="edui-clickable">' + editor.getLang("delete") + '</span></nobr>');
  15550. }
  15551. if (img.getAttribute("word_img")) {
  15552. //todo 放到dialog去做查询
  15553. editor.word_img = [img.getAttribute("word_img")];
  15554. dialogName = "wordimageDialog"
  15555. }
  15556. if(domUtils.hasClass(img, 'loadingclass') || domUtils.hasClass(img, 'loaderrorclass')) {
  15557. dialogName = "";
  15558. }
  15559. if (!dialogs[dialogName]) {
  15560. return;
  15561. }
  15562. str = '<nobr>' + editor.getLang("property") + ': '+
  15563. '<span onclick=$$._onImgSetFloat("none") class="edui-clickable">' + editor.getLang("default") + '</span>&nbsp;&nbsp;' +
  15564. '<span onclick=$$._onImgSetFloat("left") class="edui-clickable">' + editor.getLang("justifyleft") + '</span>&nbsp;&nbsp;' +
  15565. '<span onclick=$$._onImgSetFloat("right") class="edui-clickable">' + editor.getLang("justifyright") + '</span>&nbsp;&nbsp;' +
  15566. '<span onclick=$$._onImgSetFloat("center") class="edui-clickable">' + editor.getLang("justifycenter") + '</span>&nbsp;&nbsp;'+
  15567. '<span onclick="$$._onImgEditButtonClick(\'' + dialogName + '\');" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>';
  15568. !html && (html = popup.formatHtml(str))
  15569. }
  15570. if (editor.ui._dialogs.linkDialog) {
  15571. var link = editor.queryCommandValue('link');
  15572. var url;
  15573. if (link && (url = (link.getAttribute('_href') || link.getAttribute('href', 2)))) {
  15574. var txt = url;
  15575. if (url.length > 30) {
  15576. txt = url.substring(0, 20) + "...";
  15577. }
  15578. if (html) {
  15579. html += '<div style="height:5px;"></div>'
  15580. }
  15581. html += popup.formatHtml(
  15582. '<nobr>' + editor.getLang("anthorMsg") + ': <a target="_blank" href="' + url + '" title="' + url + '" >' + txt + '</a>' +
  15583. ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' + editor.getLang("modify") + '</span>' +
  15584. ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' + editor.getLang("clear") + '</span></nobr>');
  15585. popup.showAnchor(link);
  15586. }
  15587. }
  15588. if (html) {
  15589. popup.getDom('content').innerHTML = html;
  15590. popup.anchorEl = img || link;
  15591. popup.showAnchor(popup.anchorEl);
  15592. } else {
  15593. popup.hide();
  15594. }
  15595. });
  15596. }
  15597. },
  15598. _initToolbars:function () {
  15599. var editor = this.editor;
  15600. var toolbars = this.toolbars || [];
  15601. var toolbarUis = [];
  15602. for (var i = 0; i < toolbars.length; i++) {
  15603. var toolbar = toolbars[i];
  15604. var toolbarUi = new baidu.editor.ui.Toolbar({theme:editor.options.theme});
  15605. for (var j = 0; j < toolbar.length; j++) {
  15606. var toolbarItem = toolbar[j];
  15607. var toolbarItemUi = null;
  15608. if (typeof toolbarItem == 'string') {
  15609. toolbarItem = toolbarItem.toLowerCase();
  15610. if (toolbarItem == '|') {
  15611. toolbarItem = 'Separator';
  15612. }
  15613. if(toolbarItem == '||'){
  15614. toolbarItem = 'Breakline';
  15615. }
  15616. if (baidu.editor.ui[toolbarItem]) {
  15617. toolbarItemUi = new baidu.editor.ui[toolbarItem](editor);
  15618. }
  15619. //fullscreen这里单独处理一下,放到首行去
  15620. if (toolbarItem == 'fullscreen') {
  15621. if (toolbarUis && toolbarUis[0]) {
  15622. toolbarUis[0].items.splice(0, 0, toolbarItemUi);
  15623. } else {
  15624. toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi);
  15625. }
  15626. continue;
  15627. }
  15628. } else {
  15629. toolbarItemUi = toolbarItem;
  15630. }
  15631. if (toolbarItemUi && toolbarItemUi.id) {
  15632. toolbarUi.add(toolbarItemUi);
  15633. }
  15634. }
  15635. toolbarUis[i] = toolbarUi;
  15636. }
  15637. //接受外部定制的UI
  15638. utils.each(UE._customizeUI,function(obj,key){
  15639. var itemUI,index;
  15640. if(obj.id && obj.id != editor.key){
  15641. return false;
  15642. }
  15643. itemUI = obj.execFn.call(editor,editor,key);
  15644. if(itemUI){
  15645. index = obj.index;
  15646. if(index === undefined){
  15647. index = toolbarUi.items.length;
  15648. }
  15649. toolbarUi.add(itemUI,index)
  15650. }
  15651. });
  15652. this.toolbars = toolbarUis;
  15653. },
  15654. getHtmlTpl:function () {
  15655. return '<div id="##" class="%%">' +
  15656. '<div id="##_toolbarbox" class="%%-toolbarbox">' +
  15657. (this.toolbars.length ?
  15658. '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
  15659. this.renderToolbarBoxHtml() +
  15660. '</div></div>' : '') +
  15661. '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
  15662. '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' + this.editor.getLang("clickToUpload") + '</div>' +
  15663. '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
  15664. '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
  15665. '<div style="height:0;overflow:hidden;clear:both;"></div>' +
  15666. '</div>' +
  15667. '<div id="##_message_holder" class="%%-messageholder"></div>' +
  15668. '</div>' +
  15669. '<div id="##_iframeholder" class="%%-iframeholder">' +
  15670. '</div>' +
  15671. //modify wdcount by matao
  15672. '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
  15673. '<td id="##_elementpath" class="%%-bottombar"></td>' +
  15674. '<td id="##_wordcount" class="%%-wordcount"></td>' +
  15675. '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
  15676. '</tr></table></div>' +
  15677. '<div id="##_scalelayer"></div>' +
  15678. '</div>';
  15679. },
  15680. showWordImageDialog:function () {
  15681. this._dialogs['wordimageDialog'].open();
  15682. },
  15683. renderToolbarBoxHtml:function () {
  15684. var buff = [];
  15685. for (var i = 0; i < this.toolbars.length; i++) {
  15686. buff.push(this.toolbars[i].renderHtml());
  15687. }
  15688. return buff.join('');
  15689. },
  15690. setFullScreen:function (fullscreen) {
  15691. var editor = this.editor,
  15692. container = editor.container.parentNode.parentNode;
  15693. if (this._fullscreen != fullscreen) {
  15694. this._fullscreen = fullscreen;
  15695. this.editor.fireEvent('beforefullscreenchange', fullscreen);
  15696. if (baidu.editor.browser.gecko) {
  15697. var bk = editor.selection.getRange().createBookmark();
  15698. }
  15699. if (fullscreen) {
  15700. while (container.tagName != "BODY") {
  15701. var position = baidu.editor.dom.domUtils.getComputedStyle(container, "position");
  15702. nodeStack.push(position);
  15703. container.style.position = "static";
  15704. container = container.parentNode;
  15705. }
  15706. this._bakHtmlOverflow = document.documentElement.style.overflow;
  15707. this._bakBodyOverflow = document.body.style.overflow;
  15708. this._bakAutoHeight = this.editor.autoHeightEnabled;
  15709. this._bakScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
  15710. this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth;
  15711. if (this._bakAutoHeight) {
  15712. //当全屏时不能执行自动长高
  15713. editor.autoHeightEnabled = false;
  15714. this.editor.disableAutoHeight();
  15715. }
  15716. document.documentElement.style.overflow = 'hidden';
  15717. //修复,滚动条不收起的问题
  15718. window.scrollTo(0,window.scrollY);
  15719. this._bakCssText = this.getDom().style.cssText;
  15720. this._bakCssText1 = this.getDom('iframeholder').style.cssText;
  15721. editor.iframe.parentNode.style.width = '';
  15722. this._updateFullScreen();
  15723. } else {
  15724. while (container.tagName != "BODY") {
  15725. container.style.position = nodeStack.shift();
  15726. container = container.parentNode;
  15727. }
  15728. this.getDom().style.cssText = this._bakCssText;
  15729. this.getDom('iframeholder').style.cssText = this._bakCssText1;
  15730. if (this._bakAutoHeight) {
  15731. editor.autoHeightEnabled = true;
  15732. this.editor.enableAutoHeight();
  15733. }
  15734. document.documentElement.style.overflow = this._bakHtmlOverflow;
  15735. document.body.style.overflow = this._bakBodyOverflow;
  15736. editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + 'px';
  15737. window.scrollTo(0, this._bakScrollTop);
  15738. }
  15739. if (browser.gecko && editor.body.contentEditable === 'true') {
  15740. var input = document.createElement('input');
  15741. document.body.appendChild(input);
  15742. editor.body.contentEditable = false;
  15743. setTimeout(function () {
  15744. input.focus();
  15745. setTimeout(function () {
  15746. editor.body.contentEditable = true;
  15747. editor.fireEvent('fullscreenchanged', fullscreen);
  15748. editor.selection.getRange().moveToBookmark(bk).select(true);
  15749. baidu.editor.dom.domUtils.remove(input);
  15750. fullscreen && window.scroll(0, 0);
  15751. }, 0)
  15752. }, 0)
  15753. }
  15754. if(editor.body.contentEditable === 'true'){
  15755. this.editor.fireEvent('fullscreenchanged', fullscreen);
  15756. this.triggerLayout();
  15757. }
  15758. }
  15759. },
  15760. _updateFullScreen:function () {
  15761. if (this._fullscreen) {
  15762. var vpRect = uiUtils.getViewportRect();
  15763. this.getDom().style.cssText = 'border:0;position:absolute;left:0;top:' + (this.editor.options.topOffset || 0) + 'px;width:' + vpRect.width + 'px;height:' + vpRect.height + 'px;z-index:' + (this.getDom().style.zIndex * 1 + 100);
  15764. uiUtils.setViewportOffset(this.getDom(), { left:0, top:this.editor.options.topOffset || 0 });
  15765. this.editor.setHeight(vpRect.height - this.getDom('toolbarbox').offsetHeight - this.getDom('bottombar').offsetHeight - (this.editor.options.topOffset || 0),true);
  15766. //不手动调一下,会导致全屏失效
  15767. if(browser.gecko){
  15768. try{
  15769. window.onresize();
  15770. }catch(e){
  15771. }
  15772. }
  15773. }
  15774. },
  15775. _updateElementPath:function () {
  15776. var bottom = this.getDom('elementpath'), list;
  15777. if (this.elementPathEnabled && (list = this.editor.queryCommandValue('elementpath'))) {
  15778. var buff = [];
  15779. for (var i = 0, ci; ci = list[i]; i++) {
  15780. buff[i] = this.formatHtml('<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' + i + '&quot;);">' + ci + '</span>');
  15781. }
  15782. bottom.innerHTML = '<div class="edui-editor-breadcrumb" onmousedown="return false;">' + this.editor.getLang("elementPathTip") + ': ' + buff.join(' &gt; ') + '</div>';
  15783. } else {
  15784. bottom.style.display = 'none'
  15785. }
  15786. },
  15787. disableElementPath:function () {
  15788. var bottom = this.getDom('elementpath');
  15789. bottom.innerHTML = '';
  15790. bottom.style.display = 'none';
  15791. this.elementPathEnabled = false;
  15792. },
  15793. enableElementPath:function () {
  15794. var bottom = this.getDom('elementpath');
  15795. bottom.style.display = '';
  15796. this.elementPathEnabled = true;
  15797. this._updateElementPath();
  15798. },
  15799. _scale:function () {
  15800. var doc = document,
  15801. editor = this.editor,
  15802. editorHolder = editor.container,
  15803. editorDocument = editor.document,
  15804. toolbarBox = this.getDom("toolbarbox"),
  15805. bottombar = this.getDom("bottombar"),
  15806. scale = this.getDom("scale"),
  15807. scalelayer = this.getDom("scalelayer");
  15808. var isMouseMove = false,
  15809. position = null,
  15810. minEditorHeight = 0,
  15811. minEditorWidth = editor.options.minFrameWidth,
  15812. pageX = 0,
  15813. pageY = 0,
  15814. scaleWidth = 0,
  15815. scaleHeight = 0;
  15816. function down() {
  15817. position = domUtils.getXY(editorHolder);
  15818. if (!minEditorHeight) {
  15819. minEditorHeight = editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight;
  15820. }
  15821. scalelayer.style.cssText = "position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:" + editorHolder.offsetWidth + "px;height:"
  15822. + editorHolder.offsetHeight + "px;z-index:" + (editor.options.zIndex + 1);
  15823. domUtils.on(doc, "mousemove", move);
  15824. domUtils.on(editorDocument, "mouseup", up);
  15825. domUtils.on(doc, "mouseup", up);
  15826. }
  15827. var me = this;
  15828. //by xuheng 全屏时关掉缩放
  15829. this.editor.addListener('fullscreenchanged', function (e, fullScreen) {
  15830. if (fullScreen) {
  15831. me.disableScale();
  15832. } else {
  15833. if (me.editor.options.scaleEnabled) {
  15834. me.enableScale();
  15835. var tmpNode = me.editor.document.createElement('span');
  15836. me.editor.body.appendChild(tmpNode);
  15837. me.editor.body.style.height = Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px';
  15838. domUtils.remove(tmpNode)
  15839. }
  15840. }
  15841. });
  15842. function move(event) {
  15843. clearSelection();
  15844. var e = event || window.event;
  15845. pageX = e.pageX || (doc.documentElement.scrollLeft + e.clientX);
  15846. pageY = e.pageY || (doc.documentElement.scrollTop + e.clientY);
  15847. scaleWidth = pageX - position.x;
  15848. scaleHeight = pageY - position.y;
  15849. if (scaleWidth >= minEditorWidth) {
  15850. isMouseMove = true;
  15851. scalelayer.style.width = scaleWidth + 'px';
  15852. }
  15853. if (scaleHeight >= minEditorHeight) {
  15854. isMouseMove = true;
  15855. scalelayer.style.height = scaleHeight + "px";
  15856. }
  15857. }
  15858. function up() {
  15859. if (isMouseMove) {
  15860. isMouseMove = false;
  15861. editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2;
  15862. editorHolder.style.width = editor.ui._actualFrameWidth + 'px';
  15863. editor.setHeight(scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2,true);
  15864. }
  15865. if (scalelayer) {
  15866. scalelayer.style.display = "none";
  15867. }
  15868. clearSelection();
  15869. domUtils.un(doc, "mousemove", move);
  15870. domUtils.un(editorDocument, "mouseup", up);
  15871. domUtils.un(doc, "mouseup", up);
  15872. }
  15873. function clearSelection() {
  15874. if (browser.ie)
  15875. doc.selection.clear();
  15876. else
  15877. window.getSelection().removeAllRanges();
  15878. }
  15879. this.enableScale = function () {
  15880. //trace:2868
  15881. if (editor.queryCommandState("source") == 1) return;
  15882. scale.style.display = "";
  15883. this.scaleEnabled = true;
  15884. domUtils.on(scale, "mousedown", down);
  15885. };
  15886. this.disableScale = function () {
  15887. scale.style.display = "none";
  15888. this.scaleEnabled = false;
  15889. domUtils.un(scale, "mousedown", down);
  15890. };
  15891. },
  15892. isFullScreen:function () {
  15893. return this._fullscreen;
  15894. },
  15895. postRender:function () {
  15896. UIBase.prototype.postRender.call(this);
  15897. for (var i = 0; i < this.toolbars.length; i++) {
  15898. this.toolbars[i].postRender();
  15899. }
  15900. var me = this;
  15901. var timerId,
  15902. domUtils = baidu.editor.dom.domUtils,
  15903. updateFullScreenTime = function () {
  15904. clearTimeout(timerId);
  15905. timerId = setTimeout(function () {
  15906. me._updateFullScreen();
  15907. });
  15908. };
  15909. domUtils.on(window, 'resize', updateFullScreenTime);
  15910. me.addListener('destroy', function () {
  15911. domUtils.un(window, 'resize', updateFullScreenTime);
  15912. clearTimeout(timerId);
  15913. })
  15914. },
  15915. showToolbarMsg:function (msg, flag) {
  15916. this.getDom('toolbarmsg_label').innerHTML = msg;
  15917. this.getDom('toolbarmsg').style.display = '';
  15918. //
  15919. if (!flag) {
  15920. var w = this.getDom('upload_dialog');
  15921. w.style.display = 'none';
  15922. }
  15923. },
  15924. hideToolbarMsg:function () {
  15925. this.getDom('toolbarmsg').style.display = 'none';
  15926. },
  15927. mapUrl:function (url) {
  15928. return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : ''
  15929. },
  15930. triggerLayout:function () {
  15931. var dom = this.getDom();
  15932. if (dom.style.zoom == '1') {
  15933. dom.style.zoom = '100%';
  15934. } else {
  15935. dom.style.zoom = '1';
  15936. }
  15937. }
  15938. };
  15939. utils.inherits(EditorUI, baidu.editor.ui.UIBase);
  15940. var instances = {};
  15941. UE.ui.Editor = function (options) {
  15942. var editor = new UE.Editor(options);
  15943. editor.options.editor = editor;
  15944. utils.loadFile(document, {
  15945. href:editor.options.themePath + editor.options.theme + "/css/ueditor.css",
  15946. tag:"link",
  15947. type:"text/css",
  15948. rel:"stylesheet"
  15949. });
  15950. var oldRender = editor.render;
  15951. editor.render = function (holder) {
  15952. if (holder.constructor === String) {
  15953. editor.key = holder;
  15954. instances[holder] = editor;
  15955. }
  15956. utils.domReady(function () {
  15957. editor.langIsReady ? renderUI() : editor.addListener("langReady", renderUI);
  15958. function renderUI() {
  15959. editor.setOpt({
  15960. labelMap:editor.options.labelMap || editor.getLang('labelMap')
  15961. });
  15962. new EditorUI(editor.options);
  15963. if (holder) {
  15964. if (holder.constructor === String) {
  15965. holder = document.getElementById(holder);
  15966. }
  15967. holder && holder.getAttribute('name') && ( editor.options.textarea = holder.getAttribute('name'));
  15968. if (holder && /script|textarea/ig.test(holder.tagName)) {
  15969. var newDiv = document.createElement('div');
  15970. holder.parentNode.insertBefore(newDiv, holder);
  15971. var cont = holder.value || holder.innerHTML;
  15972. editor.options.initialContent = /^[\t\r\n ]*$/.test(cont) ? editor.options.initialContent :
  15973. cont.replace(/>[\n\r\t]+([ ]{4})+/g, '>')
  15974. .replace(/[\n\r\t]+([ ]{4})+</g, '<')
  15975. .replace(/>[\n\r\t]+</g, '><');
  15976. holder.className && (newDiv.className = holder.className);
  15977. holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  15978. if (/textarea/i.test(holder.tagName)) {
  15979. editor.textarea = holder;
  15980. editor.textarea.style.display = 'none';
  15981. } else {
  15982. holder.parentNode.removeChild(holder);
  15983. }
  15984. if(holder.id){
  15985. newDiv.id = holder.id;
  15986. domUtils.removeAttributes(holder,'id');
  15987. }
  15988. holder = newDiv;
  15989. holder.innerHTML = '';
  15990. }
  15991. }
  15992. domUtils.addClass(holder, "edui-" + editor.options.theme);
  15993. editor.ui.render(holder);
  15994. var opt = editor.options;
  15995. //给实例添加一个编辑器的容器引用
  15996. editor.container = editor.ui.getDom();
  15997. var parents = domUtils.findParents(holder,true);
  15998. var displays = [];
  15999. for(var i = 0 ,ci;ci=parents[i];i++){
  16000. displays[i] = ci.style.display;
  16001. ci.style.display = 'block'
  16002. }
  16003. if (opt.initialFrameWidth) {
  16004. opt.minFrameWidth = opt.initialFrameWidth;
  16005. } else {
  16006. opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth;
  16007. var styleWidth = holder.style.width;
  16008. if(/%$/.test(styleWidth)) {
  16009. opt.initialFrameWidth = styleWidth;
  16010. }
  16011. }
  16012. if (opt.initialFrameHeight) {
  16013. opt.minFrameHeight = opt.initialFrameHeight;
  16014. } else {
  16015. opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight;
  16016. }
  16017. for(var i = 0 ,ci;ci=parents[i];i++){
  16018. ci.style.display = displays[i]
  16019. }
  16020. //编辑器最外容器设置了高度,会导致,编辑器不占位
  16021. //todo 先去掉,没有找到原因
  16022. if(holder.style.height){
  16023. holder.style.height = ''
  16024. }
  16025. editor.container.style.width = opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px');
  16026. editor.container.style.zIndex = opt.zIndex;
  16027. oldRender.call(editor, editor.ui.getDom('iframeholder'));
  16028. editor.fireEvent("afteruiready");
  16029. }
  16030. })
  16031. };
  16032. return editor;
  16033. };
  16034. /**
  16035. * @file
  16036. * @name UE
  16037. * @short UE
  16038. * @desc UEditor的顶部命名空间
  16039. */
  16040. /**
  16041. * @name getEditor
  16042. * @since 1.2.4+
  16043. * @grammar UE.getEditor(id,[opt]) => Editor实例
  16044. * @desc 提供一个全局的方法得到编辑器实例
  16045. *
  16046. * * ''id'' 放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
  16047. * * ''opt'' 编辑器的可选参数
  16048. * @example
  16049. * UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
  16050. * this.setContent('hello')
  16051. * }});
  16052. * UE.getEditor('containerId'); //返回刚创建的实例
  16053. *
  16054. */
  16055. UE.getEditor = function (id, opt) {
  16056. var editor = instances[id];
  16057. if (!editor) {
  16058. editor = instances[id] = new UE.ui.Editor(opt);
  16059. editor.render(id);
  16060. }
  16061. return editor;
  16062. };
  16063. UE.delEditor = function (id) {
  16064. var editor;
  16065. if (editor = instances[id]) {
  16066. editor.key && editor.destroy();
  16067. delete instances[id]
  16068. }
  16069. };
  16070. UE.registerUI = function(uiName,fn,index,editorId){
  16071. utils.each(uiName.split(/\s+/), function (name) {
  16072. UE._customizeUI[name] = {
  16073. id : editorId,
  16074. execFn:fn,
  16075. index:index
  16076. };
  16077. })
  16078. }
  16079. })();
  16080. // adapter/message.js
  16081. UE.registerUI('message', function(editor) {
  16082. var editorui = baidu.editor.ui;
  16083. var Message = editorui.Message;
  16084. var holder;
  16085. var _messageItems = [];
  16086. var me = editor;
  16087. me.addListener('ready', function(){
  16088. holder = document.getElementById(me.ui.id + '_message_holder');
  16089. updateHolderPos();
  16090. setTimeout(function(){
  16091. updateHolderPos();
  16092. }, 500);
  16093. });
  16094. me.addListener('showmessage', function(type, opt){
  16095. opt = utils.isString(opt) ? {
  16096. 'content': opt
  16097. } : opt;
  16098. var message = new Message({
  16099. 'timeout': opt.timeout,
  16100. 'type': opt.type,
  16101. 'content': opt.content,
  16102. 'keepshow': opt.keepshow,
  16103. 'editor': me
  16104. }),
  16105. mid = opt.id || ('msg_' + (+new Date()).toString(36));
  16106. message.render(holder);
  16107. _messageItems[mid] = message;
  16108. message.reset(opt);
  16109. updateHolderPos();
  16110. return mid;
  16111. });
  16112. me.addListener('updatemessage',function(type, id, opt){
  16113. opt = utils.isString(opt) ? {
  16114. 'content': opt
  16115. } : opt;
  16116. var message = _messageItems[id];
  16117. message.render(holder);
  16118. message && message.reset(opt);
  16119. });
  16120. me.addListener('hidemessage',function(type, id){
  16121. var message = _messageItems[id];
  16122. message && message.hide();
  16123. });
  16124. function updateHolderPos(){
  16125. var toolbarbox = me.ui.getDom('toolbarbox');
  16126. if (toolbarbox) {
  16127. holder.style.top = toolbarbox.offsetHeight + 3 + 'px';
  16128. }
  16129. holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1;
  16130. }
  16131. });
  16132. // adapter/autosave.js
  16133. UE.registerUI('autosave', function(editor) {
  16134. var timer = null,uid = null;
  16135. editor.on('afterautosave',function(){
  16136. clearTimeout(timer);
  16137. timer = setTimeout(function(){
  16138. if(uid){
  16139. editor.trigger('hidemessage',uid);
  16140. }
  16141. uid = editor.trigger('showmessage',{
  16142. content : editor.getLang('autosave.success'),
  16143. timeout : 2000
  16144. });
  16145. },2000)
  16146. })
  16147. });
  16148. })();