ueditor.all.js 870 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992
  1. /*!
  2. * UEditor
  3. * version: ueditor
  4. * build: Wed Aug 10 2016 11:06:02 GMT+0800 (CST)
  5. */
  6. (function () {
  7. // editor.js
  8. UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
  9. var baidu = window.baidu || {};
  10. window.baidu = baidu;
  11. window.UE = baidu.editor = window.UE || {};
  12. UE.plugins = {};
  13. UE.commands = {};
  14. UE.instants = {};
  15. UE.I18N = {};
  16. UE._customizeUI = {};
  17. UE.version = "1.4.3";
  18. var dom = UE.dom = {};
  19. // core/browser.js
  20. /**
  21. * 浏览器判断模块
  22. * @file
  23. * @module UE.browser
  24. * @since 1.2.6.1
  25. */
  26. /**
  27. * 提供浏览器检测的模块
  28. * @unfile
  29. * @module UE.browser
  30. */
  31. var browser = UE.browser = function () {
  32. var agent = navigator.userAgent.toLowerCase(),
  33. opera = window.opera,
  34. browser = {
  35. /**
  36. * @property {boolean} ie 检测当前浏览器是否为IE
  37. * @example
  38. * ```javascript
  39. * if ( UE.browser.ie ) {
  40. * console.log( '当前浏览器是IE' );
  41. * }
  42. * ```
  43. */
  44. ie: /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  45. /**
  46. * @property {boolean} opera 检测当前浏览器是否为Opera
  47. * @example
  48. * ```javascript
  49. * if ( UE.browser.opera ) {
  50. * console.log( '当前浏览器是Opera' );
  51. * }
  52. * ```
  53. */
  54. opera: (!!opera && opera.version),
  55. /**
  56. * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
  57. * @example
  58. * ```javascript
  59. * if ( UE.browser.webkit ) {
  60. * console.log( '当前浏览器是webkit内核浏览器' );
  61. * }
  62. * ```
  63. */
  64. webkit: (agent.indexOf(' applewebkit/') > -1),
  65. /**
  66. * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
  67. * @example
  68. * ```javascript
  69. * if ( UE.browser.mac ) {
  70. * console.log( '当前浏览器运行在mac平台下' );
  71. * }
  72. * ```
  73. */
  74. mac: (agent.indexOf('macintosh') > -1),
  75. /**
  76. * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
  77. * @example
  78. * ```javascript
  79. * if ( UE.browser.quirks ) {
  80. * console.log( '当前浏览器运行处于“怪异模式”' );
  81. * }
  82. * ```
  83. */
  84. quirks: (document.compatMode == 'BackCompat')
  85. };
  86. /**
  87. * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
  88. * @example
  89. * ```javascript
  90. * if ( UE.browser.gecko ) {
  91. * console.log( '当前浏览器内核是gecko内核' );
  92. * }
  93. * ```
  94. */
  95. browser.gecko = (navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
  96. var version = 0;
  97. // Internet Explorer 6.0+
  98. if (browser.ie) {
  99. var v1 = agent.match(/(?:msie\s([\w.]+))/);
  100. var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
  101. if (v1 && v2 && v1[1] && v2[1]) {
  102. version = Math.max(v1[1] * 1, v2[1] * 1);
  103. } else if (v1 && v1[1]) {
  104. version = v1[1] * 1;
  105. } else if (v2 && v2[1]) {
  106. version = v2[1] * 1;
  107. } else {
  108. version = 0;
  109. }
  110. browser.ie11Compat = document.documentMode == 11;
  111. /**
  112. * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
  113. * @warning 如果浏览器不是IE, 则该值为undefined
  114. * @example
  115. * ```javascript
  116. * if ( UE.browser.ie9Compat ) {
  117. * console.log( '当前浏览器运行在IE9兼容模式下' );
  118. * }
  119. * ```
  120. */
  121. browser.ie9Compat = document.documentMode == 9;
  122. /**
  123. * @property { boolean } ie8 检测浏览器是否是IE8浏览器
  124. * @warning 如果浏览器不是IE, 则该值为undefined
  125. * @example
  126. * ```javascript
  127. * if ( UE.browser.ie8 ) {
  128. * console.log( '当前浏览器是IE8浏览器' );
  129. * }
  130. * ```
  131. */
  132. browser.ie8 = !!document.documentMode;
  133. /**
  134. * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
  135. * @warning 如果浏览器不是IE, 则该值为undefined
  136. * @example
  137. * ```javascript
  138. * if ( UE.browser.ie8Compat ) {
  139. * console.log( '当前浏览器运行在IE8兼容模式下' );
  140. * }
  141. * ```
  142. */
  143. browser.ie8Compat = document.documentMode == 8;
  144. /**
  145. * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
  146. * @warning 如果浏览器不是IE, 则该值为undefined
  147. * @example
  148. * ```javascript
  149. * if ( UE.browser.ie7Compat ) {
  150. * console.log( '当前浏览器运行在IE7兼容模式下' );
  151. * }
  152. * ```
  153. */
  154. browser.ie7Compat = ((version == 7 && !document.documentMode)
  155. || document.documentMode == 7);
  156. /**
  157. * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
  158. * @warning 如果浏览器不是IE, 则该值为undefined
  159. * @example
  160. * ```javascript
  161. * if ( UE.browser.ie6Compat ) {
  162. * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
  163. * }
  164. * ```
  165. */
  166. browser.ie6Compat = (version < 7 || browser.quirks);
  167. browser.ie9above = version > 8;
  168. browser.ie9below = version < 9;
  169. browser.ie11above = version > 10;
  170. browser.ie11below = version < 11;
  171. }
  172. // Gecko.
  173. if (browser.gecko) {
  174. var geckoRelease = agent.match(/rv:([\d\.]+)/);
  175. if (geckoRelease) {
  176. geckoRelease = geckoRelease[1].split('.');
  177. version = geckoRelease[0] * 10000 + (geckoRelease[1] || 0) * 100 + (geckoRelease[2] || 0) * 1;
  178. }
  179. }
  180. /**
  181. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  182. * @warning 如果浏览器不是chrome, 则该值为undefined
  183. * @example
  184. * ```javascript
  185. * if ( UE.browser.chrome ) {
  186. * console.log( '当前浏览器是Chrome' );
  187. * }
  188. * ```
  189. */
  190. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  191. browser.chrome = +RegExp['\x241'];
  192. }
  193. /**
  194. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  195. * @warning 如果浏览器不是safari, 则该值为undefined
  196. * @example
  197. * ```javascript
  198. * if ( UE.browser.safari ) {
  199. * console.log( '当前浏览器是Safari' );
  200. * }
  201. * ```
  202. */
  203. if (/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)) {
  204. browser.safari = +(RegExp['\x241'] || RegExp['\x242']);
  205. }
  206. // Opera 9.50+
  207. if (browser.opera)
  208. version = parseFloat(opera.version());
  209. // WebKit 522+ (Safari 3+)
  210. if (browser.webkit)
  211. version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]);
  212. /**
  213. * @property { Number } version 检测当前浏览器版本号
  214. * @remind
  215. * <ul>
  216. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  217. * <li>gecko系列会返回10900,158900等</li>
  218. * <li>webkit系列会返回其build号 (如 522等)</li>
  219. * </ul>
  220. * @example
  221. * ```javascript
  222. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  223. * ```
  224. */
  225. browser.version = version;
  226. /**
  227. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  228. * @example
  229. * ```javascript
  230. * if ( UE.browser.isCompatible ) {
  231. * console.log( '浏览器与UEditor能够良好兼容' );
  232. * }
  233. * ```
  234. */
  235. browser.isCompatible =
  236. !browser.mobile && (
  237. (browser.ie && version >= 6) ||
  238. (browser.gecko && version >= 10801) ||
  239. (browser.opera && version >= 9.5) ||
  240. (browser.air && version >= 1) ||
  241. (browser.webkit && version >= 522) ||
  242. false);
  243. return browser;
  244. }();
  245. //快捷方式
  246. var ie = browser.ie,
  247. webkit = browser.webkit,
  248. gecko = browser.gecko,
  249. opera = browser.opera;
  250. // core/utils.js
  251. /**
  252. * 工具函数包
  253. * @file
  254. * @module UE.utils
  255. * @since 1.2.6.1
  256. */
  257. /**
  258. * UEditor封装使用的静态工具函数
  259. * @module UE.utils
  260. * @unfile
  261. */
  262. var utils = UE.utils = {
  263. /**
  264. * 用给定的迭代器遍历对象
  265. * @method each
  266. * @param { Object } obj 需要遍历的对象
  267. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  268. * @example
  269. * ```javascript
  270. * var demoObj = {
  271. * key1: 1,
  272. * key2: 2
  273. * };
  274. *
  275. * //output: key1: 1, key2: 2
  276. * UE.utils.each( demoObj, funciton ( value, key ) {
  277. *
  278. * console.log( key + ":" + value );
  279. *
  280. * } );
  281. * ```
  282. */
  283. /**
  284. * 用给定的迭代器遍历数组或类数组对象
  285. * @method each
  286. * @param { Array } array 需要遍历的数组或者类数组
  287. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  288. * @example
  289. * ```javascript
  290. * var divs = document.getElmentByTagNames( "div" );
  291. *
  292. * //output: 0: DIV, 1: DIV ...
  293. * UE.utils.each( divs, funciton ( value, key ) {
  294. *
  295. * console.log( key + ":" + value.tagName );
  296. *
  297. * } );
  298. * ```
  299. */
  300. each: function (obj, iterator, context) {
  301. if (obj == null) return;
  302. if (obj.length === +obj.length) {
  303. for (var i = 0, l = obj.length; i < l; i++) {
  304. if (iterator.call(context, obj[i], i, obj) === false)
  305. return false;
  306. }
  307. } else {
  308. for (var key in obj) {
  309. if (obj.hasOwnProperty(key)) {
  310. if (iterator.call(context, obj[key], key, obj) === false)
  311. return false;
  312. }
  313. }
  314. }
  315. },
  316. /**
  317. * 以给定对象作为原型创建一个新对象
  318. * @method makeInstance
  319. * @param { Object } protoObject 该对象将作为新创建对象的原型
  320. * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
  321. * @example
  322. * ```javascript
  323. *
  324. * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
  325. *
  326. * var newObject = UE.utils.makeInstance( protoObject );
  327. * //output: Hello UEditor!
  328. * newObject.sayHello();
  329. * ```
  330. */
  331. makeInstance: function (obj) {
  332. var noop = new Function();
  333. noop.prototype = obj;
  334. obj = new noop;
  335. noop.prototype = null;
  336. return obj;
  337. },
  338. /**
  339. * 将source对象中的属性扩展到target对象上
  340. * @method extend
  341. * @remind 该方法将强制把source对象上的属性复制到target对象上
  342. * @see UE.utils.extend(Object,Object,Boolean)
  343. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  344. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  345. * @return { Object } 返回target对象
  346. * @example
  347. * ```javascript
  348. *
  349. * var target = { name: 'target', sex: 1 },
  350. * source = { name: 'source', age: 17 };
  351. *
  352. * UE.utils.extend( target, source );
  353. *
  354. * //output: { name: 'source', sex: 1, age: 17 }
  355. * console.log( target );
  356. *
  357. * ```
  358. */
  359. /**
  360. * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
  361. * 源对象属性名相同的属性值。
  362. * @method extend
  363. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  364. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  365. * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
  366. * @return { Object } 返回target对象
  367. * @example
  368. * ```javascript
  369. *
  370. * var target = { name: 'target', sex: 1 },
  371. * source = { name: 'source', age: 17 };
  372. *
  373. * UE.utils.extend( target, source, true );
  374. *
  375. * //output: { name: 'target', sex: 1, age: 17 }
  376. * console.log( target );
  377. *
  378. * ```
  379. */
  380. extend: function (t, s, b) {
  381. if (s) {
  382. for (var k in s) {
  383. if (!b || !t.hasOwnProperty(k)) {
  384. t[k] = s[k];
  385. }
  386. }
  387. }
  388. return t;
  389. },
  390. /**
  391. * 将给定的多个对象的属性复制到目标对象target上
  392. * @method extend2
  393. * @remind 该方法将强制把源对象上的属性复制到target对象上
  394. * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
  395. * 将会覆盖掉之前的值。
  396. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  397. * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
  398. * @return { Object } 返回target对象
  399. * @example
  400. * ```javascript
  401. *
  402. * var target = {},
  403. * source1 = { name: 'source', age: 17 },
  404. * source2 = { title: 'dev' };
  405. *
  406. * UE.utils.extend2( target, source1, source2 );
  407. *
  408. * //output: { name: 'source', age: 17, title: 'dev' }
  409. * console.log( target );
  410. *
  411. * ```
  412. */
  413. extend2: function (t) {
  414. var a = arguments;
  415. for (var i = 1; i < a.length; i++) {
  416. var x = a[i];
  417. for (var k in x) {
  418. if (!t.hasOwnProperty(k)) {
  419. t[k] = x[k];
  420. }
  421. }
  422. }
  423. return t;
  424. },
  425. /**
  426. * 模拟继承机制, 使得subClass继承自superClass
  427. * @method inherits
  428. * @param { Object } subClass 子类对象
  429. * @param { Object } superClass 超类对象
  430. * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
  431. * @return { Object } 继承superClass后的子类对象
  432. * @example
  433. * ```javascript
  434. * function SuperClass(){
  435. * this.name = "小李";
  436. * }
  437. *
  438. * SuperClass.prototype = {
  439. * hello:function(str){
  440. * console.log(this.name + str);
  441. * }
  442. * }
  443. *
  444. * function SubClass(){
  445. * this.name = "小张";
  446. * }
  447. *
  448. * UE.utils.inherits(SubClass,SuperClass);
  449. *
  450. * var sub = new SubClass();
  451. * //output: '小张早上好!
  452. * sub.hello("早上好!");
  453. * ```
  454. */
  455. inherits: function (subClass, superClass) {
  456. var oldP = subClass.prototype,
  457. newP = utils.makeInstance(superClass.prototype);
  458. utils.extend(newP, oldP, true);
  459. subClass.prototype = newP;
  460. return (newP.constructor = subClass);
  461. },
  462. /**
  463. * 用指定的context对象作为函数fn的上下文
  464. * @method bind
  465. * @param { Function } fn 需要绑定上下文的函数对象
  466. * @param { Object } content 函数fn新的上下文对象
  467. * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
  468. * @example
  469. * ```javascript
  470. *
  471. * var name = 'window',
  472. * newTest = null;
  473. *
  474. * function test () {
  475. * console.log( this.name );
  476. * }
  477. *
  478. * newTest = UE.utils.bind( test, { name: 'object' } );
  479. *
  480. * //output: object
  481. * newTest();
  482. *
  483. * //output: window
  484. * test();
  485. *
  486. * ```
  487. */
  488. bind: function (fn, context) {
  489. return function () {
  490. return fn.apply(context, arguments);
  491. };
  492. },
  493. /**
  494. * 创建延迟指定时间后执行的函数fn
  495. * @method defer
  496. * @param { Function } fn 需要延迟执行的函数对象
  497. * @param { int } delay 延迟的时间, 单位是毫秒
  498. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  499. * 而不能保证刚好到达延迟时间时执行。
  500. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  501. * @example
  502. * ```javascript
  503. * var start = 0;
  504. *
  505. * function test(){
  506. * console.log( new Date() - start );
  507. * }
  508. *
  509. * var testDefer = UE.utils.defer( test, 1000 );
  510. * //
  511. * start = new Date();
  512. * //output: (大约在1000毫秒之后输出) 1000
  513. * testDefer();
  514. * ```
  515. */
  516. /**
  517. * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
  518. * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
  519. * @method defer
  520. * @param { Function } fn 需要延迟执行的函数对象
  521. * @param { int } delay 延迟的时间, 单位是毫秒
  522. * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
  523. * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
  524. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  525. * 而不能保证刚好到达延迟时间时执行。
  526. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  527. * @example
  528. * ```javascript
  529. *
  530. * function test(){
  531. * console.log(1);
  532. * }
  533. *
  534. * var testDefer = UE.utils.defer( test, 1000, true );
  535. *
  536. * //output: (两次调用仅有一次输出) 1
  537. * testDefer();
  538. * testDefer();
  539. * ```
  540. */
  541. defer: function (fn, delay, exclusion) {
  542. var timerID;
  543. return function () {
  544. if (exclusion) {
  545. clearTimeout(timerID);
  546. }
  547. timerID = setTimeout(fn, delay);
  548. };
  549. },
  550. /**
  551. * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
  552. * @method indexOf
  553. * @remind 该方法的匹配过程使用的是恒等“===”
  554. * @param { Array } array 需要查找的数组对象
  555. * @param { * } item 需要在目标数组中查找的值
  556. * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
  557. * @example
  558. * ```javascript
  559. * var item = 1,
  560. * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
  561. *
  562. * //output: 4
  563. * console.log( UE.utils.indexOf( arr, item ) );
  564. * ```
  565. */
  566. /**
  567. * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
  568. * @method indexOf
  569. * @remind 该方法的匹配过程使用的是恒等“===”
  570. * @param { Array } array 需要查找的数组对象
  571. * @param { * } item 需要在目标数组中查找的值
  572. * @param { int } start 搜索的起始位置
  573. * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
  574. * @example
  575. * ```javascript
  576. * var item = 1,
  577. * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
  578. *
  579. * //output: 9
  580. * console.log( UE.utils.indexOf( arr, item, 5 ) );
  581. * ```
  582. */
  583. indexOf: function (array, item, start) {
  584. var index = -1;
  585. start = this.isNumber(start) ? start : 0;
  586. this.each(array, function (v, i) {
  587. if (i >= start && v === item) {
  588. index = i;
  589. return false;
  590. }
  591. });
  592. return index;
  593. },
  594. /**
  595. * 移除数组array中所有的元素item
  596. * @method removeItem
  597. * @param { Array } array 要移除元素的目标数组
  598. * @param { * } item 将要被移除的元素
  599. * @remind 该方法的匹配过程使用的是恒等“===”
  600. * @example
  601. * ```javascript
  602. * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
  603. *
  604. * UE.utils.removeItem( arr, 4 );
  605. * //output: [ 5, 7, 1, 3, 6 ]
  606. * console.log( arr );
  607. *
  608. * ```
  609. */
  610. removeItem: function (array, item) {
  611. for (var i = 0, l = array.length; i < l; i++) {
  612. if (array[i] === item) {
  613. array.splice(i, 1);
  614. i--;
  615. }
  616. }
  617. },
  618. /**
  619. * 删除字符串str的首尾空格
  620. * @method trim
  621. * @param { String } str 需要删除首尾空格的字符串
  622. * @return { String } 删除了首尾的空格后的字符串
  623. * @example
  624. * ```javascript
  625. *
  626. * var str = " UEdtior ";
  627. *
  628. * //output: 9
  629. * console.log( str.length );
  630. *
  631. * //output: 7
  632. * console.log( UE.utils.trim( " UEdtior " ).length );
  633. *
  634. * //output: 9
  635. * console.log( str.length );
  636. *
  637. * ```
  638. */
  639. trim: function (str) {
  640. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
  641. },
  642. /**
  643. * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  644. * @method listToMap
  645. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  646. * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
  647. * @return { Object } 转化之后的hash对象
  648. * @example
  649. * ```javascript
  650. *
  651. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  652. * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
  653. *
  654. * ```
  655. */
  656. /**
  657. * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  658. * @method listToMap
  659. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  660. * @param { Array } arr 字符串数组
  661. * @return { Object } 转化之后的hash对象
  662. * @example
  663. * ```javascript
  664. *
  665. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  666. * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
  667. *
  668. * ```
  669. */
  670. listToMap: function (list) {
  671. if (!list) return {};
  672. list = utils.isArray(list) ? list : list.split(',');
  673. for (var i = 0, ci, obj = {}; ci = list[i++];) {
  674. obj[ci.toUpperCase()] = obj[ci] = 1;
  675. }
  676. return obj;
  677. },
  678. /**
  679. * 将str中的html符号转义,将转义“',&,<,",>”五个字符
  680. * @method unhtml
  681. * @param { String } str 需要转义的字符串
  682. * @return { String } 转义后的字符串
  683. * @example
  684. * ```javascript
  685. * var html = '<body>&</body>';
  686. *
  687. * //output: &lt;body&gt;&amp;&lt;/body&gt;
  688. * console.log( UE.utils.unhtml( html ) );
  689. *
  690. * ```
  691. */
  692. unhtml: function (str, reg) {
  693. return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
  694. if (b) {
  695. return a;
  696. } else {
  697. return {
  698. '<': '&lt;',
  699. '&': '&amp;',
  700. '"': '&quot;',
  701. '>': '&gt;',
  702. "'": '&#39;'
  703. }[a]
  704. }
  705. }) : '';
  706. },
  707. /**
  708. * 将url中的html字符转义, 仅转义 ', ", <, > 四个字符
  709. * @param { String } str 需要转义的字符串
  710. * @param { RegExp } reg 自定义的正则
  711. * @return { String } 转义后的字符串
  712. */
  713. unhtmlForUrl: function (str, reg) {
  714. return str ? str.replace(reg || /[<">']/g, function (a) {
  715. return {
  716. '<': '&lt;',
  717. '&': '&amp;',
  718. '"': '&quot;',
  719. '>': '&gt;',
  720. "'": '&#39;'
  721. }[a]
  722. }) : '';
  723. },
  724. /**
  725. * 将str中的转义字符还原成html字符
  726. * @see UE.utils.unhtml(String);
  727. * @method html
  728. * @param { String } str 需要逆转义的字符串
  729. * @return { String } 逆转义后的字符串
  730. * @example
  731. * ```javascript
  732. *
  733. * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
  734. *
  735. * //output: <body>&</body>
  736. * console.log( UE.utils.html( str ) );
  737. *
  738. * ```
  739. */
  740. html: function (str) {
  741. return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
  742. return {
  743. '&lt;': '<',
  744. '&amp;': '&',
  745. '&quot;': '"',
  746. '&gt;': '>',
  747. '&#39;': "'",
  748. '&nbsp;': ' '
  749. }[m]
  750. }) : '';
  751. },
  752. /**
  753. * 将css样式转换为驼峰的形式
  754. * @method cssStyleToDomStyle
  755. * @param { String } cssName 需要转换的css样式名
  756. * @return { String } 转换成驼峰形式后的css样式名
  757. * @example
  758. * ```javascript
  759. *
  760. * var str = 'border-top';
  761. *
  762. * //output: borderTop
  763. * console.log( UE.utils.cssStyleToDomStyle( str ) );
  764. *
  765. * ```
  766. */
  767. cssStyleToDomStyle: function () {
  768. var test = document.createElement('div').style,
  769. cache = {
  770. 'float': test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
  771. };
  772. return function (cssName) {
  773. return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
  774. return match.charAt(1).toUpperCase();
  775. }));
  776. };
  777. }(),
  778. /**
  779. * 动态加载文件到doc中
  780. * @method loadFile
  781. * @param { DomDocument } document 需要加载资源文件的文档对象
  782. * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
  783. * @example
  784. * ```javascript
  785. *
  786. * UE.utils.loadFile( document, {
  787. * src:"test.js",
  788. * tag:"script",
  789. * type:"text/javascript",
  790. * defer:"defer"
  791. * } );
  792. *
  793. * ```
  794. */
  795. /**
  796. * 动态加载文件到doc中,加载成功后执行的回调函数fn
  797. * @method loadFile
  798. * @param { DomDocument } document 需要加载资源文件的文档对象
  799. * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
  800. * @param { Function } fn 资源文件加载成功之后执行的回调
  801. * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
  802. * 在此之后的所有同一URL的请求, 将会直接触发回调。
  803. * @example
  804. * ```javascript
  805. *
  806. * UE.utils.loadFile( document, {
  807. * src:"test.js",
  808. * tag:"script",
  809. * type:"text/javascript",
  810. * defer:"defer"
  811. * }, function () {
  812. * console.log('加载成功');
  813. * } );
  814. *
  815. * ```
  816. */
  817. loadFile: function () {
  818. var tmpList = [];
  819. function getItem(doc, obj) {
  820. try {
  821. for (var i = 0, ci; ci = tmpList[i++];) {
  822. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  823. return ci;
  824. }
  825. }
  826. } catch (e) {
  827. return null;
  828. }
  829. }
  830. return function (doc, obj, fn) {
  831. var item = getItem(doc, obj);
  832. if (item) {
  833. if (item.ready) {
  834. fn && fn();
  835. } else {
  836. item.funs.push(fn)
  837. }
  838. return;
  839. }
  840. tmpList.push({
  841. doc: doc,
  842. url: obj.src || obj.href,
  843. funs: [fn]
  844. });
  845. if (!doc.body) {
  846. var html = [];
  847. for (var p in obj) {
  848. if (p == 'tag') continue;
  849. html.push(p + '="' + obj[p] + '"')
  850. }
  851. doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
  852. return;
  853. }
  854. if (obj.id && doc.getElementById(obj.id)) {
  855. return;
  856. }
  857. var element = doc.createElement(obj.tag);
  858. delete obj.tag;
  859. for (var p in obj) {
  860. element.setAttribute(p, obj[p]);
  861. }
  862. element.onload = element.onreadystatechange = function () {
  863. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  864. item = getItem(doc, obj);
  865. if (item.funs.length > 0) {
  866. item.ready = 1;
  867. for (var fi; fi = item.funs.pop();) {
  868. fi();
  869. }
  870. }
  871. element.onload = element.onreadystatechange = null;
  872. }
  873. };
  874. element.onerror = function () {
  875. throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
  876. };
  877. doc.getElementsByTagName("head")[0].appendChild(element);
  878. }
  879. }(),
  880. /**
  881. * 判断obj对象是否为空
  882. * @method isEmptyObject
  883. * @param { * } obj 需要判断的对象
  884. * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
  885. * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
  886. * @return { Boolean } 对象是否为空
  887. * @example
  888. * ```javascript
  889. *
  890. * //output: true
  891. * console.log( UE.utils.isEmptyObject( {} ) );
  892. *
  893. * //output: true
  894. * console.log( UE.utils.isEmptyObject( [] ) );
  895. *
  896. * //output: true
  897. * console.log( UE.utils.isEmptyObject( "" ) );
  898. *
  899. * //output: false
  900. * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
  901. *
  902. * //output: false
  903. * console.log( UE.utils.isEmptyObject( [1] ) );
  904. *
  905. * //output: false
  906. * console.log( UE.utils.isEmptyObject( "1" ) );
  907. *
  908. * ```
  909. */
  910. isEmptyObject: function (obj) {
  911. if (obj == null) return true;
  912. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
  913. for (var key in obj) if (obj.hasOwnProperty(key)) return false;
  914. return true;
  915. },
  916. /**
  917. * 把rgb格式的颜色值转换成16进制格式
  918. * @method fixColor
  919. * @param { String } rgb格式的颜色值
  920. * @param { String }
  921. * @example
  922. * rgb(255,255,255) => "#ffffff"
  923. */
  924. fixColor: function (name, value) {
  925. if (/color/i.test(name) && /rgba?/.test(value)) {
  926. var array = value.split(",");
  927. if (array.length > 3)
  928. return "";
  929. value = "#";
  930. for (var i = 0, color; color = array[i++];) {
  931. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
  932. value += color.length == 1 ? "0" + color : color;
  933. }
  934. value = value.toUpperCase();
  935. }
  936. return value;
  937. },
  938. /**
  939. * 只针对border,padding,margin做了处理,因为性能问题
  940. * @public
  941. * @function
  942. * @param {String} val style字符串
  943. */
  944. optCss: function (val) {
  945. var padding, margin, border;
  946. val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
  947. if (val.split(' ').length == 1) {
  948. switch (key) {
  949. case 'padding':
  950. !padding && (padding = {});
  951. padding[name] = val;
  952. return '';
  953. case 'margin':
  954. !margin && (margin = {});
  955. margin[name] = val;
  956. return '';
  957. case 'border':
  958. return val == 'initial' ? '' : str;
  959. }
  960. }
  961. return str;
  962. });
  963. function opt(obj, name) {
  964. if (!obj) {
  965. return '';
  966. }
  967. var t = obj.top, b = obj.bottom, l = obj.left, r = obj.right, val = '';
  968. if (!t || !l || !b || !r) {
  969. for (var p in obj) {
  970. val += ';' + name + '-' + p + ':' + obj[p] + ';';
  971. }
  972. } else {
  973. val += ';' + name + ':' +
  974. (t == b && b == l && l == r ? t :
  975. t == b && l == r ? (t + ' ' + l) :
  976. l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
  977. }
  978. return val;
  979. }
  980. val += opt(padding, 'padding') + opt(margin, 'margin');
  981. return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
  982. .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
  983. return b ? b + ";;" : ';'
  984. });
  985. },
  986. /**
  987. * 克隆对象
  988. * @method clone
  989. * @param { Object } source 源对象
  990. * @return { Object } source的一个副本
  991. */
  992. /**
  993. * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
  994. * @method clone
  995. * @param { Object } source 源对象
  996. * @param { Object } target 目标对象
  997. * @return { Object } 附加了source对象所有属性的target对象
  998. */
  999. clone: function (source, target) {
  1000. var tmp;
  1001. target = target || {};
  1002. for (var i in source) {
  1003. if (source.hasOwnProperty(i)) {
  1004. tmp = source[i];
  1005. if (typeof tmp == 'object') {
  1006. target[i] = utils.isArray(tmp) ? [] : {};
  1007. utils.clone(source[i], target[i])
  1008. } else {
  1009. target[i] = tmp;
  1010. }
  1011. }
  1012. }
  1013. return target;
  1014. },
  1015. /**
  1016. * 把cm/pt为单位的值转换为px为单位的值
  1017. * @method transUnitToPx
  1018. * @param { String } 待转换的带单位的字符串
  1019. * @return { String } 转换为px为计量单位的值的字符串
  1020. * @example
  1021. * ```javascript
  1022. *
  1023. * //output: 500px
  1024. * console.log( UE.utils.transUnitToPx( '20cm' ) );
  1025. *
  1026. * //output: 27px
  1027. * console.log( UE.utils.transUnitToPx( '20pt' ) );
  1028. *
  1029. * ```
  1030. */
  1031. transUnitToPx: function (val) {
  1032. if (!/(pt|cm)/.test(val)) {
  1033. return val
  1034. }
  1035. var unit;
  1036. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  1037. val = v;
  1038. unit = u;
  1039. });
  1040. switch (unit) {
  1041. case 'cm':
  1042. val = parseFloat(val) * 25;
  1043. break;
  1044. case 'pt':
  1045. val = Math.round(parseFloat(val) * 96 / 72);
  1046. }
  1047. return val + (val ? 'px' : '');
  1048. },
  1049. /**
  1050. * 在dom树ready之后执行给定的回调函数
  1051. * @method domReady
  1052. * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
  1053. * @param { Function } fn dom树ready之后的回调函数
  1054. * @example
  1055. * ```javascript
  1056. *
  1057. * UE.utils.domReady( function () {
  1058. *
  1059. * console.log('123');
  1060. *
  1061. * } );
  1062. *
  1063. * ```
  1064. */
  1065. domReady: function () {
  1066. var fnArr = [];
  1067. function doReady(doc) {
  1068. //确保onready只执行一次
  1069. doc.isReady = true;
  1070. for (var ci; ci = fnArr.pop(); ci()) {
  1071. }
  1072. }
  1073. return function (onready, win) {
  1074. win = win || window;
  1075. var doc = win.document;
  1076. onready && fnArr.push(onready);
  1077. if (doc.readyState === "complete") {
  1078. doReady(doc);
  1079. } else {
  1080. doc.isReady && doReady(doc);
  1081. if (browser.ie && browser.version != 11) {
  1082. (function () {
  1083. if (doc.isReady) return;
  1084. try {
  1085. doc.documentElement.doScroll("left");
  1086. } catch (error) {
  1087. setTimeout(arguments.callee, 0);
  1088. return;
  1089. }
  1090. doReady(doc);
  1091. })();
  1092. win.attachEvent('onload', function () {
  1093. doReady(doc)
  1094. });
  1095. } else {
  1096. doc.addEventListener("DOMContentLoaded", function () {
  1097. doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
  1098. doReady(doc);
  1099. }, false);
  1100. win.addEventListener('load', function () {
  1101. doReady(doc)
  1102. }, false);
  1103. }
  1104. }
  1105. }
  1106. }(),
  1107. /**
  1108. * 动态添加css样式
  1109. * @method cssRule
  1110. * @param { String } 节点名称
  1111. * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  1112. * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  1113. * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  1114. * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
  1115. * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  1116. */
  1117. cssRule: browser.ie && browser.version != 11 ? function (key, style, doc) {
  1118. var indexList, index;
  1119. if (style === undefined || style && style.nodeType && style.nodeType == 9) {
  1120. //获取样式
  1121. doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
  1122. indexList = doc.indexList || (doc.indexList = {});
  1123. index = indexList[key];
  1124. if (index !== undefined) {
  1125. return doc.styleSheets[index].cssText
  1126. }
  1127. return undefined;
  1128. }
  1129. doc = doc || document;
  1130. indexList = doc.indexList || (doc.indexList = {});
  1131. index = indexList[key];
  1132. //清除样式
  1133. if (style === '') {
  1134. if (index !== undefined) {
  1135. doc.styleSheets[index].cssText = '';
  1136. delete indexList[key];
  1137. return true
  1138. }
  1139. return false;
  1140. }
  1141. //添加样式
  1142. if (index !== undefined) {
  1143. sheetStyle = doc.styleSheets[index];
  1144. } else {
  1145. sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
  1146. indexList[key] = index;
  1147. }
  1148. sheetStyle.cssText = style;
  1149. } : function (key, style, doc) {
  1150. var head, node;
  1151. if (style === undefined || style && style.nodeType && style.nodeType == 9) {
  1152. //获取样式
  1153. doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
  1154. node = doc.getElementById(key);
  1155. return node ? node.innerHTML : undefined;
  1156. }
  1157. doc = doc || document;
  1158. node = doc.getElementById(key);
  1159. //清除样式
  1160. if (style === '') {
  1161. if (node) {
  1162. node.parentNode.removeChild(node);
  1163. return true
  1164. }
  1165. return false;
  1166. }
  1167. //添加样式
  1168. if (node) {
  1169. node.innerHTML = style;
  1170. } else {
  1171. node = doc.createElement('style');
  1172. node.id = key;
  1173. node.innerHTML = style;
  1174. doc.getElementsByTagName('head')[0].appendChild(node);
  1175. }
  1176. },
  1177. sort: function (array, compareFn) {
  1178. compareFn = compareFn || function (item1, item2) {
  1179. return item1.localeCompare(item2);
  1180. };
  1181. for (var i = 0, len = array.length; i < len; i++) {
  1182. for (var j = i, length = array.length; j < length; j++) {
  1183. if (compareFn(array[i], array[j]) > 0) {
  1184. var t = array[i];
  1185. array[i] = array[j];
  1186. array[j] = t;
  1187. }
  1188. }
  1189. }
  1190. return array;
  1191. },
  1192. serializeParam: function (json) {
  1193. var strArr = [];
  1194. for (var i in json) {
  1195. //忽略默认的几个参数
  1196. if (i == "method" || i == "timeout" || i == "async") continue;
  1197. //传递过来的对象和函数不在提交之列
  1198. if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
  1199. strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
  1200. } else if (utils.isArray(json[i])) {
  1201. //支持传数组内容
  1202. for (var j = 0; j < json[i].length; j++) {
  1203. strArr.push(encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j]));
  1204. }
  1205. }
  1206. }
  1207. return strArr.join("&");
  1208. },
  1209. formatUrl: function (url) {
  1210. var u = url.replace(/&&/g, '&');
  1211. u = u.replace(/\?&/g, '?');
  1212. u = u.replace(/&$/g, '');
  1213. u = u.replace(/&#/g, '#');
  1214. u = u.replace(/&+/g, '&');
  1215. return u;
  1216. },
  1217. isCrossDomainUrl: function (url) {
  1218. var a = document.createElement('a');
  1219. a.href = url;
  1220. if (browser.ie) {
  1221. a.href = a.href;
  1222. }
  1223. return !(a.protocol == location.protocol && a.hostname == location.hostname &&
  1224. (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80')));
  1225. },
  1226. clearEmptyAttrs: function (obj) {
  1227. for (var p in obj) {
  1228. if (obj[p] === '') {
  1229. delete obj[p]
  1230. }
  1231. }
  1232. return obj;
  1233. },
  1234. str2json: function (s) {
  1235. if (!utils.isString(s)) return null;
  1236. if (window.JSON) {
  1237. return JSON.parse(s);
  1238. } else {
  1239. return (new Function("return " + utils.trim(s || '')))();
  1240. }
  1241. },
  1242. json2str: (function () {
  1243. if (window.JSON) {
  1244. return JSON.stringify;
  1245. } else {
  1246. var escapeMap = {
  1247. "\b": '\\b',
  1248. "\t": '\\t',
  1249. "\n": '\\n',
  1250. "\f": '\\f',
  1251. "\r": '\\r',
  1252. '"': '\\"',
  1253. "\\": '\\\\'
  1254. };
  1255. function encodeString(source) {
  1256. if (/["\\\x00-\x1f]/.test(source)) {
  1257. source = source.replace(
  1258. /["\\\x00-\x1f]/g,
  1259. function (match) {
  1260. var c = escapeMap[match];
  1261. if (c) {
  1262. return c;
  1263. }
  1264. c = match.charCodeAt();
  1265. return "\\u00"
  1266. + Math.floor(c / 16).toString(16)
  1267. + (c % 16).toString(16);
  1268. });
  1269. }
  1270. return '"' + source + '"';
  1271. }
  1272. function encodeArray(source) {
  1273. var result = ["["],
  1274. l = source.length,
  1275. preComma, i, item;
  1276. for (i = 0; i < l; i++) {
  1277. item = source[i];
  1278. switch (typeof item) {
  1279. case "undefined":
  1280. case "function":
  1281. case "unknown":
  1282. break;
  1283. default:
  1284. if (preComma) {
  1285. result.push(',');
  1286. }
  1287. result.push(utils.json2str(item));
  1288. preComma = 1;
  1289. }
  1290. }
  1291. result.push("]");
  1292. return result.join("");
  1293. }
  1294. function pad(source) {
  1295. return source < 10 ? '0' + source : source;
  1296. }
  1297. function encodeDate(source) {
  1298. return '"' + source.getFullYear() + "-"
  1299. + pad(source.getMonth() + 1) + "-"
  1300. + pad(source.getDate()) + "T"
  1301. + pad(source.getHours()) + ":"
  1302. + pad(source.getMinutes()) + ":"
  1303. + pad(source.getSeconds()) + '"';
  1304. }
  1305. return function (value) {
  1306. switch (typeof value) {
  1307. case 'undefined':
  1308. return 'undefined';
  1309. case 'number':
  1310. return isFinite(value) ? String(value) : "null";
  1311. case 'string':
  1312. return encodeString(value);
  1313. case 'boolean':
  1314. return String(value);
  1315. default:
  1316. if (value === null) {
  1317. return 'null';
  1318. } else if (utils.isArray(value)) {
  1319. return encodeArray(value);
  1320. } else if (utils.isDate(value)) {
  1321. return encodeDate(value);
  1322. } else {
  1323. var result = ['{'],
  1324. encode = utils.json2str,
  1325. preComma,
  1326. item;
  1327. for (var key in value) {
  1328. if (Object.prototype.hasOwnProperty.call(value, key)) {
  1329. item = value[key];
  1330. switch (typeof item) {
  1331. case 'undefined':
  1332. case 'unknown':
  1333. case 'function':
  1334. break;
  1335. default:
  1336. if (preComma) {
  1337. result.push(',');
  1338. }
  1339. preComma = 1;
  1340. result.push(encode(key) + ':' + encode(item));
  1341. }
  1342. }
  1343. }
  1344. result.push('}');
  1345. return result.join('');
  1346. }
  1347. }
  1348. };
  1349. }
  1350. })()
  1351. };
  1352. /**
  1353. * 判断给定的对象是否是字符串
  1354. * @method isString
  1355. * @param { * } object 需要判断的对象
  1356. * @return { Boolean } 给定的对象是否是字符串
  1357. */
  1358. /**
  1359. * 判断给定的对象是否是数组
  1360. * @method isArray
  1361. * @param { * } object 需要判断的对象
  1362. * @return { Boolean } 给定的对象是否是数组
  1363. */
  1364. /**
  1365. * 判断给定的对象是否是一个Function
  1366. * @method isFunction
  1367. * @param { * } object 需要判断的对象
  1368. * @return { Boolean } 给定的对象是否是Function
  1369. */
  1370. /**
  1371. * 判断给定的对象是否是Number
  1372. * @method isNumber
  1373. * @param { * } object 需要判断的对象
  1374. * @return { Boolean } 给定的对象是否是Number
  1375. */
  1376. /**
  1377. * 判断给定的对象是否是一个正则表达式
  1378. * @method isRegExp
  1379. * @param { * } object 需要判断的对象
  1380. * @return { Boolean } 给定的对象是否是正则表达式
  1381. */
  1382. /**
  1383. * 判断给定的对象是否是一个普通对象
  1384. * @method isObject
  1385. * @param { * } object 需要判断的对象
  1386. * @return { Boolean } 给定的对象是否是普通对象
  1387. */
  1388. utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) {
  1389. UE.utils['is' + v] = function (obj) {
  1390. return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
  1391. }
  1392. });
  1393. // core/EventBase.js
  1394. /**
  1395. * UE采用的事件基类
  1396. * @file
  1397. * @module UE
  1398. * @class EventBase
  1399. * @since 1.2.6.1
  1400. */
  1401. /**
  1402. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  1403. * @unfile
  1404. * @module UE
  1405. */
  1406. /**
  1407. * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  1408. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  1409. * @unfile
  1410. * @module UE
  1411. * @class EventBase
  1412. */
  1413. /**
  1414. * 通过此构造器,子类可以继承EventBase获取事件监听的方法
  1415. * @constructor
  1416. * @example
  1417. * ```javascript
  1418. * UE.EventBase.call(editor);
  1419. * ```
  1420. */
  1421. var EventBase = UE.EventBase = function () {
  1422. };
  1423. EventBase.prototype = {
  1424. /**
  1425. * 注册事件监听器
  1426. * @method addListener
  1427. * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
  1428. * @param { Function } fn 监听的事件被触发时,会执行该回调函数
  1429. * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
  1430. * @example
  1431. * ```javascript
  1432. * editor.addListener('selectionchange',function(){
  1433. * console.log("选区已经变化!");
  1434. * })
  1435. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  1436. * if(type == 'beforegetcontent'){
  1437. * //do something
  1438. * }else{
  1439. * //do something
  1440. * }
  1441. * console.log(this.getContent) // this是注册的事件的编辑器实例
  1442. * })
  1443. * ```
  1444. * @see UE.EventBase:fireEvent(String)
  1445. */
  1446. addListener: function (types, listener) {
  1447. types = utils.trim(types).split(/\s+/);
  1448. for (var i = 0, ti; ti = types[i++];) {
  1449. getListener(this, ti, true).push(listener);
  1450. }
  1451. },
  1452. on: function (types, listener) {
  1453. return this.addListener(types, listener);
  1454. },
  1455. off: function (types, listener) {
  1456. return this.removeListener(types, listener)
  1457. },
  1458. trigger: function () {
  1459. return this.fireEvent.apply(this, arguments);
  1460. },
  1461. /**
  1462. * 移除事件监听器
  1463. * @method removeListener
  1464. * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
  1465. * @param { Function } fn 移除监听事件的函数引用
  1466. * @example
  1467. * ```javascript
  1468. * //changeCallback为方法体
  1469. * editor.removeListener("selectionchange",changeCallback);
  1470. * ```
  1471. */
  1472. removeListener: function (types, listener) {
  1473. types = utils.trim(types).split(/\s+/);
  1474. for (var i = 0, ti; ti = types[i++];) {
  1475. utils.removeItem(getListener(this, ti) || [], listener);
  1476. }
  1477. },
  1478. /**
  1479. * 触发事件
  1480. * @method fireEvent
  1481. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1482. * @remind 该方法会触发addListener
  1483. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1484. * @example
  1485. * ```javascript
  1486. * editor.fireEvent("selectionchange");
  1487. * ```
  1488. */
  1489. /**
  1490. * 触发事件
  1491. * @method fireEvent
  1492. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1493. * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
  1494. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1495. * @example
  1496. * ```javascript
  1497. *
  1498. * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
  1499. *
  1500. * console.log( arg1 + " " + arg2 );
  1501. *
  1502. * } );
  1503. *
  1504. * //触发selectionchange事件, 会执行上面的事件监听器
  1505. * //output: Hello World
  1506. * editor.fireEvent("selectionchange", "Hello", "World");
  1507. * ```
  1508. */
  1509. fireEvent: function () {
  1510. var types = arguments[0];
  1511. types = utils.trim(types).split(' ');
  1512. for (var i = 0, ti; ti = types[i++];) {
  1513. var listeners = getListener(this, ti),
  1514. r, t, k;
  1515. if (listeners) {
  1516. k = listeners.length;
  1517. while (k--) {
  1518. if (!listeners[k]) continue;
  1519. t = listeners[k].apply(this, arguments);
  1520. if (t === true) {
  1521. return t;
  1522. }
  1523. if (t !== undefined) {
  1524. r = t;
  1525. }
  1526. }
  1527. }
  1528. if (t = this['on' + ti.toLowerCase()]) {
  1529. r = t.apply(this, arguments);
  1530. }
  1531. }
  1532. return r;
  1533. }
  1534. };
  1535. /**
  1536. * 获得对象所拥有监听类型的所有监听器
  1537. * @unfile
  1538. * @module UE
  1539. * @since 1.2.6.1
  1540. * @method getListener
  1541. * @public
  1542. * @param { Object } obj 查询监听器的对象
  1543. * @param { String } type 事件类型
  1544. * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  1545. * @return { Array } 监听器数组
  1546. */
  1547. function getListener(obj, type, force) {
  1548. var allListeners;
  1549. type = type.toLowerCase();
  1550. return ((allListeners = (obj.__allListeners || force && (obj.__allListeners = {})))
  1551. && (allListeners[type] || force && (allListeners[type] = [])));
  1552. }
  1553. // core/dtd.js
  1554. ///import editor.js
  1555. ///import core/dom/dom.js
  1556. ///import core/utils.js
  1557. /**
  1558. * dtd html语义化的体现类
  1559. * @constructor
  1560. * @namespace dtd
  1561. */
  1562. var dtd = dom.dtd = (function () {
  1563. function _(s) {
  1564. for (var k in s) {
  1565. s[k.toUpperCase()] = s[k];
  1566. }
  1567. return s;
  1568. }
  1569. var X = utils.extend2;
  1570. var A = _({isindex: 1, fieldset: 1}),
  1571. B = _({input: 1, button: 1, select: 1, textarea: 1, label: 1}),
  1572. C = X(_({a: 1}), B),
  1573. D = X({iframe: 1}, C),
  1574. E = _({
  1575. hr: 1,
  1576. ul: 1,
  1577. menu: 1,
  1578. div: 1,
  1579. blockquote: 1,
  1580. noscript: 1,
  1581. table: 1,
  1582. center: 1,
  1583. address: 1,
  1584. dir: 1,
  1585. pre: 1,
  1586. h5: 1,
  1587. dl: 1,
  1588. h4: 1,
  1589. noframes: 1,
  1590. h6: 1,
  1591. ol: 1,
  1592. h1: 1,
  1593. h3: 1,
  1594. h2: 1
  1595. }),
  1596. F = _({ins: 1, del: 1, script: 1, style: 1}),
  1597. G = X(_({
  1598. b: 1,
  1599. acronym: 1,
  1600. bdo: 1,
  1601. 'var': 1,
  1602. '#': 1,
  1603. abbr: 1,
  1604. code: 1,
  1605. br: 1,
  1606. i: 1,
  1607. cite: 1,
  1608. kbd: 1,
  1609. u: 1,
  1610. strike: 1,
  1611. s: 1,
  1612. tt: 1,
  1613. strong: 1,
  1614. q: 1,
  1615. samp: 1,
  1616. em: 1,
  1617. dfn: 1,
  1618. span: 1
  1619. }), F),
  1620. H = X(_({
  1621. sub: 1,
  1622. img: 1,
  1623. embed: 1,
  1624. object: 1,
  1625. sup: 1,
  1626. basefont: 1,
  1627. map: 1,
  1628. applet: 1,
  1629. font: 1,
  1630. big: 1,
  1631. small: 1
  1632. }), G),
  1633. I = X(_({p: 1}), H),
  1634. J = X(_({iframe: 1}), H, B),
  1635. K = _({
  1636. img: 1,
  1637. embed: 1,
  1638. noscript: 1,
  1639. br: 1,
  1640. kbd: 1,
  1641. center: 1,
  1642. button: 1,
  1643. basefont: 1,
  1644. h5: 1,
  1645. h4: 1,
  1646. samp: 1,
  1647. h6: 1,
  1648. ol: 1,
  1649. h1: 1,
  1650. h3: 1,
  1651. h2: 1,
  1652. form: 1,
  1653. font: 1,
  1654. '#': 1,
  1655. select: 1,
  1656. menu: 1,
  1657. ins: 1,
  1658. abbr: 1,
  1659. label: 1,
  1660. code: 1,
  1661. table: 1,
  1662. script: 1,
  1663. cite: 1,
  1664. input: 1,
  1665. iframe: 1,
  1666. strong: 1,
  1667. textarea: 1,
  1668. noframes: 1,
  1669. big: 1,
  1670. small: 1,
  1671. span: 1,
  1672. hr: 1,
  1673. sub: 1,
  1674. bdo: 1,
  1675. 'var': 1,
  1676. div: 1,
  1677. object: 1,
  1678. sup: 1,
  1679. strike: 1,
  1680. dir: 1,
  1681. map: 1,
  1682. dl: 1,
  1683. applet: 1,
  1684. del: 1,
  1685. isindex: 1,
  1686. fieldset: 1,
  1687. ul: 1,
  1688. b: 1,
  1689. acronym: 1,
  1690. a: 1,
  1691. blockquote: 1,
  1692. i: 1,
  1693. u: 1,
  1694. s: 1,
  1695. tt: 1,
  1696. address: 1,
  1697. q: 1,
  1698. pre: 1,
  1699. p: 1,
  1700. em: 1,
  1701. dfn: 1
  1702. }),
  1703. L = X(_({a: 0}), J),//a不能被切开,所以把他
  1704. M = _({tr: 1}),
  1705. N = _({'#': 1}),
  1706. O = X(_({param: 1}), K),
  1707. P = X(_({form: 1}), A, D, E, I),
  1708. Q = _({li: 1, ol: 1, ul: 1}),
  1709. R = _({style: 1, script: 1}),
  1710. S = _({base: 1, link: 1, meta: 1, title: 1}),
  1711. T = X(S, R),
  1712. U = _({head: 1, body: 1}),
  1713. V = _({html: 1});
  1714. var block = _({
  1715. address: 1,
  1716. blockquote: 1,
  1717. center: 1,
  1718. dir: 1,
  1719. div: 1,
  1720. dl: 1,
  1721. fieldset: 1,
  1722. form: 1,
  1723. h1: 1,
  1724. h2: 1,
  1725. h3: 1,
  1726. h4: 1,
  1727. h5: 1,
  1728. h6: 1,
  1729. hr: 1,
  1730. isindex: 1,
  1731. menu: 1,
  1732. noframes: 1,
  1733. ol: 1,
  1734. p: 1,
  1735. pre: 1,
  1736. table: 1,
  1737. ul: 1
  1738. }),
  1739. empty = _({
  1740. area: 1,
  1741. base: 1,
  1742. basefont: 1,
  1743. br: 1,
  1744. col: 1,
  1745. command: 1,
  1746. dialog: 1,
  1747. embed: 1,
  1748. hr: 1,
  1749. img: 1,
  1750. input: 1,
  1751. isindex: 1,
  1752. keygen: 1,
  1753. link: 1,
  1754. meta: 1,
  1755. param: 1,
  1756. source: 1,
  1757. track: 1,
  1758. wbr: 1
  1759. });
  1760. return _({
  1761. // $ 表示自定的属性
  1762. // body外的元素列表.
  1763. $nonBodyContent: X(V, U, S),
  1764. //块结构元素列表
  1765. $block: block,
  1766. //内联元素列表
  1767. $inline: L,
  1768. $inlineWithA: X(_({a: 1}), L),
  1769. $body: X(_({script: 1, style: 1}), block),
  1770. $cdata: _({script: 1, style: 1}),
  1771. //自闭和元素
  1772. $empty: empty,
  1773. //不是自闭合,但不能让range选中里边
  1774. $nonChild: _({iframe: 1, textarea: 1}),
  1775. //列表元素列表
  1776. $listItem: _({dd: 1, dt: 1, li: 1}),
  1777. //列表根元素列表
  1778. $list: _({ul: 1, ol: 1, dl: 1}),
  1779. //不能认为是空的元素
  1780. $isNotEmpty: _({
  1781. table: 1,
  1782. ul: 1,
  1783. ol: 1,
  1784. dl: 1,
  1785. iframe: 1,
  1786. area: 1,
  1787. base: 1,
  1788. col: 1,
  1789. hr: 1,
  1790. img: 1,
  1791. embed: 1,
  1792. input: 1,
  1793. link: 1,
  1794. meta: 1,
  1795. param: 1,
  1796. h1: 1,
  1797. h2: 1,
  1798. h3: 1,
  1799. h4: 1,
  1800. h5: 1,
  1801. h6: 1
  1802. }),
  1803. //如果没有子节点就可以删除的元素列表,像span,a
  1804. $removeEmpty: _({
  1805. a: 1,
  1806. abbr: 1,
  1807. acronym: 1,
  1808. address: 1,
  1809. b: 1,
  1810. bdo: 1,
  1811. big: 1,
  1812. cite: 1,
  1813. code: 1,
  1814. del: 1,
  1815. dfn: 1,
  1816. em: 1,
  1817. font: 1,
  1818. i: 1,
  1819. ins: 1,
  1820. label: 1,
  1821. kbd: 1,
  1822. q: 1,
  1823. s: 1,
  1824. samp: 1,
  1825. small: 1,
  1826. span: 1,
  1827. strike: 1,
  1828. strong: 1,
  1829. sub: 1,
  1830. sup: 1,
  1831. tt: 1,
  1832. u: 1,
  1833. 'var': 1
  1834. }),
  1835. $removeEmptyBlock: _({'p': 1, 'div': 1}),
  1836. //在table元素里的元素列表
  1837. $tableContent: _({
  1838. caption: 1,
  1839. col: 1,
  1840. colgroup: 1,
  1841. tbody: 1,
  1842. td: 1,
  1843. tfoot: 1,
  1844. th: 1,
  1845. thead: 1,
  1846. tr: 1,
  1847. table: 1
  1848. }),
  1849. //不转换的标签
  1850. $notTransContent: _({pre: 1, script: 1, style: 1, textarea: 1}),
  1851. html: U,
  1852. head: T,
  1853. style: N,
  1854. script: N,
  1855. body: P,
  1856. base: {},
  1857. link: {},
  1858. meta: {},
  1859. title: N,
  1860. col: {},
  1861. tr: _({td: 1, th: 1}),
  1862. img: {},
  1863. embed: {},
  1864. colgroup: _({thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1}),
  1865. noscript: P,
  1866. td: P,
  1867. br: {},
  1868. th: P,
  1869. center: P,
  1870. kbd: L,
  1871. button: X(I, E),
  1872. basefont: {},
  1873. h5: L,
  1874. h4: L,
  1875. samp: L,
  1876. h6: L,
  1877. ol: Q,
  1878. h1: L,
  1879. h3: L,
  1880. option: N,
  1881. h2: L,
  1882. form: X(A, D, E, I),
  1883. select: _({optgroup: 1, option: 1}),
  1884. font: L,
  1885. ins: L,
  1886. menu: Q,
  1887. abbr: L,
  1888. label: L,
  1889. table: _({thead: 1, col: 1, tbody: 1, tr: 1, colgroup: 1, caption: 1, tfoot: 1}),
  1890. code: L,
  1891. tfoot: M,
  1892. cite: L,
  1893. li: P,
  1894. input: {},
  1895. iframe: P,
  1896. strong: L,
  1897. textarea: N,
  1898. noframes: P,
  1899. big: L,
  1900. small: L,
  1901. //trace:
  1902. span: _({'#': 1, br: 1, b: 1, strong: 1, u: 1, i: 1, em: 1, sub: 1, sup: 1, strike: 1, span: 1}),
  1903. hr: L,
  1904. dt: L,
  1905. sub: L,
  1906. optgroup: _({option: 1}),
  1907. param: {},
  1908. bdo: L,
  1909. 'var': L,
  1910. div: P,
  1911. object: O,
  1912. sup: L,
  1913. dd: P,
  1914. strike: L,
  1915. area: {},
  1916. dir: Q,
  1917. map: X(_({area: 1, form: 1, p: 1}), A, F, E),
  1918. applet: O,
  1919. dl: _({dt: 1, dd: 1}),
  1920. del: L,
  1921. isindex: {},
  1922. fieldset: X(_({legend: 1}), K),
  1923. thead: M,
  1924. ul: Q,
  1925. acronym: L,
  1926. b: L,
  1927. a: X(_({a: 1}), J),
  1928. blockquote: X(_({td: 1, tr: 1, tbody: 1, li: 1}), P),
  1929. caption: L,
  1930. i: L,
  1931. u: L,
  1932. tbody: M,
  1933. s: L,
  1934. address: X(D, I),
  1935. tt: L,
  1936. legend: L,
  1937. q: L,
  1938. pre: X(G, C),
  1939. p: X(_({'a': 1}), L),
  1940. em: L,
  1941. dfn: L
  1942. });
  1943. })();
  1944. // core/domUtils.js
  1945. /**
  1946. * Dom操作工具包
  1947. * @file
  1948. * @module UE.dom.domUtils
  1949. * @since 1.2.6.1
  1950. */
  1951. /**
  1952. * Dom操作工具包
  1953. * @unfile
  1954. * @module UE.dom.domUtils
  1955. */
  1956. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  1957. var tmpNode = startFromChild && node[start],
  1958. parent;
  1959. !tmpNode && (tmpNode = node[ltr]);
  1960. while (!tmpNode && (parent = (parent || node).parentNode)) {
  1961. if (parent.tagName == 'BODY' || guard && !guard(parent)) {
  1962. return null;
  1963. }
  1964. tmpNode = parent[ltr];
  1965. }
  1966. if (tmpNode && fn && !fn(tmpNode)) {
  1967. return getDomNode(tmpNode, start, ltr, false, fn);
  1968. }
  1969. return tmpNode;
  1970. }
  1971. var attrFix = ie && browser.version < 9 ? {
  1972. tabindex: "tabIndex",
  1973. readonly: "readOnly",
  1974. "for": "htmlFor",
  1975. "class": "className",
  1976. maxlength: "maxLength",
  1977. cellspacing: "cellSpacing",
  1978. cellpadding: "cellPadding",
  1979. rowspan: "rowSpan",
  1980. colspan: "colSpan",
  1981. usemap: "useMap",
  1982. frameborder: "frameBorder"
  1983. } : {
  1984. tabindex: "tabIndex",
  1985. readonly: "readOnly"
  1986. },
  1987. styleBlock = utils.listToMap([
  1988. '-webkit-box', '-moz-box', 'block',
  1989. 'list-item', 'table', 'table-row-group',
  1990. 'table-header-group', 'table-footer-group',
  1991. 'table-row', 'table-column-group', 'table-column',
  1992. 'table-cell', 'table-caption'
  1993. ]);
  1994. var domUtils = dom.domUtils = {
  1995. //节点常量
  1996. NODE_ELEMENT: 1,
  1997. NODE_DOCUMENT: 9,
  1998. NODE_TEXT: 3,
  1999. NODE_COMMENT: 8,
  2000. NODE_DOCUMENT_FRAGMENT: 11,
  2001. //位置关系
  2002. POSITION_IDENTICAL: 0,
  2003. POSITION_DISCONNECTED: 1,
  2004. POSITION_FOLLOWING: 2,
  2005. POSITION_PRECEDING: 4,
  2006. POSITION_IS_CONTAINED: 8,
  2007. POSITION_CONTAINS: 16,
  2008. //ie6使用其他的会有一段空白出现
  2009. fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
  2010. //-------------------------Node部分--------------------------------
  2011. keys: {
  2012. /*Backspace*/ 8: 1, /*Delete*/ 46: 1,
  2013. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  2014. 37: 1, 38: 1, 39: 1, 40: 1,
  2015. 13: 1 /*enter*/
  2016. },
  2017. /**
  2018. * 获取节点A相对于节点B的位置关系
  2019. * @method getPosition
  2020. * @param { Node } nodeA 需要查询位置关系的节点A
  2021. * @param { Node } nodeB 需要查询位置关系的节点B
  2022. * @return { Number } 节点A与节点B的关系
  2023. * @example
  2024. * ```javascript
  2025. * //output: 20
  2026. * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
  2027. *
  2028. * switch ( position ) {
  2029. *
  2030. * //0
  2031. * case UE.dom.domUtils.POSITION_IDENTICAL:
  2032. * console.log('元素相同');
  2033. * break;
  2034. * //1
  2035. * case UE.dom.domUtils.POSITION_DISCONNECTED:
  2036. * console.log('两个节点在不同的文档中');
  2037. * break;
  2038. * //2
  2039. * case UE.dom.domUtils.POSITION_FOLLOWING:
  2040. * console.log('节点A在节点B之后');
  2041. * break;
  2042. * //4
  2043. * case UE.dom.domUtils.POSITION_PRECEDING;
  2044. * console.log('节点A在节点B之前');
  2045. * break;
  2046. * //8
  2047. * case UE.dom.domUtils.POSITION_IS_CONTAINED:
  2048. * console.log('节点A被节点B包含');
  2049. * break;
  2050. * case 10:
  2051. * console.log('节点A被节点B包含且节点A在节点B之后');
  2052. * break;
  2053. * //16
  2054. * case UE.dom.domUtils.POSITION_CONTAINS:
  2055. * console.log('节点A包含节点B');
  2056. * break;
  2057. * case 20:
  2058. * console.log('节点A包含节点B且节点A在节点B之前');
  2059. * break;
  2060. *
  2061. * }
  2062. * ```
  2063. */
  2064. getPosition: function (nodeA, nodeB) {
  2065. // 如果两个节点是同一个节点
  2066. if (nodeA === nodeB) {
  2067. // domUtils.POSITION_IDENTICAL
  2068. return 0;
  2069. }
  2070. var node,
  2071. parentsA = [nodeA],
  2072. parentsB = [nodeB];
  2073. node = nodeA;
  2074. while (node = node.parentNode) {
  2075. // 如果nodeB是nodeA的祖先节点
  2076. if (node === nodeB) {
  2077. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  2078. return 10;
  2079. }
  2080. parentsA.push(node);
  2081. }
  2082. node = nodeB;
  2083. while (node = node.parentNode) {
  2084. // 如果nodeA是nodeB的祖先节点
  2085. if (node === nodeA) {
  2086. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  2087. return 20;
  2088. }
  2089. parentsB.push(node);
  2090. }
  2091. parentsA.reverse();
  2092. parentsB.reverse();
  2093. if (parentsA[0] !== parentsB[0]) {
  2094. // domUtils.POSITION_DISCONNECTED
  2095. return 1;
  2096. }
  2097. var i = -1;
  2098. while (i++, parentsA[i] === parentsB[i]) {
  2099. }
  2100. nodeA = parentsA[i];
  2101. nodeB = parentsB[i];
  2102. while (nodeA = nodeA.nextSibling) {
  2103. if (nodeA === nodeB) {
  2104. // domUtils.POSITION_PRECEDING
  2105. return 4
  2106. }
  2107. }
  2108. // domUtils.POSITION_FOLLOWING
  2109. return 2;
  2110. },
  2111. /**
  2112. * 检测节点node在父节点中的索引位置
  2113. * @method getNodeIndex
  2114. * @param { Node } node 需要检测的节点对象
  2115. * @return { Number } 该节点在父节点中的位置
  2116. * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
  2117. */
  2118. /**
  2119. * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
  2120. * @method getNodeIndex
  2121. * @param { Node } node 需要检测的节点对象
  2122. * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
  2123. * @return { Number } 该节点在父节点中的位置
  2124. * @example
  2125. * ```javascript
  2126. *
  2127. * var node = document.createElement("div");
  2128. *
  2129. * node.appendChild( document.createTextNode( "hello" ) );
  2130. * node.appendChild( document.createTextNode( "world" ) );
  2131. * node.appendChild( node = document.createElement( "div" ) );
  2132. *
  2133. * //output: 2
  2134. * console.log( UE.dom.domUtils.getNodeIndex( node ) );
  2135. *
  2136. * //output: 1
  2137. * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
  2138. *
  2139. * ```
  2140. */
  2141. getNodeIndex: function (node, ignoreTextNode) {
  2142. var preNode = node,
  2143. i = 0;
  2144. while (preNode = preNode.previousSibling) {
  2145. if (ignoreTextNode && preNode.nodeType == 3) {
  2146. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  2147. i++;
  2148. }
  2149. continue;
  2150. }
  2151. i++;
  2152. }
  2153. return i;
  2154. },
  2155. /**
  2156. * 检测节点node是否在给定的document对象上
  2157. * @method inDoc
  2158. * @param { Node } node 需要检测的节点对象
  2159. * @param { DomDocument } doc 需要检测的document对象
  2160. * @return { Boolean } 该节点node是否在给定的document的dom树上
  2161. * @example
  2162. * ```javascript
  2163. *
  2164. * var node = document.createElement("div");
  2165. *
  2166. * //output: false
  2167. * console.log( UE.do.domUtils.inDoc( node, document ) );
  2168. *
  2169. * document.body.appendChild( node );
  2170. *
  2171. * //output: true
  2172. * console.log( UE.do.domUtils.inDoc( node, document ) );
  2173. *
  2174. * ```
  2175. */
  2176. inDoc: function (node, doc) {
  2177. return domUtils.getPosition(node, doc) == 10;
  2178. },
  2179. /**
  2180. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  2181. * 查找的起点是给定node节点的父节点。
  2182. * @method findParent
  2183. * @param { Node } node 需要查找的节点
  2184. * @param { Function } filterFn 自定义的过滤方法。
  2185. * @warning 查找的终点是到body节点为止
  2186. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  2187. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  2188. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  2189. * @example
  2190. * ```javascript
  2191. * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
  2192. *
  2193. * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
  2194. * return node.tagName === "HTML";
  2195. *
  2196. * } );
  2197. *
  2198. * //output: true
  2199. * console.log( filterNode === null );
  2200. * ```
  2201. */
  2202. /**
  2203. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  2204. * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
  2205. * @method findParent
  2206. * @param { Node } node 需要查找的节点
  2207. * @param { Function } filterFn 自定义的过滤方法。
  2208. * @param { Boolean } includeSelf 查找过程是否包含自身
  2209. * @warning 查找的终点是到body节点为止
  2210. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  2211. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  2212. * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
  2213. * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
  2214. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  2215. * @example
  2216. * ```html
  2217. * <body>
  2218. *
  2219. * <div id="test">
  2220. * </div>
  2221. *
  2222. * <script type="text/javascript">
  2223. *
  2224. * //output: DIV, BODY
  2225. * var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
  2226. *
  2227. * console.log( node.tagName );
  2228. * return false;
  2229. *
  2230. * }, true );
  2231. *
  2232. * </script>
  2233. * </body>
  2234. * ```
  2235. */
  2236. findParent: function (node, filterFn, includeSelf) {
  2237. if (node && !domUtils.isBody(node)) {
  2238. node = includeSelf ? node : node.parentNode;
  2239. while (node) {
  2240. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  2241. return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
  2242. }
  2243. node = node.parentNode;
  2244. }
  2245. }
  2246. return null;
  2247. },
  2248. /**
  2249. * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
  2250. * @method findParentByTagName
  2251. * @param { Node } node 需要查找的节点对象
  2252. * @param { Array } tagNames 需要查找的父节点的名称数组
  2253. * @warning 查找的终点是到body节点为止
  2254. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2255. * @example
  2256. * ```javascript
  2257. * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
  2258. * //output: BODY
  2259. * console.log( node.tagName );
  2260. * ```
  2261. */
  2262. /**
  2263. * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
  2264. * 否则, 起点是node的父节点。
  2265. * @method findParentByTagName
  2266. * @param { Node } node 需要查找的节点对象
  2267. * @param { Array } tagNames 需要查找的父节点的名称数组
  2268. * @param { Boolean } includeSelf 查找过程是否包含node节点自身
  2269. * @warning 查找的终点是到body节点为止
  2270. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2271. * @example
  2272. * ```javascript
  2273. * var queryTarget = document.getElementsByTagName("div")[0];
  2274. * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
  2275. * //output: true
  2276. * console.log( queryTarget === node );
  2277. * ```
  2278. */
  2279. findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
  2280. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  2281. return domUtils.findParent(node, function (node) {
  2282. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  2283. }, includeSelf);
  2284. },
  2285. /**
  2286. * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
  2287. * @method findParents
  2288. * @param { Node } node 需要查找的节点对象
  2289. * @return { Array } 给定节点的祖先节点数组
  2290. * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  2291. * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  2292. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  2293. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  2294. */
  2295. /**
  2296. * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
  2297. * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
  2298. * @method findParents
  2299. * @param { Node } node 需要查找的节点对象
  2300. * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
  2301. * @return { Array } 给定节点的祖先节点数组
  2302. */
  2303. findParents: function (node, includeSelf, filterFn, closerFirst) {
  2304. var parents = includeSelf && (filterFn && filterFn(node) || !filterFn) ? [node] : [];
  2305. while (node = domUtils.findParent(node, filterFn)) {
  2306. parents.push(node);
  2307. }
  2308. return closerFirst ? parents : parents.reverse();
  2309. },
  2310. /**
  2311. * 在节点node后面插入新节点newNode
  2312. * @method insertAfter
  2313. * @param { Node } node 目标节点
  2314. * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
  2315. * @return { Node } 新插入的节点
  2316. */
  2317. insertAfter: function (node, newNode) {
  2318. return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling) :
  2319. node.parentNode.appendChild(newNode);
  2320. },
  2321. /**
  2322. * 删除节点node及其下属的所有节点
  2323. * @method remove
  2324. * @param { Node } node 需要删除的节点对象
  2325. * @return { Node } 返回刚删除的节点对象
  2326. * @example
  2327. * ```html
  2328. * <div id="test">
  2329. * <div id="child">你好</div>
  2330. * </div>
  2331. * <script>
  2332. * UE.dom.domUtils.remove( document.body, false );
  2333. * //output: false
  2334. * console.log( document.getElementById( "child" ) !== null );
  2335. * </script>
  2336. * ```
  2337. */
  2338. /**
  2339. * 删除节点node,并根据keepChildren的值决定是否保留子节点
  2340. * @method remove
  2341. * @param { Node } node 需要删除的节点对象
  2342. * @param { Boolean } keepChildren 是否需要保留子节点
  2343. * @return { Node } 返回刚删除的节点对象
  2344. * @example
  2345. * ```html
  2346. * <div id="test">
  2347. * <div id="child">你好</div>
  2348. * </div>
  2349. * <script>
  2350. * UE.dom.domUtils.remove( document.body, true );
  2351. * //output: true
  2352. * console.log( document.getElementById( "child" ) !== null );
  2353. * </script>
  2354. * ```
  2355. */
  2356. remove: function (node, keepChildren) {
  2357. var parent = node.parentNode,
  2358. child;
  2359. if (parent) {
  2360. if (keepChildren && node.hasChildNodes()) {
  2361. while (child = node.firstChild) {
  2362. parent.insertBefore(child, node);
  2363. }
  2364. }
  2365. parent.removeChild(node);
  2366. }
  2367. return node;
  2368. },
  2369. /**
  2370. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  2371. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  2372. * @method getNextDomNode
  2373. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2374. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2375. * @example
  2376. * ```html
  2377. * <body>
  2378. * <div id="test">
  2379. * <span></span>
  2380. * </div>
  2381. * <i>xxx</i>
  2382. * </body>
  2383. * <script>
  2384. *
  2385. * //output: i节点
  2386. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2387. *
  2388. * </script>
  2389. * ```
  2390. * @example
  2391. * ```html
  2392. * <body>
  2393. * <div>
  2394. * <span></span>
  2395. * <i id="test">xxx</i>
  2396. * </div>
  2397. * <b>xxx</b>
  2398. * </body>
  2399. * <script>
  2400. *
  2401. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  2402. * //output: b节点
  2403. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2404. *
  2405. * </script>
  2406. * ```
  2407. */
  2408. /**
  2409. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  2410. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  2411. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  2412. * @method getNextDomNode
  2413. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2414. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  2415. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2416. * @see UE.dom.domUtils.getNextDomNode(Node)
  2417. */
  2418. getNextDomNode: function (node, startFromChild, filterFn, guard) {
  2419. return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
  2420. },
  2421. getPreDomNode: function (node, startFromChild, filterFn, guard) {
  2422. return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
  2423. },
  2424. /**
  2425. * 检测节点node是否属是UEditor定义的bookmark节点
  2426. * @method isBookmarkNode
  2427. * @private
  2428. * @param { Node } node 需要检测的节点对象
  2429. * @return { Boolean } 是否是bookmark节点
  2430. * @example
  2431. * ```html
  2432. * <span id="_baidu_bookmark_1"></span>
  2433. * <script>
  2434. * var bookmarkNode = document.getElementById("_baidu_bookmark_1");
  2435. * //output: true
  2436. * console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
  2437. * </script>
  2438. * ```
  2439. */
  2440. isBookmarkNode: function (node) {
  2441. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  2442. },
  2443. /**
  2444. * 获取节点node所属的window对象
  2445. * @method getWindow
  2446. * @param { Node } node 节点对象
  2447. * @return { Window } 当前节点所属的window对象
  2448. * @example
  2449. * ```javascript
  2450. * //output: true
  2451. * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
  2452. * ```
  2453. */
  2454. getWindow: function (node) {
  2455. var doc = node.ownerDocument || node;
  2456. return doc.defaultView || doc.parentWindow;
  2457. },
  2458. /**
  2459. * 获取离nodeA与nodeB最近的公共的祖先节点
  2460. * @method getCommonAncestor
  2461. * @param { Node } nodeA 第一个节点
  2462. * @param { Node } nodeB 第二个节点
  2463. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  2464. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  2465. * @example
  2466. * ```javascript
  2467. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  2468. * //output: true
  2469. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  2470. * ```
  2471. */
  2472. getCommonAncestor: function (nodeA, nodeB) {
  2473. if (nodeA === nodeB)
  2474. return nodeA;
  2475. var parentsA = [nodeA], parentsB = [nodeB], parent = nodeA, i = -1;
  2476. while (parent = parent.parentNode) {
  2477. if (parent === nodeB) {
  2478. return parent;
  2479. }
  2480. parentsA.push(parent);
  2481. }
  2482. parent = nodeB;
  2483. while (parent = parent.parentNode) {
  2484. if (parent === nodeA)
  2485. return parent;
  2486. parentsB.push(parent);
  2487. }
  2488. parentsA.reverse();
  2489. parentsB.reverse();
  2490. while (i++, parentsA[i] === parentsB[i]) {
  2491. }
  2492. return i == 0 ? null : parentsA[i - 1];
  2493. },
  2494. /**
  2495. * 清除node节点左右连续为空的兄弟inline节点
  2496. * @method clearEmptySibling
  2497. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2498. * 则这些兄弟节点将被删除
  2499. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  2500. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  2501. * @example
  2502. * ```html
  2503. * <body>
  2504. * <div></div>
  2505. * <span id="test"></span>
  2506. * <i></i>
  2507. * <b></b>
  2508. * <em>xxx</em>
  2509. * <span></span>
  2510. * </body>
  2511. * <script>
  2512. *
  2513. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  2514. *
  2515. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  2516. * console.log( document.body.innerHTML );
  2517. *
  2518. * </script>
  2519. * ```
  2520. */
  2521. /**
  2522. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2523. * 则忽略对右边兄弟节点的操作。
  2524. * @method clearEmptySibling
  2525. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2526. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2527. * 则这些兄弟节点将被删除
  2528. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2529. */
  2530. /**
  2531. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2532. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  2533. * @method clearEmptySibling
  2534. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2535. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2536. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  2537. * 则这些兄弟节点将被删除
  2538. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2539. */
  2540. clearEmptySibling: function (node, ignoreNext, ignorePre) {
  2541. function clear(next, dir) {
  2542. var tmpNode;
  2543. while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
  2544. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  2545. || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue))) {
  2546. tmpNode = next[dir];
  2547. domUtils.remove(next);
  2548. next = tmpNode;
  2549. }
  2550. }
  2551. !ignoreNext && clear(node.nextSibling, 'nextSibling');
  2552. !ignorePre && clear(node.previousSibling, 'previousSibling');
  2553. },
  2554. /**
  2555. * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
  2556. * @method split
  2557. * @param { Node } textNode 需要拆分的文本节点对象
  2558. * @param { int } offset 需要拆分的位置, 位置计算从0开始
  2559. * @return { Node } 拆分后形成的新节点
  2560. * @example
  2561. * ```html
  2562. * <div id="test">abcdef</div>
  2563. * <script>
  2564. * var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
  2565. * //output: def
  2566. * console.log( newNode.nodeValue );
  2567. * </script>
  2568. * ```
  2569. */
  2570. split: function (node, offset) {
  2571. var doc = node.ownerDocument;
  2572. if (browser.ie && offset == node.nodeValue.length) {
  2573. var next = doc.createTextNode('');
  2574. return domUtils.insertAfter(node, next);
  2575. }
  2576. var retval = node.splitText(offset);
  2577. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  2578. if (browser.ie8) {
  2579. var tmpNode = doc.createTextNode('');
  2580. domUtils.insertAfter(retval, tmpNode);
  2581. domUtils.remove(tmpNode);
  2582. }
  2583. return retval;
  2584. },
  2585. /**
  2586. * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
  2587. * @method isWhitespace
  2588. * @param { Node } node 需要检测的节点对象
  2589. * @return { Boolean } 检测的节点是否为空
  2590. * @example
  2591. * ```html
  2592. * <div id="test">
  2593. *
  2594. * </div>
  2595. * <script>
  2596. * //output: true
  2597. * console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
  2598. * </script>
  2599. * ```
  2600. */
  2601. isWhitespace: function (node) {
  2602. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
  2603. },
  2604. /**
  2605. * 获取元素element相对于viewport的位置坐标
  2606. * @method getXY
  2607. * @param { Node } element 需要计算位置的节点对象
  2608. * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
  2609. * y代表垂直偏移距离。
  2610. *
  2611. * @example
  2612. * ```javascript
  2613. * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
  2614. * //output: test的坐标为: 12, 24
  2615. * console.log( 'test的坐标为: ', location.x, ',', location.y );
  2616. * ```
  2617. */
  2618. getXY: function (element) {
  2619. var x = 0, y = 0;
  2620. while (element.offsetParent) {
  2621. y += element.offsetTop;
  2622. x += element.offsetLeft;
  2623. element = element.offsetParent;
  2624. }
  2625. return {'x': x, 'y': y};
  2626. },
  2627. /**
  2628. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2629. * @method on
  2630. * @param { Node } element 需要绑定事件的节点对象
  2631. * @param { String } type 绑定的事件类型
  2632. * @param { Function } handler 事件处理器
  2633. * @example
  2634. * ```javascript
  2635. * UE.dom.domUtils.on(document.body,"click",function(e){
  2636. * //e为事件对象,this为被点击元素对戏那个
  2637. * });
  2638. * ```
  2639. */
  2640. /**
  2641. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2642. * @method on
  2643. * @param { Node } element 需要绑定事件的节点对象
  2644. * @param { Array } type 绑定的事件类型数组
  2645. * @param { Function } handler 事件处理器
  2646. * @example
  2647. * ```javascript
  2648. * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
  2649. * //evt为事件对象,this为被点击元素对象
  2650. * });
  2651. * ```
  2652. */
  2653. on: function (element, type, handler) {
  2654. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2655. k = types.length;
  2656. if (k) while (k--) {
  2657. type = types[k];
  2658. if (element.addEventListener) {
  2659. element.addEventListener(type, handler, false);
  2660. } else {
  2661. if (!handler._d) {
  2662. handler._d = {
  2663. els: []
  2664. };
  2665. }
  2666. var key = type + handler.toString(), index = utils.indexOf(handler._d.els, element);
  2667. if (!handler._d[key] || index == -1) {
  2668. if (index == -1) {
  2669. handler._d.els.push(element);
  2670. }
  2671. if (!handler._d[key]) {
  2672. handler._d[key] = function (evt) {
  2673. return handler.call(evt.srcElement, evt || window.event);
  2674. };
  2675. }
  2676. element.attachEvent('on' + type, handler._d[key]);
  2677. }
  2678. }
  2679. }
  2680. element = null;
  2681. },
  2682. /**
  2683. * 解除DOM事件绑定
  2684. * @method un
  2685. * @param { Node } element 需要解除事件绑定的节点对象
  2686. * @param { String } type 需要接触绑定的事件类型
  2687. * @param { Function } handler 对应的事件处理器
  2688. * @example
  2689. * ```javascript
  2690. * UE.dom.domUtils.un(document.body,"click",function(evt){
  2691. * //evt为事件对象,this为被点击元素对象
  2692. * });
  2693. * ```
  2694. */
  2695. /**
  2696. * 解除DOM事件绑定
  2697. * @method un
  2698. * @param { Node } element 需要解除事件绑定的节点对象
  2699. * @param { Array } type 需要接触绑定的事件类型数组
  2700. * @param { Function } handler 对应的事件处理器
  2701. * @example
  2702. * ```javascript
  2703. * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
  2704. * //evt为事件对象,this为被点击元素对象
  2705. * });
  2706. * ```
  2707. */
  2708. un: function (element, type, handler) {
  2709. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2710. k = types.length;
  2711. if (k) while (k--) {
  2712. type = types[k];
  2713. if (element.removeEventListener) {
  2714. element.removeEventListener(type, handler, false);
  2715. } else {
  2716. var key = type + handler.toString();
  2717. try {
  2718. element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
  2719. } catch (e) {
  2720. }
  2721. if (handler._d && handler._d[key]) {
  2722. var index = utils.indexOf(handler._d.els, element);
  2723. if (index != -1) {
  2724. handler._d.els.splice(index, 1);
  2725. }
  2726. handler._d.els.length == 0 && delete handler._d[key];
  2727. }
  2728. }
  2729. }
  2730. },
  2731. /**
  2732. * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
  2733. * @method isSameElement
  2734. * @param { Node } nodeA 需要比较的节点
  2735. * @param { Node } nodeB 需要比较的节点
  2736. * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
  2737. * @example
  2738. * ```html
  2739. * <span style="font-size:12px">ssss</span>
  2740. * <span style="font-size:12px">bbbbb</span>
  2741. * <span style="font-size:13px">ssss</span>
  2742. * <span style="font-size:14px">bbbbb</span>
  2743. *
  2744. * <script>
  2745. *
  2746. * var nodes = document.getElementsByTagName( "span" );
  2747. *
  2748. * //output: true
  2749. * console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
  2750. *
  2751. * //output: false
  2752. * console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
  2753. *
  2754. * </script>
  2755. * ```
  2756. */
  2757. isSameElement: function (nodeA, nodeB) {
  2758. if (nodeA.tagName != nodeB.tagName) {
  2759. return false;
  2760. }
  2761. var thisAttrs = nodeA.attributes,
  2762. otherAttrs = nodeB.attributes;
  2763. if (!ie && thisAttrs.length != otherAttrs.length) {
  2764. return false;
  2765. }
  2766. var attrA, attrB, al = 0, bl = 0;
  2767. for (var i = 0; attrA = thisAttrs[i++];) {
  2768. if (attrA.nodeName == 'style') {
  2769. if (attrA.specified) {
  2770. al++;
  2771. }
  2772. if (domUtils.isSameStyle(nodeA, nodeB)) {
  2773. continue;
  2774. } else {
  2775. return false;
  2776. }
  2777. }
  2778. if (ie) {
  2779. if (attrA.specified) {
  2780. al++;
  2781. attrB = otherAttrs.getNamedItem(attrA.nodeName);
  2782. } else {
  2783. continue;
  2784. }
  2785. } else {
  2786. attrB = nodeB.attributes[attrA.nodeName];
  2787. }
  2788. if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
  2789. return false;
  2790. }
  2791. }
  2792. // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
  2793. if (ie) {
  2794. for (i = 0; attrB = otherAttrs[i++];) {
  2795. if (attrB.specified) {
  2796. bl++;
  2797. }
  2798. }
  2799. if (al != bl) {
  2800. return false;
  2801. }
  2802. }
  2803. return true;
  2804. },
  2805. /**
  2806. * 判断节点nodeA与节点nodeB的元素的style属性是否一致
  2807. * @method isSameStyle
  2808. * @param { Node } nodeA 需要比较的节点
  2809. * @param { Node } nodeB 需要比较的节点
  2810. * @return { Boolean } 两个节点是否具有相同的style属性值
  2811. * @example
  2812. * ```html
  2813. * <span style="font-size:12px">ssss</span>
  2814. * <span style="font-size:12px">bbbbb</span>
  2815. * <span style="font-size:13px">ssss</span>
  2816. * <span style="font-size:14px">bbbbb</span>
  2817. *
  2818. * <script>
  2819. *
  2820. * var nodes = document.getElementsByTagName( "span" );
  2821. *
  2822. * //output: true
  2823. * console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
  2824. *
  2825. * //output: false
  2826. * console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
  2827. *
  2828. * </script>
  2829. * ```
  2830. */
  2831. isSameStyle: function (nodeA, nodeB) {
  2832. var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
  2833. styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
  2834. if (browser.opera) {
  2835. styleA = nodeA.style;
  2836. styleB = nodeB.style;
  2837. if (styleA.length != styleB.length)
  2838. return false;
  2839. for (var p in styleA) {
  2840. if (/^(\d+|csstext)$/i.test(p)) {
  2841. continue;
  2842. }
  2843. if (styleA[p] != styleB[p]) {
  2844. return false;
  2845. }
  2846. }
  2847. return true;
  2848. }
  2849. if (!styleA || !styleB) {
  2850. return styleA == styleB;
  2851. }
  2852. styleA = styleA.split(';');
  2853. styleB = styleB.split(';');
  2854. if (styleA.length != styleB.length) {
  2855. return false;
  2856. }
  2857. for (var i = 0, ci; ci = styleA[i++];) {
  2858. if (utils.indexOf(styleB, ci) == -1) {
  2859. return false;
  2860. }
  2861. }
  2862. return true;
  2863. },
  2864. /**
  2865. * 检查节点node是否为block元素
  2866. * @method isBlockElm
  2867. * @param { Node } node 需要检测的节点对象
  2868. * @return { Boolean } 是否是block元素节点
  2869. * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
  2870. * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
  2871. * @example
  2872. * ```html
  2873. * <span id="test1" style="display: block"></span>
  2874. * <span id="test2"></span>
  2875. * <div id="test3" style="display: inline"></div>
  2876. *
  2877. * <script>
  2878. *
  2879. * //output: true
  2880. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
  2881. *
  2882. * //output: false
  2883. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
  2884. *
  2885. * //output: true
  2886. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
  2887. *
  2888. * </script>
  2889. * ```
  2890. */
  2891. isBlockElm: function (node) {
  2892. return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
  2893. },
  2894. /**
  2895. * 检测node节点是否为body节点
  2896. * @method isBody
  2897. * @param { Element } node 需要检测的dom元素
  2898. * @return { Boolean } 给定的元素是否是body元素
  2899. * @example
  2900. * ```javascript
  2901. * //output: true
  2902. * console.log( UE.dom.domUtils.isBody( document.body ) );
  2903. * ```
  2904. */
  2905. isBody: function (node) {
  2906. return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
  2907. },
  2908. /**
  2909. * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
  2910. * 拆分形成的两个节点之间是node节点
  2911. * @method breakParent
  2912. * @param { Node } node 作为分界的节点对象
  2913. * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
  2914. * @return { Node } 给定的node分界节点
  2915. * @example
  2916. * ```javascript
  2917. *
  2918. * var node = document.createElement("span"),
  2919. * wrapNode = document.createElement( "div" ),
  2920. * parent = document.createElement("p");
  2921. *
  2922. * parent.appendChild( node );
  2923. * wrapNode.appendChild( parent );
  2924. *
  2925. * //拆分前
  2926. * //output: <p><span></span></p>
  2927. * console.log( wrapNode.innerHTML );
  2928. *
  2929. *
  2930. * UE.dom.domUtils.breakParent( node, parent );
  2931. * //拆分后
  2932. * //output: <p></p><span></span><p></p>
  2933. * console.log( wrapNode.innerHTML );
  2934. *
  2935. * ```
  2936. */
  2937. breakParent: function (node, parent) {
  2938. var tmpNode,
  2939. parentClone = node,
  2940. clone = node,
  2941. leftNodes,
  2942. rightNodes;
  2943. do {
  2944. parentClone = parentClone.parentNode;
  2945. if (leftNodes) {
  2946. tmpNode = parentClone.cloneNode(false);
  2947. tmpNode.appendChild(leftNodes);
  2948. leftNodes = tmpNode;
  2949. tmpNode = parentClone.cloneNode(false);
  2950. tmpNode.appendChild(rightNodes);
  2951. rightNodes = tmpNode;
  2952. } else {
  2953. leftNodes = parentClone.cloneNode(false);
  2954. rightNodes = leftNodes.cloneNode(false);
  2955. }
  2956. while (tmpNode = clone.previousSibling) {
  2957. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  2958. }
  2959. while (tmpNode = clone.nextSibling) {
  2960. rightNodes.appendChild(tmpNode);
  2961. }
  2962. clone = parentClone;
  2963. } while (parent !== parentClone);
  2964. tmpNode = parent.parentNode;
  2965. tmpNode.insertBefore(leftNodes, parent);
  2966. tmpNode.insertBefore(rightNodes, parent);
  2967. tmpNode.insertBefore(node, rightNodes);
  2968. domUtils.remove(parent);
  2969. return node;
  2970. },
  2971. /**
  2972. * 检查节点node是否是空inline节点
  2973. * @method isEmptyInlineElement
  2974. * @param { Node } node 需要检测的节点对象
  2975. * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
  2976. * @example
  2977. * ```html
  2978. * <b><i></i></b> => 1
  2979. * <b><i></i><u></u></b> => 1
  2980. * <b></b> => 1
  2981. * <b>xx<i></i></b> => 0
  2982. * ```
  2983. */
  2984. isEmptyInlineElement: function (node) {
  2985. if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
  2986. return 0;
  2987. }
  2988. node = node.firstChild;
  2989. while (node) {
  2990. //如果是创建的bookmark就跳过
  2991. if (domUtils.isBookmarkNode(node)) {
  2992. return 0;
  2993. }
  2994. if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
  2995. node.nodeType == 3 && !domUtils.isWhitespace(node)
  2996. ) {
  2997. return 0;
  2998. }
  2999. node = node.nextSibling;
  3000. }
  3001. return 1;
  3002. },
  3003. /**
  3004. * 删除node节点下首尾两端的空白文本子节点
  3005. * @method trimWhiteTextNode
  3006. * @param { Element } node 需要执行删除操作的元素对象
  3007. * @example
  3008. * ```javascript
  3009. * var node = document.createElement("div");
  3010. *
  3011. * node.appendChild( document.createTextNode( "" ) );
  3012. *
  3013. * node.appendChild( document.createElement("div") );
  3014. *
  3015. * node.appendChild( document.createTextNode( "" ) );
  3016. *
  3017. * //3
  3018. * console.log( node.childNodes.length );
  3019. *
  3020. * UE.dom.domUtils.trimWhiteTextNode( node );
  3021. *
  3022. * //1
  3023. * console.log( node.childNodes.length );
  3024. * ```
  3025. */
  3026. trimWhiteTextNode: function (node) {
  3027. function remove(dir) {
  3028. var child;
  3029. while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
  3030. node.removeChild(child);
  3031. }
  3032. }
  3033. remove('firstChild');
  3034. remove('lastChild');
  3035. },
  3036. /**
  3037. * 合并node节点下相同的子节点
  3038. * @name mergeChild
  3039. * @desc
  3040. * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
  3041. * @example
  3042. * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
  3043. * ==> UE.dom.domUtils.mergeChild(node,'span')
  3044. * <p><span style="font-size:12px;">xxaaxx</span></p>
  3045. */
  3046. mergeChild: function (node, tagName, attrs) {
  3047. var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
  3048. for (var i = 0, ci; ci = list[i++];) {
  3049. if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
  3050. continue;
  3051. }
  3052. //span单独处理
  3053. if (ci.tagName.toLowerCase() == 'span') {
  3054. if (node === ci.parentNode) {
  3055. domUtils.trimWhiteTextNode(node);
  3056. if (node.childNodes.length == 1) {
  3057. node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
  3058. domUtils.remove(ci, true);
  3059. continue;
  3060. }
  3061. }
  3062. ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
  3063. if (attrs) {
  3064. var style = attrs.style;
  3065. if (style) {
  3066. style = style.split(';');
  3067. for (var j = 0, s; s = style[j++];) {
  3068. ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
  3069. }
  3070. }
  3071. }
  3072. if (domUtils.isSameStyle(ci, node)) {
  3073. domUtils.remove(ci, true);
  3074. }
  3075. continue;
  3076. }
  3077. if (domUtils.isSameElement(node, ci)) {
  3078. domUtils.remove(ci, true);
  3079. }
  3080. }
  3081. },
  3082. /**
  3083. * 原生方法getElementsByTagName的封装
  3084. * @method getElementsByTagName
  3085. * @param { Node } node 目标节点对象
  3086. * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
  3087. * @return { Array } 符合条件的节点集合
  3088. */
  3089. getElementsByTagName: function (node, name, filter) {
  3090. if (filter && utils.isString(filter)) {
  3091. var className = filter;
  3092. filter = function (node) {
  3093. return domUtils.hasClass(node, className)
  3094. }
  3095. }
  3096. name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
  3097. var arr = [];
  3098. for (var n = 0, ni; ni = name[n++];) {
  3099. var list = node.getElementsByTagName(ni);
  3100. for (var i = 0, ci; ci = list[i++];) {
  3101. if (!filter || filter(ci))
  3102. arr.push(ci);
  3103. }
  3104. }
  3105. return arr;
  3106. },
  3107. /**
  3108. * 将节点node提取到父节点上
  3109. * @method mergeToParent
  3110. * @param { Element } node 需要提取的元素对象
  3111. * @example
  3112. * ```html
  3113. * <div id="parent">
  3114. * <div id="sub">
  3115. * <span id="child"></span>
  3116. * </div>
  3117. * </div>
  3118. *
  3119. * <script>
  3120. *
  3121. * var child = document.getElementById( "child" );
  3122. *
  3123. * //output: sub
  3124. * console.log( child.parentNode.id );
  3125. *
  3126. * UE.dom.domUtils.mergeToParent( child );
  3127. *
  3128. * //output: parent
  3129. * console.log( child.parentNode.id );
  3130. *
  3131. * </script>
  3132. * ```
  3133. */
  3134. mergeToParent: function (node) {
  3135. var parent = node.parentNode;
  3136. while (parent && dtd.$removeEmpty[parent.tagName]) {
  3137. if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
  3138. domUtils.trimWhiteTextNode(parent);
  3139. //span需要特殊处理 不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
  3140. if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
  3141. || (parent.tagName == 'A' && node.tagName == 'SPAN')) {
  3142. if (parent.childNodes.length > 1 || parent !== node.parentNode) {
  3143. node.style.cssText = parent.style.cssText + ";" + node.style.cssText;
  3144. parent = parent.parentNode;
  3145. continue;
  3146. } else {
  3147. parent.style.cssText += ";" + node.style.cssText;
  3148. //trace:952 a标签要保持下划线
  3149. if (parent.tagName == 'A') {
  3150. parent.style.textDecoration = 'underline';
  3151. }
  3152. }
  3153. }
  3154. if (parent.tagName != 'A') {
  3155. parent === node.parentNode && domUtils.remove(node, true);
  3156. break;
  3157. }
  3158. }
  3159. parent = parent.parentNode;
  3160. }
  3161. },
  3162. /**
  3163. * 合并节点node的左右兄弟节点
  3164. * @method mergeSibling
  3165. * @param { Element } node 需要合并的目标节点
  3166. * @example
  3167. * ```html
  3168. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3169. *
  3170. * <script>
  3171. * var demoNode = document.getElementById("test");
  3172. * UE.dom.domUtils.mergeSibling( demoNode );
  3173. * //output: xxxxoooxxxx
  3174. * console.log( demoNode.innerHTML );
  3175. * </script>
  3176. * ```
  3177. */
  3178. /**
  3179. * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
  3180. * @method mergeSibling
  3181. * @param { Element } node 需要合并的目标节点
  3182. * @param { Boolean } ignorePre 是否忽略合并左节点
  3183. * @example
  3184. * ```html
  3185. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3186. *
  3187. * <script>
  3188. * var demoNode = document.getElementById("test");
  3189. * UE.dom.domUtils.mergeSibling( demoNode, true );
  3190. * //output: oooxxxx
  3191. * console.log( demoNode.innerHTML );
  3192. * </script>
  3193. * ```
  3194. */
  3195. /**
  3196. * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
  3197. * @method mergeSibling
  3198. * @param { Element } node 需要合并的目标节点
  3199. * @param { Boolean } ignorePre 是否忽略合并左节点
  3200. * @param { Boolean } ignoreNext 是否忽略合并右节点
  3201. * @remind 如果同时忽略左右节点, 则该操作什么也不会做
  3202. * @example
  3203. * ```html
  3204. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3205. *
  3206. * <script>
  3207. * var demoNode = document.getElementById("test");
  3208. * UE.dom.domUtils.mergeSibling( demoNode, false, true );
  3209. * //output: xxxxooo
  3210. * console.log( demoNode.innerHTML );
  3211. * </script>
  3212. * ```
  3213. */
  3214. mergeSibling: function (node, ignorePre, ignoreNext) {
  3215. function merge(rtl, start, node) {
  3216. var next;
  3217. if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
  3218. while (next.firstChild) {
  3219. if (start == 'firstChild') {
  3220. node.insertBefore(next.lastChild, node.firstChild);
  3221. } else {
  3222. node.appendChild(next.firstChild);
  3223. }
  3224. }
  3225. domUtils.remove(next);
  3226. }
  3227. }
  3228. !ignorePre && merge('previousSibling', 'firstChild', node);
  3229. !ignoreNext && merge('nextSibling', 'lastChild', node);
  3230. },
  3231. /**
  3232. * 设置节点node及其子节点不会被选中
  3233. * @method unSelectable
  3234. * @param { Element } node 需要执行操作的dom元素
  3235. * @remind 执行该操作后的节点, 将不能被鼠标选中
  3236. * @example
  3237. * ```javascript
  3238. * UE.dom.domUtils.unSelectable( document.body );
  3239. * ```
  3240. */
  3241. unSelectable: ie && browser.ie9below || browser.opera ? function (node) {
  3242. //for ie9
  3243. node.onselectstart = function () {
  3244. return false;
  3245. };
  3246. node.onclick = node.onkeyup = node.onkeydown = function () {
  3247. return false;
  3248. };
  3249. node.unselectable = 'on';
  3250. node.setAttribute("unselectable", "on");
  3251. for (var i = 0, ci; ci = node.all[i++];) {
  3252. switch (ci.tagName.toLowerCase()) {
  3253. case 'iframe' :
  3254. case 'textarea' :
  3255. case 'input' :
  3256. case 'select' :
  3257. break;
  3258. default :
  3259. ci.unselectable = 'on';
  3260. node.setAttribute("unselectable", "on");
  3261. }
  3262. }
  3263. } : function (node) {
  3264. node.style.MozUserSelect =
  3265. node.style.webkitUserSelect =
  3266. node.style.msUserSelect =
  3267. node.style.KhtmlUserSelect = 'none';
  3268. },
  3269. /**
  3270. * 删除节点node上的指定属性名称的属性
  3271. * @method removeAttributes
  3272. * @param { Node } node 需要删除属性的节点对象
  3273. * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
  3274. * @example
  3275. * ```html
  3276. * <div id="wrap">
  3277. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3278. * </div>
  3279. *
  3280. * <script>
  3281. *
  3282. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
  3283. *
  3284. * //output: <span style="font-size:14px;">xxxxx</span>
  3285. * console.log( document.getElementById("wrap").innerHTML );
  3286. *
  3287. * </script>
  3288. * ```
  3289. */
  3290. /**
  3291. * 删除节点node上的指定属性名称的属性
  3292. * @method removeAttributes
  3293. * @param { Node } node 需要删除属性的节点对象
  3294. * @param { Array } attrNames 需要删除的属性名数组
  3295. * @example
  3296. * ```html
  3297. * <div id="wrap">
  3298. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3299. * </div>
  3300. *
  3301. * <script>
  3302. *
  3303. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
  3304. *
  3305. * //output: <span style="font-size:14px;">xxxxx</span>
  3306. * console.log( document.getElementById("wrap").innerHTML );
  3307. *
  3308. * </script>
  3309. * ```
  3310. */
  3311. removeAttributes: function (node, attrNames) {
  3312. attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
  3313. for (var i = 0, ci; ci = attrNames[i++];) {
  3314. ci = attrFix[ci] || ci;
  3315. switch (ci) {
  3316. case 'className':
  3317. node[ci] = '';
  3318. break;
  3319. case 'style':
  3320. node.style.cssText = '';
  3321. var val = node.getAttributeNode('style');
  3322. !browser.ie && val && node.removeAttributeNode(val);
  3323. }
  3324. node.removeAttribute(ci);
  3325. }
  3326. },
  3327. /**
  3328. * 在doc下创建一个标签名为tag,属性为attrs的元素
  3329. * @method createElement
  3330. * @param { DomDocument } doc 新创建的元素属于该document节点创建
  3331. * @param { String } tagName 需要创建的元素的标签名
  3332. * @param { Object } attrs 新创建的元素的属性key-value集合
  3333. * @return { Element } 新创建的元素对象
  3334. * @example
  3335. * ```javascript
  3336. * var ele = UE.dom.domUtils.createElement( document, 'div', {
  3337. * id: 'test'
  3338. * } );
  3339. *
  3340. * //output: DIV
  3341. * console.log( ele.tagName );
  3342. *
  3343. * //output: test
  3344. * console.log( ele.id );
  3345. *
  3346. * ```
  3347. */
  3348. createElement: function (doc, tag, attrs) {
  3349. return domUtils.setAttributes(doc.createElement(tag), attrs)
  3350. },
  3351. /**
  3352. * 为节点node添加属性attrs,attrs为属性键值对
  3353. * @method setAttributes
  3354. * @param { Element } node 需要设置属性的元素对象
  3355. * @param { Object } attrs 需要设置的属性名-值对
  3356. * @return { Element } 设置属性的元素对象
  3357. * @example
  3358. * ```html
  3359. * <span id="test"></span>
  3360. *
  3361. * <script>
  3362. *
  3363. * var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
  3364. * id: 'demo'
  3365. * } );
  3366. *
  3367. * //output: demo
  3368. * console.log( testNode.id );
  3369. *
  3370. * </script>
  3371. *
  3372. */
  3373. setAttributes: function (node, attrs) {
  3374. for (var attr in attrs) {
  3375. if (attrs.hasOwnProperty(attr)) {
  3376. var value = attrs[attr];
  3377. switch (attr) {
  3378. case 'class':
  3379. //ie下要这样赋值,setAttribute不起作用
  3380. node.className = value;
  3381. break;
  3382. case 'style' :
  3383. node.style.cssText = node.style.cssText + ";" + value;
  3384. break;
  3385. case 'innerHTML':
  3386. node[attr] = value;
  3387. break;
  3388. case 'value':
  3389. node.value = value;
  3390. break;
  3391. default:
  3392. node.setAttribute(attrFix[attr] || attr, value);
  3393. }
  3394. }
  3395. }
  3396. return node;
  3397. },
  3398. /**
  3399. * 获取元素element经过计算后的样式值
  3400. * @method getComputedStyle
  3401. * @param { Element } element 需要获取样式的元素对象
  3402. * @param { String } styleName 需要获取的样式名
  3403. * @return { String } 获取到的样式值
  3404. * @example
  3405. * ```html
  3406. * <style type="text/css">
  3407. * #test {
  3408. * font-size: 15px;
  3409. * }
  3410. * </style>
  3411. *
  3412. * <span id="test"></span>
  3413. *
  3414. * <script>
  3415. * //output: 15px
  3416. * console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
  3417. * </script>
  3418. * ```
  3419. */
  3420. getComputedStyle: function (element, styleName) {
  3421. //一下的属性单独处理
  3422. var pros = 'width height top left';
  3423. if (pros.indexOf(styleName) > -1) {
  3424. return element['offset' + styleName.replace(/^\w/, function (s) {
  3425. return s.toUpperCase()
  3426. })] + 'px';
  3427. }
  3428. //忽略文本节点
  3429. if (element.nodeType == 3) {
  3430. element = element.parentNode;
  3431. }
  3432. //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
  3433. if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize &&
  3434. !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
  3435. var span = element.ownerDocument.createElement('span');
  3436. span.style.cssText = 'padding:0;border:0;font-family:simsun;';
  3437. span.innerHTML = '.';
  3438. element.appendChild(span);
  3439. var result = span.offsetHeight;
  3440. element.removeChild(span);
  3441. span = null;
  3442. return result + 'px';
  3443. }
  3444. try {
  3445. var value = domUtils.getStyle(element, styleName) ||
  3446. (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
  3447. (element.currentStyle || element.style)[utils.cssStyleToDomStyle(styleName)]);
  3448. } catch (e) {
  3449. return "";
  3450. }
  3451. return utils.transUnitToPx(utils.fixColor(styleName, value));
  3452. },
  3453. /**
  3454. * 删除元素element指定的className
  3455. * @method removeClasses
  3456. * @param { Element } ele 需要删除class的元素节点
  3457. * @param { String } classNames 需要删除的className, 多个className之间以空格分开
  3458. * @example
  3459. * ```html
  3460. * <span id="test" class="test1 test2 test3">xxx</span>
  3461. *
  3462. * <script>
  3463. *
  3464. * var testNode = document.getElementById( "test" );
  3465. * UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
  3466. *
  3467. * //output: test3
  3468. * console.log( testNode.className );
  3469. *
  3470. * </script>
  3471. * ```
  3472. */
  3473. /**
  3474. * 删除元素element指定的className
  3475. * @method removeClasses
  3476. * @param { Element } ele 需要删除class的元素节点
  3477. * @param { Array } classNames 需要删除的className数组
  3478. * @example
  3479. * ```html
  3480. * <span id="test" class="test1 test2 test3">xxx</span>
  3481. *
  3482. * <script>
  3483. *
  3484. * var testNode = document.getElementById( "test" );
  3485. * UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
  3486. *
  3487. * //output: test3
  3488. * console.log( testNode.className );
  3489. *
  3490. * </script>
  3491. * ```
  3492. */
  3493. removeClasses: function (elm, classNames) {
  3494. classNames = utils.isArray(classNames) ? classNames :
  3495. utils.trim(classNames).replace(/[ ]{2,}/g, ' ').split(' ');
  3496. for (var i = 0, ci, cls = elm.className; ci = classNames[i++];) {
  3497. cls = cls.replace(new RegExp('\\b' + ci + '\\b'), '')
  3498. }
  3499. cls = utils.trim(cls).replace(/[ ]{2,}/g, ' ');
  3500. if (cls) {
  3501. elm.className = cls;
  3502. } else {
  3503. domUtils.removeAttributes(elm, ['class']);
  3504. }
  3505. },
  3506. /**
  3507. * 给元素element添加className
  3508. * @method addClass
  3509. * @param { Node } ele 需要增加className的元素
  3510. * @param { String } classNames 需要添加的className, 多个className之间以空格分割
  3511. * @remind 相同的类名不会被重复添加
  3512. * @example
  3513. * ```html
  3514. * <span id="test" class="cls1 cls2"></span>
  3515. *
  3516. * <script>
  3517. * var testNode = document.getElementById("test");
  3518. *
  3519. * UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
  3520. *
  3521. * //output: cl1 cls2 cls3 cls4
  3522. * console.log( testNode.className );
  3523. *
  3524. * <script>
  3525. * ```
  3526. */
  3527. /**
  3528. * 给元素element添加className
  3529. * @method addClass
  3530. * @param { Node } ele 需要增加className的元素
  3531. * @param { Array } classNames 需要添加的className的数组
  3532. * @remind 相同的类名不会被重复添加
  3533. * @example
  3534. * ```html
  3535. * <span id="test" class="cls1 cls2"></span>
  3536. *
  3537. * <script>
  3538. * var testNode = document.getElementById("test");
  3539. *
  3540. * UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
  3541. *
  3542. * //output: cl1 cls2 cls3 cls4
  3543. * console.log( testNode.className );
  3544. *
  3545. * <script>
  3546. * ```
  3547. */
  3548. addClass: function (elm, classNames) {
  3549. if (!elm) return;
  3550. classNames = utils.trim(classNames).replace(/[ ]{2,}/g, ' ').split(' ');
  3551. for (var i = 0, ci, cls = elm.className; ci = classNames[i++];) {
  3552. if (!new RegExp('\\b' + ci + '\\b').test(cls)) {
  3553. cls += ' ' + ci;
  3554. }
  3555. }
  3556. elm.className = utils.trim(cls);
  3557. },
  3558. /**
  3559. * 判断元素element是否包含给定的样式类名className
  3560. * @method hasClass
  3561. * @param { Node } ele 需要检测的元素
  3562. * @param { String } classNames 需要检测的className, 多个className之间用空格分割
  3563. * @return { Boolean } 元素是否包含所有给定的className
  3564. * @example
  3565. * ```html
  3566. * <span id="test1" class="cls1 cls2"></span>
  3567. *
  3568. * <script>
  3569. * var test1 = document.getElementById("test1");
  3570. *
  3571. * //output: false
  3572. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
  3573. *
  3574. * //output: true
  3575. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
  3576. * </script>
  3577. * ```
  3578. */
  3579. /**
  3580. * 判断元素element是否包含给定的样式类名className
  3581. * @method hasClass
  3582. * @param { Node } ele 需要检测的元素
  3583. * @param { Array } classNames 需要检测的className数组
  3584. * @return { Boolean } 元素是否包含所有给定的className
  3585. * @example
  3586. * ```html
  3587. * <span id="test1" class="cls1 cls2"></span>
  3588. *
  3589. * <script>
  3590. * var test1 = document.getElementById("test1");
  3591. *
  3592. * //output: false
  3593. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
  3594. *
  3595. * //output: true
  3596. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
  3597. * </script>
  3598. * ```
  3599. */
  3600. hasClass: function (element, className) {
  3601. if (utils.isRegExp(className)) {
  3602. return className.test(element.className)
  3603. }
  3604. className = utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' ');
  3605. for (var i = 0, ci, cls = element.className; ci = className[i++];) {
  3606. if (!new RegExp('\\b' + ci + '\\b', 'i').test(cls)) {
  3607. return false;
  3608. }
  3609. }
  3610. return i - 1 == className.length;
  3611. },
  3612. /**
  3613. * 阻止事件默认行为
  3614. * @method preventDefault
  3615. * @param { Event } evt 需要阻止默认行为的事件对象
  3616. * @example
  3617. * ```javascript
  3618. * UE.dom.domUtils.preventDefault( evt );
  3619. * ```
  3620. */
  3621. preventDefault: function (evt) {
  3622. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  3623. },
  3624. /**
  3625. * 删除元素element指定的样式
  3626. * @method removeStyle
  3627. * @param { Element } element 需要删除样式的元素
  3628. * @param { String } styleName 需要删除的样式名
  3629. * @example
  3630. * ```html
  3631. * <span id="test" style="color: red; background: blue;"></span>
  3632. *
  3633. * <script>
  3634. *
  3635. * var testNode = document.getElementById("test");
  3636. *
  3637. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  3638. *
  3639. * //output: background: blue;
  3640. * console.log( testNode.style.cssText );
  3641. *
  3642. * </script>
  3643. * ```
  3644. */
  3645. removeStyle: function (element, name) {
  3646. if (browser.ie) {
  3647. //针对color先单独处理一下
  3648. if (name == 'color') {
  3649. name = '(^|;)' + name;
  3650. }
  3651. element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?', 'ig'), '')
  3652. } else {
  3653. if (element.style.removeProperty) {
  3654. element.style.removeProperty(name);
  3655. } else {
  3656. element.style.removeAttribute(utils.cssStyleToDomStyle(name));
  3657. }
  3658. }
  3659. if (!element.style.cssText) {
  3660. domUtils.removeAttributes(element, ['style']);
  3661. }
  3662. },
  3663. /**
  3664. * 获取元素element的style属性的指定值
  3665. * @method getStyle
  3666. * @param { Element } element 需要获取属性值的元素
  3667. * @param { String } styleName 需要获取的style的名称
  3668. * @warning 该方法仅获取元素style属性中所标明的值
  3669. * @return { String } 该元素包含指定的style属性值
  3670. * @example
  3671. * ```html
  3672. * <div id="test" style="color: red;"></div>
  3673. *
  3674. * <script>
  3675. *
  3676. * var testNode = document.getElementById( "test" );
  3677. *
  3678. * //output: red
  3679. * console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
  3680. *
  3681. * //output: ""
  3682. * console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
  3683. *
  3684. * </script>
  3685. * ```
  3686. */
  3687. getStyle: function (element, name) {
  3688. var value = element.style[utils.cssStyleToDomStyle(name)];
  3689. return utils.fixColor(name, value);
  3690. },
  3691. /**
  3692. * 为元素element设置样式属性值
  3693. * @method setStyle
  3694. * @param { Element } element 需要设置样式的元素
  3695. * @param { String } styleName 样式名
  3696. * @param { String } styleValue 样式值
  3697. * @example
  3698. * ```html
  3699. * <div id="test"></div>
  3700. *
  3701. * <script>
  3702. *
  3703. * var testNode = document.getElementById( "test" );
  3704. *
  3705. * //output: ""
  3706. * console.log( testNode.style.color );
  3707. *
  3708. * UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
  3709. * //output: "red"
  3710. * console.log( testNode.style.color );
  3711. *
  3712. * </script>
  3713. * ```
  3714. */
  3715. setStyle: function (element, name, value) {
  3716. element.style[utils.cssStyleToDomStyle(name)] = value;
  3717. if (!utils.trim(element.style.cssText)) {
  3718. this.removeAttributes(element, 'style')
  3719. }
  3720. },
  3721. /**
  3722. * 为元素element设置多个样式属性值
  3723. * @method setStyles
  3724. * @param { Element } element 需要设置样式的元素
  3725. * @param { Object } styles 样式名值对
  3726. * @example
  3727. * ```html
  3728. * <div id="test"></div>
  3729. *
  3730. * <script>
  3731. *
  3732. * var testNode = document.getElementById( "test" );
  3733. *
  3734. * //output: ""
  3735. * console.log( testNode.style.color );
  3736. *
  3737. * UE.dom.domUtils.setStyles( testNode, {
  3738. * 'color': 'red'
  3739. * } );
  3740. * //output: "red"
  3741. * console.log( testNode.style.color );
  3742. *
  3743. * </script>
  3744. * ```
  3745. */
  3746. setStyles: function (element, styles) {
  3747. for (var name in styles) {
  3748. if (styles.hasOwnProperty(name)) {
  3749. domUtils.setStyle(element, name, styles[name]);
  3750. }
  3751. }
  3752. },
  3753. /**
  3754. * 删除_moz_dirty属性
  3755. * @private
  3756. * @method removeDirtyAttr
  3757. */
  3758. removeDirtyAttr: function (node) {
  3759. for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
  3760. ci.removeAttribute('_moz_dirty');
  3761. }
  3762. node.removeAttribute('_moz_dirty');
  3763. },
  3764. /**
  3765. * 获取子节点的数量
  3766. * @method getChildCount
  3767. * @param { Element } node 需要检测的元素
  3768. * @return { Number } 给定的node元素的子节点数量
  3769. * @example
  3770. * ```html
  3771. * <div id="test">
  3772. * <span></span>
  3773. * </div>
  3774. *
  3775. * <script>
  3776. *
  3777. * //output: 3
  3778. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
  3779. *
  3780. * </script>
  3781. * ```
  3782. */
  3783. /**
  3784. * 根据给定的过滤规则, 获取符合条件的子节点的数量
  3785. * @method getChildCount
  3786. * @param { Element } node 需要检测的元素
  3787. * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
  3788. * @return { Number } 符合过滤条件的node元素的子节点数量
  3789. * @example
  3790. * ```html
  3791. * <div id="test">
  3792. * <span></span>
  3793. * </div>
  3794. *
  3795. * <script>
  3796. *
  3797. * //output: 1
  3798. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
  3799. *
  3800. * return node.nodeType === 1;
  3801. *
  3802. * } ) );
  3803. *
  3804. * </script>
  3805. * ```
  3806. */
  3807. getChildCount: function (node, fn) {
  3808. var count = 0, first = node.firstChild;
  3809. fn = fn || function () {
  3810. return 1;
  3811. };
  3812. while (first) {
  3813. if (fn(first)) {
  3814. count++;
  3815. }
  3816. first = first.nextSibling;
  3817. }
  3818. return count;
  3819. },
  3820. /**
  3821. * 判断给定节点是否为空节点
  3822. * @method isEmptyNode
  3823. * @param { Node } node 需要检测的节点对象
  3824. * @return { Boolean } 节点是否为空
  3825. * @example
  3826. * ```javascript
  3827. * UE.dom.domUtils.isEmptyNode( document.body );
  3828. * ```
  3829. */
  3830. isEmptyNode: function (node) {
  3831. return !node.firstChild || domUtils.getChildCount(node, function (node) {
  3832. return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
  3833. }) == 0
  3834. },
  3835. clearSelectedArr: function (nodes) {
  3836. var node;
  3837. while (node = nodes.pop()) {
  3838. domUtils.removeAttributes(node, ['class']);
  3839. }
  3840. },
  3841. /**
  3842. * 将显示区域滚动到指定节点的位置
  3843. * @method scrollToView
  3844. * @param {Node} node 节点
  3845. * @param {window} win window对象
  3846. * @param {Number} offsetTop 距离上方的偏移量
  3847. */
  3848. scrollToView: function (node, win, offsetTop) {
  3849. var getViewPaneSize = function () {
  3850. var doc = win.document,
  3851. mode = doc.compatMode == 'CSS1Compat';
  3852. return {
  3853. width: (mode ? doc.documentElement.clientWidth : doc.body.clientWidth) || 0,
  3854. height: (mode ? doc.documentElement.clientHeight : doc.body.clientHeight) || 0
  3855. };
  3856. },
  3857. getScrollPosition = function (win) {
  3858. if ('pageXOffset' in win) {
  3859. return {
  3860. x: win.pageXOffset || 0,
  3861. y: win.pageYOffset || 0
  3862. };
  3863. } else {
  3864. var doc = win.document;
  3865. return {
  3866. x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
  3867. y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
  3868. };
  3869. }
  3870. };
  3871. var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
  3872. offset += (node.offsetHeight || 0);
  3873. var elementPosition = domUtils.getXY(node);
  3874. offset += elementPosition.y;
  3875. var currentScroll = getScrollPosition(win).y;
  3876. // offset += 50;
  3877. if (offset > currentScroll || offset < currentScroll - winHeight) {
  3878. win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
  3879. }
  3880. },
  3881. /**
  3882. * 判断给定节点是否为br
  3883. * @method isBr
  3884. * @param { Node } node 需要判断的节点对象
  3885. * @return { Boolean } 给定的节点是否是br节点
  3886. */
  3887. isBr: function (node) {
  3888. return node.nodeType == 1 && node.tagName == 'BR';
  3889. },
  3890. /**
  3891. * 判断给定的节点是否是一个“填充”节点
  3892. * @private
  3893. * @method isFillChar
  3894. * @param { Node } node 需要判断的节点
  3895. * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
  3896. * @returns { Boolean } 节点是否是填充节点
  3897. */
  3898. isFillChar: function (node, isInStart) {
  3899. if (node.nodeType != 3)
  3900. return false;
  3901. var text = node.nodeValue;
  3902. if (isInStart) {
  3903. return new RegExp('^' + domUtils.fillChar).test(text)
  3904. }
  3905. return !text.replace(new RegExp(domUtils.fillChar, 'g'), '').length
  3906. },
  3907. isStartInblock: function (range) {
  3908. var tmpRange = range.cloneRange(),
  3909. flag = 0,
  3910. start = tmpRange.startContainer,
  3911. tmp;
  3912. if (start.nodeType == 1 && start.childNodes[tmpRange.startOffset]) {
  3913. start = start.childNodes[tmpRange.startOffset];
  3914. var pre = start.previousSibling;
  3915. while (pre && domUtils.isFillChar(pre)) {
  3916. start = pre;
  3917. pre = pre.previousSibling;
  3918. }
  3919. }
  3920. if (this.isFillChar(start, true) && tmpRange.startOffset == 1) {
  3921. tmpRange.setStartBefore(start);
  3922. start = tmpRange.startContainer;
  3923. }
  3924. while (start && domUtils.isFillChar(start)) {
  3925. tmp = start;
  3926. start = start.previousSibling
  3927. }
  3928. if (tmp) {
  3929. tmpRange.setStartBefore(tmp);
  3930. start = tmpRange.startContainer;
  3931. }
  3932. if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
  3933. tmpRange.setStart(start, 0).collapse(true);
  3934. }
  3935. while (!tmpRange.startOffset) {
  3936. start = tmpRange.startContainer;
  3937. if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
  3938. flag = 1;
  3939. break;
  3940. }
  3941. var pre = tmpRange.startContainer.previousSibling,
  3942. tmpNode;
  3943. if (!pre) {
  3944. tmpRange.setStartBefore(tmpRange.startContainer);
  3945. } else {
  3946. while (pre && domUtils.isFillChar(pre)) {
  3947. tmpNode = pre;
  3948. pre = pre.previousSibling;
  3949. }
  3950. if (tmpNode) {
  3951. tmpRange.setStartBefore(tmpNode);
  3952. } else {
  3953. tmpRange.setStartBefore(tmpRange.startContainer);
  3954. }
  3955. }
  3956. }
  3957. return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
  3958. },
  3959. /**
  3960. * 判断给定的元素是否是一个空元素
  3961. * @method isEmptyBlock
  3962. * @param { Element } node 需要判断的元素
  3963. * @return { Boolean } 是否是空元素
  3964. * @example
  3965. * ```html
  3966. * <div id="test"></div>
  3967. *
  3968. * <script>
  3969. * //output: true
  3970. * console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
  3971. * </script>
  3972. * ```
  3973. */
  3974. /**
  3975. * 根据指定的判断规则判断给定的元素是否是一个空元素
  3976. * @method isEmptyBlock
  3977. * @param { Element } node 需要判断的元素
  3978. * @param { RegExp } reg 对内容执行判断的正则表达式对象
  3979. * @return { Boolean } 是否是空元素
  3980. */
  3981. isEmptyBlock: function (node, reg) {
  3982. if (node.nodeType != 1)
  3983. return 0;
  3984. reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g');
  3985. if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
  3986. return 0;
  3987. }
  3988. for (var n in dtd.$isNotEmpty) {
  3989. if (node.getElementsByTagName(n).length) {
  3990. return 0;
  3991. }
  3992. }
  3993. return 1;
  3994. },
  3995. /**
  3996. * 移动元素使得该元素的位置移动指定的偏移量的距离
  3997. * @method setViewportOffset
  3998. * @param { Element } element 需要设置偏移量的元素
  3999. * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
  4000. * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
  4001. * offset.top的距离
  4002. * @example
  4003. * ```html
  4004. * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
  4005. *
  4006. * <script>
  4007. *
  4008. * var testNode = document.getElementById("test");
  4009. *
  4010. * UE.dom.domUtils.setViewportOffset( testNode, {
  4011. * left: 200,
  4012. * top: 50
  4013. * } );
  4014. *
  4015. * //output: top: 300px; left: 100px; position: absolute;
  4016. * console.log( testNode.style.cssText );
  4017. *
  4018. * </script>
  4019. * ```
  4020. */
  4021. setViewportOffset: function (element, offset) {
  4022. var left = parseInt(element.style.left) | 0;
  4023. var top = parseInt(element.style.top) | 0;
  4024. var rect = element.getBoundingClientRect();
  4025. var offsetLeft = offset.left - rect.left;
  4026. var offsetTop = offset.top - rect.top;
  4027. if (offsetLeft) {
  4028. element.style.left = left + offsetLeft + 'px';
  4029. }
  4030. if (offsetTop) {
  4031. element.style.top = top + offsetTop + 'px';
  4032. }
  4033. },
  4034. /**
  4035. * 用“填充字符”填充节点
  4036. * @method fillNode
  4037. * @private
  4038. * @param { DomDocument } doc 填充的节点所在的docment对象
  4039. * @param { Node } node 需要填充的节点对象
  4040. * @example
  4041. * ```html
  4042. * <div id="test"></div>
  4043. *
  4044. * <script>
  4045. * var testNode = document.getElementById("test");
  4046. *
  4047. * //output: 0
  4048. * console.log( testNode.childNodes.length );
  4049. *
  4050. * UE.dom.domUtils.fillNode( document, testNode );
  4051. *
  4052. * //output: 1
  4053. * console.log( testNode.childNodes.length );
  4054. *
  4055. * </script>
  4056. * ```
  4057. */
  4058. fillNode: function (doc, node) {
  4059. var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
  4060. node.innerHTML = '';
  4061. node.appendChild(tmpNode);
  4062. },
  4063. /**
  4064. * 把节点src的所有子节点追加到另一个节点tag上去
  4065. * @method moveChild
  4066. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  4067. * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
  4068. * @example
  4069. * ```html
  4070. * <div id="test1">
  4071. * <span></span>
  4072. * </div>
  4073. * <div id="test2">
  4074. * <div></div>
  4075. * </div>
  4076. *
  4077. * <script>
  4078. *
  4079. * var test1 = document.getElementById("test1"),
  4080. * test2 = document.getElementById("test2");
  4081. *
  4082. * UE.dom.domUtils.moveChild( test1, test2 );
  4083. *
  4084. * //output: ""(空字符串)
  4085. * console.log( test1.innerHTML );
  4086. *
  4087. * //output: "<div></div><span></span>"
  4088. * console.log( test2.innerHTML );
  4089. *
  4090. * </script>
  4091. * ```
  4092. */
  4093. /**
  4094. * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
  4095. * @method moveChild
  4096. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  4097. * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
  4098. * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
  4099. * @example
  4100. * ```html
  4101. * <div id="test1">
  4102. * <span></span>
  4103. * </div>
  4104. * <div id="test2">
  4105. * <div></div>
  4106. * </div>
  4107. *
  4108. * <script>
  4109. *
  4110. * var test1 = document.getElementById("test1"),
  4111. * test2 = document.getElementById("test2");
  4112. *
  4113. * UE.dom.domUtils.moveChild( test1, test2, true );
  4114. *
  4115. * //output: ""(空字符串)
  4116. * console.log( test1.innerHTML );
  4117. *
  4118. * //output: "<span></span><div></div>"
  4119. * console.log( test2.innerHTML );
  4120. *
  4121. * </script>
  4122. * ```
  4123. */
  4124. moveChild: function (src, tag, dir) {
  4125. while (src.firstChild) {
  4126. if (dir && tag.firstChild) {
  4127. tag.insertBefore(src.lastChild, tag.firstChild);
  4128. } else {
  4129. tag.appendChild(src.firstChild);
  4130. }
  4131. }
  4132. },
  4133. /**
  4134. * 判断节点的标签上是否不存在任何属性
  4135. * @method hasNoAttributes
  4136. * @private
  4137. * @param { Node } node 需要检测的节点对象
  4138. * @return { Boolean } 节点是否不包含任何属性
  4139. * @example
  4140. * ```html
  4141. * <div id="test"><span>xxxx</span></div>
  4142. *
  4143. * <script>
  4144. *
  4145. * //output: false
  4146. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
  4147. *
  4148. * //output: true
  4149. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
  4150. *
  4151. * </script>
  4152. * ```
  4153. */
  4154. hasNoAttributes: function (node) {
  4155. return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0;
  4156. },
  4157. /**
  4158. * 检测节点是否是UEditor所使用的辅助节点
  4159. * @method isCustomeNode
  4160. * @private
  4161. * @param { Node } node 需要检测的节点
  4162. * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
  4163. * @return { Boolean } 给定的节点是否是一个辅助节点
  4164. */
  4165. isCustomeNode: function (node) {
  4166. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
  4167. },
  4168. /**
  4169. * 检测节点的标签是否是给定的标签
  4170. * @method isTagNode
  4171. * @param { Node } node 需要检测的节点对象
  4172. * @param { String } tagName 标签
  4173. * @return { Boolean } 节点的标签是否是给定的标签
  4174. * @example
  4175. * ```html
  4176. * <div id="test"></div>
  4177. *
  4178. * <script>
  4179. *
  4180. * //output: true
  4181. * console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
  4182. *
  4183. * </script>
  4184. * ```
  4185. */
  4186. isTagNode: function (node, tagNames) {
  4187. return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b', 'i').test(tagNames)
  4188. },
  4189. /**
  4190. * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
  4191. * @method filterNodeList
  4192. * @param { Array } nodeList 需要过滤的节点数组
  4193. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  4194. * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
  4195. * @example
  4196. * ```javascript
  4197. * var divNodes = document.getElementsByTagName("div");
  4198. * divNodes = [].slice.call( divNodes, 0 );
  4199. *
  4200. * //output: null
  4201. * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4202. * return node.tagName.toLowerCase() !== 'div';
  4203. * } ) );
  4204. * ```
  4205. */
  4206. /**
  4207. * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
  4208. * @method filterNodeList
  4209. * @param { Array } nodeList 需要过滤的节点数组
  4210. * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
  4211. * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
  4212. * @example
  4213. * ```javascript
  4214. * var divNodes = document.getElementsByTagName("div");
  4215. * divNodes = [].slice.call( divNodes, 0 );
  4216. *
  4217. * //output: null
  4218. * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
  4219. * ```
  4220. */
  4221. /**
  4222. * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
  4223. * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
  4224. * @method filterNodeList
  4225. * @param { Array } nodeList 需要过滤的节点数组
  4226. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  4227. * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
  4228. * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
  4229. * 过滤条件的节点数组或第一个节点, 否则返回NULL
  4230. * @example
  4231. * ```javascript
  4232. * var divNodes = document.getElementsByTagName("div");
  4233. * divNodes = [].slice.call( divNodes, 0 );
  4234. *
  4235. * //output: 3(假定有3个div)
  4236. * console.log( divNodes.length );
  4237. *
  4238. * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4239. * return node.tagName.toLowerCase() === 'div';
  4240. * }, true );
  4241. *
  4242. * //output: 3
  4243. * console.log( nodes.length );
  4244. *
  4245. * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4246. * return node.tagName.toLowerCase() === 'div';
  4247. * }, false );
  4248. *
  4249. * //output: div
  4250. * console.log( node.nodeName );
  4251. * ```
  4252. */
  4253. filterNodeList: function (nodelist, filter, forAll) {
  4254. var results = [];
  4255. if (!utils.isFunction(filter)) {
  4256. var str = filter;
  4257. filter = function (n) {
  4258. return utils.indexOf(utils.isArray(str) ? str : str.split(' '), n.tagName.toLowerCase()) != -1
  4259. };
  4260. }
  4261. utils.each(nodelist, function (n) {
  4262. filter(n) && results.push(n)
  4263. });
  4264. return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
  4265. },
  4266. /**
  4267. * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
  4268. * @method isInNodeEndBoundary
  4269. * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
  4270. * @param node 需要检测的节点对象
  4271. * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
  4272. */
  4273. isInNodeEndBoundary: function (rng, node) {
  4274. var start = rng.startContainer;
  4275. if (start.nodeType == 3 && rng.startOffset != start.nodeValue.length) {
  4276. return 0;
  4277. }
  4278. if (start.nodeType == 1 && rng.startOffset != start.childNodes.length) {
  4279. return 0;
  4280. }
  4281. while (start !== node) {
  4282. if (start.nextSibling) {
  4283. return 0
  4284. }
  4285. ;
  4286. start = start.parentNode;
  4287. }
  4288. return 1;
  4289. },
  4290. isBoundaryNode: function (node, dir) {
  4291. var tmp;
  4292. while (!domUtils.isBody(node)) {
  4293. tmp = node;
  4294. node = node.parentNode;
  4295. if (tmp !== node[dir]) {
  4296. return false;
  4297. }
  4298. }
  4299. return true;
  4300. },
  4301. fillHtml: browser.ie11below ? '&nbsp;' : '<br/>'
  4302. };
  4303. var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  4304. // core/Range.js
  4305. /**
  4306. * Range封装
  4307. * @file
  4308. * @module UE.dom
  4309. * @class Range
  4310. * @since 1.2.6.1
  4311. */
  4312. /**
  4313. * dom操作封装
  4314. * @unfile
  4315. * @module UE.dom
  4316. */
  4317. /**
  4318. * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
  4319. * @unfile
  4320. * @module UE.dom
  4321. * @class Range
  4322. */
  4323. (function () {
  4324. var guid = 0,
  4325. fillChar = domUtils.fillChar,
  4326. fillData;
  4327. /**
  4328. * 更新range的collapse状态
  4329. * @param {Range} range range对象
  4330. */
  4331. function updateCollapse(range) {
  4332. range.collapsed =
  4333. range.startContainer && range.endContainer &&
  4334. range.startContainer === range.endContainer &&
  4335. range.startOffset == range.endOffset;
  4336. }
  4337. function selectOneNode(rng) {
  4338. return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
  4339. }
  4340. function setEndPoint(toStart, node, offset, range) {
  4341. //如果node是自闭合标签要处理
  4342. if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
  4343. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
  4344. node = node.parentNode;
  4345. }
  4346. if (toStart) {
  4347. range.startContainer = node;
  4348. range.startOffset = offset;
  4349. if (!range.endContainer) {
  4350. range.collapse(true);
  4351. }
  4352. } else {
  4353. range.endContainer = node;
  4354. range.endOffset = offset;
  4355. if (!range.startContainer) {
  4356. range.collapse(false);
  4357. }
  4358. }
  4359. updateCollapse(range);
  4360. return range;
  4361. }
  4362. function execContentsAction(range, action) {
  4363. //调整边界
  4364. //range.includeBookmark();
  4365. var start = range.startContainer,
  4366. end = range.endContainer,
  4367. startOffset = range.startOffset,
  4368. endOffset = range.endOffset,
  4369. doc = range.document,
  4370. frag = doc.createDocumentFragment(),
  4371. tmpStart, tmpEnd;
  4372. if (start.nodeType == 1) {
  4373. start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
  4374. }
  4375. if (end.nodeType == 1) {
  4376. end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
  4377. }
  4378. if (start === end && start.nodeType == 3) {
  4379. frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
  4380. //is not clone
  4381. if (action) {
  4382. start.deleteData(startOffset, endOffset - startOffset);
  4383. range.collapse(true);
  4384. }
  4385. return frag;
  4386. }
  4387. var current, currentLevel, clone = frag,
  4388. startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
  4389. for (var i = 0; startParents[i] == endParents[i];) {
  4390. i++;
  4391. }
  4392. for (var j = i, si; si = startParents[j]; j++) {
  4393. current = si.nextSibling;
  4394. if (si == start) {
  4395. if (!tmpStart) {
  4396. if (range.startContainer.nodeType == 3) {
  4397. clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
  4398. //is not clone
  4399. if (action) {
  4400. start.deleteData(startOffset, start.nodeValue.length - startOffset);
  4401. }
  4402. } else {
  4403. clone.appendChild(!action ? start.cloneNode(true) : start);
  4404. }
  4405. }
  4406. } else {
  4407. currentLevel = si.cloneNode(false);
  4408. clone.appendChild(currentLevel);
  4409. }
  4410. while (current) {
  4411. if (current === end || current === endParents[j]) {
  4412. break;
  4413. }
  4414. si = current.nextSibling;
  4415. clone.appendChild(!action ? current.cloneNode(true) : current);
  4416. current = si;
  4417. }
  4418. clone = currentLevel;
  4419. }
  4420. clone = frag;
  4421. if (!startParents[i]) {
  4422. clone.appendChild(startParents[i - 1].cloneNode(false));
  4423. clone = clone.firstChild;
  4424. }
  4425. for (var j = i, ei; ei = endParents[j]; j++) {
  4426. current = ei.previousSibling;
  4427. if (ei == end) {
  4428. if (!tmpEnd && range.endContainer.nodeType == 3) {
  4429. clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
  4430. //is not clone
  4431. if (action) {
  4432. end.deleteData(0, endOffset);
  4433. }
  4434. }
  4435. } else {
  4436. currentLevel = ei.cloneNode(false);
  4437. clone.appendChild(currentLevel);
  4438. }
  4439. //如果两端同级,右边第一次已经被开始做了
  4440. if (j != i || !startParents[i]) {
  4441. while (current) {
  4442. if (current === start) {
  4443. break;
  4444. }
  4445. ei = current.previousSibling;
  4446. clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
  4447. current = ei;
  4448. }
  4449. }
  4450. clone = currentLevel;
  4451. }
  4452. if (action) {
  4453. range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
  4454. }
  4455. tmpStart && domUtils.remove(tmpStart);
  4456. tmpEnd && domUtils.remove(tmpEnd);
  4457. return frag;
  4458. }
  4459. /**
  4460. * 创建一个跟document绑定的空的Range实例
  4461. * @constructor
  4462. * @param { Document } document 新建的选区所属的文档对象
  4463. */
  4464. /**
  4465. * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
  4466. */
  4467. /**
  4468. * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
  4469. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4470. */
  4471. /**
  4472. * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
  4473. */
  4474. /**
  4475. * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
  4476. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4477. */
  4478. /**
  4479. * @property { Boolean } collapsed 当前Range是否闭合
  4480. * @default true
  4481. * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
  4482. */
  4483. /**
  4484. * @property { Document } document 当前Range所属的Document对象
  4485. * @remind 不同range的的document属性可以是不同的
  4486. */
  4487. var Range = dom.Range = function (document) {
  4488. var me = this;
  4489. me.startContainer =
  4490. me.startOffset =
  4491. me.endContainer =
  4492. me.endOffset = null;
  4493. me.document = document;
  4494. me.collapsed = true;
  4495. };
  4496. /**
  4497. * 删除fillData
  4498. * @param doc
  4499. * @param excludeNode
  4500. */
  4501. function removeFillData(doc, excludeNode) {
  4502. try {
  4503. if (fillData && domUtils.inDoc(fillData, doc)) {
  4504. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  4505. var tmpNode = fillData.parentNode;
  4506. domUtils.remove(fillData);
  4507. while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
  4508. //safari的contains有bug
  4509. (browser.safari ? !(domUtils.getPosition(tmpNode, excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
  4510. ) {
  4511. fillData = tmpNode.parentNode;
  4512. domUtils.remove(tmpNode);
  4513. tmpNode = fillData;
  4514. }
  4515. } else {
  4516. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
  4517. }
  4518. }
  4519. } catch (e) {
  4520. }
  4521. }
  4522. /**
  4523. * @param node
  4524. * @param dir
  4525. */
  4526. function mergeSibling(node, dir) {
  4527. var tmpNode;
  4528. node = node[dir];
  4529. while (node && domUtils.isFillChar(node)) {
  4530. tmpNode = node[dir];
  4531. domUtils.remove(node);
  4532. node = tmpNode;
  4533. }
  4534. }
  4535. Range.prototype = {
  4536. /**
  4537. * 克隆选区的内容到一个DocumentFragment里
  4538. * @method cloneContents
  4539. * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
  4540. * @example
  4541. * ```html
  4542. * <body>
  4543. * <!-- 中括号表示选区 -->
  4544. * <b>x<i>x[x</i>xx]x</b>
  4545. *
  4546. * <script>
  4547. * //range是已选中的选区
  4548. * var fragment = range.cloneContents(),
  4549. * node = document.createElement("div");
  4550. *
  4551. * node.appendChild( fragment );
  4552. *
  4553. * //output: <i>x</i>xx
  4554. * console.log( node.innerHTML );
  4555. *
  4556. * </script>
  4557. * </body>
  4558. * ```
  4559. */
  4560. cloneContents: function () {
  4561. return this.collapsed ? null : execContentsAction(this, 0);
  4562. },
  4563. /**
  4564. * 删除当前选区范围中的所有内容
  4565. * @method deleteContents
  4566. * @remind 执行完该操作后, 当前Range对象变成了闭合状态
  4567. * @return { UE.dom.Range } 当前操作的Range对象
  4568. * @example
  4569. * ```html
  4570. * <body>
  4571. * <!-- 中括号表示选区 -->
  4572. * <b>x<i>x[x</i>xx]x</b>
  4573. *
  4574. * <script>
  4575. * //range是已选中的选区
  4576. * range.deleteContents();
  4577. *
  4578. * //竖线表示闭合后的选区位置
  4579. * //output: <b>x<i>x</i>|x</b>
  4580. * console.log( document.body.innerHTML );
  4581. *
  4582. * //此时, range的各项属性为
  4583. * //output: B
  4584. * console.log( range.startContainer.tagName );
  4585. * //output: 2
  4586. * console.log( range.startOffset );
  4587. * //output: B
  4588. * console.log( range.endContainer.tagName );
  4589. * //output: 2
  4590. * console.log( range.endOffset );
  4591. * //output: true
  4592. * console.log( range.collapsed );
  4593. *
  4594. * </script>
  4595. * </body>
  4596. * ```
  4597. */
  4598. deleteContents: function () {
  4599. var txt;
  4600. if (!this.collapsed) {
  4601. execContentsAction(this, 1);
  4602. }
  4603. if (browser.webkit) {
  4604. txt = this.startContainer;
  4605. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  4606. this.setStartBefore(txt).collapse(true);
  4607. domUtils.remove(txt);
  4608. }
  4609. }
  4610. return this;
  4611. },
  4612. /**
  4613. * 将当前选区的内容提取到一个DocumentFragment里
  4614. * @method extractContents
  4615. * @remind 执行该操作后, 选区将变成闭合状态
  4616. * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
  4617. * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
  4618. * @example
  4619. * ```html
  4620. * <body>
  4621. * <!-- 中括号表示选区 -->
  4622. * <b>x<i>x[x</i>xx]x</b>
  4623. *
  4624. * <script>
  4625. * //range是已选中的选区
  4626. * var fragment = range.extractContents(),
  4627. * node = document.createElement( "div" );
  4628. *
  4629. * node.appendChild( fragment );
  4630. *
  4631. * //竖线表示闭合后的选区位置
  4632. *
  4633. * //output: <b>x<i>x</i>|x</b>
  4634. * console.log( document.body.innerHTML );
  4635. * //output: <i>x</i>xx
  4636. * console.log( node.innerHTML );
  4637. *
  4638. * //此时, range的各项属性为
  4639. * //output: B
  4640. * console.log( range.startContainer.tagName );
  4641. * //output: 2
  4642. * console.log( range.startOffset );
  4643. * //output: B
  4644. * console.log( range.endContainer.tagName );
  4645. * //output: 2
  4646. * console.log( range.endOffset );
  4647. * //output: true
  4648. * console.log( range.collapsed );
  4649. *
  4650. * </script>
  4651. * </body>
  4652. */
  4653. extractContents: function () {
  4654. return this.collapsed ? null : execContentsAction(this, 2);
  4655. },
  4656. /**
  4657. * 设置Range的开始容器节点和偏移量
  4658. * @method setStart
  4659. * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
  4660. * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
  4661. * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
  4662. * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
  4663. * 中的索引
  4664. * @param { Node } node 将被设为当前选区开始边界容器的节点对象
  4665. * @param { int } offset 选区的开始位置偏移量
  4666. * @return { UE.dom.Range } 当前range对象
  4667. * @example
  4668. * ```html
  4669. * <!-- 选区 -->
  4670. * <b>xxx<i>x<span>xx</span>xx<em>xx</em>xxx</i>[xxx]</b>
  4671. *
  4672. * <script>
  4673. *
  4674. * //执行操作
  4675. * range.setStart( document.getElementsByTagName("i")[0], 1 );
  4676. *
  4677. * //此时, 选区变成了
  4678. * //<b>xxx<i>x[<span>xx</span>xx<em>xx</em>xxx</i>xxx]</b>
  4679. *
  4680. * </script>
  4681. * ```
  4682. * @example
  4683. * ```html
  4684. * <!-- 选区 -->
  4685. * <b>xxx<img>[xx]x</b>
  4686. *
  4687. * <script>
  4688. *
  4689. * //执行操作
  4690. * range.setStart( document.getElementsByTagName("img")[0], 3 );
  4691. *
  4692. * //此时, 选区变成了
  4693. * //<b>xxx[<img>xx]x</b>
  4694. *
  4695. * </script>
  4696. * ```
  4697. */
  4698. setStart: function (node, offset) {
  4699. return setEndPoint(true, node, offset, this);
  4700. },
  4701. /**
  4702. * 设置Range的结束容器和偏移量
  4703. * @method setEnd
  4704. * @param { Node } node 作为当前选区结束边界容器的节点对象
  4705. * @param { int } offset 结束边界的偏移量
  4706. * @see UE.dom.Range:setStart(Node,int)
  4707. * @return { UE.dom.Range } 当前range对象
  4708. */
  4709. setEnd: function (node, offset) {
  4710. return setEndPoint(false, node, offset, this);
  4711. },
  4712. /**
  4713. * 将Range开始位置设置到node节点之后
  4714. * @method setStartAfter
  4715. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
  4716. * @param { Node } node 选区的开始边界将紧接着该节点之后
  4717. * @return { UE.dom.Range } 当前range对象
  4718. * @example
  4719. * ```html
  4720. * <!-- 选区示例 -->
  4721. * <b>xx<i>xxx</i><span>xx[x</span>xxx]</b>
  4722. *
  4723. * <script>
  4724. *
  4725. * //执行操作
  4726. * range.setStartAfter( document.getElementsByTagName("i")[0] );
  4727. *
  4728. * //结果选区
  4729. * //<b>xx<i>xxx</i>[<span>xxx</span>xxx]</b>
  4730. *
  4731. * </script>
  4732. * ```
  4733. */
  4734. setStartAfter: function (node) {
  4735. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
  4736. },
  4737. /**
  4738. * 将Range开始位置设置到node节点之前
  4739. * @method setStartBefore
  4740. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
  4741. * @param { Node } node 新的选区开始位置在该节点之前
  4742. * @see UE.dom.Range:setStartAfter(Node)
  4743. * @return { UE.dom.Range } 当前range对象
  4744. */
  4745. setStartBefore: function (node) {
  4746. return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
  4747. },
  4748. /**
  4749. * 将Range结束位置设置到node节点之后
  4750. * @method setEndAfter
  4751. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
  4752. * @param { Node } node 目标节点
  4753. * @see UE.dom.Range:setStartAfter(Node)
  4754. * @return { UE.dom.Range } 当前range对象
  4755. * @example
  4756. * ```html
  4757. * <!-- 选区示例 -->
  4758. * <b>[xx<i>xxx</i><span>xx]x</span>xxx</b>
  4759. *
  4760. * <script>
  4761. *
  4762. * //执行操作
  4763. * range.setStartAfter( document.getElementsByTagName("span")[0] );
  4764. *
  4765. * //结果选区
  4766. * //<b>[xx<i>xxx</i><span>xxx</span>]xxx</b>
  4767. *
  4768. * </script>
  4769. * ```
  4770. */
  4771. setEndAfter: function (node) {
  4772. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
  4773. },
  4774. /**
  4775. * 将Range结束位置设置到node节点之前
  4776. * @method setEndBefore
  4777. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
  4778. * @param { Node } node 目标节点
  4779. * @see UE.dom.Range:setEndAfter(Node)
  4780. * @return { UE.dom.Range } 当前range对象
  4781. */
  4782. setEndBefore: function (node) {
  4783. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
  4784. },
  4785. /**
  4786. * 设置Range的开始位置到node节点内的第一个子节点之前
  4787. * @method setStartAtFirst
  4788. * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
  4789. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  4790. * @param { Node } node 目标节点
  4791. * @see UE.dom.Range:setStartBefore(Node)
  4792. * @return { UE.dom.Range } 当前range对象
  4793. * @example
  4794. * ```html
  4795. * <!-- 选区示例 -->
  4796. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4797. *
  4798. * <script>
  4799. *
  4800. * //执行操作
  4801. * range.setStartAtFirst( document.getElementsByTagName("i")[0] );
  4802. *
  4803. * //结果选区
  4804. * //<b>xx<i>[xxx</i><span>xx]x</span>xxx</b>
  4805. *
  4806. * </script>
  4807. * ```
  4808. */
  4809. setStartAtFirst: function (node) {
  4810. return this.setStart(node, 0);
  4811. },
  4812. /**
  4813. * 设置Range的开始位置到node节点内的最后一个节点之后
  4814. * @method setStartAtLast
  4815. * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
  4816. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  4817. * @param { Node } node 目标节点
  4818. * @see UE.dom.Range:setStartAtFirst(Node)
  4819. * @return { UE.dom.Range } 当前range对象
  4820. */
  4821. setStartAtLast: function (node) {
  4822. return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  4823. },
  4824. /**
  4825. * 设置Range的结束位置到node节点内的第一个节点之前
  4826. * @method setEndAtFirst
  4827. * @param { Node } node 目标节点
  4828. * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
  4829. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  4830. * @see UE.dom.Range:setStartAtFirst(Node)
  4831. * @return { UE.dom.Range } 当前range对象
  4832. */
  4833. setEndAtFirst: function (node) {
  4834. return this.setEnd(node, 0);
  4835. },
  4836. /**
  4837. * 设置Range的结束位置到node节点内的最后一个节点之后
  4838. * @method setEndAtLast
  4839. * @param { Node } node 目标节点
  4840. * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
  4841. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  4842. * @see UE.dom.Range:setStartAtFirst(Node)
  4843. * @return { UE.dom.Range } 当前range对象
  4844. */
  4845. setEndAtLast: function (node) {
  4846. return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  4847. },
  4848. /**
  4849. * 选中给定节点
  4850. * @method selectNode
  4851. * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
  4852. * 而endOffset为startOffset+1
  4853. * @param { Node } node 需要选中的节点
  4854. * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
  4855. * @example
  4856. * ```html
  4857. * <!-- 选区示例 -->
  4858. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4859. *
  4860. * <script>
  4861. *
  4862. * //执行操作
  4863. * range.selectNode( document.getElementsByTagName("i")[0] );
  4864. *
  4865. * //结果选区
  4866. * //<b>xx[<i>xxx</i>]<span>xxx</span>xxx</b>
  4867. *
  4868. * </script>
  4869. * ```
  4870. */
  4871. selectNode: function (node) {
  4872. return this.setStartBefore(node).setEndAfter(node);
  4873. },
  4874. /**
  4875. * 选中给定节点内部的所有节点
  4876. * @method selectNodeContents
  4877. * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
  4878. * 而endOffset是该节点的子节点数。
  4879. * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
  4880. * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
  4881. * @example
  4882. * ```html
  4883. * <!-- 选区示例 -->
  4884. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4885. *
  4886. * <script>
  4887. *
  4888. * //执行操作
  4889. * range.selectNode( document.getElementsByTagName("b")[0] );
  4890. *
  4891. * //结果选区
  4892. * //<b>[xx<i>xxx</i><span>xxx</span>xxx]</b>
  4893. *
  4894. * </script>
  4895. * ```
  4896. */
  4897. selectNodeContents: function (node) {
  4898. return this.setStart(node, 0).setEndAtLast(node);
  4899. },
  4900. /**
  4901. * clone当前Range对象
  4902. * @method cloneRange
  4903. * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
  4904. * @return { UE.dom.Range } 当前range对象的一个副本
  4905. */
  4906. cloneRange: function () {
  4907. var me = this;
  4908. return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  4909. },
  4910. /**
  4911. * 向当前选区的结束处闭合选区
  4912. * @method collapse
  4913. * @return { UE.dom.Range } 当前range对象
  4914. * @example
  4915. * ```html
  4916. * <!-- 选区示例 -->
  4917. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4918. *
  4919. * <script>
  4920. *
  4921. * //执行操作
  4922. * range.collapse();
  4923. *
  4924. * //结果选区
  4925. * //“|”表示选区已闭合
  4926. * //<b>xx<i>xxx</i><span>xx|x</span>xxx</b>
  4927. *
  4928. * </script>
  4929. * ```
  4930. */
  4931. /**
  4932. * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
  4933. * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
  4934. * @method collapse
  4935. * @param { Boolean } toStart 是否向选区开始处闭合
  4936. * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
  4937. * @see UE.dom.Range:collapse()
  4938. * @example
  4939. * ```html
  4940. * <!-- 选区示例 -->
  4941. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  4942. *
  4943. * <script>
  4944. *
  4945. * //执行操作
  4946. * range.collapse( true );
  4947. *
  4948. * //结果选区
  4949. * //“|”表示选区已闭合
  4950. * //<b>xx<i>xxx</i><span>|xxx</span>xxx</b>
  4951. *
  4952. * </script>
  4953. * ```
  4954. */
  4955. collapse: function (toStart) {
  4956. var me = this;
  4957. if (toStart) {
  4958. me.endContainer = me.startContainer;
  4959. me.endOffset = me.startOffset;
  4960. } else {
  4961. me.startContainer = me.endContainer;
  4962. me.startOffset = me.endOffset;
  4963. }
  4964. me.collapsed = true;
  4965. return me;
  4966. },
  4967. /**
  4968. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
  4969. * @method shrinkBoundary
  4970. * @return { UE.dom.Range } 当前range对象
  4971. * @example
  4972. * ```html
  4973. * <span>xx<b>xx[</b>xxxxx]</span> => <span>xx<b>xx</b>[xxxxx]</span>
  4974. * ```
  4975. *
  4976. * @example
  4977. * ```html
  4978. * <!-- 选区示例 -->
  4979. * <b>x[xx</b><i>]xxx</i>
  4980. *
  4981. * <script>
  4982. *
  4983. * //执行收缩
  4984. * range.shrinkBoundary();
  4985. *
  4986. * //结果选区
  4987. * //<b>x[xx]</b><i>xxx</i>
  4988. * </script>
  4989. * ```
  4990. *
  4991. * @example
  4992. * ```html
  4993. * [<b><i>xxxx</i>xxxxxxx</b>] => <b><i>[xxxx</i>xxxxxxx]</b>
  4994. * ```
  4995. */
  4996. /**
  4997. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
  4998. * 如果ignoreEnd的值为true,则忽略对结束位置的调整
  4999. * @method shrinkBoundary
  5000. * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
  5001. * @return { UE.dom.Range } 当前range对象
  5002. * @see UE.dom.domUtils.Range:shrinkBoundary()
  5003. */
  5004. shrinkBoundary: function (ignoreEnd) {
  5005. var me = this, child,
  5006. collapsed = me.collapsed;
  5007. function check(node) {
  5008. return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
  5009. }
  5010. while (me.startContainer.nodeType == 1 //是element
  5011. && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
  5012. && check(child)) {
  5013. me.setStart(child, 0);
  5014. }
  5015. if (collapsed) {
  5016. return me.collapse(true);
  5017. }
  5018. if (!ignoreEnd) {
  5019. while (me.endContainer.nodeType == 1//是element
  5020. && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  5021. && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
  5022. && check(child)) {
  5023. me.setEnd(child, child.childNodes.length);
  5024. }
  5025. }
  5026. return me;
  5027. },
  5028. /**
  5029. * 获取离当前选区内包含的所有节点最近的公共祖先节点,
  5030. * @method getCommonAncestor
  5031. * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
  5032. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5033. * @example
  5034. * ```html
  5035. * //选区示例
  5036. * <span>xxx<b>x[x<em>xx]x</em>xxx</b>xx</span>
  5037. * <script>
  5038. *
  5039. * var node = range.getCommonAncestor();
  5040. *
  5041. * //公共祖先节点是: b节点
  5042. * //输出: B
  5043. * console.log(node.tagName);
  5044. *
  5045. * </script>
  5046. * ```
  5047. */
  5048. /**
  5049. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  5050. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  5051. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
  5052. * @method getCommonAncestor
  5053. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  5054. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5055. * @see UE.dom.Range:getCommonAncestor()
  5056. * @example
  5057. * ```html
  5058. * <body>
  5059. *
  5060. * <!-- 选区示例 -->
  5061. * <b>xxx<i>xxxx<span>xx[x</span>xx]x</i>xxxxxxx</b>
  5062. *
  5063. * <script>
  5064. *
  5065. * var node = range.getCommonAncestor( false );
  5066. *
  5067. * //这里的公共祖先节点是B而不是I, 是因为参数限制了获取到的节点不能是容器节点
  5068. * //output: B
  5069. * console.log( node.tagName );
  5070. *
  5071. * </script>
  5072. *
  5073. * </body>
  5074. * ```
  5075. */
  5076. /**
  5077. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  5078. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  5079. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
  5080. * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
  5081. * @method getCommonAncestor
  5082. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  5083. * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
  5084. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5085. * @see UE.dom.Range:getCommonAncestor()
  5086. * @see UE.dom.Range:getCommonAncestor(Boolean)
  5087. * @example
  5088. * ```html
  5089. * <body>
  5090. *
  5091. * <!-- 选区示例 -->
  5092. * <b>xxx<i>xxxx<span>x[x]x</span>xxx</i>xxxxxxx</b>
  5093. *
  5094. * <script>
  5095. *
  5096. * var node = range.getCommonAncestor( true, false );
  5097. *
  5098. * //output: SPAN
  5099. * console.log( node.tagName );
  5100. *
  5101. * </script>
  5102. *
  5103. * </body>
  5104. * ```
  5105. */
  5106. getCommonAncestor: function (includeSelf, ignoreTextNode) {
  5107. var me = this,
  5108. start = me.startContainer,
  5109. end = me.endContainer;
  5110. if (start === end) {
  5111. if (includeSelf && selectOneNode(this)) {
  5112. start = start.childNodes[me.startOffset];
  5113. if (start.nodeType == 1)
  5114. return start;
  5115. }
  5116. //只有在上来就相等的情况下才会出现是文本的情况
  5117. return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
  5118. }
  5119. return domUtils.getCommonAncestor(start, end);
  5120. },
  5121. /**
  5122. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
  5123. * @method trimBoundary
  5124. * @remind 该操作有可能会引起文本节点被切开
  5125. * @return { UE.dom.Range } 当前range对象
  5126. * @example
  5127. * ```html
  5128. *
  5129. * //选区示例
  5130. * <b>xxx<i>[xxxxx]</i>xxx</b>
  5131. *
  5132. * <script>
  5133. * //未调整前, 选区的开始容器和结束都是文本节点
  5134. * //执行调整
  5135. * range.trimBoundary();
  5136. *
  5137. * //调整之后, 容器节点变成了i节点
  5138. * //<b>xxx[<i>xxxxx</i>]xxx</b>
  5139. * </script>
  5140. * ```
  5141. */
  5142. /**
  5143. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
  5144. * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
  5145. * @method trimBoundary
  5146. * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
  5147. * @return { UE.dom.Range } 当前range对象
  5148. * @example
  5149. * ```html
  5150. *
  5151. * //选区示例
  5152. * <b>xxx<i>[xxxxx]</i>xxx</b>
  5153. *
  5154. * <script>
  5155. * //未调整前, 选区的开始容器和结束都是文本节点
  5156. * //执行调整
  5157. * range.trimBoundary( true );
  5158. *
  5159. * //调整之后, 开始容器节点变成了i节点
  5160. * //但是, 结束容器没有发生变化
  5161. * //<b>xxx[<i>xxxxx]</i>xxx</b>
  5162. * </script>
  5163. * ```
  5164. */
  5165. trimBoundary: function (ignoreEnd) {
  5166. this.txtToElmBoundary();
  5167. var start = this.startContainer,
  5168. offset = this.startOffset,
  5169. collapsed = this.collapsed,
  5170. end = this.endContainer;
  5171. if (start.nodeType == 3) {
  5172. if (offset == 0) {
  5173. this.setStartBefore(start);
  5174. } else {
  5175. if (offset >= start.nodeValue.length) {
  5176. this.setStartAfter(start);
  5177. } else {
  5178. var textNode = domUtils.split(start, offset);
  5179. //跟新结束边界
  5180. if (start === end) {
  5181. this.setEnd(textNode, this.endOffset - offset);
  5182. } else if (start.parentNode === end) {
  5183. this.endOffset += 1;
  5184. }
  5185. this.setStartBefore(textNode);
  5186. }
  5187. }
  5188. if (collapsed) {
  5189. return this.collapse(true);
  5190. }
  5191. }
  5192. if (!ignoreEnd) {
  5193. offset = this.endOffset;
  5194. end = this.endContainer;
  5195. if (end.nodeType == 3) {
  5196. if (offset == 0) {
  5197. this.setEndBefore(end);
  5198. } else {
  5199. offset < end.nodeValue.length && domUtils.split(end, offset);
  5200. this.setEndAfter(end);
  5201. }
  5202. }
  5203. }
  5204. return this;
  5205. },
  5206. /**
  5207. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
  5208. * @method txtToElmBoundary
  5209. * @remind 该操作不会修改dom节点
  5210. * @return { UE.dom.Range } 当前range对象
  5211. */
  5212. /**
  5213. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
  5214. * ignoreCollapsed 的值决定是否执行该调整
  5215. * @method txtToElmBoundary
  5216. * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
  5217. * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
  5218. * @return { UE.dom.Range } 当前range对象
  5219. */
  5220. txtToElmBoundary: function (ignoreCollapsed) {
  5221. function adjust(r, c) {
  5222. var container = r[c + 'Container'],
  5223. offset = r[c + 'Offset'];
  5224. if (container.nodeType == 3) {
  5225. if (!offset) {
  5226. r['set' + c.replace(/(\w)/, function (a) {
  5227. return a.toUpperCase();
  5228. }) + 'Before'](container);
  5229. } else if (offset >= container.nodeValue.length) {
  5230. r['set' + c.replace(/(\w)/, function (a) {
  5231. return a.toUpperCase();
  5232. }) + 'After'](container);
  5233. }
  5234. }
  5235. }
  5236. if (ignoreCollapsed || !this.collapsed) {
  5237. adjust(this, 'start');
  5238. adjust(this, 'end');
  5239. }
  5240. return this;
  5241. },
  5242. /**
  5243. * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
  5244. * @method insertNode
  5245. * @param { Node } node 需要插入的节点
  5246. * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
  5247. * @return { UE.dom.Range } 当前range对象
  5248. */
  5249. insertNode: function (node) {
  5250. var first = node, length = 1;
  5251. if (node.nodeType == 11) {
  5252. first = node.firstChild;
  5253. length = node.childNodes.length;
  5254. }
  5255. this.trimBoundary(true);
  5256. var start = this.startContainer,
  5257. offset = this.startOffset;
  5258. var nextNode = start.childNodes[offset];
  5259. if (nextNode) {
  5260. start.insertBefore(node, nextNode);
  5261. } else {
  5262. start.appendChild(node);
  5263. }
  5264. if (first.parentNode === this.endContainer) {
  5265. this.endOffset = this.endOffset + length;
  5266. }
  5267. return this.setStartBefore(first);
  5268. },
  5269. /**
  5270. * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
  5271. * @method setCursor
  5272. * @return { UE.dom.Range } 当前range对象
  5273. * @see UE.dom.Range:collapse()
  5274. */
  5275. /**
  5276. * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
  5277. * @method setCursor
  5278. * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
  5279. * 反之,则向开始容器方向闭合
  5280. * @return { UE.dom.Range } 当前range对象
  5281. * @see UE.dom.Range:collapse(Boolean)
  5282. */
  5283. setCursor: function (toEnd, noFillData) {
  5284. return this.collapse(!toEnd).select(noFillData);
  5285. },
  5286. /**
  5287. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  5288. * @method createBookmark
  5289. * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
  5290. * 返回标记位置的ID, 反之则返回标记位置节点的引用
  5291. * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
  5292. * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
  5293. * 返回的记录的类型为ID, 反之则为引用
  5294. */
  5295. createBookmark: function (serialize, same) {
  5296. var endNode,
  5297. startNode = this.document.createElement('span');
  5298. startNode.style.cssText = 'display:none;line-height:0px;';
  5299. startNode.appendChild(this.document.createTextNode('\u200D'));
  5300. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  5301. if (!this.collapsed) {
  5302. endNode = startNode.cloneNode(true);
  5303. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
  5304. }
  5305. this.insertNode(startNode);
  5306. if (endNode) {
  5307. this.collapse().insertNode(endNode).setEndBefore(endNode);
  5308. }
  5309. this.setStartAfter(startNode);
  5310. return {
  5311. start: serialize ? startNode.id : startNode,
  5312. end: endNode ? serialize ? endNode.id : endNode : null,
  5313. id: serialize
  5314. }
  5315. },
  5316. /**
  5317. * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
  5318. * @method moveToBookmark
  5319. * @param { BookMark } bookmark createBookmark所创建的标签对象
  5320. * @return { UE.dom.Range } 当前range对象
  5321. * @see UE.dom.Range:createBookmark(Boolean)
  5322. */
  5323. moveToBookmark: function (bookmark) {
  5324. var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
  5325. end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
  5326. this.setStartBefore(start);
  5327. domUtils.remove(start);
  5328. if (end) {
  5329. this.setEndBefore(end);
  5330. domUtils.remove(end);
  5331. } else {
  5332. this.collapse(true);
  5333. }
  5334. return this;
  5335. },
  5336. /**
  5337. * 调整range的边界,使其"放大"到最近的父节点
  5338. * @method enlarge
  5339. * @remind 会引起选区的变化
  5340. * @return { UE.dom.Range } 当前range对象
  5341. */
  5342. /**
  5343. * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
  5344. * 要求扩大之后的父节点是block节点
  5345. * @method enlarge
  5346. * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
  5347. * @return { UE.dom.Range } 当前range对象
  5348. */
  5349. enlarge: function (toBlock, stopFn) {
  5350. var isBody = domUtils.isBody,
  5351. pre, node, tmp = this.document.createTextNode('');
  5352. if (toBlock) {
  5353. node = this.startContainer;
  5354. if (node.nodeType == 1) {
  5355. if (node.childNodes[this.startOffset]) {
  5356. pre = node = node.childNodes[this.startOffset]
  5357. } else {
  5358. node.appendChild(tmp);
  5359. pre = node = tmp;
  5360. }
  5361. } else {
  5362. pre = node;
  5363. }
  5364. while (1) {
  5365. if (domUtils.isBlockElm(node)) {
  5366. node = pre;
  5367. while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
  5368. node = pre;
  5369. }
  5370. this.setStartBefore(node);
  5371. break;
  5372. }
  5373. pre = node;
  5374. node = node.parentNode;
  5375. }
  5376. node = this.endContainer;
  5377. if (node.nodeType == 1) {
  5378. if (pre = node.childNodes[this.endOffset]) {
  5379. node.insertBefore(tmp, pre);
  5380. } else {
  5381. node.appendChild(tmp);
  5382. }
  5383. pre = node = tmp;
  5384. } else {
  5385. pre = node;
  5386. }
  5387. while (1) {
  5388. if (domUtils.isBlockElm(node)) {
  5389. node = pre;
  5390. while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
  5391. node = pre;
  5392. }
  5393. this.setEndAfter(node);
  5394. break;
  5395. }
  5396. pre = node;
  5397. node = node.parentNode;
  5398. }
  5399. if (tmp.parentNode === this.endContainer) {
  5400. this.endOffset--;
  5401. }
  5402. domUtils.remove(tmp);
  5403. }
  5404. // 扩展边界到最大
  5405. if (!this.collapsed) {
  5406. while (this.startOffset == 0) {
  5407. if (stopFn && stopFn(this.startContainer)) {
  5408. break;
  5409. }
  5410. if (isBody(this.startContainer)) {
  5411. break;
  5412. }
  5413. this.setStartBefore(this.startContainer);
  5414. }
  5415. while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) {
  5416. if (stopFn && stopFn(this.endContainer)) {
  5417. break;
  5418. }
  5419. if (isBody(this.endContainer)) {
  5420. break;
  5421. }
  5422. this.setEndAfter(this.endContainer);
  5423. }
  5424. }
  5425. return this;
  5426. },
  5427. enlargeToBlockElm: function (ignoreEnd) {
  5428. while (!domUtils.isBlockElm(this.startContainer)) {
  5429. this.setStartBefore(this.startContainer);
  5430. }
  5431. if (!ignoreEnd) {
  5432. while (!domUtils.isBlockElm(this.endContainer)) {
  5433. this.setEndAfter(this.endContainer);
  5434. }
  5435. }
  5436. return this;
  5437. },
  5438. /**
  5439. * 调整Range的边界,使其"缩小"到最合适的位置
  5440. * @method adjustmentBoundary
  5441. * @return { UE.dom.Range } 当前range对象
  5442. * @see UE.dom.Range:shrinkBoundary()
  5443. */
  5444. adjustmentBoundary: function () {
  5445. if (!this.collapsed) {
  5446. while (!domUtils.isBody(this.startContainer) &&
  5447. this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
  5448. this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  5449. ) {
  5450. this.setStartAfter(this.startContainer);
  5451. }
  5452. while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
  5453. this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  5454. ) {
  5455. this.setEndBefore(this.endContainer);
  5456. }
  5457. }
  5458. return this;
  5459. },
  5460. /**
  5461. * 给range选区中的内容添加给定的inline标签
  5462. * @method applyInlineStyle
  5463. * @param { String } tagName 需要添加的标签名
  5464. * @example
  5465. * ```html
  5466. * <p>xxxx[xxxx]x</p> ==> range.applyInlineStyle("strong") ==> <p>xxxx[<strong>xxxx</strong>]x</p>
  5467. * ```
  5468. */
  5469. /**
  5470. * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
  5471. * @method applyInlineStyle
  5472. * @param { String } tagName 需要添加的标签名
  5473. * @param { Object } attrs 跟随新添加的标签的属性
  5474. * @return { UE.dom.Range } 当前选区
  5475. * @example
  5476. * ```html
  5477. * <p>xxxx[xxxx]x</p>
  5478. *
  5479. * ==>
  5480. *
  5481. * <!-- 执行操作 -->
  5482. * range.applyInlineStyle("strong",{"style":"font-size:12px"})
  5483. *
  5484. * ==>
  5485. *
  5486. * <p>xxxx[<strong style="font-size:12px">xxxx</strong>]x</p>
  5487. * ```
  5488. */
  5489. applyInlineStyle: function (tagName, attrs, list) {
  5490. if (this.collapsed) return this;
  5491. this.trimBoundary().enlarge(false,
  5492. function (node) {
  5493. return node.nodeType == 1 && domUtils.isBlockElm(node)
  5494. }).adjustmentBoundary();
  5495. var bookmark = this.createBookmark(),
  5496. end = bookmark.end,
  5497. filterFn = function (node) {
  5498. return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
  5499. },
  5500. current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
  5501. node,
  5502. pre,
  5503. range = this.cloneRange();
  5504. while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
  5505. if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
  5506. range.setStartBefore(current);
  5507. node = current;
  5508. while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
  5509. pre = node;
  5510. node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
  5511. return dtd[tagName][parent.tagName];
  5512. });
  5513. }
  5514. var frag = range.setEndAfter(pre).extractContents(), elm;
  5515. if (list && list.length > 0) {
  5516. var level, top;
  5517. top = level = list[0].cloneNode(false);
  5518. for (var i = 1, ci; ci = list[i++];) {
  5519. level.appendChild(ci.cloneNode(false));
  5520. level = level.firstChild;
  5521. }
  5522. elm = level;
  5523. } else {
  5524. elm = range.document.createElement(tagName);
  5525. }
  5526. if (attrs) {
  5527. domUtils.setAttributes(elm, attrs);
  5528. }
  5529. elm.appendChild(frag);
  5530. range.insertNode(list ? top : elm);
  5531. //处理下滑线在a上的情况
  5532. var aNode;
  5533. if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
  5534. domUtils.setAttributes(aNode, attrs);
  5535. domUtils.remove(elm, true);
  5536. elm = aNode;
  5537. } else {
  5538. domUtils.mergeSibling(elm);
  5539. domUtils.clearEmptySibling(elm);
  5540. }
  5541. //去除子节点相同的
  5542. domUtils.mergeChild(elm, attrs);
  5543. current = domUtils.getNextDomNode(elm, false, filterFn);
  5544. domUtils.mergeToParent(elm);
  5545. if (node === end) {
  5546. break;
  5547. }
  5548. } else {
  5549. current = domUtils.getNextDomNode(current, true, filterFn);
  5550. }
  5551. }
  5552. return this.moveToBookmark(bookmark);
  5553. },
  5554. /**
  5555. * 移除当前选区内指定的inline标签,但保留其中的内容
  5556. * @method removeInlineStyle
  5557. * @param { String } tagName 需要移除的标签名
  5558. * @return { UE.dom.Range } 当前的range对象
  5559. * @example
  5560. * ```html
  5561. * xx[x<span>xxx<em>yyy</em>zz]z</span> => range.removeInlineStyle(["em"]) => xx[x<span>xxxyyyzz]z</span>
  5562. * ```
  5563. */
  5564. /**
  5565. * 移除当前选区内指定的一组inline标签,但保留其中的内容
  5566. * @method removeInlineStyle
  5567. * @param { Array } tagNameArr 需要移除的标签名的数组
  5568. * @return { UE.dom.Range } 当前的range对象
  5569. * @see UE.dom.Range:removeInlineStyle(String)
  5570. */
  5571. removeInlineStyle: function (tagNames) {
  5572. if (this.collapsed) return this;
  5573. tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
  5574. this.shrinkBoundary().adjustmentBoundary();
  5575. var start = this.startContainer, end = this.endContainer;
  5576. while (1) {
  5577. if (start.nodeType == 1) {
  5578. if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
  5579. break;
  5580. }
  5581. if (start.tagName.toLowerCase() == 'body') {
  5582. start = null;
  5583. break;
  5584. }
  5585. }
  5586. start = start.parentNode;
  5587. }
  5588. while (1) {
  5589. if (end.nodeType == 1) {
  5590. if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
  5591. break;
  5592. }
  5593. if (end.tagName.toLowerCase() == 'body') {
  5594. end = null;
  5595. break;
  5596. }
  5597. }
  5598. end = end.parentNode;
  5599. }
  5600. var bookmark = this.createBookmark(),
  5601. frag,
  5602. tmpRange;
  5603. if (start) {
  5604. tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
  5605. frag = tmpRange.extractContents();
  5606. tmpRange.insertNode(frag);
  5607. domUtils.clearEmptySibling(start, true);
  5608. start.parentNode.insertBefore(bookmark.start, start);
  5609. }
  5610. if (end) {
  5611. tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
  5612. frag = tmpRange.extractContents();
  5613. tmpRange.insertNode(frag);
  5614. domUtils.clearEmptySibling(end, false, true);
  5615. end.parentNode.insertBefore(bookmark.end, end.nextSibling);
  5616. }
  5617. var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
  5618. return node.nodeType == 1;
  5619. }), next;
  5620. while (current && current !== bookmark.end) {
  5621. next = domUtils.getNextDomNode(current, true, function (node) {
  5622. return node.nodeType == 1;
  5623. });
  5624. if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
  5625. domUtils.remove(current, true);
  5626. }
  5627. current = next;
  5628. }
  5629. return this.moveToBookmark(bookmark);
  5630. },
  5631. /**
  5632. * 获取当前选中的自闭合的节点
  5633. * @method getClosedNode
  5634. * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
  5635. */
  5636. getClosedNode: function () {
  5637. var node;
  5638. if (!this.collapsed) {
  5639. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
  5640. if (selectOneNode(range)) {
  5641. var child = range.startContainer.childNodes[range.startOffset];
  5642. if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
  5643. node = child;
  5644. }
  5645. }
  5646. }
  5647. return node;
  5648. },
  5649. /**
  5650. * 在页面上高亮range所表示的选区
  5651. * @method select
  5652. * @return { UE.dom.Range } 返回当前Range对象
  5653. */
  5654. //这里不区分ie9以上,trace:3824
  5655. select: browser.ie ? function (noFillData, textRange) {
  5656. var nativeRange;
  5657. if (!this.collapsed)
  5658. this.shrinkBoundary();
  5659. var node = this.getClosedNode();
  5660. if (node && !textRange) {
  5661. try {
  5662. nativeRange = this.document.body.createControlRange();
  5663. nativeRange.addElement(node);
  5664. nativeRange.select();
  5665. } catch (e) {
  5666. }
  5667. return this;
  5668. }
  5669. var bookmark = this.createBookmark(),
  5670. start = bookmark.start,
  5671. end;
  5672. nativeRange = this.document.body.createTextRange();
  5673. nativeRange.moveToElementText(start);
  5674. nativeRange.moveStart('character', 1);
  5675. if (!this.collapsed) {
  5676. var nativeRangeEnd = this.document.body.createTextRange();
  5677. end = bookmark.end;
  5678. nativeRangeEnd.moveToElementText(end);
  5679. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
  5680. } else {
  5681. if (!noFillData && this.startContainer.nodeType != 3) {
  5682. //使用<span>|x<span>固定住光标
  5683. var tmpText = this.document.createTextNode(fillChar),
  5684. tmp = this.document.createElement('span');
  5685. tmp.appendChild(this.document.createTextNode(fillChar));
  5686. start.parentNode.insertBefore(tmp, start);
  5687. start.parentNode.insertBefore(tmpText, start);
  5688. //当点b,i,u时,不能清除i上边的b
  5689. removeFillData(this.document, tmpText);
  5690. fillData = tmpText;
  5691. mergeSibling(tmp, 'previousSibling');
  5692. mergeSibling(start, 'nextSibling');
  5693. nativeRange.moveStart('character', -1);
  5694. nativeRange.collapse(true);
  5695. }
  5696. }
  5697. this.moveToBookmark(bookmark);
  5698. tmp && domUtils.remove(tmp);
  5699. //IE在隐藏状态下不支持range操作,catch一下
  5700. try {
  5701. nativeRange.select();
  5702. } catch (e) {
  5703. }
  5704. return this;
  5705. } : function (notInsertFillData) {
  5706. function checkOffset(rng) {
  5707. function check(node, offset, dir) {
  5708. if (node.nodeType == 3 && node.nodeValue.length < offset) {
  5709. rng[dir + 'Offset'] = node.nodeValue.length
  5710. }
  5711. }
  5712. check(rng.startContainer, rng.startOffset, 'start');
  5713. check(rng.endContainer, rng.endOffset, 'end');
  5714. }
  5715. var win = domUtils.getWindow(this.document),
  5716. sel = win.getSelection(),
  5717. txtNode;
  5718. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  5719. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  5720. browser.gecko ? this.document.body.focus() : win.focus();
  5721. if (sel) {
  5722. sel.removeAllRanges();
  5723. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  5724. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  5725. if (this.collapsed && !notInsertFillData) {
  5726. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  5727. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  5728. // var tmp = this.document.createTextNode('');
  5729. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  5730. // }
  5731. //
  5732. //处理光标落在文本节点的情况
  5733. //处理以下的情况
  5734. //<b>|xxxx</b>
  5735. //<b>xxxx</b>|xxxx
  5736. //xxxx<b>|</b>
  5737. var start = this.startContainer, child = start;
  5738. if (start.nodeType == 1) {
  5739. child = start.childNodes[this.startOffset];
  5740. }
  5741. if (!(start.nodeType == 3 && this.startOffset) &&
  5742. (child ?
  5743. (!child.previousSibling || child.previousSibling.nodeType != 3)
  5744. :
  5745. (!start.lastChild || start.lastChild.nodeType != 3)
  5746. )
  5747. ) {
  5748. txtNode = this.document.createTextNode(fillChar);
  5749. //跟着前边走
  5750. this.insertNode(txtNode);
  5751. removeFillData(this.document, txtNode);
  5752. mergeSibling(txtNode, 'previousSibling');
  5753. mergeSibling(txtNode, 'nextSibling');
  5754. fillData = txtNode;
  5755. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
  5756. }
  5757. }
  5758. var nativeRange = this.document.createRange();
  5759. if (this.collapsed && browser.opera && this.startContainer.nodeType == 1) {
  5760. var child = this.startContainer.childNodes[this.startOffset];
  5761. if (!child) {
  5762. //往前靠拢
  5763. child = this.startContainer.lastChild;
  5764. if (child && domUtils.isBr(child)) {
  5765. this.setStartBefore(child).collapse(true);
  5766. }
  5767. } else {
  5768. //向后靠拢
  5769. while (child && domUtils.isBlockElm(child)) {
  5770. if (child.nodeType == 1 && child.childNodes[0]) {
  5771. child = child.childNodes[0]
  5772. } else {
  5773. break;
  5774. }
  5775. }
  5776. child && this.setStartBefore(child).collapse(true)
  5777. }
  5778. }
  5779. //是createAddress最后一位算的不准,现在这里进行微调
  5780. checkOffset(this);
  5781. nativeRange.setStart(this.startContainer, this.startOffset);
  5782. nativeRange.setEnd(this.endContainer, this.endOffset);
  5783. sel.addRange(nativeRange);
  5784. }
  5785. return this;
  5786. },
  5787. /**
  5788. * 滚动到当前range开始的位置
  5789. * @method scrollToView
  5790. * @param { Window } win 当前range对象所属的window对象
  5791. * @return { UE.dom.Range } 当前Range对象
  5792. */
  5793. /**
  5794. * 滚动到距离当前range开始位置 offset 的位置处
  5795. * @method scrollToView
  5796. * @param { Window } win 当前range对象所属的window对象
  5797. * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
  5798. * @return { UE.dom.Range } 当前Range对象
  5799. */
  5800. scrollToView: function (win, offset) {
  5801. win = win ? window : domUtils.getWindow(this.document);
  5802. var me = this,
  5803. span = me.document.createElement('span');
  5804. //trace:717
  5805. span.innerHTML = '&nbsp;';
  5806. me.cloneRange().insertNode(span);
  5807. domUtils.scrollToView(span, win, offset);
  5808. domUtils.remove(span);
  5809. return me;
  5810. },
  5811. /**
  5812. * 判断当前选区内容是否占位符
  5813. * @private
  5814. * @method inFillChar
  5815. * @return { Boolean } 如果是占位符返回true,否则返回false
  5816. */
  5817. inFillChar: function () {
  5818. var start = this.startContainer;
  5819. if (this.collapsed && start.nodeType == 3
  5820. && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '').length + 1 == start.nodeValue.length
  5821. ) {
  5822. return true;
  5823. }
  5824. return false;
  5825. },
  5826. /**
  5827. * 保存
  5828. * @method createAddress
  5829. * @private
  5830. * @return { Boolean } 返回开始和结束的位置
  5831. * @example
  5832. * ```html
  5833. * <body>
  5834. * <p>
  5835. * aaaa
  5836. * <em>
  5837. * <!-- 选区开始 -->
  5838. * bbbb
  5839. * <!-- 选区结束 -->
  5840. * </em>
  5841. * </p>
  5842. *
  5843. * <script>
  5844. * //output: {startAddress:[0,1,0,0],endAddress:[0,1,0,4]}
  5845. * console.log( range.createAddress() );
  5846. * </script>
  5847. * </body>
  5848. * ```
  5849. */
  5850. createAddress: function (ignoreEnd, ignoreTxt) {
  5851. var addr = {}, me = this;
  5852. function getAddress(isStart) {
  5853. var node = isStart ? me.startContainer : me.endContainer;
  5854. var parents = domUtils.findParents(node, true, function (node) {
  5855. return !domUtils.isBody(node)
  5856. }),
  5857. addrs = [];
  5858. for (var i = 0, ci; ci = parents[i++];) {
  5859. addrs.push(domUtils.getNodeIndex(ci, ignoreTxt));
  5860. }
  5861. var firstIndex = 0;
  5862. if (ignoreTxt) {
  5863. if (node.nodeType == 3) {
  5864. var tmpNode = node.previousSibling;
  5865. while (tmpNode && tmpNode.nodeType == 3) {
  5866. firstIndex += tmpNode.nodeValue.replace(fillCharReg, '').length;
  5867. tmpNode = tmpNode.previousSibling;
  5868. }
  5869. firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  5870. } else {
  5871. node = node.childNodes[isStart ? me.startOffset : me.endOffset];
  5872. if (node) {
  5873. firstIndex = domUtils.getNodeIndex(node, ignoreTxt);
  5874. } else {
  5875. node = isStart ? me.startContainer : me.endContainer;
  5876. var first = node.firstChild;
  5877. while (first) {
  5878. if (domUtils.isFillChar(first)) {
  5879. first = first.nextSibling;
  5880. continue;
  5881. }
  5882. firstIndex++;
  5883. if (first.nodeType == 3) {
  5884. while (first && first.nodeType == 3) {
  5885. first = first.nextSibling;
  5886. }
  5887. } else {
  5888. first = first.nextSibling;
  5889. }
  5890. }
  5891. }
  5892. }
  5893. } else {
  5894. firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
  5895. }
  5896. if (firstIndex < 0) {
  5897. firstIndex = 0;
  5898. }
  5899. addrs.push(firstIndex);
  5900. return addrs;
  5901. }
  5902. addr.startAddress = getAddress(true);
  5903. if (!ignoreEnd) {
  5904. addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
  5905. }
  5906. return addr;
  5907. },
  5908. /**
  5909. * 保存
  5910. * @method createAddress
  5911. * @private
  5912. * @return { Boolean } 返回开始和结束的位置
  5913. * @example
  5914. * ```html
  5915. * <body>
  5916. * <p>
  5917. * aaaa
  5918. * <em>
  5919. * <!-- 选区开始 -->
  5920. * bbbb
  5921. * <!-- 选区结束 -->
  5922. * </em>
  5923. * </p>
  5924. *
  5925. * <script>
  5926. * var range = editor.selection.getRange();
  5927. * range.moveToAddress({startAddress:[0,1,0,0],endAddress:[0,1,0,4]});
  5928. * range.select();
  5929. * //output: 'bbbb'
  5930. * console.log(editor.selection.getText());
  5931. * </script>
  5932. * </body>
  5933. * ```
  5934. */
  5935. moveToAddress: function (addr, ignoreEnd) {
  5936. var me = this;
  5937. function getNode(address, isStart) {
  5938. var tmpNode = me.document.body,
  5939. parentNode, offset;
  5940. for (var i = 0, ci, l = address.length; i < l; i++) {
  5941. ci = address[i];
  5942. parentNode = tmpNode;
  5943. tmpNode = tmpNode.childNodes[ci];
  5944. if (!tmpNode) {
  5945. offset = ci;
  5946. break;
  5947. }
  5948. }
  5949. if (isStart) {
  5950. if (tmpNode) {
  5951. me.setStartBefore(tmpNode)
  5952. } else {
  5953. me.setStart(parentNode, offset)
  5954. }
  5955. } else {
  5956. if (tmpNode) {
  5957. me.setEndBefore(tmpNode)
  5958. } else {
  5959. me.setEnd(parentNode, offset)
  5960. }
  5961. }
  5962. }
  5963. getNode(addr.startAddress, true);
  5964. !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
  5965. return me;
  5966. },
  5967. /**
  5968. * 判断给定的Range对象是否和当前Range对象表示的是同一个选区
  5969. * @method equals
  5970. * @param { UE.dom.Range } 需要判断的Range对象
  5971. * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false
  5972. */
  5973. equals: function (rng) {
  5974. for (var p in this) {
  5975. if (this.hasOwnProperty(p)) {
  5976. if (this[p] !== rng[p])
  5977. return false
  5978. }
  5979. }
  5980. return true;
  5981. },
  5982. /**
  5983. * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  5984. * 作为其参数。
  5985. * @method traversal
  5986. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  5987. * @return { UE.dom.Range } 当前range对象
  5988. * @example
  5989. * ```html
  5990. *
  5991. * <body>
  5992. *
  5993. * <!-- 选区开始 -->
  5994. * <span></span>
  5995. * <a></a>
  5996. * <!-- 选区结束 -->
  5997. * </body>
  5998. *
  5999. * <script>
  6000. *
  6001. * //output: <span></span><a></a>
  6002. * console.log( range.cloneContents() );
  6003. *
  6004. * range.traversal( function ( node ) {
  6005. *
  6006. * if ( node.nodeType === 1 ) {
  6007. * node.className = "test";
  6008. * }
  6009. *
  6010. * } );
  6011. *
  6012. * //output: <span class="test"></span><a class="test"></a>
  6013. * console.log( range.cloneContents() );
  6014. *
  6015. * </script>
  6016. * ```
  6017. */
  6018. /**
  6019. * 遍历range内的节点。
  6020. * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  6021. * 作为其参数。
  6022. * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
  6023. * 发doFn函数的执行
  6024. * @method traversal
  6025. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  6026. * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
  6027. * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
  6028. * 会触发doFn。
  6029. * @return { UE.dom.Range } 当前range对象
  6030. * @see UE.dom.Range:traversal(Function)
  6031. * @example
  6032. * ```html
  6033. *
  6034. * <body>
  6035. *
  6036. * <!-- 选区开始 -->
  6037. * <span></span>
  6038. * <a></a>
  6039. * <!-- 选区结束 -->
  6040. * </body>
  6041. *
  6042. * <script>
  6043. *
  6044. * //output: <span></span><a></a>
  6045. * console.log( range.cloneContents() );
  6046. *
  6047. * range.traversal( function ( node ) {
  6048. *
  6049. * node.className = "test";
  6050. *
  6051. * }, function ( node ) {
  6052. * return node.nodeType === 1;
  6053. * } );
  6054. *
  6055. * //output: <span class="test"></span><a class="test"></a>
  6056. * console.log( range.cloneContents() );
  6057. *
  6058. * </script>
  6059. * ```
  6060. */
  6061. traversal: function (doFn, filterFn) {
  6062. if (this.collapsed)
  6063. return this;
  6064. var bookmark = this.createBookmark(),
  6065. end = bookmark.end,
  6066. current = domUtils.getNextDomNode(bookmark.start, false, filterFn);
  6067. while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
  6068. var tmpNode = domUtils.getNextDomNode(current, false, filterFn);
  6069. doFn(current);
  6070. current = tmpNode;
  6071. }
  6072. return this.moveToBookmark(bookmark);
  6073. }
  6074. };
  6075. })();
  6076. // core/Selection.js
  6077. /**
  6078. * 选集
  6079. * @file
  6080. * @module UE.dom
  6081. * @class Selection
  6082. * @since 1.2.6.1
  6083. */
  6084. /**
  6085. * 选区集合
  6086. * @unfile
  6087. * @module UE.dom
  6088. * @class Selection
  6089. */
  6090. (function () {
  6091. function getBoundaryInformation(range, start) {
  6092. var getIndex = domUtils.getNodeIndex;
  6093. range = range.duplicate();
  6094. range.collapse(start);
  6095. var parent = range.parentElement();
  6096. //如果节点里没有子节点,直接退出
  6097. if (!parent.hasChildNodes()) {
  6098. return {container: parent, offset: 0};
  6099. }
  6100. var siblings = parent.children,
  6101. child,
  6102. testRange = range.duplicate(),
  6103. startIndex = 0, endIndex = siblings.length - 1, index = -1,
  6104. distance;
  6105. while (startIndex <= endIndex) {
  6106. index = Math.floor((startIndex + endIndex) / 2);
  6107. child = siblings[index];
  6108. testRange.moveToElementText(child);
  6109. var position = testRange.compareEndPoints('StartToStart', range);
  6110. if (position > 0) {
  6111. endIndex = index - 1;
  6112. } else if (position < 0) {
  6113. startIndex = index + 1;
  6114. } else {
  6115. //trace:1043
  6116. return {container: parent, offset: getIndex(child)};
  6117. }
  6118. }
  6119. if (index == -1) {
  6120. testRange.moveToElementText(parent);
  6121. testRange.setEndPoint('StartToStart', range);
  6122. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length;
  6123. siblings = parent.childNodes;
  6124. if (!distance) {
  6125. child = siblings[siblings.length - 1];
  6126. return {container: child, offset: child.nodeValue.length};
  6127. }
  6128. var i = siblings.length;
  6129. while (distance > 0) {
  6130. distance -= siblings[--i].nodeValue.length;
  6131. }
  6132. return {container: siblings[i], offset: -distance};
  6133. }
  6134. testRange.collapse(position > 0);
  6135. testRange.setEndPoint(position > 0 ? 'StartToStart' : 'EndToStart', range);
  6136. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length;
  6137. if (!distance) {
  6138. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
  6139. {container: parent, offset: getIndex(child) + (position > 0 ? 0 : 1)} :
  6140. {container: child, offset: position > 0 ? 0 : child.childNodes.length}
  6141. }
  6142. while (distance > 0) {
  6143. try {
  6144. var pre = child;
  6145. child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
  6146. distance -= child.nodeValue.length;
  6147. } catch (e) {
  6148. return {container: parent, offset: getIndex(pre)};
  6149. }
  6150. }
  6151. return {container: child, offset: position > 0 ? -distance : child.nodeValue.length + distance}
  6152. }
  6153. /**
  6154. * 将ieRange转换为Range对象
  6155. * @param {Range} ieRange ieRange对象
  6156. * @param {Range} range Range对象
  6157. * @return {Range} range 返回转换后的Range对象
  6158. */
  6159. function transformIERangeToRange(ieRange, range) {
  6160. if (ieRange.item) {
  6161. range.selectNode(ieRange.item(0));
  6162. } else {
  6163. var bi = getBoundaryInformation(ieRange, true);
  6164. range.setStart(bi.container, bi.offset);
  6165. if (ieRange.compareEndPoints('StartToEnd', ieRange) != 0) {
  6166. bi = getBoundaryInformation(ieRange, false);
  6167. range.setEnd(bi.container, bi.offset);
  6168. }
  6169. }
  6170. return range;
  6171. }
  6172. /**
  6173. * 获得ieRange
  6174. * @param {Selection} sel Selection对象
  6175. * @return {ieRange} 得到ieRange
  6176. */
  6177. function _getIERange(sel) {
  6178. var ieRange;
  6179. //ie下有可能报错
  6180. try {
  6181. ieRange = sel.getNative().createRange();
  6182. } catch (e) {
  6183. return null;
  6184. }
  6185. var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
  6186. if ((el.ownerDocument || el) === sel.document) {
  6187. return ieRange;
  6188. }
  6189. return null;
  6190. }
  6191. var Selection = dom.Selection = function (doc) {
  6192. var me = this, iframe;
  6193. me.document = doc;
  6194. if (browser.ie9below) {
  6195. iframe = domUtils.getWindow(doc).frameElement;
  6196. domUtils.on(iframe, 'beforedeactivate', function () {
  6197. me._bakIERange = me.getIERange();
  6198. });
  6199. domUtils.on(iframe, 'activate', function () {
  6200. try {
  6201. if (!_getIERange(me) && me._bakIERange) {
  6202. me._bakIERange.select();
  6203. }
  6204. } catch (ex) {
  6205. }
  6206. me._bakIERange = null;
  6207. });
  6208. }
  6209. iframe = doc = null;
  6210. };
  6211. Selection.prototype = {
  6212. rangeInBody: function (rng, txtRange) {
  6213. var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  6214. return node === this.document.body || domUtils.inDoc(node, this.document);
  6215. },
  6216. /**
  6217. * 获取原生seleciton对象
  6218. * @method getNative
  6219. * @return { Object } 获得selection对象
  6220. * @example
  6221. * ```javascript
  6222. * editor.selection.getNative();
  6223. * ```
  6224. */
  6225. getNative: function () {
  6226. var doc = this.document;
  6227. try {
  6228. return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow(doc).getSelection();
  6229. } catch (e) {
  6230. return null;
  6231. }
  6232. },
  6233. /**
  6234. * 获得ieRange
  6235. * @method getIERange
  6236. * @return { Object } 返回ie原生的Range
  6237. * @example
  6238. * ```javascript
  6239. * editor.selection.getIERange();
  6240. * ```
  6241. */
  6242. getIERange: function () {
  6243. var ieRange = _getIERange(this);
  6244. if (!ieRange) {
  6245. if (this._bakIERange) {
  6246. return this._bakIERange;
  6247. }
  6248. }
  6249. return ieRange;
  6250. },
  6251. /**
  6252. * 缓存当前选区的range和选区的开始节点
  6253. * @method cache
  6254. */
  6255. cache: function () {
  6256. this.clear();
  6257. this._cachedRange = this.getRange();
  6258. this._cachedStartElement = this.getStart();
  6259. this._cachedStartElementPath = this.getStartElementPath();
  6260. },
  6261. /**
  6262. * 获取选区开始位置的父节点到body
  6263. * @method getStartElementPath
  6264. * @return { Array } 返回父节点集合
  6265. * @example
  6266. * ```javascript
  6267. * editor.selection.getStartElementPath();
  6268. * ```
  6269. */
  6270. getStartElementPath: function () {
  6271. if (this._cachedStartElementPath) {
  6272. return this._cachedStartElementPath;
  6273. }
  6274. var start = this.getStart();
  6275. if (start) {
  6276. return domUtils.findParents(start, true, null, true)
  6277. }
  6278. return [];
  6279. },
  6280. /**
  6281. * 清空缓存
  6282. * @method clear
  6283. */
  6284. clear: function () {
  6285. this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
  6286. },
  6287. /**
  6288. * 编辑器是否得到了选区
  6289. * @method isFocus
  6290. */
  6291. isFocus: function () {
  6292. try {
  6293. if (browser.ie9below) {
  6294. var nativeRange = _getIERange(this);
  6295. return !!(nativeRange && this.rangeInBody(nativeRange));
  6296. } else {
  6297. return !!this.getNative().rangeCount;
  6298. }
  6299. } catch (e) {
  6300. return false;
  6301. }
  6302. },
  6303. /**
  6304. * 获取选区对应的Range
  6305. * @method getRange
  6306. * @return { Object } 得到Range对象
  6307. * @example
  6308. * ```javascript
  6309. * editor.selection.getRange();
  6310. * ```
  6311. */
  6312. getRange: function () {
  6313. var me = this;
  6314. function optimze(range) {
  6315. var child = me.document.body.firstChild,
  6316. collapsed = range.collapsed;
  6317. while (child && child.firstChild) {
  6318. range.setStart(child, 0);
  6319. child = child.firstChild;
  6320. }
  6321. if (!range.startContainer) {
  6322. range.setStart(me.document.body, 0)
  6323. }
  6324. if (collapsed) {
  6325. range.collapse(true);
  6326. }
  6327. }
  6328. if (me._cachedRange != null) {
  6329. return this._cachedRange;
  6330. }
  6331. var range = new baidu.editor.dom.Range(me.document);
  6332. if (browser.ie9below) {
  6333. var nativeRange = me.getIERange();
  6334. if (nativeRange) {
  6335. //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
  6336. try {
  6337. transformIERangeToRange(nativeRange, range);
  6338. } catch (e) {
  6339. optimze(range);
  6340. }
  6341. } else {
  6342. optimze(range);
  6343. }
  6344. } else {
  6345. var sel = me.getNative();
  6346. if (sel && sel.rangeCount) {
  6347. var firstRange = sel.getRangeAt(0);
  6348. var lastRange = sel.getRangeAt(sel.rangeCount - 1);
  6349. range.setStart(firstRange.startContainer, firstRange.startOffset).setEnd(lastRange.endContainer, lastRange.endOffset);
  6350. if (range.collapsed && domUtils.isBody(range.startContainer) && !range.startOffset) {
  6351. optimze(range);
  6352. }
  6353. } else {
  6354. //trace:1734 有可能已经不在dom树上了,标识的节点
  6355. if (this._bakRange && domUtils.inDoc(this._bakRange.startContainer, this.document)) {
  6356. return this._bakRange;
  6357. }
  6358. optimze(range);
  6359. }
  6360. }
  6361. return this._bakRange = range;
  6362. },
  6363. /**
  6364. * 获取开始元素,用于状态反射
  6365. * @method getStart
  6366. * @return { Element } 获得开始元素
  6367. * @example
  6368. * ```javascript
  6369. * editor.selection.getStart();
  6370. * ```
  6371. */
  6372. getStart: function () {
  6373. if (this._cachedStartElement) {
  6374. return this._cachedStartElement;
  6375. }
  6376. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  6377. tmpRange,
  6378. start, tmp, parent;
  6379. if (browser.ie9below) {
  6380. if (!range) {
  6381. //todo 给第一个值可能会有问题
  6382. return this.document.body.firstChild;
  6383. }
  6384. //control元素
  6385. if (range.item) {
  6386. return range.item(0);
  6387. }
  6388. tmpRange = range.duplicate();
  6389. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  6390. tmpRange.text.length > 0 && tmpRange.moveStart('character', 1);
  6391. tmpRange.collapse(1);
  6392. start = tmpRange.parentElement();
  6393. parent = tmp = range.parentElement();
  6394. while (tmp = tmp.parentNode) {
  6395. if (tmp == start) {
  6396. start = parent;
  6397. break;
  6398. }
  6399. }
  6400. } else {
  6401. range.shrinkBoundary();
  6402. start = range.startContainer;
  6403. if (start.nodeType == 1 && start.hasChildNodes()) {
  6404. start = start.childNodes[Math.min(start.childNodes.length - 1, range.startOffset)];
  6405. }
  6406. if (start.nodeType == 3) {
  6407. return start.parentNode;
  6408. }
  6409. }
  6410. return start;
  6411. },
  6412. /**
  6413. * 得到选区中的文本
  6414. * @method getText
  6415. * @return { String } 选区中包含的文本
  6416. * @example
  6417. * ```javascript
  6418. * editor.selection.getText();
  6419. * ```
  6420. */
  6421. getText: function () {
  6422. var nativeSel, nativeRange;
  6423. if (this.isFocus() && (nativeSel = this.getNative())) {
  6424. nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt(0);
  6425. return browser.ie9below ? nativeRange.text : nativeRange.toString();
  6426. }
  6427. return '';
  6428. },
  6429. /**
  6430. * 清除选区
  6431. * @method clearRange
  6432. * @example
  6433. * ```javascript
  6434. * editor.selection.clearRange();
  6435. * ```
  6436. */
  6437. clearRange: function () {
  6438. this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
  6439. }
  6440. };
  6441. })();
  6442. // core/Editor.js
  6443. /**
  6444. * 编辑器主类,包含编辑器提供的大部分公用接口
  6445. * @file
  6446. * @module UE
  6447. * @class Editor
  6448. * @since 1.2.6.1
  6449. */
  6450. /**
  6451. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  6452. * @unfile
  6453. * @module UE
  6454. */
  6455. /**
  6456. * UEditor的核心类,为用户提供与编辑器交互的接口。
  6457. * @unfile
  6458. * @module UE
  6459. * @class Editor
  6460. */
  6461. (function () {
  6462. var uid = 0, _selectionChangeTimer;
  6463. /**
  6464. * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
  6465. * @private
  6466. * @method setValue
  6467. * @param { UE.Editor } editor 编辑器事例
  6468. */
  6469. function setValue(form, editor) {
  6470. var textarea;
  6471. if (editor.textarea) {
  6472. if (utils.isString(editor.textarea)) {
  6473. for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
  6474. if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
  6475. textarea = ti;
  6476. break;
  6477. }
  6478. }
  6479. } else {
  6480. textarea = editor.textarea;
  6481. }
  6482. }
  6483. if (!textarea) {
  6484. form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
  6485. 'name': editor.options.textarea,
  6486. 'id': 'ueditor_textarea_' + editor.options.textarea,
  6487. 'style': "display:none"
  6488. }));
  6489. //不要产生多个textarea
  6490. editor.textarea = textarea;
  6491. }
  6492. !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea);
  6493. textarea.value = editor.hasContents() ?
  6494. (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
  6495. ''
  6496. }
  6497. function loadPlugins(me) {
  6498. //初始化插件
  6499. for (var pi in UE.plugins) {
  6500. UE.plugins[pi].call(me);
  6501. }
  6502. }
  6503. function checkCurLang(I18N) {
  6504. for (var lang in I18N) {
  6505. return lang
  6506. }
  6507. }
  6508. function langReadied(me) {
  6509. me.langIsReady = true;
  6510. me.fireEvent("langReady");
  6511. }
  6512. /**
  6513. * 编辑器准备就绪后会触发该事件
  6514. * @module UE
  6515. * @class Editor
  6516. * @event ready
  6517. * @remind render方法执行完成之后,会触发该事件
  6518. * @remind
  6519. * @example
  6520. * ```javascript
  6521. * editor.addListener( 'ready', function( editor ) {
  6522. * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
  6523. * } );
  6524. * ```
  6525. */
  6526. /**
  6527. * 执行destroy方法,会触发该事件
  6528. * @module UE
  6529. * @class Editor
  6530. * @event destroy
  6531. * @see UE.Editor:destroy()
  6532. */
  6533. /**
  6534. * 执行reset方法,会触发该事件
  6535. * @module UE
  6536. * @class Editor
  6537. * @event reset
  6538. * @see UE.Editor:reset()
  6539. */
  6540. /**
  6541. * 执行focus方法,会触发该事件
  6542. * @module UE
  6543. * @class Editor
  6544. * @event focus
  6545. * @see UE.Editor:focus(Boolean)
  6546. */
  6547. /**
  6548. * 语言加载完成会触发该事件
  6549. * @module UE
  6550. * @class Editor
  6551. * @event langReady
  6552. */
  6553. /**
  6554. * 运行命令之后会触发该命令
  6555. * @module UE
  6556. * @class Editor
  6557. * @event beforeExecCommand
  6558. */
  6559. /**
  6560. * 运行命令之后会触发该命令
  6561. * @module UE
  6562. * @class Editor
  6563. * @event afterExecCommand
  6564. */
  6565. /**
  6566. * 运行命令之前会触发该命令
  6567. * @module UE
  6568. * @class Editor
  6569. * @event firstBeforeExecCommand
  6570. */
  6571. /**
  6572. * 在getContent方法执行之前会触发该事件
  6573. * @module UE
  6574. * @class Editor
  6575. * @event beforeGetContent
  6576. * @see UE.Editor:getContent()
  6577. */
  6578. /**
  6579. * 在getContent方法执行之后会触发该事件
  6580. * @module UE
  6581. * @class Editor
  6582. * @event afterGetContent
  6583. * @see UE.Editor:getContent()
  6584. */
  6585. /**
  6586. * 在getAllHtml方法执行时会触发该事件
  6587. * @module UE
  6588. * @class Editor
  6589. * @event getAllHtml
  6590. * @see UE.Editor:getAllHtml()
  6591. */
  6592. /**
  6593. * 在setContent方法执行之前会触发该事件
  6594. * @module UE
  6595. * @class Editor
  6596. * @event beforeSetContent
  6597. * @see UE.Editor:setContent(String)
  6598. */
  6599. /**
  6600. * 在setContent方法执行之后会触发该事件
  6601. * @module UE
  6602. * @class Editor
  6603. * @event afterSetContent
  6604. * @see UE.Editor:setContent(String)
  6605. */
  6606. /**
  6607. * 每当编辑器内部选区发生改变时,将触发该事件
  6608. * @event selectionchange
  6609. * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
  6610. * @example
  6611. * ```javascript
  6612. * editor.addListener( 'selectionchange', function( editor ) {
  6613. * console.log('选区发生改变');
  6614. * }
  6615. */
  6616. /**
  6617. * 在所有selectionchange的监听函数执行之前,会触发该事件
  6618. * @module UE
  6619. * @class Editor
  6620. * @event beforeSelectionChange
  6621. * @see UE.Editor:selectionchange
  6622. */
  6623. /**
  6624. * 在所有selectionchange的监听函数执行完之后,会触发该事件
  6625. * @module UE
  6626. * @class Editor
  6627. * @event afterSelectionChange
  6628. * @see UE.Editor:selectionchange
  6629. */
  6630. /**
  6631. * 编辑器内容发生改变时会触发该事件
  6632. * @module UE
  6633. * @class Editor
  6634. * @event contentChange
  6635. */
  6636. /**
  6637. * 以默认参数构建一个编辑器实例
  6638. * @constructor
  6639. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  6640. * @example
  6641. * ```javascript
  6642. * var editor = new UE.Editor();
  6643. * editor.execCommand('blod');
  6644. * ```
  6645. * @see UE.Config
  6646. */
  6647. /**
  6648. * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
  6649. * @constructor
  6650. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  6651. * @param { Object } setting 创建编辑器的参数
  6652. * @example
  6653. * ```javascript
  6654. * var editor = new UE.Editor();
  6655. * editor.execCommand('blod');
  6656. * ```
  6657. * @see UE.Config
  6658. */
  6659. var Editor = UE.Editor = function (options) {
  6660. var me = this;
  6661. me.uid = uid++;
  6662. EventBase.call(me);
  6663. me.commands = {};
  6664. me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true);
  6665. me.shortcutkeys = {};
  6666. me.inputRules = [];
  6667. me.outputRules = [];
  6668. //设置默认的常用属性
  6669. me.setOpt(Editor.defaultOptions(me));
  6670. /* 尝试异步加载后台配置 */
  6671. me.loadServerConfig();
  6672. if (!utils.isEmptyObject(UE.I18N)) {
  6673. //修改默认的语言类型
  6674. me.options.lang = checkCurLang(UE.I18N);
  6675. UE.plugin.load(me);
  6676. langReadied(me);
  6677. } else {
  6678. utils.loadFile(document, {
  6679. src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
  6680. tag: "script",
  6681. type: "text/javascript",
  6682. defer: "defer"
  6683. }, function () {
  6684. UE.plugin.load(me);
  6685. langReadied(me);
  6686. });
  6687. }
  6688. UE.instants['ueditorInstant' + me.uid] = me;
  6689. };
  6690. Editor.prototype = {
  6691. registerCommand: function (name, obj) {
  6692. this.commands[name] = obj;
  6693. },
  6694. /**
  6695. * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
  6696. * @method ready
  6697. * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
  6698. * 立即触发该回调。
  6699. * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
  6700. * @example
  6701. * ```javascript
  6702. * editor.ready( function( editor ) {
  6703. * editor.setContent('初始化完毕');
  6704. * } );
  6705. * ```
  6706. * @see UE.Editor.event:ready
  6707. */
  6708. ready: function (fn) {
  6709. var me = this;
  6710. if (fn) {
  6711. me.isReady ? fn.apply(me) : me.addListener('ready', fn);
  6712. }
  6713. },
  6714. /**
  6715. * 该方法是提供给插件里面使用,设置配置项默认值
  6716. * @method setOpt
  6717. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  6718. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  6719. * @param { String } key 编辑器的可接受的选项名称
  6720. * @param { * } val 该选项可接受的值
  6721. * @example
  6722. * ```javascript
  6723. * editor.setOpt( 'initContent', '欢迎使用编辑器' );
  6724. * ```
  6725. */
  6726. /**
  6727. * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
  6728. * @method setOpt
  6729. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  6730. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  6731. * @param { Object } options 将要设置的选项的键值对对象
  6732. * @example
  6733. * ```javascript
  6734. * editor.setOpt( {
  6735. * 'initContent': '欢迎使用编辑器'
  6736. * } );
  6737. * ```
  6738. */
  6739. setOpt: function (key, val) {
  6740. var obj = {};
  6741. if (utils.isString(key)) {
  6742. obj[key] = val
  6743. } else {
  6744. obj = key;
  6745. }
  6746. utils.extend(this.options, obj, true);
  6747. },
  6748. getOpt: function (key) {
  6749. return this.options[key]
  6750. },
  6751. /**
  6752. * 销毁编辑器实例,使用textarea代替
  6753. * @method destroy
  6754. * @example
  6755. * ```javascript
  6756. * editor.destroy();
  6757. * ```
  6758. */
  6759. destroy: function () {
  6760. var me = this;
  6761. me.fireEvent('destroy');
  6762. var container = me.container.parentNode;
  6763. var textarea = me.textarea;
  6764. if (!textarea) {
  6765. textarea = document.createElement('textarea');
  6766. container.parentNode.insertBefore(textarea, container);
  6767. } else {
  6768. textarea.style.display = ''
  6769. }
  6770. textarea.style.width = me.iframe.offsetWidth + 'px';
  6771. textarea.style.height = me.iframe.offsetHeight + 'px';
  6772. textarea.value = me.getContent();
  6773. textarea.id = me.key;
  6774. container.innerHTML = '';
  6775. domUtils.remove(container);
  6776. var key = me.key;
  6777. //trace:2004
  6778. for (var p in me) {
  6779. if (me.hasOwnProperty(p)) {
  6780. delete this[p];
  6781. }
  6782. }
  6783. UE.delEditor(key);
  6784. },
  6785. /**
  6786. * 渲染编辑器的DOM到指定容器
  6787. * @method render
  6788. * @param { String } containerId 指定一个容器ID
  6789. * @remind 执行该方法,会触发ready事件
  6790. * @warning 必须且只能调用一次
  6791. */
  6792. /**
  6793. * 渲染编辑器的DOM到指定容器
  6794. * @method render
  6795. * @param { Element } containerDom 直接指定容器对象
  6796. * @remind 执行该方法,会触发ready事件
  6797. * @warning 必须且只能调用一次
  6798. */
  6799. render: function (container) {
  6800. var me = this,
  6801. options = me.options,
  6802. getStyleValue = function (attr) {
  6803. return parseInt(domUtils.getComputedStyle(container, attr));
  6804. };
  6805. if (utils.isString(container)) {
  6806. container = document.getElementById(container);
  6807. }
  6808. if (container) {
  6809. if (options.initialFrameWidth) {
  6810. options.minFrameWidth = options.initialFrameWidth
  6811. } else {
  6812. options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
  6813. }
  6814. if (options.initialFrameHeight) {
  6815. options.minFrameHeight = options.initialFrameHeight
  6816. } else {
  6817. options.initialFrameHeight = options.minFrameHeight = container.offsetHeight;
  6818. }
  6819. container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth -
  6820. getStyleValue("padding-left") - getStyleValue("padding-right") + 'px';
  6821. container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight -
  6822. getStyleValue("padding-top") - getStyleValue("padding-bottom") + 'px';
  6823. container.style.zIndex = options.zIndex;
  6824. var html = (ie && browser.version < 9 ? '' : '<!DOCTYPE html>') +
  6825. '<html xmlns=\'http://www.w3.org/1999/xhtml\' class=\'view\' ><head>' +
  6826. '<style type=\'text/css\'>' +
  6827. //设置四周的留边
  6828. '.view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n' +
  6829. //设置默认字体和字号
  6830. //font-family不能呢随便改,在safari下fillchar会有解析问题
  6831. 'body{margin:8px;font-family:sans-serif;font-size:16px;}' +
  6832. //设置段落间距
  6833. 'p{margin:5px 0;}' +
  6834. //设置图片自适应
  6835. 'img{width:100%;}</style>' +
  6836. (options.iframeCssUrl ? '<link rel=\'stylesheet\' type=\'text/css\' href=\'' + utils.unhtml(options.iframeCssUrl) + '\'/>' : '') +
  6837. (options.initialStyle ? '<style>' + options.initialStyle + '</style>' : '') +
  6838. '</head><body class=\'view\' ></body>' +
  6839. '<script type=\'text/javascript\' ' + (ie ? 'defer=\'defer\'' : '') + ' id=\'_initialScript\'>' +
  6840. 'setTimeout(function(){editor = window.parent.UE.instants[\'ueditorInstant' + me.uid + '\'];editor._setup(document);},0);' +
  6841. 'var _tmpScript = document.getElementById(\'_initialScript\');_tmpScript.parentNode.removeChild(_tmpScript);</script></html>';
  6842. container.appendChild(domUtils.createElement(document, 'iframe', {
  6843. id: 'ueditor_' + me.uid,
  6844. width: "100%",
  6845. height: "100%",
  6846. frameborder: "0",
  6847. //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
  6848. // scrolling :'no',
  6849. src: 'javascript:void(function(){document.open();' + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : '') +
  6850. 'document.write("' + html + '");document.close();}())'
  6851. }));
  6852. container.style.overflow = 'hidden';
  6853. //解决如果是给定的百分比,会导致高度算不对的问题
  6854. setTimeout(function () {
  6855. if (/%$/.test(options.initialFrameWidth)) {
  6856. options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
  6857. //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
  6858. // container.style.width = options.initialFrameWidth + 'px';
  6859. }
  6860. if (/%$/.test(options.initialFrameHeight)) {
  6861. options.minFrameHeight = options.initialFrameHeight = container.offsetHeight;
  6862. container.style.height = options.initialFrameHeight + 'px';
  6863. }
  6864. })
  6865. }
  6866. },
  6867. /**
  6868. * 编辑器初始化
  6869. * @method _setup
  6870. * @private
  6871. * @param { Element } doc 编辑器Iframe中的文档对象
  6872. */
  6873. _setup: function (doc) {
  6874. var me = this,
  6875. options = me.options;
  6876. if (ie) {
  6877. doc.body.disabled = true;
  6878. doc.body.contentEditable = true;
  6879. doc.body.disabled = false;
  6880. } else {
  6881. doc.body.contentEditable = true;
  6882. }
  6883. doc.body.spellcheck = false;
  6884. me.document = doc;
  6885. me.window = doc.defaultView || doc.parentWindow;
  6886. me.iframe = me.window.frameElement;
  6887. me.body = doc.body;
  6888. me.selection = new dom.Selection(doc);
  6889. //gecko初始化就能得到range,无法判断isFocus了
  6890. var geckoSel;
  6891. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  6892. geckoSel.removeAllRanges();
  6893. }
  6894. this._initEvents();
  6895. //为form提交提供一个隐藏的textarea
  6896. for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) {
  6897. if (form.tagName == 'FORM') {
  6898. me.form = form;
  6899. if (me.options.autoSyncData) {
  6900. domUtils.on(me.window, 'blur', function () {
  6901. setValue(form, me);
  6902. });
  6903. } else {
  6904. domUtils.on(form, 'submit', function () {
  6905. setValue(this, me);
  6906. });
  6907. }
  6908. break;
  6909. }
  6910. }
  6911. if (options.initialContent) {
  6912. if (options.autoClearinitialContent) {
  6913. var oldExecCommand = me.execCommand;
  6914. me.execCommand = function () {
  6915. me.fireEvent('firstBeforeExecCommand');
  6916. return oldExecCommand.apply(me, arguments);
  6917. };
  6918. this._setDefaultContent(options.initialContent);
  6919. } else
  6920. this.setContent(options.initialContent, false, true);
  6921. }
  6922. //编辑器不能为空内容
  6923. if (domUtils.isEmptyNode(me.body)) {
  6924. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  6925. }
  6926. //如果要求focus, 就把光标定位到内容开始
  6927. if (options.focus) {
  6928. setTimeout(function () {
  6929. me.focus(me.options.focusInEnd);
  6930. //如果自动清除开着,就不需要做selectionchange;
  6931. !me.options.autoClearinitialContent && me._selectionChange();
  6932. }, 0);
  6933. }
  6934. if (!me.container) {
  6935. me.container = this.iframe.parentNode;
  6936. }
  6937. if (options.fullscreen && me.ui) {
  6938. me.ui.setFullScreen(true);
  6939. }
  6940. try {
  6941. me.document.execCommand('2D-position', false, false);
  6942. } catch (e) {
  6943. }
  6944. try {
  6945. me.document.execCommand('enableInlineTableEditing', false, false);
  6946. } catch (e) {
  6947. }
  6948. try {
  6949. me.document.execCommand('enableObjectResizing', false, false);
  6950. } catch (e) {
  6951. }
  6952. //挂接快捷键
  6953. me._bindshortcutKeys();
  6954. me.isReady = 1;
  6955. me.fireEvent('ready');
  6956. options.onready && options.onready.call(me);
  6957. if (!browser.ie9below) {
  6958. domUtils.on(me.window, ['blur', 'focus'], function (e) {
  6959. //chrome下会出现alt+tab切换时,导致选区位置不对
  6960. if (e.type == 'blur') {
  6961. me._bakRange = me.selection.getRange();
  6962. try {
  6963. me._bakNativeRange = me.selection.getNative().getRangeAt(0);
  6964. me.selection.getNative().removeAllRanges();
  6965. } catch (e) {
  6966. me._bakNativeRange = null;
  6967. }
  6968. } else {
  6969. try {
  6970. me._bakRange && me._bakRange.select();
  6971. } catch (e) {
  6972. }
  6973. }
  6974. });
  6975. }
  6976. //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
  6977. if (browser.gecko && browser.version <= 10902) {
  6978. //修复ff3.6初始化进来,不能点击获得焦点
  6979. me.body.contentEditable = false;
  6980. setTimeout(function () {
  6981. me.body.contentEditable = true;
  6982. }, 100);
  6983. setInterval(function () {
  6984. me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
  6985. }, 100)
  6986. }
  6987. !options.isShow && me.setHide();
  6988. options.readonly && me.setDisabled();
  6989. },
  6990. /**
  6991. * 同步数据到编辑器所在的form
  6992. * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
  6993. * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
  6994. * @method sync
  6995. * @example
  6996. * ```javascript
  6997. * editor.sync();
  6998. * form.sumbit(); //form变量已经指向了form元素
  6999. * ```
  7000. */
  7001. /**
  7002. * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
  7003. * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
  7004. * @method sync
  7005. * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  7006. */
  7007. sync: function (formId) {
  7008. var me = this,
  7009. form = formId ? document.getElementById(formId) :
  7010. domUtils.findParent(me.iframe.parentNode, function (node) {
  7011. return node.tagName == 'FORM'
  7012. }, true);
  7013. form && setValue(form, me);
  7014. },
  7015. /**
  7016. * 设置编辑器高度
  7017. * @method setHeight
  7018. * @remind 当配置项autoHeightEnabled为真时,该方法无效
  7019. * @param { Number } number 设置的高度值,纯数值,不带单位
  7020. * @example
  7021. * ```javascript
  7022. * editor.setHeight(number);
  7023. * ```
  7024. */
  7025. setHeight: function (height, notSetHeight) {
  7026. if (height !== parseInt(this.iframe.parentNode.style.height)) {
  7027. this.iframe.parentNode.style.height = height + 'px';
  7028. }
  7029. !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height);
  7030. this.body.style.height = height + 'px';
  7031. !notSetHeight && this.trigger('setHeight')
  7032. },
  7033. /**
  7034. * 为编辑器的编辑命令提供快捷键
  7035. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  7036. * @method addshortcutkey
  7037. * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
  7038. * @example
  7039. * ```javascript
  7040. * editor.addshortcutkey({
  7041. * "Bold" : "ctrl+66",//^B
  7042. * "Italic" : "ctrl+73", //^I
  7043. * });
  7044. * ```
  7045. */
  7046. /**
  7047. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  7048. * @method addshortcutkey
  7049. * @param { String } cmd 触发快捷键时,响应的命令
  7050. * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
  7051. * @example
  7052. * ```javascript
  7053. * editor.addshortcutkey("Underline", "ctrl+85"); //^U
  7054. * ```
  7055. */
  7056. addshortcutkey: function (cmd, keys) {
  7057. var obj = {};
  7058. if (keys) {
  7059. obj[cmd] = keys
  7060. } else {
  7061. obj = cmd;
  7062. }
  7063. utils.extend(this.shortcutkeys, obj)
  7064. },
  7065. /**
  7066. * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
  7067. * @method _bindshortcutKeys
  7068. * @private
  7069. */
  7070. _bindshortcutKeys: function () {
  7071. var me = this, shortcutkeys = this.shortcutkeys;
  7072. me.addListener('keydown', function (type, e) {
  7073. var keyCode = e.keyCode || e.which;
  7074. for (var i in shortcutkeys) {
  7075. var tmp = shortcutkeys[i].split(',');
  7076. for (var t = 0, ti; ti = tmp[t++];) {
  7077. ti = ti.split(':');
  7078. var key = ti[0], param = ti[1];
  7079. if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
  7080. if (((RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
  7081. && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
  7082. && keyCode == RegExp.$3
  7083. ) ||
  7084. keyCode == RegExp.$1
  7085. ) {
  7086. if (me.queryCommandState(i, param) != -1)
  7087. me.execCommand(i, param);
  7088. domUtils.preventDefault(e);
  7089. }
  7090. }
  7091. }
  7092. }
  7093. });
  7094. },
  7095. /**
  7096. * 获取编辑器的内容
  7097. * @method getContent
  7098. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  7099. * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”&lt;p&gt;&lt;br/&gt;&lt;/p&gt;“), 则返回空字符串
  7100. * @example
  7101. * ```javascript
  7102. * //编辑器html内容:<p>1<strong>2<em>34</em>5</strong>6</p>
  7103. * var content = editor.getContent(); //返回值:<p>1<strong>2<em>34</em>5</strong>6</p>
  7104. * ```
  7105. */
  7106. /**
  7107. * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
  7108. * @method getContent
  7109. * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
  7110. * 代表当前编辑器的内容是否空,
  7111. * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
  7112. * 经过内置过滤规则处理后的内容。
  7113. * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
  7114. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  7115. * @return { String } 编辑器的内容字符串
  7116. * @example
  7117. * ```javascript
  7118. * // editor 是一个编辑器的实例
  7119. * var content = editor.getContent( function ( editor ) {
  7120. * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
  7121. * } );
  7122. * ```
  7123. */
  7124. getContent: function (cmd, fn, notSetCursor, ignoreBlank, formatter) {
  7125. var me = this;
  7126. if (cmd && utils.isFunction(cmd)) {
  7127. fn = cmd;
  7128. cmd = '';
  7129. }
  7130. if (fn ? !fn() : !this.hasContents()) {
  7131. return '';
  7132. }
  7133. me.fireEvent('beforegetcontent');
  7134. var root = UE.htmlparser(me.body.innerHTML, ignoreBlank);
  7135. me.filterOutputRule(root);
  7136. me.fireEvent('aftergetcontent', cmd, root);
  7137. return root.toHtml(formatter);
  7138. },
  7139. /**
  7140. * 取得完整的html代码,可以直接显示成完整的html文档
  7141. * @method getAllHtml
  7142. * @return { String } 编辑器的内容html文档字符串
  7143. * @eaxmple
  7144. * ```javascript
  7145. * editor.getAllHtml(); //返回格式大致是: <html><head>...</head><body>...</body></html>
  7146. * ```
  7147. */
  7148. getAllHtml: function () {
  7149. var me = this,
  7150. headHtml = [],
  7151. html = '';
  7152. me.fireEvent('getAllHtml', headHtml);
  7153. if (browser.ie && browser.version > 8) {
  7154. var headHtmlForIE9 = '';
  7155. utils.each(me.document.styleSheets, function (si) {
  7156. headHtmlForIE9 += (si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
  7157. });
  7158. utils.each(me.document.getElementsByTagName('script'), function (si) {
  7159. headHtmlForIE9 += si.outerHTML;
  7160. });
  7161. }
  7162. return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
  7163. + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
  7164. + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
  7165. },
  7166. /**
  7167. * 得到编辑器的纯文本内容,但会保留段落格式
  7168. * @method getPlainTxt
  7169. * @return { String } 编辑器带段落格式的纯文本内容字符串
  7170. * @example
  7171. * ```javascript
  7172. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  7173. * console.log(editor.getPlainTxt()); //输出:"1\n2\n
  7174. * ```
  7175. */
  7176. getPlainTxt: function () {
  7177. var reg = new RegExp(domUtils.fillChar, 'g'),
  7178. html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
  7179. html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  7180. .replace(/<br\/?>/gi, '\n')
  7181. .replace(/<[^>/]+>/g, '')
  7182. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  7183. return dtd.$block[c] ? '\n' : b ? b : '';
  7184. });
  7185. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  7186. return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
  7187. },
  7188. /**
  7189. * 获取编辑器中的纯文本内容,没有段落格式
  7190. * @method getContentTxt
  7191. * @return { String } 编辑器不带段落格式的纯文本内容字符串
  7192. * @example
  7193. * ```javascript
  7194. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  7195. * console.log(editor.getPlainTxt()); //输出:"12
  7196. * ```
  7197. */
  7198. getContentTxt: function () {
  7199. var reg = new RegExp(domUtils.fillChar, 'g');
  7200. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  7201. return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
  7202. },
  7203. /**
  7204. * 设置编辑器的内容,可修改编辑器当前的html内容
  7205. * @method setContent
  7206. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  7207. * @warning 该方法会触发selectionchange事件
  7208. * @param { String } html 要插入的html内容
  7209. * @example
  7210. * ```javascript
  7211. * editor.getContent('<p>test</p>');
  7212. * ```
  7213. */
  7214. /**
  7215. * 设置编辑器的内容,可修改编辑器当前的html内容
  7216. * @method setContent
  7217. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  7218. * @warning 该方法会触发selectionchange事件
  7219. * @param { String } html 要插入的html内容
  7220. * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
  7221. * @example
  7222. * ```javascript
  7223. * //假设设置前的编辑器内容是 <p>old text</p>
  7224. * editor.setContent('<p>new text</p>', true); //插入的结果是<p>old text</p><p>new text</p>
  7225. * ```
  7226. */
  7227. setContent: function (html, isAppendTo, notFireSelectionchange) {
  7228. var me = this;
  7229. me.fireEvent('beforesetcontent', html);
  7230. var root = UE.htmlparser(html);
  7231. me.filterInputRule(root);
  7232. html = root.toHtml();
  7233. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  7234. function isCdataDiv(node) {
  7235. return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
  7236. }
  7237. //给文本或者inline节点套p标签
  7238. if (me.options.enterTag == 'p') {
  7239. var child = this.body.firstChild, tmpNode;
  7240. if (!child || child.nodeType == 1 &&
  7241. (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
  7242. domUtils.isCustomeNode(child)
  7243. )
  7244. && child === this.body.lastChild) {
  7245. this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  7246. } else {
  7247. var p = me.document.createElement('p');
  7248. while (child) {
  7249. while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
  7250. tmpNode = child.nextSibling;
  7251. p.appendChild(child);
  7252. child = tmpNode;
  7253. }
  7254. if (p.firstChild) {
  7255. if (!child) {
  7256. me.body.appendChild(p);
  7257. break;
  7258. } else {
  7259. child.parentNode.insertBefore(p, child);
  7260. p = me.document.createElement('p');
  7261. }
  7262. }
  7263. child = child.nextSibling;
  7264. }
  7265. }
  7266. }
  7267. me.fireEvent('aftersetcontent');
  7268. me.fireEvent('contentchange');
  7269. !notFireSelectionchange && me._selectionChange();
  7270. //清除保存的选区
  7271. me._bakRange = me._bakIERange = me._bakNativeRange = null;
  7272. //trace:1742 setContent后gecko能得到焦点问题
  7273. var geckoSel;
  7274. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  7275. geckoSel.removeAllRanges();
  7276. }
  7277. if (me.options.autoSyncData) {
  7278. me.form && setValue(me.form, me);
  7279. }
  7280. },
  7281. /**
  7282. * 让编辑器获得焦点,默认focus到编辑器头部
  7283. * @method focus
  7284. * @example
  7285. * ```javascript
  7286. * editor.focus()
  7287. * ```
  7288. */
  7289. /**
  7290. * 让编辑器获得焦点,toEnd确定focus位置
  7291. * @method focus
  7292. * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
  7293. * @example
  7294. * ```javascript
  7295. * editor.focus(true)
  7296. * ```
  7297. */
  7298. focus: function (toEnd) {
  7299. try {
  7300. var me = this,
  7301. rng = me.selection.getRange();
  7302. if (toEnd) {
  7303. var node = me.body.lastChild;
  7304. if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
  7305. if (domUtils.isEmptyBlock(node)) {
  7306. rng.setStartAtFirst(node)
  7307. } else {
  7308. rng.setStartAtLast(node)
  7309. }
  7310. rng.collapse(true);
  7311. }
  7312. rng.setCursor(true);
  7313. } else {
  7314. if (!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0) {
  7315. var node = me.body.firstChild;
  7316. if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
  7317. rng.setStartAtFirst(node).collapse(true);
  7318. }
  7319. }
  7320. rng.select(true);
  7321. }
  7322. this.fireEvent('focus selectionchange');
  7323. } catch (e) {
  7324. }
  7325. },
  7326. isFocus: function () {
  7327. return this.selection.isFocus();
  7328. },
  7329. blur: function () {
  7330. var sel = this.selection.getNative();
  7331. if (sel.empty && browser.ie) {
  7332. var nativeRng = document.body.createTextRange();
  7333. nativeRng.moveToElementText(document.body);
  7334. nativeRng.collapse(true);
  7335. nativeRng.select();
  7336. sel.empty()
  7337. } else {
  7338. sel.removeAllRanges()
  7339. }
  7340. //this.fireEvent('blur selectionchange');
  7341. },
  7342. /**
  7343. * 初始化UE事件及部分事件代理
  7344. * @method _initEvents
  7345. * @private
  7346. */
  7347. _initEvents: function () {
  7348. var me = this,
  7349. doc = me.document,
  7350. win = me.window;
  7351. me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
  7352. domUtils.on(doc, ['click', 'contextmenu', 'mousedown', 'keydown', 'keyup', 'keypress', 'mouseup', 'mouseover', 'mouseout', 'selectstart'], me._proxyDomEvent);
  7353. domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent);
  7354. domUtils.on(me.body, 'drop', function (e) {
  7355. //阻止ff下默认的弹出新页面打开图片
  7356. if (browser.gecko && e.stopPropagation) {
  7357. e.stopPropagation();
  7358. }
  7359. me.fireEvent('contentchange')
  7360. });
  7361. domUtils.on(doc, ['mouseup', 'keydown'], function (evt) {
  7362. //特殊键不触发selectionchange
  7363. if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
  7364. return;
  7365. }
  7366. if (evt.button == 2) return;
  7367. me._selectionChange(250, evt);
  7368. });
  7369. },
  7370. /**
  7371. * 触发事件代理
  7372. * @method _proxyDomEvent
  7373. * @private
  7374. * @return { * } fireEvent的返回值
  7375. * @see UE.EventBase:fireEvent(String)
  7376. */
  7377. _proxyDomEvent: function (evt) {
  7378. if (this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false) {
  7379. return false;
  7380. }
  7381. if (this.fireEvent(evt.type.replace(/^on/, ''), evt) === false) {
  7382. return false;
  7383. }
  7384. return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase())
  7385. },
  7386. /**
  7387. * 变化选区
  7388. * @method _selectionChange
  7389. * @private
  7390. */
  7391. _selectionChange: function (delay, evt) {
  7392. var me = this;
  7393. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  7394. // if ( !me.selection.isFocus() ){
  7395. // return;
  7396. // }
  7397. var hackForMouseUp = false;
  7398. var mouseX, mouseY;
  7399. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  7400. var range = this.selection.getRange();
  7401. if (!range.collapsed) {
  7402. hackForMouseUp = true;
  7403. mouseX = evt.clientX;
  7404. mouseY = evt.clientY;
  7405. }
  7406. }
  7407. clearTimeout(_selectionChangeTimer);
  7408. _selectionChangeTimer = setTimeout(function () {
  7409. if (!me.selection || !me.selection.getNative()) {
  7410. return;
  7411. }
  7412. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  7413. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  7414. var ieRange;
  7415. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  7416. ieRange = me.document.body.createTextRange();
  7417. try {
  7418. ieRange.moveToPoint(mouseX, mouseY);
  7419. } catch (ex) {
  7420. ieRange = null;
  7421. }
  7422. }
  7423. var bakGetIERange;
  7424. if (ieRange) {
  7425. bakGetIERange = me.selection.getIERange;
  7426. me.selection.getIERange = function () {
  7427. return ieRange;
  7428. };
  7429. }
  7430. me.selection.cache();
  7431. if (bakGetIERange) {
  7432. me.selection.getIERange = bakGetIERange;
  7433. }
  7434. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  7435. me.fireEvent('beforeselectionchange');
  7436. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  7437. me.fireEvent('selectionchange', !!evt);
  7438. me.fireEvent('afterselectionchange');
  7439. me.selection.clear();
  7440. }
  7441. }, delay || 50);
  7442. },
  7443. /**
  7444. * 执行编辑命令
  7445. * @method _callCmdFn
  7446. * @private
  7447. * @param { String } fnName 函数名称
  7448. * @param { * } args 传给命令函数的参数
  7449. * @return { * } 返回命令函数运行的返回值
  7450. */
  7451. _callCmdFn: function (fnName, args) {
  7452. var cmdName = args[0].toLowerCase(),
  7453. cmd, cmdFn;
  7454. cmd = this.commands[cmdName] || UE.commands[cmdName];
  7455. cmdFn = cmd && cmd[fnName];
  7456. //没有querycommandstate或者没有command的都默认返回0
  7457. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  7458. return 0;
  7459. } else if (cmdFn) {
  7460. return cmdFn.apply(this, args);
  7461. }
  7462. },
  7463. /**
  7464. * 执行编辑命令cmdName,完成富文本编辑效果
  7465. * @method execCommand
  7466. * @param { String } cmdName 需要执行的命令
  7467. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7468. * @return { * } 返回命令函数运行的返回值
  7469. * @example
  7470. * ```javascript
  7471. * editor.execCommand(cmdName);
  7472. * ```
  7473. */
  7474. execCommand: function (cmdName) {
  7475. cmdName = cmdName.toLowerCase();
  7476. var me = this,
  7477. result,
  7478. cmd = me.commands[cmdName] || UE.commands[cmdName];
  7479. if (!cmd || !cmd.execCommand) {
  7480. return null;
  7481. }
  7482. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  7483. me.__hasEnterExecCommand = true;
  7484. if (me.queryCommandState.apply(me, arguments) != -1) {
  7485. me.fireEvent('saveScene');
  7486. me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments));
  7487. result = this._callCmdFn('execCommand', arguments);
  7488. //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
  7489. // (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  7490. me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments));
  7491. me.fireEvent('saveScene');
  7492. }
  7493. me.__hasEnterExecCommand = false;
  7494. } else {
  7495. result = this._callCmdFn('execCommand', arguments);
  7496. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
  7497. }
  7498. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
  7499. return result;
  7500. },
  7501. /**
  7502. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  7503. * @method queryCommandState
  7504. * @param { String } cmdName 需要查询的命令名称
  7505. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7506. * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
  7507. * @example
  7508. * ```javascript
  7509. * editor.queryCommandState(cmdName) => (-1|0|1)
  7510. * ```
  7511. * @see COMMAND.LIST
  7512. */
  7513. queryCommandState: function (cmdName) {
  7514. return this._callCmdFn('queryCommandState', arguments);
  7515. },
  7516. /**
  7517. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  7518. * @method queryCommandValue
  7519. * @param { String } cmdName 需要查询的命令名称
  7520. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  7521. * @remind 只有部分插件有此方法
  7522. * @return { * } 返回每个命令特定的当前状态值
  7523. * @grammar editor.queryCommandValue(cmdName) => {*}
  7524. * @see COMMAND.LIST
  7525. */
  7526. queryCommandValue: function (cmdName) {
  7527. return this._callCmdFn('queryCommandValue', arguments);
  7528. },
  7529. /**
  7530. * 检查编辑区域中是否有内容
  7531. * @method hasContents
  7532. * @remind 默认有文本内容,或者有以下节点都不认为是空
  7533. * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
  7534. * @return { Boolean } 检查有内容返回true,否则返回false
  7535. * @example
  7536. * ```javascript
  7537. * editor.hasContents()
  7538. * ```
  7539. */
  7540. /**
  7541. * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
  7542. * @method hasContents
  7543. * @param { Array } tags 传入数组判断时用到的节点类型
  7544. * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
  7545. * @example
  7546. * ```javascript
  7547. * editor.hasContents(['span']);
  7548. * ```
  7549. */
  7550. hasContents: function (tags) {
  7551. if (tags) {
  7552. for (var i = 0, ci; ci = tags[i++];) {
  7553. if (this.document.getElementsByTagName(ci).length > 0) {
  7554. return true;
  7555. }
  7556. }
  7557. }
  7558. if (!domUtils.isEmptyBlock(this.body)) {
  7559. return true
  7560. }
  7561. //随时添加,定义的特殊标签如果存在,不能认为是空
  7562. tags = ['div'];
  7563. for (i = 0; ci = tags[i++];) {
  7564. var nodes = domUtils.getElementsByTagName(this.document, ci);
  7565. for (var n = 0, cn; cn = nodes[n++];) {
  7566. if (domUtils.isCustomeNode(cn)) {
  7567. return true;
  7568. }
  7569. }
  7570. }
  7571. return false;
  7572. },
  7573. /**
  7574. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  7575. * @method reset
  7576. * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
  7577. * @example
  7578. * ```javascript
  7579. * editor.reset()
  7580. * ```
  7581. */
  7582. reset: function () {
  7583. this.fireEvent('reset');
  7584. },
  7585. /**
  7586. * 设置当前编辑区域可以编辑
  7587. * @method setEnabled
  7588. * @example
  7589. * ```javascript
  7590. * editor.setEnabled()
  7591. * ```
  7592. */
  7593. setEnabled: function () {
  7594. var me = this, range;
  7595. if (me.body.contentEditable == 'false') {
  7596. me.body.contentEditable = true;
  7597. range = me.selection.getRange();
  7598. //有可能内容丢失了
  7599. try {
  7600. range.moveToBookmark(me.lastBk);
  7601. delete me.lastBk
  7602. } catch (e) {
  7603. range.setStartAtFirst(me.body).collapse(true)
  7604. }
  7605. range.select(true);
  7606. if (me.bkqueryCommandState) {
  7607. me.queryCommandState = me.bkqueryCommandState;
  7608. delete me.bkqueryCommandState;
  7609. }
  7610. if (me.bkqueryCommandValue) {
  7611. me.queryCommandValue = me.bkqueryCommandValue;
  7612. delete me.bkqueryCommandValue;
  7613. }
  7614. me.fireEvent('selectionchange');
  7615. }
  7616. },
  7617. enable: function () {
  7618. return this.setEnabled();
  7619. },
  7620. /** 设置当前编辑区域不可编辑
  7621. * @method setDisabled
  7622. */
  7623. /** 设置当前编辑区域不可编辑,except中的命令除外
  7624. * @method setDisabled
  7625. * @param { String } except 例外命令的字符串
  7626. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  7627. * @example
  7628. * ```javascript
  7629. * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
  7630. * ```
  7631. */
  7632. /** 设置当前编辑区域不可编辑,except中的命令除外
  7633. * @method setDisabled
  7634. * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
  7635. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  7636. * @example
  7637. * ```javascript
  7638. * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
  7639. * ```
  7640. */
  7641. setDisabled: function (except) {
  7642. var me = this;
  7643. except = except ? utils.isArray(except) ? except : [except] : [];
  7644. if (me.body.contentEditable == 'true') {
  7645. if (!me.lastBk) {
  7646. me.lastBk = me.selection.getRange().createBookmark(true);
  7647. }
  7648. me.body.contentEditable = false;
  7649. me.bkqueryCommandState = me.queryCommandState;
  7650. me.bkqueryCommandValue = me.queryCommandValue;
  7651. me.queryCommandState = function (type) {
  7652. if (utils.indexOf(except, type) != -1) {
  7653. return me.bkqueryCommandState.apply(me, arguments);
  7654. }
  7655. return -1;
  7656. };
  7657. me.queryCommandValue = function (type) {
  7658. if (utils.indexOf(except, type) != -1) {
  7659. return me.bkqueryCommandValue.apply(me, arguments);
  7660. }
  7661. return null;
  7662. };
  7663. me.fireEvent('selectionchange');
  7664. }
  7665. },
  7666. disable: function (except) {
  7667. return this.setDisabled(except);
  7668. },
  7669. /**
  7670. * 设置默认内容
  7671. * @method _setDefaultContent
  7672. * @private
  7673. * @param { String } cont 要存入的内容
  7674. */
  7675. _setDefaultContent: function () {
  7676. function clear() {
  7677. var me = this;
  7678. if (me.document.getElementById('initContent')) {
  7679. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
  7680. me.removeListener('firstBeforeExecCommand focus', clear);
  7681. setTimeout(function () {
  7682. me.focus();
  7683. me._selectionChange();
  7684. }, 0)
  7685. }
  7686. }
  7687. return function (cont) {
  7688. var me = this;
  7689. me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  7690. me.addListener('firstBeforeExecCommand focus', clear);
  7691. }
  7692. }(),
  7693. /**
  7694. * 显示编辑器
  7695. * @method setShow
  7696. * @example
  7697. * ```javascript
  7698. * editor.setShow()
  7699. * ```
  7700. */
  7701. setShow: function () {
  7702. var me = this, range = me.selection.getRange();
  7703. if (me.container.style.display == 'none') {
  7704. //有可能内容丢失了
  7705. try {
  7706. range.moveToBookmark(me.lastBk);
  7707. delete me.lastBk
  7708. } catch (e) {
  7709. range.setStartAtFirst(me.body).collapse(true)
  7710. }
  7711. //ie下focus实效,所以做了个延迟
  7712. setTimeout(function () {
  7713. range.select(true);
  7714. }, 100);
  7715. me.container.style.display = '';
  7716. }
  7717. },
  7718. show: function () {
  7719. return this.setShow();
  7720. },
  7721. /**
  7722. * 隐藏编辑器
  7723. * @method setHide
  7724. * @example
  7725. * ```javascript
  7726. * editor.setHide()
  7727. * ```
  7728. */
  7729. setHide: function () {
  7730. var me = this;
  7731. if (!me.lastBk) {
  7732. me.lastBk = me.selection.getRange().createBookmark(true);
  7733. }
  7734. me.container.style.display = 'none'
  7735. },
  7736. hide: function () {
  7737. return this.setHide();
  7738. },
  7739. /**
  7740. * 根据指定的路径,获取对应的语言资源
  7741. * @method getLang
  7742. * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
  7743. * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
  7744. * @example
  7745. * ```javascript
  7746. * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
  7747. * ```
  7748. */
  7749. getLang: function (path) {
  7750. var lang = UE.I18N[this.options.lang];
  7751. if (!lang) {
  7752. throw Error("not import language file");
  7753. }
  7754. path = (path || "").split(".");
  7755. for (var i = 0, ci; ci = path[i++];) {
  7756. lang = lang[ci];
  7757. if (!lang) break;
  7758. }
  7759. return lang;
  7760. },
  7761. /**
  7762. * 计算编辑器html内容字符串的长度
  7763. * @method getContentLength
  7764. * @return { Number } 返回计算的长度
  7765. * @example
  7766. * ```javascript
  7767. * //编辑器html内容<p><strong>132</strong></p>
  7768. * editor.getContentLength() //返回27
  7769. * ```
  7770. */
  7771. /**
  7772. * 计算编辑器当前纯文本内容的长度
  7773. * @method getContentLength
  7774. * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
  7775. * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
  7776. * @example
  7777. * ```javascript
  7778. * //编辑器html内容<p><strong>132</strong></p>
  7779. * editor.getContentLength() //返回3
  7780. * ```
  7781. */
  7782. getContentLength: function (ingoneHtml, tagNames) {
  7783. var count = this.getContent(false, false, true).length;
  7784. if (ingoneHtml) {
  7785. tagNames = (tagNames || []).concat(['hr', 'img', 'iframe']);
  7786. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
  7787. for (var i = 0, ci; ci = tagNames[i++];) {
  7788. count += this.document.getElementsByTagName(ci).length;
  7789. }
  7790. }
  7791. return count;
  7792. },
  7793. /**
  7794. * 注册输入过滤规则
  7795. * @method addInputRule
  7796. * @param { Function } rule 要添加的过滤规则
  7797. * @example
  7798. * ```javascript
  7799. * editor.addInputRule(function(root){
  7800. * $.each(root.getNodesByTagName('div'),function(i,node){
  7801. * node.tagName="p";
  7802. * });
  7803. * });
  7804. * ```
  7805. */
  7806. addInputRule: function (rule) {
  7807. this.inputRules.push(rule);
  7808. },
  7809. /**
  7810. * 执行注册的过滤规则
  7811. * @method filterInputRule
  7812. * @param { UE.uNode } root 要过滤的uNode节点
  7813. * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
  7814. * @example
  7815. * ```javascript
  7816. * editor.filterInputRule(editor.body);
  7817. * ```
  7818. * @see UE.Editor:addInputRule
  7819. */
  7820. filterInputRule: function (root) {
  7821. for (var i = 0, ci; ci = this.inputRules[i++];) {
  7822. ci.call(this, root)
  7823. }
  7824. },
  7825. /**
  7826. * 注册输出过滤规则
  7827. * @method addOutputRule
  7828. * @param { Function } rule 要添加的过滤规则
  7829. * @example
  7830. * ```javascript
  7831. * editor.addOutputRule(function(root){
  7832. * $.each(root.getNodesByTagName('p'),function(i,node){
  7833. * node.tagName="div";
  7834. * });
  7835. * });
  7836. * ```
  7837. */
  7838. addOutputRule: function (rule) {
  7839. this.outputRules.push(rule)
  7840. },
  7841. /**
  7842. * 根据输出过滤规则,过滤编辑器内容
  7843. * @method filterOutputRule
  7844. * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
  7845. * @param { UE.uNode } root 要过滤的uNode节点
  7846. * @example
  7847. * ```javascript
  7848. * editor.filterOutputRule(editor.body);
  7849. * ```
  7850. * @see UE.Editor:addOutputRule
  7851. */
  7852. filterOutputRule: function (root) {
  7853. for (var i = 0, ci; ci = this.outputRules[i++];) {
  7854. ci.call(this, root)
  7855. }
  7856. },
  7857. /**
  7858. * 根据action名称获取请求的路径
  7859. * @method getActionUrl
  7860. * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
  7861. * @param { String } action action名称
  7862. * @example
  7863. * ```javascript
  7864. * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
  7865. * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
  7866. * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
  7867. * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
  7868. * ```
  7869. */
  7870. getActionUrl: function (action) {
  7871. var actionName = this.getOpt(action) || action,
  7872. imageUrl = this.getOpt('imageUrl'),
  7873. serverUrl = this.getOpt('serverUrl');
  7874. if (!serverUrl && imageUrl) {
  7875. serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2');
  7876. }
  7877. if (serverUrl) {
  7878. serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?' : '&') + 'action=' + (actionName || '');
  7879. return utils.formatUrl(serverUrl);
  7880. } else {
  7881. return '';
  7882. }
  7883. }
  7884. };
  7885. utils.inherits(Editor, EventBase);
  7886. })();
  7887. // core/Editor.defaultoptions.js
  7888. //维护编辑器一下默认的不在插件中的配置项
  7889. UE.Editor.defaultOptions = function (editor) {
  7890. var _url = editor.options.UEDITOR_HOME_URL;
  7891. return {
  7892. isShow: true,
  7893. initialContent: '',
  7894. initialStyle: '',
  7895. autoClearinitialContent: false,
  7896. iframeCssUrl: _url + 'themes/iframe.css',
  7897. textarea: 'editorValue',
  7898. focus: false,
  7899. focusInEnd: true,
  7900. autoClearEmptyNode: true,
  7901. fullscreen: false,
  7902. readonly: false,
  7903. zIndex: 999,
  7904. imagePopup: true,
  7905. enterTag: 'p',
  7906. customDomain: false,
  7907. lang: 'zh-cn',
  7908. langPath: _url + 'lang/',
  7909. theme: 'default',
  7910. themePath: _url + 'themes/',
  7911. allHtmlEnabled: false,
  7912. scaleEnabled: false,
  7913. tableNativeEditInFF: false,
  7914. autoSyncData: true,
  7915. fileNameFormat: '{time}{rand:6}'
  7916. }
  7917. };
  7918. // core/loadconfig.js
  7919. (function () {
  7920. UE.Editor.prototype.loadServerConfig = function () {
  7921. var me = this;
  7922. setTimeout(function () {
  7923. try {
  7924. me.options.imageUrl && me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'));
  7925. var configUrl = me.getActionUrl('config'),
  7926. isJsonp = utils.isCrossDomainUrl(configUrl);
  7927. /* 发出ajax请求 */
  7928. me._serverConfigLoaded = false;
  7929. configUrl && UE.ajax.request(configUrl, {
  7930. 'method': 'GET',
  7931. 'dataType': isJsonp ? 'jsonp' : '',
  7932. 'onsuccess': function (r) {
  7933. try {
  7934. var config = isJsonp ? r : eval("(" + r.responseText + ")");
  7935. utils.extend(me.options, config);
  7936. me.fireEvent('serverConfigLoaded');
  7937. me._serverConfigLoaded = true;
  7938. } catch (e) {
  7939. showErrorMsg(me.getLang('loadconfigFormatError'));
  7940. }
  7941. },
  7942. 'onerror': function () {
  7943. showErrorMsg(me.getLang('loadconfigHttpError'));
  7944. }
  7945. });
  7946. } catch (e) {
  7947. showErrorMsg(me.getLang('loadconfigError'));
  7948. }
  7949. });
  7950. function showErrorMsg(msg) {
  7951. console && console.error(msg);
  7952. //me.fireEvent('showMessage', {
  7953. // 'title': msg,
  7954. // 'type': 'error'
  7955. //});
  7956. }
  7957. };
  7958. UE.Editor.prototype.isServerConfigLoaded = function () {
  7959. var me = this;
  7960. return me._serverConfigLoaded || false;
  7961. };
  7962. UE.Editor.prototype.afterConfigReady = function (handler) {
  7963. if (!handler || !utils.isFunction(handler)) return;
  7964. var me = this;
  7965. var readyHandler = function () {
  7966. handler.apply(me, arguments);
  7967. me.removeListener('serverConfigLoaded', readyHandler);
  7968. };
  7969. if (me.isServerConfigLoaded()) {
  7970. handler.call(me, 'serverConfigLoaded');
  7971. } else {
  7972. me.addListener('serverConfigLoaded', readyHandler);
  7973. }
  7974. };
  7975. })();
  7976. // core/ajax.js
  7977. /**
  7978. * @file
  7979. * @module UE.ajax
  7980. * @since 1.2.6.1
  7981. */
  7982. /**
  7983. * 提供对ajax请求的支持
  7984. * @module UE.ajax
  7985. */
  7986. UE.ajax = function () {
  7987. //创建一个ajaxRequest对象
  7988. var fnStr = 'XMLHttpRequest()';
  7989. try {
  7990. new ActiveXObject("Msxml2.XMLHTTP");
  7991. fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')';
  7992. } catch (e) {
  7993. try {
  7994. new ActiveXObject("Microsoft.XMLHTTP");
  7995. fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
  7996. } catch (e) {
  7997. }
  7998. }
  7999. var creatAjaxRequest = new Function('return new ' + fnStr);
  8000. /**
  8001. * 将json参数转化成适合ajax提交的参数列表
  8002. * @param json
  8003. */
  8004. function json2str(json) {
  8005. var strArr = [];
  8006. for (var i in json) {
  8007. //忽略默认的几个参数
  8008. if (i == "method" || i == "timeout" || i == "async" || i == "dataType" || i == "callback") continue;
  8009. //忽略控制
  8010. if (json[i] == undefined || json[i] == null) continue;
  8011. //传递过来的对象和函数不在提交之列
  8012. if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
  8013. strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
  8014. } else if (utils.isArray(json[i])) {
  8015. //支持传数组内容
  8016. for (var j = 0; j < json[i].length; j++) {
  8017. strArr.push(encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j]));
  8018. }
  8019. }
  8020. }
  8021. return strArr.join("&");
  8022. }
  8023. function doAjax(url, ajaxOptions) {
  8024. var xhr = creatAjaxRequest(),
  8025. //是否超时
  8026. timeIsOut = false,
  8027. //默认参数
  8028. defaultAjaxOptions = {
  8029. method: "POST",
  8030. timeout: 5000,
  8031. async: true,
  8032. data: {},//需要传递对象的话只能覆盖
  8033. onsuccess: function () {
  8034. },
  8035. onerror: function () {
  8036. }
  8037. };
  8038. if (typeof url === "object") {
  8039. ajaxOptions = url;
  8040. url = ajaxOptions.url;
  8041. }
  8042. if (!xhr || !url) return;
  8043. var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions, ajaxOptions) : defaultAjaxOptions;
  8044. var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  8045. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  8046. if (!utils.isEmptyObject(ajaxOpts.data)) {
  8047. submitStr += (submitStr ? "&" : "") + json2str(ajaxOpts.data);
  8048. }
  8049. //超时检测
  8050. var timerID = setTimeout(function () {
  8051. if (xhr.readyState != 4) {
  8052. timeIsOut = true;
  8053. xhr.abort();
  8054. clearTimeout(timerID);
  8055. }
  8056. }, ajaxOpts.timeout);
  8057. var method = ajaxOpts.method.toUpperCase();
  8058. var str = url + (url.indexOf("?") == -1 ? "?" : "&") + (method == "POST" ? "" : submitStr + "&noCache=" + +new Date);
  8059. xhr.open(method, str, ajaxOpts.async);
  8060. xhr.onreadystatechange = function () {
  8061. if (xhr.readyState == 4) {
  8062. if (!timeIsOut && xhr.status == 200) {
  8063. ajaxOpts.onsuccess(xhr);
  8064. } else {
  8065. ajaxOpts.onerror(xhr);
  8066. }
  8067. }
  8068. };
  8069. if (method == "POST") {
  8070. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  8071. xhr.send(submitStr);
  8072. } else {
  8073. xhr.send(null);
  8074. }
  8075. }
  8076. function doJsonp(url, opts) {
  8077. var successhandler = opts.onsuccess || function () {
  8078. },
  8079. scr = document.createElement('SCRIPT'),
  8080. options = opts || {},
  8081. charset = options['charset'],
  8082. callbackField = options['jsonp'] || 'callback',
  8083. callbackFnName,
  8084. timeOut = options['timeOut'] || 0,
  8085. timer,
  8086. reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'),
  8087. matches;
  8088. if (utils.isFunction(successhandler)) {
  8089. callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36);
  8090. window[callbackFnName] = getCallBack(0);
  8091. } else if (utils.isString(successhandler)) {
  8092. callbackFnName = successhandler;
  8093. } else {
  8094. if (matches = reg.exec(url)) {
  8095. callbackFnName = matches[2];
  8096. }
  8097. }
  8098. url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName);
  8099. if (url.search(reg) < 0) {
  8100. url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName;
  8101. }
  8102. var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  8103. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  8104. if (!utils.isEmptyObject(opts.data)) {
  8105. queryStr += (queryStr ? "&" : "") + json2str(opts.data);
  8106. }
  8107. if (queryStr) {
  8108. url = url.replace(/\?/, '?' + queryStr + '&');
  8109. }
  8110. scr.onerror = getCallBack(1);
  8111. if (timeOut) {
  8112. timer = setTimeout(getCallBack(1), timeOut);
  8113. }
  8114. createScriptTag(scr, url, charset);
  8115. function createScriptTag(scr, url, charset) {
  8116. scr.setAttribute('type', 'text/javascript');
  8117. scr.setAttribute('defer', 'defer');
  8118. charset && scr.setAttribute('charset', charset);
  8119. scr.setAttribute('src', url);
  8120. document.getElementsByTagName('head')[0].appendChild(scr);
  8121. }
  8122. function getCallBack(onTimeOut) {
  8123. return function () {
  8124. try {
  8125. if (onTimeOut) {
  8126. options.onerror && options.onerror();
  8127. } else {
  8128. try {
  8129. clearTimeout(timer);
  8130. successhandler.apply(window, arguments);
  8131. } catch (e) {
  8132. }
  8133. }
  8134. } catch (exception) {
  8135. options.onerror && options.onerror.call(window, exception);
  8136. } finally {
  8137. options.oncomplete && options.oncomplete.apply(window, arguments);
  8138. scr.parentNode && scr.parentNode.removeChild(scr);
  8139. window[callbackFnName] = null;
  8140. try {
  8141. delete window[callbackFnName];
  8142. } catch (e) {
  8143. }
  8144. }
  8145. }
  8146. }
  8147. }
  8148. return {
  8149. /**
  8150. * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  8151. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
  8152. * @method request
  8153. * @param { URLString } url ajax请求的url地址
  8154. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  8155. * @example
  8156. * ```javascript
  8157. * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
  8158. * UE.ajax.requeset( 'sayhello.php', {
  8159. *
  8160. * //请求方法。可选值: 'GET', 'POST',默认值是'POST'
  8161. * method: 'GET',
  8162. *
  8163. * //超时时间。 默认为5000, 单位是ms
  8164. * timeout: 10000,
  8165. *
  8166. * //是否是异步请求。 true为异步请求, false为同步请求
  8167. * async: true,
  8168. *
  8169. * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
  8170. * data: {
  8171. * name: 'ueditor'
  8172. * },
  8173. *
  8174. * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
  8175. * onsuccess: function ( xhr ) {
  8176. * console.log( xhr.responseText );
  8177. * },
  8178. *
  8179. * //请求失败或者超时后的回调。
  8180. * onerror: function ( xhr ) {
  8181. * alert( 'Ajax请求失败' );
  8182. * }
  8183. *
  8184. * } );
  8185. * ```
  8186. */
  8187. /**
  8188. * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  8189. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
  8190. * @method request
  8191. * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
  8192. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  8193. * @example
  8194. * ```javascript
  8195. *
  8196. * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
  8197. * UE.ajax.requeset( 'sayhello.php', {
  8198. *
  8199. * //请求的地址, 该项是必须的。
  8200. * url: 'sayhello.php'
  8201. *
  8202. * } );
  8203. * ```
  8204. */
  8205. request: function (url, opts) {
  8206. if (opts && opts.dataType == 'jsonp') {
  8207. doJsonp(url, opts);
  8208. } else {
  8209. doAjax(url, opts);
  8210. }
  8211. },
  8212. getJSONP: function (url, data, fn) {
  8213. var opts = {
  8214. 'data': data,
  8215. 'oncomplete': fn
  8216. };
  8217. doJsonp(url, opts);
  8218. }
  8219. };
  8220. }();
  8221. // core/filterword.js
  8222. /**
  8223. * UE过滤word的静态方法
  8224. * @file
  8225. */
  8226. /**
  8227. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  8228. * @module UE
  8229. */
  8230. /**
  8231. * 根据传入html字符串过滤word
  8232. * @module UE
  8233. * @since 1.2.6.1
  8234. * @method filterWord
  8235. * @param { String } html html字符串
  8236. * @return { String } 已过滤后的结果字符串
  8237. * @example
  8238. * ```javascript
  8239. * UE.filterWord(html);
  8240. * ```
  8241. */
  8242. var filterWord = UE.filterWord = function () {
  8243. //是否是word过来的内容
  8244. function isWordDocument(str) {
  8245. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test(str);
  8246. }
  8247. //去掉小数
  8248. function transUnit(v) {
  8249. v = v.replace(/[\d.]+\w+/g, function (m) {
  8250. return utils.transUnitToPx(m);
  8251. });
  8252. return v;
  8253. }
  8254. function filterPasteWord(str) {
  8255. return str.replace(/[\t\r\n]+/g, ' ')
  8256. .replace(/<!--[\s\S]*?-->/ig, "")
  8257. //转换图片
  8258. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi, function (str) {
  8259. //opera能自己解析出image所这里直接返回空
  8260. if (browser.opera) {
  8261. return '';
  8262. }
  8263. try {
  8264. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  8265. if (/Bitmap/i.test(str)) {
  8266. return '';
  8267. }
  8268. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  8269. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  8270. src = str.match(/src=\s*"([^"]*)"/i)[1];
  8271. return '<img width="' + transUnit(width) + '" height="' + transUnit(height) + '" src="' + src + '" />';
  8272. } catch (e) {
  8273. return '';
  8274. }
  8275. })
  8276. //针对wps添加的多余标签处理
  8277. .replace(/<\/?div[^>]*>/g, '')
  8278. //去掉多余的属性
  8279. .replace(/v:\w+=(["']?)[^'"]+\1/g, '')
  8280. .replace(/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "")
  8281. .replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>")
  8282. //去掉多余的属性
  8283. .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function (str, name, marks, val) {
  8284. //保留list的标示
  8285. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  8286. })
  8287. //清除多余的font/span不能匹配&nbsp;有可能是空格
  8288. .replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function (a, b, c) {
  8289. return c.replace(/[\t\r\n ]+/g, ' ')
  8290. })
  8291. //处理style的问题
  8292. .replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function (str, tag, tmp, style) {
  8293. var n = [],
  8294. s = style.replace(/^\s+|\s+$/, '')
  8295. .replace(/&#39;/g, '\'')
  8296. .replace(/&quot;/gi, "'")
  8297. .replace(/[\d.]+(cm|pt)/g, function (str) {
  8298. return utils.transUnitToPx(str)
  8299. })
  8300. .split(/;\s*/g);
  8301. for (var i = 0, v; v = s[i]; i++) {
  8302. var name, value,
  8303. parts = v.split(":");
  8304. if (parts.length == 2) {
  8305. name = parts[0].toLowerCase();
  8306. value = parts[1].toLowerCase();
  8307. if (/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g, '').length == 0
  8308. ||
  8309. /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
  8310. ) {
  8311. continue;
  8312. }
  8313. switch (name) {
  8314. case "mso-padding-alt":
  8315. case "mso-padding-top-alt":
  8316. case "mso-padding-right-alt":
  8317. case "mso-padding-bottom-alt":
  8318. case "mso-padding-left-alt":
  8319. case "mso-margin-alt":
  8320. case "mso-margin-top-alt":
  8321. case "mso-margin-right-alt":
  8322. case "mso-margin-bottom-alt":
  8323. case "mso-margin-left-alt":
  8324. //ie下会出现挤到一起的情况
  8325. //case "mso-table-layout-alt":
  8326. case "mso-height":
  8327. case "mso-width":
  8328. case "mso-vertical-align-alt":
  8329. //trace:1819 ff下会解析出padding在table上
  8330. if (!/<table/.test(tag))
  8331. n[i] = name.replace(/^mso-|-alt$/g, "") + ":" + transUnit(value);
  8332. continue;
  8333. case "horiz-align":
  8334. n[i] = "text-align:" + value;
  8335. continue;
  8336. case "vert-align":
  8337. n[i] = "vertical-align:" + value;
  8338. continue;
  8339. case "font-color":
  8340. case "mso-foreground":
  8341. n[i] = "color:" + value;
  8342. continue;
  8343. case "mso-background":
  8344. case "mso-highlight":
  8345. n[i] = "background:" + value;
  8346. continue;
  8347. case "mso-default-height":
  8348. n[i] = "min-height:" + transUnit(value);
  8349. continue;
  8350. case "mso-default-width":
  8351. n[i] = "min-width:" + transUnit(value);
  8352. continue;
  8353. case "mso-padding-between-alt":
  8354. n[i] = "border-collapse:separate;border-spacing:" + transUnit(value);
  8355. continue;
  8356. case "text-line-through":
  8357. if ((value == "single") || (value == "double")) {
  8358. n[i] = "text-decoration:line-through";
  8359. }
  8360. continue;
  8361. case "mso-zero-height":
  8362. if (value == "yes") {
  8363. n[i] = "display:none";
  8364. }
  8365. continue;
  8366. // case 'background':
  8367. // break;
  8368. case 'margin':
  8369. if (!/[1-9]/.test(value)) {
  8370. continue;
  8371. }
  8372. }
  8373. 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)
  8374. ||
  8375. /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
  8376. ) {
  8377. continue;
  8378. }
  8379. n[i] = name + ":" + parts[1];
  8380. }
  8381. }
  8382. return tag + (n.length ? ' style="' + n.join(';').replace(/;{2,}/g, ';') + '"' : '');
  8383. })
  8384. }
  8385. return function (html) {
  8386. return (isWordDocument(html) ? filterPasteWord(html) : html);
  8387. };
  8388. }();
  8389. // core/node.js
  8390. /**
  8391. * 编辑器模拟的节点类
  8392. * @file
  8393. * @module UE
  8394. * @class uNode
  8395. * @since 1.2.6.1
  8396. */
  8397. /**
  8398. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  8399. * @unfile
  8400. * @module UE
  8401. */
  8402. (function () {
  8403. /**
  8404. * 编辑器模拟的节点类
  8405. * @unfile
  8406. * @module UE
  8407. * @class uNode
  8408. */
  8409. /**
  8410. * 通过一个键值对,创建一个uNode对象
  8411. * @constructor
  8412. * @param { Object } attr 传入要创建的uNode的初始属性
  8413. * @example
  8414. * ```javascript
  8415. * var node = new uNode({
  8416. * type:'element',
  8417. * tagName:'span',
  8418. * attrs:{style:'font-size:14px;'}
  8419. * }
  8420. * ```
  8421. */
  8422. var uNode = UE.uNode = function (obj) {
  8423. this.type = obj.type;
  8424. this.data = obj.data;
  8425. this.tagName = obj.tagName;
  8426. this.parentNode = obj.parentNode;
  8427. this.attrs = obj.attrs || {};
  8428. this.children = obj.children;
  8429. };
  8430. var notTransAttrs = {
  8431. 'href': 1,
  8432. 'src': 1,
  8433. '_src': 1,
  8434. '_href': 1,
  8435. 'cdata_data': 1
  8436. };
  8437. var notTransTagName = {
  8438. style: 1,
  8439. script: 1
  8440. };
  8441. var indentChar = ' ',
  8442. breakChar = '\n';
  8443. function insertLine(arr, current, begin) {
  8444. arr.push(breakChar);
  8445. return current + (begin ? 1 : -1);
  8446. }
  8447. function insertIndent(arr, current) {
  8448. //插入缩进
  8449. for (var i = 0; i < current; i++) {
  8450. arr.push(indentChar);
  8451. }
  8452. }
  8453. //创建uNode的静态方法
  8454. //支持标签和html
  8455. uNode.createElement = function (html) {
  8456. if (/[<>]/.test(html)) {
  8457. return UE.htmlparser(html).children[0]
  8458. } else {
  8459. return new uNode({
  8460. type: 'element',
  8461. children: [],
  8462. tagName: html
  8463. })
  8464. }
  8465. };
  8466. uNode.createText = function (data, noTrans) {
  8467. return new UE.uNode({
  8468. type: 'text',
  8469. 'data': noTrans ? data : utils.unhtml(data || '')
  8470. })
  8471. };
  8472. function nodeToHtml(node, arr, formatter, current) {
  8473. switch (node.type) {
  8474. case 'root':
  8475. for (var i = 0, ci; ci = node.children[i++];) {
  8476. //插入新行
  8477. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  8478. insertLine(arr, current, true);
  8479. insertIndent(arr, current)
  8480. }
  8481. nodeToHtml(ci, arr, formatter, current)
  8482. }
  8483. break;
  8484. case 'text':
  8485. isText(node, arr);
  8486. break;
  8487. case 'element':
  8488. isElement(node, arr, formatter, current);
  8489. break;
  8490. case 'comment':
  8491. isComment(node, arr, formatter);
  8492. }
  8493. return arr;
  8494. }
  8495. function isText(node, arr) {
  8496. if (node.parentNode.tagName == 'pre') {
  8497. //源码模式下输入html标签,不能做转换处理,直接输出
  8498. arr.push(node.data)
  8499. } else {
  8500. arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g, ' &nbsp;'))
  8501. }
  8502. }
  8503. function isElement(node, arr, formatter, current) {
  8504. var attrhtml = '';
  8505. if (node.attrs) {
  8506. attrhtml = [];
  8507. var attrs = node.attrs;
  8508. for (var a in attrs) {
  8509. //这里就针对
  8510. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  8511. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  8512. //有可能做的不够
  8513. attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  8514. return '&quot;'
  8515. }) : utils.unhtml(attrs[a])) + '"' : ''))
  8516. }
  8517. attrhtml = attrhtml.join(' ');
  8518. }
  8519. arr.push('<' + node.tagName +
  8520. (attrhtml ? ' ' + attrhtml : '') +
  8521. (dtd.$empty[node.tagName] ? '\/' : '') + '>'
  8522. );
  8523. //插入新行
  8524. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  8525. if (node.children && node.children.length) {
  8526. current = insertLine(arr, current, true);
  8527. insertIndent(arr, current)
  8528. }
  8529. }
  8530. if (node.children && node.children.length) {
  8531. for (var i = 0, ci; ci = node.children[i++];) {
  8532. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  8533. insertLine(arr, current);
  8534. insertIndent(arr, current)
  8535. }
  8536. nodeToHtml(ci, arr, formatter, current)
  8537. }
  8538. }
  8539. if (!dtd.$empty[node.tagName]) {
  8540. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  8541. if (node.children && node.children.length) {
  8542. current = insertLine(arr, current);
  8543. insertIndent(arr, current)
  8544. }
  8545. }
  8546. arr.push('<\/' + node.tagName + '>');
  8547. }
  8548. }
  8549. function isComment(node, arr) {
  8550. arr.push('<!--' + node.data + '-->');
  8551. }
  8552. function getNodeById(root, id) {
  8553. var node;
  8554. if (root.type == 'element' && root.getAttr('id') == id) {
  8555. return root;
  8556. }
  8557. if (root.children && root.children.length) {
  8558. for (var i = 0, ci; ci = root.children[i++];) {
  8559. if (node = getNodeById(ci, id)) {
  8560. return node;
  8561. }
  8562. }
  8563. }
  8564. }
  8565. function getNodesByTagName(node, tagName, arr) {
  8566. if (node.type == 'element' && node.tagName == tagName) {
  8567. arr.push(node);
  8568. }
  8569. if (node.children && node.children.length) {
  8570. for (var i = 0, ci; ci = node.children[i++];) {
  8571. getNodesByTagName(ci, tagName, arr)
  8572. }
  8573. }
  8574. }
  8575. function nodeTraversal(root, fn) {
  8576. if (root.children && root.children.length) {
  8577. for (var i = 0, ci; ci = root.children[i];) {
  8578. nodeTraversal(ci, fn);
  8579. //ci被替换的情况,这里就不再走 fn了
  8580. if (ci.parentNode) {
  8581. if (ci.children && ci.children.length) {
  8582. fn(ci)
  8583. }
  8584. if (ci.parentNode) i++
  8585. }
  8586. }
  8587. } else {
  8588. fn(root)
  8589. }
  8590. }
  8591. uNode.prototype = {
  8592. /**
  8593. * 当前节点对象,转换成html文本
  8594. * @method toHtml
  8595. * @return { String } 返回转换后的html字符串
  8596. * @example
  8597. * ```javascript
  8598. * node.toHtml();
  8599. * ```
  8600. */
  8601. /**
  8602. * 当前节点对象,转换成html文本
  8603. * @method toHtml
  8604. * @param { Boolean } formatter 是否格式化返回值
  8605. * @return { String } 返回转换后的html字符串
  8606. * @example
  8607. * ```javascript
  8608. * node.toHtml( true );
  8609. * ```
  8610. */
  8611. toHtml: function (formatter) {
  8612. var arr = [];
  8613. nodeToHtml(this, arr, formatter, 0);
  8614. return arr.join('')
  8615. },
  8616. /**
  8617. * 获取节点的html内容
  8618. * @method innerHTML
  8619. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8620. * @return { String } 返回节点的html内容
  8621. * @example
  8622. * ```javascript
  8623. * var htmlstr = node.innerHTML();
  8624. * ```
  8625. */
  8626. /**
  8627. * 设置节点的html内容
  8628. * @method innerHTML
  8629. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8630. * @param { String } htmlstr 传入要设置的html内容
  8631. * @return { UE.uNode } 返回节点本身
  8632. * @example
  8633. * ```javascript
  8634. * node.innerHTML('<span>text</span>');
  8635. * ```
  8636. */
  8637. innerHTML: function (htmlstr) {
  8638. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8639. return this;
  8640. }
  8641. if (utils.isString(htmlstr)) {
  8642. if (this.children) {
  8643. for (var i = 0, ci; ci = this.children[i++];) {
  8644. ci.parentNode = null;
  8645. }
  8646. }
  8647. this.children = [];
  8648. var tmpRoot = UE.htmlparser(htmlstr);
  8649. for (var i = 0, ci; ci = tmpRoot.children[i++];) {
  8650. this.children.push(ci);
  8651. ci.parentNode = this;
  8652. }
  8653. return this;
  8654. } else {
  8655. var tmpRoot = new UE.uNode({
  8656. type: 'root',
  8657. children: this.children
  8658. });
  8659. return tmpRoot.toHtml();
  8660. }
  8661. },
  8662. /**
  8663. * 获取节点的纯文本内容
  8664. * @method innerText
  8665. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8666. * @return { String } 返回节点的存文本内容
  8667. * @example
  8668. * ```javascript
  8669. * var textStr = node.innerText();
  8670. * ```
  8671. */
  8672. /**
  8673. * 设置节点的纯文本内容
  8674. * @method innerText
  8675. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  8676. * @param { String } textStr 传入要设置的文本内容
  8677. * @return { UE.uNode } 返回节点本身
  8678. * @example
  8679. * ```javascript
  8680. * node.innerText('<span>text</span>');
  8681. * ```
  8682. */
  8683. innerText: function (textStr, noTrans) {
  8684. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8685. return this;
  8686. }
  8687. if (textStr) {
  8688. if (this.children) {
  8689. for (var i = 0, ci; ci = this.children[i++];) {
  8690. ci.parentNode = null;
  8691. }
  8692. }
  8693. this.children = [];
  8694. this.appendChild(uNode.createText(textStr, noTrans));
  8695. return this;
  8696. } else {
  8697. return this.toHtml().replace(/<[^>]+>/g, '');
  8698. }
  8699. },
  8700. /**
  8701. * 获取当前对象的data属性
  8702. * @method getData
  8703. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  8704. * @example
  8705. * ```javascript
  8706. * node.getData();
  8707. * ```
  8708. */
  8709. getData: function () {
  8710. if (this.type == 'element')
  8711. return '';
  8712. return this.data
  8713. },
  8714. /**
  8715. * 获取当前节点下的第一个子节点
  8716. * @method firstChild
  8717. * @return { UE.uNode } 返回第一个子节点
  8718. * @example
  8719. * ```javascript
  8720. * node.firstChild(); //返回第一个子节点
  8721. * ```
  8722. */
  8723. firstChild: function () {
  8724. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  8725. // return this;
  8726. // }
  8727. return this.children ? this.children[0] : null;
  8728. },
  8729. /**
  8730. * 获取当前节点下的最后一个子节点
  8731. * @method lastChild
  8732. * @return { UE.uNode } 返回最后一个子节点
  8733. * @example
  8734. * ```javascript
  8735. * node.lastChild(); //返回最后一个子节点
  8736. * ```
  8737. */
  8738. lastChild: function () {
  8739. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  8740. // return this;
  8741. // }
  8742. return this.children ? this.children[this.children.length - 1] : null;
  8743. },
  8744. /**
  8745. * 获取和当前节点有相同父亲节点的前一个节点
  8746. * @method previousSibling
  8747. * @return { UE.uNode } 返回前一个节点
  8748. * @example
  8749. * ```javascript
  8750. * node.children[2].previousSibling(); //返回子节点node.children[1]
  8751. * ```
  8752. */
  8753. previousSibling: function () {
  8754. var parent = this.parentNode;
  8755. for (var i = 0, ci; ci = parent.children[i]; i++) {
  8756. if (ci === this) {
  8757. return i == 0 ? null : parent.children[i - 1];
  8758. }
  8759. }
  8760. },
  8761. /**
  8762. * 获取和当前节点有相同父亲节点的后一个节点
  8763. * @method nextSibling
  8764. * @return { UE.uNode } 返回后一个节点,找不到返回null
  8765. * @example
  8766. * ```javascript
  8767. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  8768. * ```
  8769. */
  8770. nextSibling: function () {
  8771. var parent = this.parentNode;
  8772. for (var i = 0, ci; ci = parent.children[i++];) {
  8773. if (ci === this) {
  8774. return parent.children[i];
  8775. }
  8776. }
  8777. },
  8778. /**
  8779. * 用新的节点替换当前节点
  8780. * @method replaceChild
  8781. * @param { UE.uNode } target 要替换成该节点参数
  8782. * @param { UE.uNode } source 要被替换掉的节点
  8783. * @return { UE.uNode } 返回替换之后的节点对象
  8784. * @example
  8785. * ```javascript
  8786. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  8787. * ```
  8788. */
  8789. replaceChild: function (target, source) {
  8790. if (this.children) {
  8791. if (target.parentNode) {
  8792. target.parentNode.removeChild(target);
  8793. }
  8794. for (var i = 0, ci; ci = this.children[i]; i++) {
  8795. if (ci === source) {
  8796. this.children.splice(i, 1, target);
  8797. source.parentNode = null;
  8798. target.parentNode = this;
  8799. return target;
  8800. }
  8801. }
  8802. }
  8803. },
  8804. /**
  8805. * 在节点的子节点列表最后位置插入一个节点
  8806. * @method appendChild
  8807. * @param { UE.uNode } node 要插入的节点
  8808. * @return { UE.uNode } 返回刚插入的子节点
  8809. * @example
  8810. * ```javascript
  8811. * node.appendChild( newNode ); //在node内插入子节点newNode
  8812. * ```
  8813. */
  8814. appendChild: function (node) {
  8815. if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
  8816. if (!this.children) {
  8817. this.children = []
  8818. }
  8819. if (node.parentNode) {
  8820. node.parentNode.removeChild(node);
  8821. }
  8822. for (var i = 0, ci; ci = this.children[i]; i++) {
  8823. if (ci === node) {
  8824. this.children.splice(i, 1);
  8825. break;
  8826. }
  8827. }
  8828. this.children.push(node);
  8829. node.parentNode = this;
  8830. return node;
  8831. }
  8832. },
  8833. /**
  8834. * 在传入节点的前面插入一个节点
  8835. * @method insertBefore
  8836. * @param { UE.uNode } target 要插入的节点
  8837. * @param { UE.uNode } source 在该参数节点前面插入
  8838. * @return { UE.uNode } 返回刚插入的子节点
  8839. * @example
  8840. * ```javascript
  8841. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  8842. * ```
  8843. */
  8844. insertBefore: function (target, source) {
  8845. if (this.children) {
  8846. if (target.parentNode) {
  8847. target.parentNode.removeChild(target);
  8848. }
  8849. for (var i = 0, ci; ci = this.children[i]; i++) {
  8850. if (ci === source) {
  8851. this.children.splice(i, 0, target);
  8852. target.parentNode = this;
  8853. return target;
  8854. }
  8855. }
  8856. }
  8857. },
  8858. /**
  8859. * 在传入节点的后面插入一个节点
  8860. * @method insertAfter
  8861. * @param { UE.uNode } target 要插入的节点
  8862. * @param { UE.uNode } source 在该参数节点后面插入
  8863. * @return { UE.uNode } 返回刚插入的子节点
  8864. * @example
  8865. * ```javascript
  8866. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  8867. * ```
  8868. */
  8869. insertAfter: function (target, source) {
  8870. if (this.children) {
  8871. if (target.parentNode) {
  8872. target.parentNode.removeChild(target);
  8873. }
  8874. for (var i = 0, ci; ci = this.children[i]; i++) {
  8875. if (ci === source) {
  8876. this.children.splice(i + 1, 0, target);
  8877. target.parentNode = this;
  8878. return target;
  8879. }
  8880. }
  8881. }
  8882. },
  8883. /**
  8884. * 从当前节点的子节点列表中,移除节点
  8885. * @method removeChild
  8886. * @param { UE.uNode } node 要移除的节点引用
  8887. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  8888. * @return { * } 返回刚移除的子节点
  8889. * @example
  8890. * ```javascript
  8891. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  8892. * ```
  8893. */
  8894. removeChild: function (node, keepChildren) {
  8895. if (this.children) {
  8896. for (var i = 0, ci; ci = this.children[i]; i++) {
  8897. if (ci === node) {
  8898. this.children.splice(i, 1);
  8899. ci.parentNode = null;
  8900. if (keepChildren && ci.children && ci.children.length) {
  8901. for (var j = 0, cj; cj = ci.children[j]; j++) {
  8902. this.children.splice(i + j, 0, cj);
  8903. cj.parentNode = this;
  8904. }
  8905. }
  8906. return ci;
  8907. }
  8908. }
  8909. }
  8910. },
  8911. /**
  8912. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  8913. * @method getAttr
  8914. * @param { String } attrName 要获取的属性名称
  8915. * @return { * } 返回attrs对象下的属性值
  8916. * @example
  8917. * ```javascript
  8918. * node.getAttr('title');
  8919. * ```
  8920. */
  8921. getAttr: function (attrName) {
  8922. return this.attrs && this.attrs[attrName.toLowerCase()]
  8923. },
  8924. /**
  8925. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  8926. * @method setAttr
  8927. * @param { String } attrName 要设置的属性名称
  8928. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  8929. * @return { * } 返回attrs对象下的属性值
  8930. * @example
  8931. * ```javascript
  8932. * node.setAttr('title','标题');
  8933. * ```
  8934. */
  8935. setAttr: function (attrName, attrVal) {
  8936. if (!attrName) {
  8937. delete this.attrs;
  8938. return;
  8939. }
  8940. if (!this.attrs) {
  8941. this.attrs = {};
  8942. }
  8943. if (utils.isObject(attrName)) {
  8944. for (var a in attrName) {
  8945. if (!attrName[a]) {
  8946. delete this.attrs[a]
  8947. } else {
  8948. this.attrs[a.toLowerCase()] = attrName[a];
  8949. }
  8950. }
  8951. } else {
  8952. if (!attrVal) {
  8953. delete this.attrs[attrName]
  8954. } else {
  8955. this.attrs[attrName.toLowerCase()] = attrVal;
  8956. }
  8957. }
  8958. },
  8959. /**
  8960. * 获取当前节点在父节点下的位置索引
  8961. * @method getIndex
  8962. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  8963. * @example
  8964. * ```javascript
  8965. * node.getIndex();
  8966. * ```
  8967. */
  8968. getIndex: function () {
  8969. var parent = this.parentNode;
  8970. for (var i = 0, ci; ci = parent.children[i]; i++) {
  8971. if (ci === this) {
  8972. return i;
  8973. }
  8974. }
  8975. return -1;
  8976. },
  8977. /**
  8978. * 在当前节点下,根据id查找节点
  8979. * @method getNodeById
  8980. * @param { String } id 要查找的id
  8981. * @return { UE.uNode } 返回找到的节点
  8982. * @example
  8983. * ```javascript
  8984. * node.getNodeById('textId');
  8985. * ```
  8986. */
  8987. getNodeById: function (id) {
  8988. var node;
  8989. if (this.children && this.children.length) {
  8990. for (var i = 0, ci; ci = this.children[i++];) {
  8991. if (node = getNodeById(ci, id)) {
  8992. return node;
  8993. }
  8994. }
  8995. }
  8996. },
  8997. /**
  8998. * 在当前节点下,根据元素名称查找节点列表
  8999. * @method getNodesByTagName
  9000. * @param { String } tagNames 要查找的元素名称
  9001. * @return { Array } 返回找到的节点列表
  9002. * @example
  9003. * ```javascript
  9004. * node.getNodesByTagName('span');
  9005. * ```
  9006. */
  9007. getNodesByTagName: function (tagNames) {
  9008. tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
  9009. var arr = [], me = this;
  9010. utils.each(tagNames, function (tagName) {
  9011. if (me.children && me.children.length) {
  9012. for (var i = 0, ci; ci = me.children[i++];) {
  9013. getNodesByTagName(ci, tagName, arr)
  9014. }
  9015. }
  9016. });
  9017. return arr;
  9018. },
  9019. /**
  9020. * 根据样式名称,获取节点的样式值
  9021. * @method getStyle
  9022. * @param { String } name 要获取的样式名称
  9023. * @return { String } 返回样式值
  9024. * @example
  9025. * ```javascript
  9026. * node.getStyle('font-size');
  9027. * ```
  9028. */
  9029. getStyle: function (name) {
  9030. var cssStyle = this.getAttr('style');
  9031. if (!cssStyle) {
  9032. return ''
  9033. }
  9034. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)', 'i');
  9035. var match = cssStyle.match(reg);
  9036. if (match && match[0]) {
  9037. return match[2]
  9038. }
  9039. return '';
  9040. },
  9041. /**
  9042. * 给节点设置样式
  9043. * @method setStyle
  9044. * @param { String } name 要设置的的样式名称
  9045. * @param { String } val 要设置的的样值
  9046. * @example
  9047. * ```javascript
  9048. * node.setStyle('font-size', '12px');
  9049. * ```
  9050. */
  9051. setStyle: function (name, val) {
  9052. function exec(name, val) {
  9053. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
  9054. cssStyle = cssStyle.replace(reg, '$1');
  9055. if (val) {
  9056. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  9057. }
  9058. }
  9059. var cssStyle = this.getAttr('style');
  9060. if (!cssStyle) {
  9061. cssStyle = '';
  9062. }
  9063. if (utils.isObject(name)) {
  9064. for (var a in name) {
  9065. exec(a, name[a])
  9066. }
  9067. } else {
  9068. exec(name, val)
  9069. }
  9070. this.setAttr('style', utils.trim(cssStyle))
  9071. },
  9072. /**
  9073. * 传入一个函数,递归遍历当前节点下的所有节点
  9074. * @method traversal
  9075. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  9076. * @example
  9077. * ```javascript
  9078. * traversal(node, function(){
  9079. * console.log(node.type);
  9080. * });
  9081. * ```
  9082. */
  9083. traversal: function (fn) {
  9084. if (this.children && this.children.length) {
  9085. nodeTraversal(this, fn);
  9086. }
  9087. return this;
  9088. }
  9089. }
  9090. })();
  9091. // core/htmlparser.js
  9092. /**
  9093. * html字符串转换成uNode节点
  9094. * @file
  9095. * @module UE
  9096. * @since 1.2.6.1
  9097. */
  9098. /**
  9099. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  9100. * @unfile
  9101. * @module UE
  9102. */
  9103. /**
  9104. * html字符串转换成uNode节点的静态方法
  9105. * @method htmlparser
  9106. * @param { String } htmlstr 要转换的html代码
  9107. * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
  9108. * @return { uNode } 给定的html片段转换形成的uNode对象
  9109. * @example
  9110. * ```javascript
  9111. * var root = UE.htmlparser('<p><b>htmlparser</b></p>', true);
  9112. * ```
  9113. */
  9114. var htmlparser = UE.htmlparser = function (htmlstr, ignoreBlank) {
  9115. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  9116. //先去掉了,加上的原因忘了,这里先记录
  9117. var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  9118. re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  9119. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  9120. var allowEmptyTags = {
  9121. b: 1, code: 1, i: 1, u: 1, strike: 1, s: 1, tt: 1, strong: 1, q: 1, samp: 1, em: 1, span: 1,
  9122. sub: 1, img: 1, sup: 1, font: 1, big: 1, small: 1, iframe: 1, a: 1, br: 1, pre: 1
  9123. };
  9124. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
  9125. if (!ignoreBlank) {
  9126. htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n' + (ignoreBlank ? '' : ' ') + ']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n' + (ignoreBlank ? '' : ' ') + ']*', 'g'), function (a, b) {
  9127. //br暂时单独处理
  9128. if (b && allowEmptyTags[b.toLowerCase()]) {
  9129. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, '');
  9130. }
  9131. return a.replace(new RegExp('^[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+'), '').replace(new RegExp('[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+$'), '');
  9132. });
  9133. }
  9134. var notTransAttrs = {
  9135. 'href': 1,
  9136. 'src': 1
  9137. };
  9138. var uNode = UE.uNode,
  9139. needParentNode = {
  9140. 'td': 'tr',
  9141. 'tr': ['tbody', 'thead', 'tfoot'],
  9142. 'tbody': 'table',
  9143. 'th': 'tr',
  9144. 'thead': 'table',
  9145. 'tfoot': 'table',
  9146. 'caption': 'table',
  9147. 'li': ['ul', 'ol'],
  9148. 'dt': 'dl',
  9149. 'dd': 'dl',
  9150. 'option': 'select'
  9151. },
  9152. needChild = {
  9153. 'ol': 'li',
  9154. 'ul': 'li'
  9155. };
  9156. function text(parent, data) {
  9157. if (needChild[parent.tagName]) {
  9158. var tmpNode = uNode.createElement(needChild[parent.tagName]);
  9159. parent.appendChild(tmpNode);
  9160. tmpNode.appendChild(uNode.createText(data));
  9161. parent = tmpNode;
  9162. } else {
  9163. parent.appendChild(uNode.createText(data));
  9164. }
  9165. }
  9166. function element(parent, tagName, htmlattr) {
  9167. var needParentTag;
  9168. if (needParentTag = needParentNode[tagName]) {
  9169. var tmpParent = parent, hasParent;
  9170. while (tmpParent.type != 'root') {
  9171. if (utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName) {
  9172. parent = tmpParent;
  9173. hasParent = true;
  9174. break;
  9175. }
  9176. tmpParent = tmpParent.parentNode;
  9177. }
  9178. if (!hasParent) {
  9179. parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
  9180. }
  9181. }
  9182. //按dtd处理嵌套
  9183. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  9184. // parent = parent.parentNode;
  9185. var elm = new uNode({
  9186. parentNode: parent,
  9187. type: 'element',
  9188. tagName: tagName.toLowerCase(),
  9189. //是自闭合的处理一下
  9190. children: dtd.$empty[tagName] ? null : []
  9191. });
  9192. //如果属性存在,处理属性
  9193. if (htmlattr) {
  9194. var attrs = {}, match;
  9195. while (match = re_attr.exec(htmlattr)) {
  9196. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
  9197. }
  9198. elm.attrs = attrs;
  9199. }
  9200. //trace:3970
  9201. // //如果parent下不能放elm
  9202. // if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
  9203. // parent = parent.parentNode;
  9204. // elm.parentNode = parent;
  9205. // }
  9206. parent.children.push(elm);
  9207. //如果是自闭合节点返回父亲节点
  9208. return dtd.$empty[tagName] ? parent : elm
  9209. }
  9210. function comment(parent, data) {
  9211. parent.children.push(new uNode({
  9212. type: 'comment',
  9213. data: data,
  9214. parentNode: parent
  9215. }));
  9216. }
  9217. var match, currentIndex = 0, nextIndex = 0;
  9218. //设置根节点
  9219. var root = new uNode({
  9220. type: 'root',
  9221. children: []
  9222. });
  9223. var currentParent = root;
  9224. while (match = re_tag.exec(htmlstr)) {
  9225. currentIndex = match.index;
  9226. try {
  9227. if (currentIndex > nextIndex) {
  9228. //text node
  9229. text(currentParent, htmlstr.slice(nextIndex, currentIndex));
  9230. }
  9231. if (match[3]) {
  9232. if (dtd.$cdata[currentParent.tagName]) {
  9233. text(currentParent, match[0]);
  9234. } else {
  9235. //start tag
  9236. currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
  9237. }
  9238. } else if (match[1]) {
  9239. if (currentParent.type != 'root') {
  9240. if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
  9241. text(currentParent, match[0]);
  9242. } else {
  9243. var tmpParent = currentParent;
  9244. while (currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()) {
  9245. currentParent = currentParent.parentNode;
  9246. if (currentParent.type == 'root') {
  9247. currentParent = tmpParent;
  9248. throw 'break'
  9249. }
  9250. }
  9251. //end tag
  9252. currentParent = currentParent.parentNode;
  9253. }
  9254. }
  9255. } else if (match[2]) {
  9256. //comment
  9257. comment(currentParent, match[2])
  9258. }
  9259. } catch (e) {
  9260. }
  9261. nextIndex = re_tag.lastIndex;
  9262. }
  9263. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  9264. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  9265. if (nextIndex < htmlstr.length) {
  9266. text(currentParent, htmlstr.slice(nextIndex));
  9267. }
  9268. return root;
  9269. };
  9270. // core/filternode.js
  9271. /**
  9272. * UE过滤节点的静态方法
  9273. * @file
  9274. */
  9275. /**
  9276. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  9277. * @module UE
  9278. */
  9279. /**
  9280. * 根据传入节点和过滤规则过滤相应节点
  9281. * @module UE
  9282. * @since 1.2.6.1
  9283. * @method filterNode
  9284. * @param { Object } root 指定root节点
  9285. * @param { Object } rules 过滤规则json对象
  9286. * @example
  9287. * ```javascript
  9288. * UE.filterNode(root,editor.options.filterRules);
  9289. * ```
  9290. */
  9291. var filterNode = UE.filterNode = function () {
  9292. function filterNode(node, rules) {
  9293. switch (node.type) {
  9294. case 'text':
  9295. break;
  9296. case 'element':
  9297. var val;
  9298. if (val = rules[node.tagName]) {
  9299. if (val === '-') {
  9300. node.parentNode.removeChild(node)
  9301. } else if (utils.isFunction(val)) {
  9302. var parentNode = node.parentNode,
  9303. index = node.getIndex();
  9304. val(node);
  9305. if (node.parentNode) {
  9306. if (node.children) {
  9307. for (var i = 0, ci; ci = node.children[i];) {
  9308. filterNode(ci, rules);
  9309. if (ci.parentNode) {
  9310. i++;
  9311. }
  9312. }
  9313. }
  9314. } else {
  9315. for (var i = index, ci; ci = parentNode.children[i];) {
  9316. filterNode(ci, rules);
  9317. if (ci.parentNode) {
  9318. i++;
  9319. }
  9320. }
  9321. }
  9322. } else {
  9323. var attrs = val['$'];
  9324. if (attrs && node.attrs) {
  9325. var tmpAttrs = {}, tmpVal;
  9326. for (var a in attrs) {
  9327. tmpVal = node.getAttr(a);
  9328. //todo 只先对style单独处理
  9329. if (a == 'style' && utils.isArray(attrs[a])) {
  9330. var tmpCssStyle = [];
  9331. utils.each(attrs[a], function (v) {
  9332. var tmp;
  9333. if (tmp = node.getStyle(v)) {
  9334. tmpCssStyle.push(v + ':' + tmp);
  9335. }
  9336. });
  9337. tmpVal = tmpCssStyle.join(';')
  9338. }
  9339. if (tmpVal) {
  9340. tmpAttrs[a] = tmpVal;
  9341. }
  9342. }
  9343. node.attrs = tmpAttrs;
  9344. }
  9345. if (node.children) {
  9346. for (var i = 0, ci; ci = node.children[i];) {
  9347. filterNode(ci, rules);
  9348. if (ci.parentNode) {
  9349. i++;
  9350. }
  9351. }
  9352. }
  9353. }
  9354. } else {
  9355. //如果不在名单里扣出子节点并删除该节点,cdata除外
  9356. if (dtd.$cdata[node.tagName]) {
  9357. node.parentNode.removeChild(node)
  9358. } else {
  9359. var parentNode = node.parentNode,
  9360. index = node.getIndex();
  9361. node.parentNode.removeChild(node, true);
  9362. for (var i = index, ci; ci = parentNode.children[i];) {
  9363. filterNode(ci, rules);
  9364. if (ci.parentNode) {
  9365. i++;
  9366. }
  9367. }
  9368. }
  9369. }
  9370. break;
  9371. case 'comment':
  9372. node.parentNode.removeChild(node)
  9373. }
  9374. }
  9375. return function (root, rules) {
  9376. if (utils.isEmptyObject(rules)) {
  9377. return root;
  9378. }
  9379. var val;
  9380. if (val = rules['-']) {
  9381. utils.each(val.split(' '), function (k) {
  9382. rules[k] = '-'
  9383. })
  9384. }
  9385. for (var i = 0, ci; ci = root.children[i];) {
  9386. filterNode(ci, rules);
  9387. if (ci.parentNode) {
  9388. i++;
  9389. }
  9390. }
  9391. return root;
  9392. }
  9393. }();
  9394. // core/plugin.js
  9395. /**
  9396. * Created with JetBrains PhpStorm.
  9397. * User: campaign
  9398. * Date: 10/8/13
  9399. * Time: 6:15 PM
  9400. * To change this template use File | Settings | File Templates.
  9401. */
  9402. UE.plugin = function () {
  9403. var _plugins = {};
  9404. return {
  9405. register: function (pluginName, fn, oldOptionName, afterDisabled) {
  9406. if (oldOptionName && utils.isFunction(oldOptionName)) {
  9407. afterDisabled = oldOptionName;
  9408. oldOptionName = null
  9409. }
  9410. _plugins[pluginName] = {
  9411. optionName: oldOptionName || pluginName,
  9412. execFn: fn,
  9413. //当插件被禁用时执行
  9414. afterDisabled: afterDisabled
  9415. }
  9416. },
  9417. load: function (editor) {
  9418. utils.each(_plugins, function (plugin) {
  9419. var _export = plugin.execFn.call(editor);
  9420. if (editor.options[plugin.optionName] !== false) {
  9421. if (_export) {
  9422. //后边需要再做扩展
  9423. utils.each(_export, function (v, k) {
  9424. switch (k.toLowerCase()) {
  9425. case 'shortcutkey':
  9426. editor.addshortcutkey(v);
  9427. break;
  9428. case 'bindevents':
  9429. utils.each(v, function (fn, eventName) {
  9430. editor.addListener(eventName, fn);
  9431. });
  9432. break;
  9433. case 'bindmultievents':
  9434. utils.each(utils.isArray(v) ? v : [v], function (event) {
  9435. var types = utils.trim(event.type).split(/\s+/);
  9436. utils.each(types, function (eventName) {
  9437. editor.addListener(eventName, event.handler);
  9438. });
  9439. });
  9440. break;
  9441. case 'commands':
  9442. utils.each(v, function (execFn, execName) {
  9443. editor.commands[execName] = execFn
  9444. });
  9445. break;
  9446. case 'outputrule':
  9447. editor.addOutputRule(v);
  9448. break;
  9449. case 'inputrule':
  9450. editor.addInputRule(v);
  9451. break;
  9452. case 'defaultoptions':
  9453. editor.setOpt(v)
  9454. }
  9455. })
  9456. }
  9457. } else if (plugin.afterDisabled) {
  9458. plugin.afterDisabled.call(editor)
  9459. }
  9460. });
  9461. //向下兼容
  9462. utils.each(UE.plugins, function (plugin) {
  9463. plugin.call(editor);
  9464. });
  9465. },
  9466. run: function (pluginName, editor) {
  9467. var plugin = _plugins[pluginName];
  9468. if (plugin) {
  9469. plugin.exeFn.call(editor)
  9470. }
  9471. }
  9472. }
  9473. }();
  9474. // core/keymap.js
  9475. var keymap = UE.keymap = {
  9476. 'Backspace': 8,
  9477. 'Tab': 9,
  9478. 'Enter': 13,
  9479. 'Shift': 16,
  9480. 'Control': 17,
  9481. 'Alt': 18,
  9482. 'CapsLock': 20,
  9483. 'Esc': 27,
  9484. 'Spacebar': 32,
  9485. 'PageUp': 33,
  9486. 'PageDown': 34,
  9487. 'End': 35,
  9488. 'Home': 36,
  9489. 'Left': 37,
  9490. 'Up': 38,
  9491. 'Right': 39,
  9492. 'Down': 40,
  9493. 'Insert': 45,
  9494. 'Del': 46,
  9495. 'NumLock': 144,
  9496. 'Cmd': 91,
  9497. '=': 187,
  9498. '-': 189,
  9499. "b": 66,
  9500. 'i': 73,
  9501. //回退
  9502. 'z': 90,
  9503. 'y': 89,
  9504. //粘贴
  9505. 'v': 86,
  9506. 'x': 88,
  9507. 's': 83,
  9508. 'n': 78
  9509. };
  9510. // core/localstorage.js
  9511. //存储媒介封装
  9512. var LocalStorage = UE.LocalStorage = (function () {
  9513. var storage = window.localStorage || getUserData() || null,
  9514. LOCAL_FILE = 'localStorage';
  9515. return {
  9516. saveLocalData: function (key, data) {
  9517. if (storage && data) {
  9518. storage.setItem(key, data);
  9519. return true;
  9520. }
  9521. return false;
  9522. },
  9523. getLocalData: function (key) {
  9524. if (storage) {
  9525. return storage.getItem(key);
  9526. }
  9527. return null;
  9528. },
  9529. removeItem: function (key) {
  9530. storage && storage.removeItem(key);
  9531. }
  9532. };
  9533. function getUserData() {
  9534. var container = document.createElement("div");
  9535. container.style.display = "none";
  9536. if (!container.addBehavior) {
  9537. return null;
  9538. }
  9539. container.addBehavior("#default#userdata");
  9540. return {
  9541. getItem: function (key) {
  9542. var result = null;
  9543. try {
  9544. document.body.appendChild(container);
  9545. container.load(LOCAL_FILE);
  9546. result = container.getAttribute(key);
  9547. document.body.removeChild(container);
  9548. } catch (e) {
  9549. }
  9550. return result;
  9551. },
  9552. setItem: function (key, value) {
  9553. document.body.appendChild(container);
  9554. container.setAttribute(key, value);
  9555. container.save(LOCAL_FILE);
  9556. document.body.removeChild(container);
  9557. },
  9558. //// 暂时没有用到
  9559. //clear: function () {
  9560. //
  9561. // var expiresTime = new Date();
  9562. // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
  9563. // document.body.appendChild(container);
  9564. // container.expires = expiresTime.toUTCString();
  9565. // container.save(LOCAL_FILE);
  9566. // document.body.removeChild(container);
  9567. //
  9568. //},
  9569. removeItem: function (key) {
  9570. document.body.appendChild(container);
  9571. container.removeAttribute(key);
  9572. container.save(LOCAL_FILE);
  9573. document.body.removeChild(container);
  9574. }
  9575. };
  9576. }
  9577. })();
  9578. (function () {
  9579. var ROOTKEY = 'ueditor_preference';
  9580. UE.Editor.prototype.setPreferences = function (key, value) {
  9581. var obj = {};
  9582. if (utils.isString(key)) {
  9583. obj[key] = value;
  9584. } else {
  9585. obj = key;
  9586. }
  9587. var data = LocalStorage.getLocalData(ROOTKEY);
  9588. if (data && (data = utils.str2json(data))) {
  9589. utils.extend(data, obj);
  9590. } else {
  9591. data = obj;
  9592. }
  9593. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
  9594. };
  9595. UE.Editor.prototype.getPreferences = function (key) {
  9596. var data = LocalStorage.getLocalData(ROOTKEY);
  9597. if (data && (data = utils.str2json(data))) {
  9598. return key ? data[key] : data
  9599. }
  9600. return null;
  9601. };
  9602. UE.Editor.prototype.removePreferences = function (key) {
  9603. var data = LocalStorage.getLocalData(ROOTKEY);
  9604. if (data && (data = utils.str2json(data))) {
  9605. data[key] = undefined;
  9606. delete data[key]
  9607. }
  9608. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
  9609. };
  9610. })();
  9611. // plugins/defaultfilter.js
  9612. ///import core
  9613. ///plugin 编辑器默认的过滤转换机制
  9614. UE.plugins['defaultfilter'] = function () {
  9615. var me = this;
  9616. me.setOpt({
  9617. // 'allowDivTransToP': true,
  9618. 'disabledTableInTable': true
  9619. });
  9620. //默认的过滤处理
  9621. //进入编辑器的内容处理
  9622. me.addInputRule(function (root) {
  9623. var allowDivTransToP = this.options.allowDivTransToP;
  9624. var val;
  9625. function tdParent(node) {
  9626. while (node && node.type == 'element') {
  9627. if (node.tagName == 'td') {
  9628. return true;
  9629. }
  9630. node = node.parentNode;
  9631. }
  9632. return false;
  9633. }
  9634. //进行默认的处理
  9635. root.traversal(function (node) {
  9636. if (node.type == 'element') {
  9637. if (!dtd.$cdata[node.tagName] && me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9638. if (!node.firstChild()) node.parentNode.removeChild(node);
  9639. else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9640. node.parentNode.removeChild(node, true)
  9641. }
  9642. return;
  9643. }
  9644. switch (node.tagName) {
  9645. case 'style':
  9646. case 'script':
  9647. node.setAttr({
  9648. cdata_tag: node.tagName,
  9649. cdata_data: (node.innerHTML() || ''),
  9650. '_ue_custom_node_': 'true'
  9651. });
  9652. node.tagName = 'div';
  9653. node.innerHTML('');
  9654. break;
  9655. case 'a':
  9656. if (val = node.getAttr('href')) {
  9657. node.setAttr('_href', val)
  9658. }
  9659. break;
  9660. case 'img':
  9661. //todo base64暂时去掉,后边做远程图片上传后,干掉这个
  9662. if (val = node.getAttr('src')) {
  9663. if (/^data:/.test(val)) {
  9664. node.parentNode.removeChild(node);
  9665. break;
  9666. }
  9667. }
  9668. node.setAttr('_src', node.getAttr('src'));
  9669. break;
  9670. case 'span':
  9671. if (browser.webkit && (val = node.getStyle('white-space'))) {
  9672. if (/nowrap|normal/.test(val)) {
  9673. node.setStyle('white-space', '');
  9674. if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
  9675. node.parentNode.removeChild(node, true)
  9676. }
  9677. }
  9678. }
  9679. val = node.getAttr('id');
  9680. if (val && /^_baidu_bookmark_/i.test(val)) {
  9681. node.parentNode.removeChild(node)
  9682. }
  9683. break;
  9684. case 'p':
  9685. if (val = node.getAttr('align')) {
  9686. node.setAttr('align');
  9687. node.setStyle('text-align', val)
  9688. }
  9689. //trace:3431
  9690. // var cssStyle = node.getAttr('style');
  9691. // if (cssStyle) {
  9692. // cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
  9693. // node.setAttr('style', cssStyle)
  9694. //
  9695. // }
  9696. //p标签不允许嵌套
  9697. utils.each(node.children, function (n) {
  9698. if (n.type == 'element' && n.tagName == 'p') {
  9699. var next = n.nextSibling();
  9700. node.parentNode.insertAfter(n, node);
  9701. var last = n;
  9702. while (next) {
  9703. var tmp = next.nextSibling();
  9704. node.parentNode.insertAfter(next, last);
  9705. last = next;
  9706. next = tmp;
  9707. }
  9708. return false;
  9709. }
  9710. });
  9711. if (!node.firstChild()) {
  9712. node.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
  9713. }
  9714. break;
  9715. case 'div':
  9716. if (node.getAttr('cdata_tag')) {
  9717. break;
  9718. }
  9719. //针对代码这里不处理插入代码的div
  9720. val = node.getAttr('class');
  9721. if (val && /^line number\d+/.test(val)) {
  9722. break;
  9723. }
  9724. if (!allowDivTransToP) {
  9725. break;
  9726. }
  9727. var tmpNode, p = UE.uNode.createElement('p');
  9728. while (tmpNode = node.firstChild()) {
  9729. if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
  9730. p.appendChild(tmpNode);
  9731. } else {
  9732. if (p.firstChild()) {
  9733. node.parentNode.insertBefore(p, node);
  9734. p = UE.uNode.createElement('p');
  9735. } else {
  9736. node.parentNode.insertBefore(tmpNode, node);
  9737. }
  9738. }
  9739. }
  9740. if (p.firstChild()) {
  9741. node.parentNode.insertBefore(p, node);
  9742. }
  9743. node.parentNode.removeChild(node);
  9744. break;
  9745. case 'dl':
  9746. node.tagName = 'ul';
  9747. break;
  9748. case 'dt':
  9749. case 'dd':
  9750. node.tagName = 'li';
  9751. break;
  9752. case 'li':
  9753. var className = node.getAttr('class');
  9754. if (!className || !/list\-/.test(className)) {
  9755. node.setAttr()
  9756. }
  9757. var tmpNodes = node.getNodesByTagName('ol ul');
  9758. UE.utils.each(tmpNodes, function (n) {
  9759. node.parentNode.insertAfter(n, node);
  9760. });
  9761. break;
  9762. case 'td':
  9763. case 'th':
  9764. case 'caption':
  9765. if (!node.children || !node.children.length) {
  9766. node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br'))
  9767. }
  9768. break;
  9769. case 'table':
  9770. if (me.options.disabledTableInTable && tdParent(node)) {
  9771. node.parentNode.insertBefore(UE.uNode.createText(node.innerText()), node);
  9772. node.parentNode.removeChild(node)
  9773. }
  9774. }
  9775. }
  9776. // if(node.type == 'comment'){
  9777. // node.parentNode.removeChild(node);
  9778. // }
  9779. })
  9780. });
  9781. //从编辑器出去的内容处理
  9782. me.addOutputRule(function (root) {
  9783. var val;
  9784. root.traversal(function (node) {
  9785. if (node.type == 'element') {
  9786. if (me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9787. if (!node.firstChild()) node.parentNode.removeChild(node);
  9788. else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
  9789. node.parentNode.removeChild(node, true)
  9790. }
  9791. return;
  9792. }
  9793. switch (node.tagName) {
  9794. case 'div':
  9795. if (val = node.getAttr('cdata_tag')) {
  9796. node.tagName = val;
  9797. node.appendChild(UE.uNode.createText(node.getAttr('cdata_data')));
  9798. node.setAttr({cdata_tag: '', cdata_data: '', '_ue_custom_node_': ''});
  9799. }
  9800. break;
  9801. case 'a':
  9802. if (val = node.getAttr('_href')) {
  9803. node.setAttr({
  9804. 'href': utils.html(val),
  9805. '_href': ''
  9806. })
  9807. }
  9808. break;
  9809. case 'span':
  9810. val = node.getAttr('id');
  9811. if (val && /^_baidu_bookmark_/i.test(val)) {
  9812. node.parentNode.removeChild(node)
  9813. }
  9814. break;
  9815. case 'img':
  9816. if (val = node.getAttr('_src')) {
  9817. node.setAttr({
  9818. 'src': node.getAttr('_src'),
  9819. '_src': ''
  9820. })
  9821. }
  9822. }
  9823. }
  9824. })
  9825. });
  9826. };
  9827. // plugins/inserthtml.js
  9828. /**
  9829. * 插入html字符串插件
  9830. * @file
  9831. * @since 1.2.6.1
  9832. */
  9833. /**
  9834. * 插入html代码
  9835. * @command inserthtml
  9836. * @method execCommand
  9837. * @param { String } cmd 命令字符串
  9838. * @param { String } html 插入的html字符串
  9839. * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
  9840. * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
  9841. * @example
  9842. * ```javascript
  9843. * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
  9844. * //执行命令,插入<b>CC</b>
  9845. * //插入后的效果 xxx<b>CC</b>xxx
  9846. * //<p>xx|xxx</p> 当前选区为闭合状态
  9847. * //插入<p>CC</p>
  9848. * //结果 <p>xx</p><p>CC</p><p>xxx</p>
  9849. * //<p>xxxx</p>|</p>xxx</p> 当前选区在两个p标签之间
  9850. * //插入 xxxx
  9851. * //结果 <p>xxxx</p><p>xxxx</p></p>xxx</p>
  9852. * ```
  9853. */
  9854. UE.commands['inserthtml'] = {
  9855. execCommand: function (command, html, notNeedFilter) {
  9856. var me = this,
  9857. range,
  9858. div;
  9859. if (!html) {
  9860. return;
  9861. }
  9862. if (me.fireEvent('beforeinserthtml', html) === true) {
  9863. return;
  9864. }
  9865. range = me.selection.getRange();
  9866. div = range.document.createElement('div');
  9867. div.style.display = 'inline';
  9868. if (!notNeedFilter) {
  9869. var root = UE.htmlparser(html);
  9870. //如果给了过滤规则就先进行过滤
  9871. if (me.options.filterRules) {
  9872. UE.filterNode(root, me.options.filterRules);
  9873. }
  9874. //执行默认的处理
  9875. me.filterInputRule(root);
  9876. html = root.toHtml()
  9877. }
  9878. div.innerHTML = utils.trim(html);
  9879. if (!range.collapsed) {
  9880. var tmpNode = range.startContainer;
  9881. if (domUtils.isFillChar(tmpNode)) {
  9882. range.setStartBefore(tmpNode)
  9883. }
  9884. tmpNode = range.endContainer;
  9885. if (domUtils.isFillChar(tmpNode)) {
  9886. range.setEndAfter(tmpNode)
  9887. }
  9888. range.txtToElmBoundary();
  9889. //结束边界可能放到了br的前边,要把br包含进来
  9890. // x[xxx]<br/>
  9891. if (range.endContainer && range.endContainer.nodeType == 1) {
  9892. tmpNode = range.endContainer.childNodes[range.endOffset];
  9893. if (tmpNode && domUtils.isBr(tmpNode)) {
  9894. range.setEndAfter(tmpNode);
  9895. }
  9896. }
  9897. if (range.startOffset == 0) {
  9898. tmpNode = range.startContainer;
  9899. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  9900. tmpNode = range.endContainer;
  9901. if (range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode, 'lastChild')) {
  9902. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  9903. range.setStart(me.body.firstChild, 0).collapse(true)
  9904. }
  9905. }
  9906. }
  9907. !range.collapsed && range.deleteContents();
  9908. if (range.startContainer.nodeType == 1) {
  9909. var child = range.startContainer.childNodes[range.startOffset], pre;
  9910. if (child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)) {
  9911. range.setEnd(pre, pre.childNodes.length).collapse();
  9912. while (child.firstChild) {
  9913. pre.appendChild(child.firstChild);
  9914. }
  9915. domUtils.remove(child);
  9916. }
  9917. }
  9918. }
  9919. var child, parent, pre, tmp, hadBreak = 0, nextNode;
  9920. //如果当前位置选中了fillchar要干掉,要不会产生空行
  9921. if (range.inFillChar()) {
  9922. child = range.startContainer;
  9923. if (domUtils.isFillChar(child)) {
  9924. range.setStartBefore(child).collapse(true);
  9925. domUtils.remove(child);
  9926. } else if (domUtils.isFillChar(child, true)) {
  9927. child.nodeValue = child.nodeValue.replace(fillCharReg, '');
  9928. range.startOffset--;
  9929. range.collapsed && range.collapse(true)
  9930. }
  9931. }
  9932. //列表单独处理
  9933. var li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  9934. if (li) {
  9935. var next, last;
  9936. while (child = div.firstChild) {
  9937. //针对hr单独处理一下先
  9938. while (child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName == 'HR')) {
  9939. next = child.nextSibling;
  9940. range.insertNode(child).collapse();
  9941. last = child;
  9942. child = next;
  9943. }
  9944. if (child) {
  9945. if (/^(ol|ul)$/i.test(child.tagName)) {
  9946. while (child.firstChild) {
  9947. last = child.firstChild;
  9948. domUtils.insertAfter(li, child.firstChild);
  9949. li = li.nextSibling;
  9950. }
  9951. domUtils.remove(child)
  9952. } else {
  9953. var tmpLi;
  9954. next = child.nextSibling;
  9955. tmpLi = me.document.createElement('li');
  9956. domUtils.insertAfter(li, tmpLi);
  9957. tmpLi.appendChild(child);
  9958. last = child;
  9959. child = next;
  9960. li = tmpLi;
  9961. }
  9962. }
  9963. }
  9964. li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  9965. if (domUtils.isEmptyBlock(li)) {
  9966. domUtils.remove(li)
  9967. }
  9968. if (last) {
  9969. range.setStartAfter(last).collapse(true).select(true)
  9970. }
  9971. } else {
  9972. while (child = div.firstChild) {
  9973. if (hadBreak) {
  9974. var p = me.document.createElement('p');
  9975. while (child && (child.nodeType == 3 || !dtd.$block[child.tagName])) {
  9976. nextNode = child.nextSibling;
  9977. p.appendChild(child);
  9978. child = nextNode;
  9979. }
  9980. if (p.firstChild) {
  9981. child = p
  9982. }
  9983. }
  9984. range.insertNode(child);
  9985. nextNode = child.nextSibling;
  9986. if (!hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm(child)) {
  9987. parent = domUtils.findParent(child, function (node) {
  9988. return domUtils.isBlockElm(node);
  9989. });
  9990. if (parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)) {
  9991. if (!dtd[parent.tagName][child.nodeName]) {
  9992. pre = parent;
  9993. } else {
  9994. tmp = child.parentNode;
  9995. while (tmp !== parent) {
  9996. pre = tmp;
  9997. tmp = tmp.parentNode;
  9998. }
  9999. }
  10000. domUtils.breakParent(child, pre || tmp);
  10001. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  10002. var pre = child.previousSibling;
  10003. domUtils.trimWhiteTextNode(pre);
  10004. if (!pre.childNodes.length) {
  10005. domUtils.remove(pre);
  10006. }
  10007. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  10008. if (!browser.ie &&
  10009. (next = child.nextSibling) &&
  10010. domUtils.isBlockElm(next) &&
  10011. next.lastChild &&
  10012. !domUtils.isBr(next.lastChild)) {
  10013. next.appendChild(me.document.createElement('br'));
  10014. }
  10015. hadBreak = 1;
  10016. }
  10017. }
  10018. var next = child.nextSibling;
  10019. if (!div.firstChild && next && domUtils.isBlockElm(next)) {
  10020. range.setStart(next, 0).collapse(true);
  10021. break;
  10022. }
  10023. range.setEndAfter(child).collapse();
  10024. }
  10025. child = range.startContainer;
  10026. if (nextNode && domUtils.isBr(nextNode)) {
  10027. domUtils.remove(nextNode)
  10028. }
  10029. //用chrome可能有空白展位符
  10030. if (domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)) {
  10031. if (nextNode = child.nextSibling) {
  10032. domUtils.remove(child);
  10033. if (nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]) {
  10034. range.setStart(nextNode, 0).collapse(true).shrinkBoundary()
  10035. }
  10036. } else {
  10037. try {
  10038. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  10039. } catch (e) {
  10040. range.setStartBefore(child);
  10041. domUtils.remove(child)
  10042. }
  10043. }
  10044. }
  10045. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  10046. try {
  10047. range.select(true);
  10048. } catch (e) {
  10049. }
  10050. }
  10051. setTimeout(function () {
  10052. range = me.selection.getRange();
  10053. range.scrollToView(me.autoHeightEnabled, me.autoHeightEnabled ? domUtils.getXY(me.iframe).y : 0);
  10054. me.fireEvent('afterinserthtml', html);
  10055. }, 200);
  10056. }
  10057. };
  10058. // plugins/autotypeset.js
  10059. /**
  10060. * 自动排版
  10061. * @file
  10062. * @since 1.2.6.1
  10063. */
  10064. /**
  10065. * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。
  10066. * @command autotypeset
  10067. * @method execCommand
  10068. * @param { String } cmd 命令字符串
  10069. * @example
  10070. * ```javascript
  10071. * editor.execCommand( 'autotypeset' );
  10072. * ```
  10073. */
  10074. UE.plugins['autotypeset'] = function () {
  10075. this.setOpt({
  10076. 'autotypeset': {
  10077. mergeEmptyline: true, //合并空行
  10078. removeClass: true, //去掉冗余的class
  10079. removeEmptyline: false, //去掉空行
  10080. textAlign: "left", //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
  10081. imageBlockLine: 'center', //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
  10082. pasteFilter: false, //根据规则过滤没事粘贴进来的内容
  10083. clearFontSize: false, //去掉所有的内嵌字号,使用编辑器默认的字号
  10084. clearFontFamily: false, //去掉所有的内嵌字体,使用编辑器默认的字体
  10085. removeEmptyNode: false, // 去掉空节点
  10086. //可以去掉的标签
  10087. removeTagNames: utils.extend({div: 1}, dtd.$removeEmpty),
  10088. indent: false, // 行首缩进
  10089. indentValue: '2em', //行首缩进的大小
  10090. bdc2sb: false,
  10091. tobdc: false
  10092. }
  10093. });
  10094. var me = this,
  10095. opt = me.options.autotypeset,
  10096. remainClass = {
  10097. 'selectTdClass': 1,
  10098. 'pagebreak': 1,
  10099. 'anchorclass': 1
  10100. },
  10101. remainTag = {
  10102. 'li': 1
  10103. },
  10104. tags = {
  10105. div: 1,
  10106. p: 1,
  10107. //trace:2183 这些也认为是行
  10108. blockquote: 1, center: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1,
  10109. span: 1
  10110. },
  10111. highlightCont;
  10112. //升级了版本,但配置项目里没有autotypeset
  10113. if (!opt) {
  10114. return;
  10115. }
  10116. readLocalOpts();
  10117. function isLine(node, notEmpty) {
  10118. if (!node || node.nodeType == 3)
  10119. return 0;
  10120. if (domUtils.isBr(node))
  10121. return 1;
  10122. if (node && node.parentNode && tags[node.tagName.toLowerCase()]) {
  10123. if (highlightCont && highlightCont.contains(node)
  10124. ||
  10125. node.getAttribute('pagebreak')
  10126. ) {
  10127. return 0;
  10128. }
  10129. return notEmpty ? !domUtils.isEmptyBlock(node) : domUtils.isEmptyBlock(node, new RegExp('[\\s' + domUtils.fillChar
  10130. + ']', 'g'));
  10131. }
  10132. }
  10133. function removeNotAttributeSpan(node) {
  10134. if (!node.style.cssText) {
  10135. domUtils.removeAttributes(node, ['style']);
  10136. if (node.tagName.toLowerCase() == 'span' && domUtils.hasNoAttributes(node)) {
  10137. domUtils.remove(node, true);
  10138. }
  10139. }
  10140. }
  10141. function autotype(type, html) {
  10142. var me = this, cont;
  10143. if (html) {
  10144. if (!opt.pasteFilter) {
  10145. return;
  10146. }
  10147. cont = me.document.createElement('div');
  10148. cont.innerHTML = html.html;
  10149. } else {
  10150. cont = me.document.body;
  10151. }
  10152. var nodes = domUtils.getElementsByTagName(cont, '*');
  10153. // 行首缩进,段落方向,段间距,段内间距
  10154. for (var i = 0, ci; ci = nodes[i++];) {
  10155. if (me.fireEvent('excludeNodeinautotype', ci) === true) {
  10156. continue;
  10157. }
  10158. //font-size
  10159. if (opt.clearFontSize && ci.style.fontSize) {
  10160. domUtils.removeStyle(ci, 'font-size');
  10161. removeNotAttributeSpan(ci);
  10162. }
  10163. //font-family
  10164. if (opt.clearFontFamily && ci.style.fontFamily) {
  10165. domUtils.removeStyle(ci, 'font-family');
  10166. removeNotAttributeSpan(ci);
  10167. }
  10168. if (isLine(ci)) {
  10169. //合并空行
  10170. if (opt.mergeEmptyline) {
  10171. var next = ci.nextSibling, tmpNode, isBr = domUtils.isBr(ci);
  10172. while (isLine(next)) {
  10173. tmpNode = next;
  10174. next = tmpNode.nextSibling;
  10175. if (isBr && (!next || next && !domUtils.isBr(next))) {
  10176. break;
  10177. }
  10178. domUtils.remove(tmpNode);
  10179. }
  10180. }
  10181. //去掉空行,保留占位的空行
  10182. if (opt.removeEmptyline && domUtils.inDoc(ci, cont) && !remainTag[ci.parentNode.tagName.toLowerCase()]) {
  10183. if (domUtils.isBr(ci)) {
  10184. next = ci.nextSibling;
  10185. if (next && !domUtils.isBr(next)) {
  10186. continue;
  10187. }
  10188. }
  10189. domUtils.remove(ci);
  10190. continue;
  10191. }
  10192. }
  10193. if (isLine(ci, true) && ci.tagName != 'SPAN') {
  10194. if (opt.indent) {
  10195. ci.style.textIndent = opt.indentValue;
  10196. }
  10197. if (opt.textAlign) {
  10198. ci.style.textAlign = opt.textAlign;
  10199. }
  10200. // if(opt.lineHeight)
  10201. // ci.style.lineHeight = opt.lineHeight + 'cm';
  10202. }
  10203. //去掉class,保留的class不去掉
  10204. if (opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()]) {
  10205. if (highlightCont && highlightCont.contains(ci)) {
  10206. continue;
  10207. }
  10208. domUtils.removeAttributes(ci, ['class']);
  10209. }
  10210. //表情不处理
  10211. if (opt.imageBlockLine && ci.tagName.toLowerCase() == 'img' && !ci.getAttribute('emotion')) {
  10212. if (html) {
  10213. var img = ci;
  10214. switch (opt.imageBlockLine) {
  10215. case 'left':
  10216. case 'right':
  10217. case 'none':
  10218. var pN = img.parentNode, tmpNode, pre, next;
  10219. while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
  10220. pN = pN.parentNode;
  10221. }
  10222. tmpNode = pN;
  10223. if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
  10224. if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) {
  10225. return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  10226. }) == 1) {
  10227. pre = tmpNode.previousSibling;
  10228. next = tmpNode.nextSibling;
  10229. if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) {
  10230. pre.appendChild(tmpNode.firstChild);
  10231. while (next.firstChild) {
  10232. pre.appendChild(next.firstChild);
  10233. }
  10234. domUtils.remove(tmpNode);
  10235. domUtils.remove(next);
  10236. } else {
  10237. domUtils.setStyle(tmpNode, 'text-align', '');
  10238. }
  10239. }
  10240. }
  10241. domUtils.setStyle(img, 'float', opt.imageBlockLine);
  10242. break;
  10243. case 'center':
  10244. if (me.queryCommandValue('imagefloat') != 'center') {
  10245. pN = img.parentNode;
  10246. domUtils.setStyle(img, 'float', 'none');
  10247. tmpNode = img;
  10248. while (pN && domUtils.getChildCount(pN, function (node) {
  10249. return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  10250. }) == 1
  10251. && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) {
  10252. tmpNode = pN;
  10253. pN = pN.parentNode;
  10254. }
  10255. var pNode = me.document.createElement('p');
  10256. domUtils.setAttributes(pNode, {
  10257. style: 'text-align:center'
  10258. });
  10259. tmpNode.parentNode.insertBefore(pNode, tmpNode);
  10260. pNode.appendChild(tmpNode);
  10261. domUtils.setStyle(tmpNode, 'float', '');
  10262. }
  10263. }
  10264. } else {
  10265. var range = me.selection.getRange();
  10266. range.selectNode(ci).select();
  10267. me.execCommand('imagefloat', opt.imageBlockLine);
  10268. }
  10269. }
  10270. //去掉冗余的标签
  10271. if (opt.removeEmptyNode) {
  10272. if (opt.removeTagNames[ci.tagName.toLowerCase()] && domUtils.hasNoAttributes(ci) && domUtils.isEmptyBlock(ci)) {
  10273. domUtils.remove(ci);
  10274. }
  10275. }
  10276. }
  10277. if (opt.tobdc) {
  10278. var root = UE.htmlparser(cont.innerHTML);
  10279. root.traversal(function (node) {
  10280. if (node.type == 'text') {
  10281. node.data = ToDBC(node.data)
  10282. }
  10283. });
  10284. cont.innerHTML = root.toHtml()
  10285. }
  10286. if (opt.bdc2sb) {
  10287. var root = UE.htmlparser(cont.innerHTML);
  10288. root.traversal(function (node) {
  10289. if (node.type == 'text') {
  10290. node.data = DBC2SB(node.data)
  10291. }
  10292. });
  10293. cont.innerHTML = root.toHtml()
  10294. }
  10295. if (html) {
  10296. html.html = cont.innerHTML;
  10297. }
  10298. }
  10299. if (opt.pasteFilter) {
  10300. me.addListener('beforepaste', autotype);
  10301. }
  10302. function DBC2SB(str) {
  10303. var result = '';
  10304. for (var i = 0; i < str.length; i++) {
  10305. var code = str.charCodeAt(i); //获取当前字符的unicode编码
  10306. if (code >= 65281 && code <= 65373)//在这个unicode编码范围中的是所有的英文字母已经各种字符
  10307. {
  10308. result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码
  10309. } else if (code == 12288)//空格
  10310. {
  10311. result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
  10312. } else {
  10313. result += str.charAt(i);
  10314. }
  10315. }
  10316. return result;
  10317. }
  10318. function ToDBC(txtstring) {
  10319. txtstring = utils.html(txtstring);
  10320. var tmp = "";
  10321. var mark = "";/*用于判断,如果是html尖括里的标记,则不进行全角的转换*/
  10322. for (var i = 0; i < txtstring.length; i++) {
  10323. if (txtstring.charCodeAt(i) == 32) {
  10324. tmp = tmp + String.fromCharCode(12288);
  10325. } else if (txtstring.charCodeAt(i) < 127) {
  10326. tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248);
  10327. } else {
  10328. tmp += txtstring.charAt(i);
  10329. }
  10330. }
  10331. return tmp;
  10332. }
  10333. function readLocalOpts() {
  10334. var cookieOpt = me.getPreferences('autotypeset');
  10335. utils.extend(me.options.autotypeset, cookieOpt);
  10336. }
  10337. me.commands['autotypeset'] = {
  10338. execCommand: function () {
  10339. me.removeListener('beforepaste', autotype);
  10340. if (opt.pasteFilter) {
  10341. me.addListener('beforepaste', autotype);
  10342. }
  10343. autotype.call(me)
  10344. }
  10345. };
  10346. };
  10347. // plugins/autosubmit.js
  10348. /**
  10349. * 快捷键提交
  10350. * @file
  10351. * @since 1.2.6.1
  10352. */
  10353. /**
  10354. * 提交表单
  10355. * @command autosubmit
  10356. * @method execCommand
  10357. * @param { String } cmd 命令字符串
  10358. * @example
  10359. * ```javascript
  10360. * editor.execCommand( 'autosubmit' );
  10361. * ```
  10362. */
  10363. UE.plugin.register('autosubmit', function () {
  10364. return {
  10365. shortcutkey: {
  10366. "autosubmit": "ctrl+13" //手动提交
  10367. },
  10368. commands: {
  10369. 'autosubmit': {
  10370. execCommand: function () {
  10371. var me = this,
  10372. form = domUtils.findParentByTagName(me.iframe, "form", false);
  10373. if (form) {
  10374. if (me.fireEvent("beforesubmit") === false) {
  10375. return;
  10376. }
  10377. me.sync();
  10378. form.submit();
  10379. }
  10380. }
  10381. }
  10382. }
  10383. }
  10384. });
  10385. // plugins/background.js
  10386. /**
  10387. * 背景插件,为UEditor提供设置背景功能
  10388. * @file
  10389. * @since 1.2.6.1
  10390. */
  10391. UE.plugin.register('background', function () {
  10392. var me = this,
  10393. cssRuleId = 'editor_background',
  10394. isSetColored,
  10395. reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i');
  10396. function stringToObj(str) {
  10397. var obj = {}, styles = str.split(';');
  10398. utils.each(styles, function (v) {
  10399. var index = v.indexOf(':'),
  10400. key = utils.trim(v.substr(0, index)).toLowerCase();
  10401. key && (obj[key] = utils.trim(v.substr(index + 1) || ''));
  10402. });
  10403. return obj;
  10404. }
  10405. function setBackground(obj) {
  10406. if (obj) {
  10407. var styles = [];
  10408. for (var name in obj) {
  10409. if (obj.hasOwnProperty(name)) {
  10410. styles.push(name + ":" + obj[name] + '; ');
  10411. }
  10412. }
  10413. utils.cssRule(cssRuleId, styles.length ? ('body{' + styles.join("") + '}') : '', me.document);
  10414. } else {
  10415. utils.cssRule(cssRuleId, '', me.document)
  10416. }
  10417. }
  10418. //重写editor.hasContent方法
  10419. var orgFn = me.hasContents;
  10420. me.hasContents = function () {
  10421. if (me.queryCommandValue('background')) {
  10422. return true
  10423. }
  10424. return orgFn.apply(me, arguments);
  10425. };
  10426. return {
  10427. bindEvents: {
  10428. 'getAllHtml': function (type, headHtml) {
  10429. var body = this.body,
  10430. su = domUtils.getComputedStyle(body, "background-image"),
  10431. url = "";
  10432. if (su.indexOf(me.options.imagePath) > 0) {
  10433. url = su.substring(su.indexOf(me.options.imagePath), su.length - 1).replace(/"|\(|\)/ig, "");
  10434. } else {
  10435. url = su != "none" ? su.replace(/url\("?|"?\)/ig, "") : "";
  10436. }
  10437. var html = '<style type="text/css">body{';
  10438. var bgObj = {
  10439. "background-color": domUtils.getComputedStyle(body, "background-color") || "#ffffff",
  10440. 'background-image': url ? 'url(' + url + ')' : '',
  10441. 'background-repeat': domUtils.getComputedStyle(body, "background-repeat") || "",
  10442. 'background-position': browser.ie ? (domUtils.getComputedStyle(body, "background-position-x") + " " + domUtils.getComputedStyle(body, "background-position-y")) : domUtils.getComputedStyle(body, "background-position"),
  10443. 'height': domUtils.getComputedStyle(body, "height")
  10444. };
  10445. for (var name in bgObj) {
  10446. if (bgObj.hasOwnProperty(name)) {
  10447. html += name + ":" + bgObj[name] + "; ";
  10448. }
  10449. }
  10450. html += '}</style> ';
  10451. headHtml.push(html);
  10452. },
  10453. 'aftersetcontent': function () {
  10454. if (isSetColored == false) setBackground();
  10455. }
  10456. },
  10457. inputRule: function (root) {
  10458. isSetColored = false;
  10459. utils.each(root.getNodesByTagName('p'), function (p) {
  10460. var styles = p.getAttr('data-background');
  10461. if (styles) {
  10462. isSetColored = true;
  10463. setBackground(stringToObj(styles));
  10464. p.parentNode.removeChild(p);
  10465. }
  10466. })
  10467. },
  10468. outputRule: function (root) {
  10469. var me = this,
  10470. styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
  10471. if (styles) {
  10472. root.appendChild(UE.uNode.createElement('<p style="display:none;" data-background="' + utils.trim(styles[1].replace(/"/g, '').replace(/[\s]+/g, ' ')) + '"><br/></p>'));
  10473. }
  10474. },
  10475. commands: {
  10476. 'background': {
  10477. execCommand: function (cmd, obj) {
  10478. setBackground(obj);
  10479. },
  10480. queryCommandValue: function () {
  10481. var me = this,
  10482. styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg);
  10483. return styles ? stringToObj(styles[1]) : null;
  10484. },
  10485. notNeedUndo: true
  10486. }
  10487. }
  10488. }
  10489. });
  10490. // plugins/image.js
  10491. /**
  10492. * 图片插入、排版插件
  10493. * @file
  10494. * @since 1.2.6.1
  10495. */
  10496. /**
  10497. * 图片对齐方式
  10498. * @command imagefloat
  10499. * @method execCommand
  10500. * @remind 值center为独占一行居中
  10501. * @param { String } cmd 命令字符串
  10502. * @param { String } align 对齐方式,可传left、right、none、center
  10503. * @remaind center表示图片独占一行
  10504. * @example
  10505. * ```javascript
  10506. * editor.execCommand( 'imagefloat', 'center' );
  10507. * ```
  10508. */
  10509. /**
  10510. * 如果选区所在位置是图片区域
  10511. * @command imagefloat
  10512. * @method queryCommandValue
  10513. * @param { String } cmd 命令字符串
  10514. * @return { String } 返回图片对齐方式
  10515. * @example
  10516. * ```javascript
  10517. * editor.queryCommandValue( 'imagefloat' );
  10518. * ```
  10519. */
  10520. UE.commands['imagefloat'] = {
  10521. execCommand: function (cmd, align) {
  10522. var me = this,
  10523. range = me.selection.getRange();
  10524. if (!range.collapsed) {
  10525. var img = range.getClosedNode();
  10526. if (img && img.tagName == 'IMG') {
  10527. switch (align) {
  10528. case 'left':
  10529. case 'right':
  10530. case 'none':
  10531. var pN = img.parentNode, tmpNode, pre, next;
  10532. while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
  10533. pN = pN.parentNode;
  10534. }
  10535. tmpNode = pN;
  10536. if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
  10537. if (!domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function (node) {
  10538. return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
  10539. }) == 1) {
  10540. pre = tmpNode.previousSibling;
  10541. next = tmpNode.nextSibling;
  10542. if (pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre)) {
  10543. pre.appendChild(tmpNode.firstChild);
  10544. while (next.firstChild) {
  10545. pre.appendChild(next.firstChild);
  10546. }
  10547. domUtils.remove(tmpNode);
  10548. domUtils.remove(next);
  10549. } else {
  10550. domUtils.setStyle(tmpNode, 'text-align', '');
  10551. }
  10552. }
  10553. range.selectNode(img).select();
  10554. }
  10555. domUtils.setStyle(img, 'float', align == 'none' ? '' : align);
  10556. if (align == 'none') {
  10557. domUtils.removeAttributes(img, 'align');
  10558. }
  10559. break;
  10560. case 'center':
  10561. if (me.queryCommandValue('imagefloat') != 'center') {
  10562. pN = img.parentNode;
  10563. domUtils.setStyle(img, 'float', '');
  10564. domUtils.removeAttributes(img, 'align');
  10565. tmpNode = img;
  10566. while (pN && domUtils.getChildCount(pN, function (node) {
  10567. return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
  10568. }) == 1
  10569. && (dtd.$inline[pN.tagName] || pN.tagName == 'A')) {
  10570. tmpNode = pN;
  10571. pN = pN.parentNode;
  10572. }
  10573. range.setStartBefore(tmpNode).setCursor(false);
  10574. pN = me.document.createElement('div');
  10575. pN.appendChild(tmpNode);
  10576. domUtils.setStyle(tmpNode, 'float', '');
  10577. me.execCommand('insertHtml', '<p id="_img_parent_tmp" style="text-align:center">' + pN.innerHTML + '</p>');
  10578. tmpNode = me.document.getElementById('_img_parent_tmp');
  10579. tmpNode.removeAttribute('id');
  10580. tmpNode = tmpNode.firstChild;
  10581. range.selectNode(tmpNode).select();
  10582. //去掉后边多余的元素
  10583. next = tmpNode.parentNode.nextSibling;
  10584. if (next && domUtils.isEmptyNode(next)) {
  10585. domUtils.remove(next);
  10586. }
  10587. }
  10588. break;
  10589. }
  10590. }
  10591. }
  10592. },
  10593. queryCommandValue: function () {
  10594. var range = this.selection.getRange(),
  10595. startNode, floatStyle;
  10596. if (range.collapsed) {
  10597. return 'none';
  10598. }
  10599. startNode = range.getClosedNode();
  10600. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  10601. floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align');
  10602. if (floatStyle == 'none') {
  10603. floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle;
  10604. }
  10605. return {
  10606. left: 1,
  10607. right: 1,
  10608. center: 1
  10609. }[floatStyle] ? floatStyle : 'none';
  10610. }
  10611. return 'none';
  10612. },
  10613. queryCommandState: function () {
  10614. var range = this.selection.getRange(),
  10615. startNode;
  10616. if (range.collapsed) return -1;
  10617. startNode = range.getClosedNode();
  10618. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  10619. return 0;
  10620. }
  10621. return -1;
  10622. }
  10623. };
  10624. /**
  10625. * 插入图片
  10626. * @command insertimage
  10627. * @method execCommand
  10628. * @param { String } cmd 命令字符串
  10629. * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
  10630. * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
  10631. * 此时数组的每一个元素都是一个Object类型的图片属性集合。
  10632. * @example
  10633. * ```javascript
  10634. * editor.execCommand( 'insertimage', {
  10635. * src:'a/b/c.jpg',
  10636. * width:'100',
  10637. * height:'100'
  10638. * } );
  10639. * ```
  10640. * @example
  10641. * ```javascript
  10642. * editor.execCommand( 'insertimage', [{
  10643. * src:'a/b/c.jpg',
  10644. * width:'100',
  10645. * height:'100'
  10646. * },{
  10647. * src:'a/b/d.jpg',
  10648. * width:'100',
  10649. * height:'100'
  10650. * }] );
  10651. * ```
  10652. */
  10653. UE.commands['insertimage'] = {
  10654. execCommand: function (cmd, opt) {
  10655. opt = utils.isArray(opt) ? opt : [opt];
  10656. if (!opt.length) {
  10657. return;
  10658. }
  10659. var me = this,
  10660. range = me.selection.getRange(),
  10661. img = range.getClosedNode();
  10662. if (me.fireEvent('beforeinsertimage', opt) === true) {
  10663. return;
  10664. }
  10665. function unhtmlData(imgCi) {
  10666. utils.each('width,height,border,hspace,vspace'.split(','), function (item) {
  10667. if (imgCi[item]) {
  10668. imgCi[item] = parseInt(imgCi[item], 10) || 0;
  10669. }
  10670. });
  10671. utils.each('src,_src'.split(','), function (item) {
  10672. if (imgCi[item]) {
  10673. imgCi[item] = utils.unhtmlForUrl(imgCi[item]);
  10674. }
  10675. });
  10676. utils.each('title,alt'.split(','), function (item) {
  10677. if (imgCi[item]) {
  10678. imgCi[item] = utils.unhtml(imgCi[item]);
  10679. }
  10680. });
  10681. }
  10682. if (img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video") != -1) && !img.getAttribute("word_img")) {
  10683. var first = opt.shift();
  10684. var floatStyle = first['floatStyle'];
  10685. delete first['floatStyle'];
  10686. //// img.style.border = (first.border||0) +"px solid #000";
  10687. //// img.style.margin = (first.margin||0) +"px";
  10688. // img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
  10689. domUtils.setAttributes(img, first);
  10690. me.execCommand('imagefloat', floatStyle);
  10691. if (opt.length > 0) {
  10692. range.setStartAfter(img).setCursor(false, true);
  10693. me.execCommand('insertimage', opt);
  10694. }
  10695. } else {
  10696. var html = [], str = '', ci;
  10697. ci = opt[0];
  10698. if (opt.length == 1) {
  10699. unhtmlData(ci);
  10700. str = '<img src="' + ns.img(ci.src) + '" ' + (ns.img(ci.src) ? ' _src="' + ns.img(ci.src) + '" ' : '') +
  10701. (ci.width ? 'width="' + ci.width + '" ' : '') +
  10702. (ci.height ? ' height="' + ci.height + '" ' : '') +
  10703. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
  10704. (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
  10705. (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
  10706. (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
  10707. (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
  10708. (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
  10709. if (ci['floatStyle'] == 'center') {
  10710. str = '<p style="text-align: center">' + str + '</p>';
  10711. }
  10712. html.push(str);
  10713. } else {
  10714. for (var i = 0; ci = opt[i++];) {
  10715. unhtmlData(ci);
  10716. str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ns.img(ci.src) + '" ' +
  10717. (ci.width ? 'width="' + ci.width + '" ' : '') + (ns.img(ci.src) ? ' _src="' + ns.img(ci.src) + '" ' : '') +
  10718. (ci.height ? ' height="' + ci.height + '" ' : '') +
  10719. ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
  10720. (ci.border || '') + '" ' +
  10721. (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
  10722. html.push(str);
  10723. }
  10724. }
  10725. me.execCommand('insertHtml', html.join(''));
  10726. }
  10727. me.fireEvent('afterinsertimage', opt)
  10728. }
  10729. };
  10730. // plugins/justify.js
  10731. /**
  10732. * 段落格式
  10733. * @file
  10734. * @since 1.2.6.1
  10735. */
  10736. /**
  10737. * 段落对齐方式
  10738. * @command justify
  10739. * @method execCommand
  10740. * @param { String } cmd 命令字符串
  10741. * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
  10742. * @example
  10743. * ```javascript
  10744. * editor.execCommand( 'justify', 'center' );
  10745. * ```
  10746. */
  10747. /**
  10748. * 如果选区所在位置是段落区域,返回当前段落对齐方式
  10749. * @command justify
  10750. * @method queryCommandValue
  10751. * @param { String } cmd 命令字符串
  10752. * @return { String } 返回段落对齐方式
  10753. * @example
  10754. * ```javascript
  10755. * editor.queryCommandValue( 'justify' );
  10756. * ```
  10757. */
  10758. UE.plugins['justify'] = function () {
  10759. var me = this,
  10760. block = domUtils.isBlockElm,
  10761. defaultValue = {
  10762. left: 1,
  10763. right: 1,
  10764. center: 1,
  10765. justify: 1
  10766. },
  10767. doJustify = function (range, style) {
  10768. var bookmark = range.createBookmark(),
  10769. filterFn = function (node) {
  10770. return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
  10771. };
  10772. range.enlarge(true);
  10773. var bookmark2 = range.createBookmark(),
  10774. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  10775. tmpRange = range.cloneRange(),
  10776. tmpNode;
  10777. while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
  10778. if (current.nodeType == 3 || !block(current)) {
  10779. tmpRange.setStartBefore(current);
  10780. while (current && current !== bookmark2.end && !block(current)) {
  10781. tmpNode = current;
  10782. current = domUtils.getNextDomNode(current, false, null, function (node) {
  10783. return !block(node);
  10784. });
  10785. }
  10786. tmpRange.setEndAfter(tmpNode);
  10787. var common = tmpRange.getCommonAncestor();
  10788. if (!domUtils.isBody(common) && block(common)) {
  10789. domUtils.setStyles(common, utils.isString(style) ? {'text-align': style} : style);
  10790. current = common;
  10791. } else {
  10792. var p = range.document.createElement('p');
  10793. domUtils.setStyles(p, utils.isString(style) ? {'text-align': style} : style);
  10794. var frag = tmpRange.extractContents();
  10795. p.appendChild(frag);
  10796. tmpRange.insertNode(p);
  10797. current = p;
  10798. }
  10799. current = domUtils.getNextDomNode(current, false, filterFn);
  10800. } else {
  10801. current = domUtils.getNextDomNode(current, true, filterFn);
  10802. }
  10803. }
  10804. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
  10805. };
  10806. UE.commands['justify'] = {
  10807. execCommand: function (cmdName, align) {
  10808. var range = this.selection.getRange(),
  10809. txt;
  10810. //闭合时单独处理
  10811. if (range.collapsed) {
  10812. txt = this.document.createTextNode('p');
  10813. range.insertNode(txt);
  10814. }
  10815. doJustify(range, align);
  10816. if (txt) {
  10817. range.setStartBefore(txt).collapse(true);
  10818. domUtils.remove(txt);
  10819. }
  10820. range.select();
  10821. return true;
  10822. },
  10823. queryCommandValue: function () {
  10824. var startNode = this.selection.getStart(),
  10825. value = domUtils.getComputedStyle(startNode, 'text-align');
  10826. return defaultValue[value] ? value : 'left';
  10827. },
  10828. queryCommandState: function () {
  10829. var start = this.selection.getStart(),
  10830. cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
  10831. return cell ? -1 : 0;
  10832. }
  10833. };
  10834. };
  10835. // plugins/font.js
  10836. /**
  10837. * 字体颜色,背景色,字号,字体,下划线,删除线
  10838. * @file
  10839. * @since 1.2.6.1
  10840. */
  10841. /**
  10842. * 字体颜色
  10843. * @command forecolor
  10844. * @method execCommand
  10845. * @param { String } cmd 命令字符串
  10846. * @param { String } value 色值(必须十六进制)
  10847. * @example
  10848. * ```javascript
  10849. * editor.execCommand( 'forecolor', '#000' );
  10850. * ```
  10851. */
  10852. /**
  10853. * 返回选区字体颜色
  10854. * @command forecolor
  10855. * @method queryCommandValue
  10856. * @param { String } cmd 命令字符串
  10857. * @return { String } 返回字体颜色
  10858. * @example
  10859. * ```javascript
  10860. * editor.queryCommandValue( 'forecolor' );
  10861. * ```
  10862. */
  10863. /**
  10864. * 字体背景颜色
  10865. * @command backcolor
  10866. * @method execCommand
  10867. * @param { String } cmd 命令字符串
  10868. * @param { String } value 色值(必须十六进制)
  10869. * @example
  10870. * ```javascript
  10871. * editor.execCommand( 'backcolor', '#000' );
  10872. * ```
  10873. */
  10874. /**
  10875. * 返回选区字体颜色
  10876. * @command backcolor
  10877. * @method queryCommandValue
  10878. * @param { String } cmd 命令字符串
  10879. * @return { String } 返回字体背景颜色
  10880. * @example
  10881. * ```javascript
  10882. * editor.queryCommandValue( 'backcolor' );
  10883. * ```
  10884. */
  10885. /**
  10886. * 字体大小
  10887. * @command fontsize
  10888. * @method execCommand
  10889. * @param { String } cmd 命令字符串
  10890. * @param { String } value 字体大小
  10891. * @example
  10892. * ```javascript
  10893. * editor.execCommand( 'fontsize', '14px' );
  10894. * ```
  10895. */
  10896. /**
  10897. * 返回选区字体大小
  10898. * @command fontsize
  10899. * @method queryCommandValue
  10900. * @param { String } cmd 命令字符串
  10901. * @return { String } 返回字体大小
  10902. * @example
  10903. * ```javascript
  10904. * editor.queryCommandValue( 'fontsize' );
  10905. * ```
  10906. */
  10907. /**
  10908. * 字体样式
  10909. * @command fontfamily
  10910. * @method execCommand
  10911. * @param { String } cmd 命令字符串
  10912. * @param { String } value 字体样式
  10913. * @example
  10914. * ```javascript
  10915. * editor.execCommand( 'fontfamily', '微软雅黑' );
  10916. * ```
  10917. */
  10918. /**
  10919. * 返回选区字体样式
  10920. * @command fontfamily
  10921. * @method queryCommandValue
  10922. * @param { String } cmd 命令字符串
  10923. * @return { String } 返回字体样式
  10924. * @example
  10925. * ```javascript
  10926. * editor.queryCommandValue( 'fontfamily' );
  10927. * ```
  10928. */
  10929. /**
  10930. * 字体下划线,与删除线互斥
  10931. * @command underline
  10932. * @method execCommand
  10933. * @param { String } cmd 命令字符串
  10934. * @example
  10935. * ```javascript
  10936. * editor.execCommand( 'underline' );
  10937. * ```
  10938. */
  10939. /**
  10940. * 字体删除线,与下划线互斥
  10941. * @command strikethrough
  10942. * @method execCommand
  10943. * @param { String } cmd 命令字符串
  10944. * @example
  10945. * ```javascript
  10946. * editor.execCommand( 'strikethrough' );
  10947. * ```
  10948. */
  10949. /**
  10950. * 字体边框
  10951. * @command fontborder
  10952. * @method execCommand
  10953. * @param { String } cmd 命令字符串
  10954. * @example
  10955. * ```javascript
  10956. * editor.execCommand( 'fontborder' );
  10957. * ```
  10958. */
  10959. UE.plugins['font'] = function () {
  10960. var me = this,
  10961. fonts = {
  10962. 'forecolor': 'color',
  10963. 'backcolor': 'background-color',
  10964. 'fontsize': 'font-size',
  10965. 'fontfamily': 'font-family',
  10966. 'underline': 'text-decoration',
  10967. 'strikethrough': 'text-decoration',
  10968. 'fontborder': 'border'
  10969. },
  10970. needCmd = {'underline': 1, 'strikethrough': 1, 'fontborder': 1},
  10971. needSetChild = {
  10972. 'forecolor': 'color',
  10973. 'backcolor': 'background-color',
  10974. 'fontsize': 'font-size',
  10975. 'fontfamily': 'font-family'
  10976. };
  10977. me.setOpt({
  10978. 'fontfamily': [
  10979. {name: 'songti', val: '宋体,SimSun'},
  10980. {name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
  10981. {name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
  10982. {name: 'heiti', val: '黑体, SimHei'},
  10983. {name: 'lishu', val: '隶书, SimLi'},
  10984. {name: 'andaleMono', val: 'andale mono'},
  10985. {name: 'arial', val: 'arial, helvetica,sans-serif'},
  10986. {name: 'arialBlack', val: 'arial black,avant garde'},
  10987. {name: 'comicSansMs', val: 'comic sans ms'},
  10988. {name: 'impact', val: 'impact,chicago'},
  10989. {name: 'timesNewRoman', val: 'times new roman'}
  10990. ],
  10991. 'fontsize': [10, 11, 12, 14, 16, 18, 20, 24, 36]
  10992. });
  10993. function mergeWithParent(node) {
  10994. var parent;
  10995. while (parent = node.parentNode) {
  10996. if (parent.tagName == 'SPAN' && domUtils.getChildCount(parent, function (child) {
  10997. return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
  10998. }) == 1) {
  10999. parent.style.cssText += node.style.cssText;
  11000. domUtils.remove(node, true);
  11001. node = parent;
  11002. } else {
  11003. break;
  11004. }
  11005. }
  11006. }
  11007. function mergeChild(rng, cmdName, value) {
  11008. if (needSetChild[cmdName]) {
  11009. rng.adjustmentBoundary();
  11010. if (!rng.collapsed && rng.startContainer.nodeType == 1) {
  11011. var start = rng.startContainer.childNodes[rng.startOffset];
  11012. if (start && domUtils.isTagNode(start, 'span')) {
  11013. var bk = rng.createBookmark();
  11014. utils.each(domUtils.getElementsByTagName(start, 'span'), function (span) {
  11015. if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
  11016. if (cmdName == 'backcolor' && domUtils.getComputedStyle(span, 'background-color').toLowerCase() === value) {
  11017. return;
  11018. }
  11019. domUtils.removeStyle(span, needSetChild[cmdName]);
  11020. if (span.style.cssText.replace(/^\s+$/, '').length == 0) {
  11021. domUtils.remove(span, true)
  11022. }
  11023. });
  11024. rng.moveToBookmark(bk)
  11025. }
  11026. }
  11027. }
  11028. }
  11029. function mergesibling(rng, cmdName, value) {
  11030. var collapsed = rng.collapsed,
  11031. bk = rng.createBookmark(), common;
  11032. if (collapsed) {
  11033. common = bk.start.parentNode;
  11034. while (dtd.$inline[common.tagName]) {
  11035. common = common.parentNode;
  11036. }
  11037. } else {
  11038. common = domUtils.getCommonAncestor(bk.start, bk.end);
  11039. }
  11040. utils.each(domUtils.getElementsByTagName(common, 'span'), function (span) {
  11041. if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
  11042. if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
  11043. if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
  11044. domUtils.remove(span, true);
  11045. } else {
  11046. domUtils.removeStyle(span, 'border');
  11047. }
  11048. return
  11049. }
  11050. if (/border/i.test(span.style.cssText) && span.parentNode.tagName == 'SPAN' && /border/i.test(span.parentNode.style.cssText)) {
  11051. span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, '');
  11052. }
  11053. if (!(cmdName == 'fontborder' && value == 'none')) {
  11054. var next = span.nextSibling;
  11055. while (next && next.nodeType == 1 && next.tagName == 'SPAN') {
  11056. if (domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
  11057. span.appendChild(next);
  11058. next = span.nextSibling;
  11059. continue;
  11060. }
  11061. if (next.style.cssText == span.style.cssText) {
  11062. domUtils.moveChild(next, span);
  11063. domUtils.remove(next);
  11064. }
  11065. if (span.nextSibling === next)
  11066. break;
  11067. next = span.nextSibling;
  11068. }
  11069. }
  11070. mergeWithParent(span);
  11071. if (browser.ie && browser.version > 8) {
  11072. //拷贝父亲们的特别的属性,这里只做背景颜色的处理
  11073. var parent = domUtils.findParent(span, function (n) {
  11074. return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)
  11075. });
  11076. if (parent && !/background-color/.test(span.style.cssText)) {
  11077. span.style.backgroundColor = parent.style.backgroundColor;
  11078. }
  11079. }
  11080. });
  11081. rng.moveToBookmark(bk);
  11082. mergeChild(rng, cmdName, value)
  11083. }
  11084. me.addInputRule(function (root) {
  11085. utils.each(root.getNodesByTagName('u s del font strike'), function (node) {
  11086. if (node.tagName == 'font') {
  11087. var cssStyle = [];
  11088. for (var p in node.attrs) {
  11089. switch (p) {
  11090. case 'size':
  11091. cssStyle.push('font-size:' +
  11092. ({
  11093. '1': '10',
  11094. '2': '12',
  11095. '3': '16',
  11096. '4': '18',
  11097. '5': '24',
  11098. '6': '32',
  11099. '7': '48'
  11100. }[node.attrs[p]] || node.attrs[p]) + 'px');
  11101. break;
  11102. case 'color':
  11103. cssStyle.push('color:' + node.attrs[p]);
  11104. break;
  11105. case 'face':
  11106. cssStyle.push('font-family:' + node.attrs[p]);
  11107. break;
  11108. case 'style':
  11109. cssStyle.push(node.attrs[p]);
  11110. }
  11111. }
  11112. node.attrs = {
  11113. 'style': cssStyle.join(';')
  11114. };
  11115. } else {
  11116. var val = node.tagName == 'u' ? 'underline' : 'line-through';
  11117. node.attrs = {
  11118. 'style': (node.getAttr('style') || '') + 'text-decoration:' + val + ';'
  11119. }
  11120. }
  11121. node.tagName = 'span';
  11122. });
  11123. // utils.each(root.getNodesByTagName('span'), function (node) {
  11124. // var val;
  11125. // if(val = node.getAttr('class')){
  11126. // if(/fontstrikethrough/.test(val)){
  11127. // node.setStyle('text-decoration','line-through');
  11128. // if(node.attrs['class']){
  11129. // node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
  11130. // }else{
  11131. // node.setAttr('class')
  11132. // }
  11133. // }
  11134. // if(/fontborder/.test(val)){
  11135. // node.setStyle('border','1px solid #000');
  11136. // if(node.attrs['class']){
  11137. // node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
  11138. // }else{
  11139. // node.setAttr('class')
  11140. // }
  11141. // }
  11142. // }
  11143. // });
  11144. });
  11145. // me.addOutputRule(function(root){
  11146. // utils.each(root.getNodesByTagName('span'), function (node) {
  11147. // var val;
  11148. // if(val = node.getStyle('text-decoration')){
  11149. // if(/line-through/.test(val)){
  11150. // if(node.attrs['class']){
  11151. // node.attrs['class'] += ' fontstrikethrough';
  11152. // }else{
  11153. // node.setAttr('class','fontstrikethrough')
  11154. // }
  11155. // }
  11156. //
  11157. // node.setStyle('text-decoration')
  11158. // }
  11159. // if(val = node.getStyle('border')){
  11160. // if(/1px/.test(val) && /solid/.test(val)){
  11161. // if(node.attrs['class']){
  11162. // node.attrs['class'] += ' fontborder';
  11163. //
  11164. // }else{
  11165. // node.setAttr('class','fontborder')
  11166. // }
  11167. // }
  11168. // node.setStyle('border')
  11169. //
  11170. // }
  11171. // });
  11172. // });
  11173. for (var p in fonts) {
  11174. (function (cmd, style) {
  11175. UE.commands[cmd] = {
  11176. execCommand: function (cmdName, value) {
  11177. value = value || (this.queryCommandState(cmdName) ? 'none' : cmdName == 'underline' ? 'underline' :
  11178. cmdName == 'fontborder' ? '1px solid #000' :
  11179. 'line-through');
  11180. var me = this,
  11181. range = this.selection.getRange(),
  11182. text;
  11183. if (value == 'default') {
  11184. if (range.collapsed) {
  11185. text = me.document.createTextNode('font');
  11186. range.insertNode(text).select();
  11187. }
  11188. me.execCommand('removeFormat', 'span,a', style);
  11189. if (text) {
  11190. range.setStartBefore(text).collapse(true);
  11191. domUtils.remove(text);
  11192. }
  11193. mergesibling(range, cmdName, value);
  11194. range.select()
  11195. } else {
  11196. if (!range.collapsed) {
  11197. if (needCmd[cmd] && me.queryCommandValue(cmd)) {
  11198. me.execCommand('removeFormat', 'span,a', style);
  11199. }
  11200. range = me.selection.getRange();
  11201. range.applyInlineStyle('span', {'style': style + ':' + value});
  11202. mergesibling(range, cmdName, value);
  11203. range.select();
  11204. } else {
  11205. var span = domUtils.findParentByTagName(range.startContainer, 'span', true);
  11206. text = me.document.createTextNode('font');
  11207. if (span && !span.children.length && !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length) {
  11208. //for ie hack when enter
  11209. range.insertNode(text);
  11210. if (needCmd[cmd]) {
  11211. range.selectNode(text).select();
  11212. me.execCommand('removeFormat', 'span,a', style, null);
  11213. span = domUtils.findParentByTagName(text, 'span', true);
  11214. range.setStartBefore(text);
  11215. }
  11216. span && (span.style.cssText += ';' + style + ':' + value);
  11217. range.collapse(true).select();
  11218. } else {
  11219. range.insertNode(text);
  11220. range.selectNode(text).select();
  11221. span = range.document.createElement('span');
  11222. if (needCmd[cmd]) {
  11223. //a标签内的不处理跳过
  11224. if (domUtils.findParentByTagName(text, 'a', true)) {
  11225. range.setStartBefore(text).setCursor();
  11226. domUtils.remove(text);
  11227. return;
  11228. }
  11229. me.execCommand('removeFormat', 'span,a', style);
  11230. }
  11231. span.style.cssText = style + ':' + value;
  11232. text.parentNode.insertBefore(span, text);
  11233. //修复,span套span 但样式不继承的问题
  11234. if (!browser.ie || browser.ie && browser.version == 9) {
  11235. var spanParent = span.parentNode;
  11236. while (!domUtils.isBlockElm(spanParent)) {
  11237. if (spanParent.tagName == 'SPAN') {
  11238. //opera合并style不会加入";"
  11239. span.style.cssText = spanParent.style.cssText + ";" + span.style.cssText;
  11240. }
  11241. spanParent = spanParent.parentNode;
  11242. }
  11243. }
  11244. if (opera) {
  11245. setTimeout(function () {
  11246. range.setStart(span, 0).collapse(true);
  11247. mergesibling(range, cmdName, value);
  11248. range.select();
  11249. });
  11250. } else {
  11251. range.setStart(span, 0).collapse(true);
  11252. mergesibling(range, cmdName, value);
  11253. range.select();
  11254. }
  11255. //trace:981
  11256. //domUtils.mergeToParent(span)
  11257. }
  11258. domUtils.remove(text);
  11259. }
  11260. }
  11261. return true;
  11262. },
  11263. queryCommandValue: function (cmdName) {
  11264. var startNode = this.selection.getStart();
  11265. //trace:946
  11266. if (cmdName == 'underline' || cmdName == 'strikethrough') {
  11267. var tmpNode = startNode, value;
  11268. while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) {
  11269. if (tmpNode.nodeType == 1) {
  11270. value = domUtils.getComputedStyle(tmpNode, style);
  11271. if (value != 'none') {
  11272. return value;
  11273. }
  11274. }
  11275. tmpNode = tmpNode.parentNode;
  11276. }
  11277. return 'none';
  11278. }
  11279. if (cmdName == 'fontborder') {
  11280. var tmp = startNode, val;
  11281. while (tmp && dtd.$inline[tmp.tagName]) {
  11282. if (val = domUtils.getComputedStyle(tmp, 'border')) {
  11283. if (/1px/.test(val) && /solid/.test(val)) {
  11284. return val;
  11285. }
  11286. }
  11287. tmp = tmp.parentNode;
  11288. }
  11289. return ''
  11290. }
  11291. if (cmdName == 'FontSize') {
  11292. var styleVal = domUtils.getComputedStyle(startNode, style),
  11293. tmp = /^([\d\.]+)(\w+)$/.exec(styleVal);
  11294. if (tmp) {
  11295. return Math.floor(tmp[1]) + tmp[2];
  11296. }
  11297. return styleVal;
  11298. }
  11299. return domUtils.getComputedStyle(startNode, style);
  11300. },
  11301. queryCommandState: function (cmdName) {
  11302. if (!needCmd[cmdName])
  11303. return 0;
  11304. var val = this.queryCommandValue(cmdName);
  11305. if (cmdName == 'fontborder') {
  11306. return /1px/.test(val) && /solid/.test(val)
  11307. } else {
  11308. return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val);
  11309. }
  11310. }
  11311. };
  11312. })(p, fonts[p]);
  11313. }
  11314. };
  11315. // plugins/link.js
  11316. /**
  11317. * 超链接
  11318. * @file
  11319. * @since 1.2.6.1
  11320. */
  11321. /**
  11322. * 插入超链接
  11323. * @command link
  11324. * @method execCommand
  11325. * @param { String } cmd 命令字符串
  11326. * @param { Object } options 设置自定义属性,例如:url、title、target
  11327. * @example
  11328. * ```javascript
  11329. * editor.execCommand( 'link', '{
  11330. * url:'ueditor.baidu.com',
  11331. * title:'ueditor',
  11332. * target:'_blank'
  11333. * }' );
  11334. * ```
  11335. */
  11336. /**
  11337. * 返回当前选中的第一个超链接节点
  11338. * @command link
  11339. * @method queryCommandValue
  11340. * @param { String } cmd 命令字符串
  11341. * @return { Element } 超链接节点
  11342. * @example
  11343. * ```javascript
  11344. * editor.queryCommandValue( 'link' );
  11345. * ```
  11346. */
  11347. /**
  11348. * 取消超链接
  11349. * @command unlink
  11350. * @method execCommand
  11351. * @param { String } cmd 命令字符串
  11352. * @example
  11353. * ```javascript
  11354. * editor.execCommand( 'unlink');
  11355. * ```
  11356. */
  11357. UE.plugins['link'] = function () {
  11358. function optimize(range) {
  11359. var start = range.startContainer, end = range.endContainer;
  11360. if (start = domUtils.findParentByTagName(start, 'a', true)) {
  11361. range.setStartBefore(start);
  11362. }
  11363. if (end = domUtils.findParentByTagName(end, 'a', true)) {
  11364. range.setEndAfter(end);
  11365. }
  11366. }
  11367. UE.commands['unlink'] = {
  11368. execCommand: function () {
  11369. var range = this.selection.getRange(),
  11370. bookmark;
  11371. if (range.collapsed && !domUtils.findParentByTagName(range.startContainer, 'a', true)) {
  11372. return;
  11373. }
  11374. bookmark = range.createBookmark();
  11375. optimize(range);
  11376. range.removeInlineStyle('a').moveToBookmark(bookmark).select();
  11377. },
  11378. queryCommandState: function () {
  11379. return !this.highlight && this.queryCommandValue('link') ? 0 : -1;
  11380. }
  11381. };
  11382. function doLink(range, opt, me) {
  11383. var rngClone = range.cloneRange(),
  11384. link = me.queryCommandValue('link');
  11385. optimize(range = range.adjustmentBoundary());
  11386. var start = range.startContainer;
  11387. if (start.nodeType == 1 && link) {
  11388. start = start.childNodes[range.startOffset];
  11389. if (start && start.nodeType == 1 && start.tagName == 'A' && /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie ? 'innerText' : 'textContent'])) {
  11390. start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue || opt.href);
  11391. }
  11392. }
  11393. if (!rngClone.collapsed || link) {
  11394. range.removeInlineStyle('a');
  11395. rngClone = range.cloneRange();
  11396. }
  11397. if (rngClone.collapsed) {
  11398. var a = range.document.createElement('a'),
  11399. text = '';
  11400. if (opt.textValue) {
  11401. text = utils.html(opt.textValue);
  11402. delete opt.textValue;
  11403. } else {
  11404. text = utils.html(opt.href);
  11405. }
  11406. domUtils.setAttributes(a, opt);
  11407. start = domUtils.findParentByTagName(rngClone.startContainer, 'a', true);
  11408. if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
  11409. range.setStartAfter(start).collapse(true);
  11410. }
  11411. a[browser.ie ? 'innerText' : 'textContent'] = text;
  11412. range.insertNode(a).selectNode(a);
  11413. } else {
  11414. range.applyInlineStyle('a', opt);
  11415. }
  11416. }
  11417. UE.commands['link'] = {
  11418. execCommand: function (cmdName, opt) {
  11419. var range;
  11420. opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g));
  11421. opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g));
  11422. opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g));
  11423. doLink(range = this.selection.getRange(), opt, this);
  11424. //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
  11425. range.collapse().select(true);
  11426. },
  11427. queryCommandValue: function () {
  11428. var range = this.selection.getRange(),
  11429. node;
  11430. if (range.collapsed) {
  11431. // node = this.selection.getStart();
  11432. //在ie下getstart()取值偏上了
  11433. node = range.startContainer;
  11434. node = node.nodeType == 1 ? node : node.parentNode;
  11435. if (node && (node = domUtils.findParentByTagName(node, 'a', true)) && !domUtils.isInNodeEndBoundary(range, node)) {
  11436. return node;
  11437. }
  11438. } else {
  11439. //trace:1111 如果是<p><a>xx</a></p> startContainer是p就会找不到a
  11440. range.shrinkBoundary();
  11441. var start = range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset] ? range.startContainer : range.startContainer.childNodes[range.startOffset],
  11442. end = range.endContainer.nodeType == 3 || range.endOffset == 0 ? range.endContainer : range.endContainer.childNodes[range.endOffset - 1],
  11443. common = range.getCommonAncestor();
  11444. node = domUtils.findParentByTagName(common, 'a', true);
  11445. if (!node && common.nodeType == 1) {
  11446. var as = common.getElementsByTagName('a'),
  11447. ps, pe;
  11448. for (var i = 0, ci; ci = as[i++];) {
  11449. ps = domUtils.getPosition(ci, start), pe = domUtils.getPosition(ci, end);
  11450. if ((ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
  11451. &&
  11452. (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
  11453. ) {
  11454. node = ci;
  11455. break;
  11456. }
  11457. }
  11458. }
  11459. return node;
  11460. }
  11461. },
  11462. queryCommandState: function () {
  11463. //判断如果是视频的话连接不可用
  11464. //fix 853
  11465. var img = this.selection.getRange().getClosedNode(),
  11466. flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video") != -1);
  11467. return flag ? -1 : 0;
  11468. }
  11469. };
  11470. };
  11471. // plugins/iframe.js
  11472. ///import core
  11473. ///import plugins\inserthtml.js
  11474. ///commands 插入框架
  11475. ///commandsName InsertFrame
  11476. ///commandsTitle 插入Iframe
  11477. ///commandsDialog dialogs\insertframe
  11478. UE.plugins['insertframe'] = function () {
  11479. var me = this;
  11480. function deleteIframe() {
  11481. me._iframe && delete me._iframe;
  11482. }
  11483. me.addListener("selectionchange", function () {
  11484. deleteIframe();
  11485. });
  11486. };
  11487. // plugins/scrawl.js
  11488. ///import core
  11489. ///commands 涂鸦
  11490. ///commandsName Scrawl
  11491. ///commandsTitle 涂鸦
  11492. ///commandsDialog dialogs\scrawl
  11493. UE.commands['scrawl'] = {
  11494. queryCommandState: function () {
  11495. return (browser.ie && browser.version <= 8) ? -1 : 0;
  11496. }
  11497. };
  11498. // plugins/removeformat.js
  11499. /**
  11500. * 清除格式
  11501. * @file
  11502. * @since 1.2.6.1
  11503. */
  11504. /**
  11505. * 清除文字样式
  11506. * @command removeformat
  11507. * @method execCommand
  11508. * @param { String } cmd 命令字符串
  11509. * @param {String} tags 以逗号隔开的标签。如:strong
  11510. * @param {String} style 样式如:color
  11511. * @param {String} attrs 属性如:width
  11512. * @example
  11513. * ```javascript
  11514. * editor.execCommand( 'removeformat', 'strong','color','width' );
  11515. * ```
  11516. */
  11517. UE.plugins['removeformat'] = function () {
  11518. var me = this;
  11519. me.setOpt({
  11520. 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
  11521. 'removeFormatAttributes': 'class,style,lang,width,height,align,hspace,valign'
  11522. });
  11523. me.commands['removeformat'] = {
  11524. execCommand: function (cmdName, tags, style, attrs, notIncludeA) {
  11525. var tagReg = new RegExp('^(?:' + (tags || this.options.removeFormatTags).replace(/,/g, '|') + ')$', 'i'),
  11526. removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split(','),
  11527. range = new dom.Range(this.document),
  11528. bookmark, node, parent,
  11529. filter = function (node) {
  11530. return node.nodeType == 1;
  11531. };
  11532. function isRedundantSpan(node) {
  11533. if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span') {
  11534. return 0;
  11535. }
  11536. if (browser.ie) {
  11537. //ie 下判断实效,所以只能简单用style来判断
  11538. //return node.style.cssText == '' ? 1 : 0;
  11539. var attrs = node.attributes;
  11540. if (attrs.length) {
  11541. for (var i = 0, l = attrs.length; i < l; i++) {
  11542. if (attrs[i].specified) {
  11543. return 0;
  11544. }
  11545. }
  11546. return 1;
  11547. }
  11548. }
  11549. return !node.attributes.length;
  11550. }
  11551. function doRemove(range) {
  11552. var bookmark1 = range.createBookmark();
  11553. if (range.collapsed) {
  11554. range.enlarge(true);
  11555. }
  11556. //不能把a标签切了
  11557. if (!notIncludeA) {
  11558. var aNode = domUtils.findParentByTagName(range.startContainer, 'a', true);
  11559. if (aNode) {
  11560. range.setStartBefore(aNode);
  11561. }
  11562. aNode = domUtils.findParentByTagName(range.endContainer, 'a', true);
  11563. if (aNode) {
  11564. range.setEndAfter(aNode);
  11565. }
  11566. }
  11567. bookmark = range.createBookmark();
  11568. node = bookmark.start;
  11569. //切开始
  11570. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  11571. domUtils.breakParent(node, parent);
  11572. domUtils.clearEmptySibling(node);
  11573. }
  11574. if (bookmark.end) {
  11575. //切结束
  11576. node = bookmark.end;
  11577. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  11578. domUtils.breakParent(node, parent);
  11579. domUtils.clearEmptySibling(node);
  11580. }
  11581. //开始去除样式
  11582. var current = domUtils.getNextDomNode(bookmark.start, false, filter),
  11583. next;
  11584. while (current) {
  11585. if (current == bookmark.end) {
  11586. break;
  11587. }
  11588. next = domUtils.getNextDomNode(current, true, filter);
  11589. if (!dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode(current)) {
  11590. if (tagReg.test(current.tagName)) {
  11591. if (style) {
  11592. domUtils.removeStyle(current, style);
  11593. if (isRedundantSpan(current) && style != 'text-decoration') {
  11594. domUtils.remove(current, true);
  11595. }
  11596. } else {
  11597. domUtils.remove(current, true);
  11598. }
  11599. } else {
  11600. //trace:939 不能把list上的样式去掉
  11601. if (!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]) {
  11602. domUtils.removeAttributes(current, removeFormatAttributes);
  11603. if (isRedundantSpan(current)) {
  11604. domUtils.remove(current, true);
  11605. }
  11606. }
  11607. }
  11608. }
  11609. current = next;
  11610. }
  11611. }
  11612. //trace:1035
  11613. //trace:1096 不能把td上的样式去掉,比如边框
  11614. var pN = bookmark.start.parentNode;
  11615. if (domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
  11616. domUtils.removeAttributes(pN, removeFormatAttributes);
  11617. }
  11618. pN = bookmark.end.parentNode;
  11619. if (bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
  11620. domUtils.removeAttributes(pN, removeFormatAttributes);
  11621. }
  11622. range.moveToBookmark(bookmark).moveToBookmark(bookmark1);
  11623. //清除冗余的代码 <b><bookmark></b>
  11624. var node = range.startContainer,
  11625. tmp,
  11626. collapsed = range.collapsed;
  11627. while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
  11628. tmp = node.parentNode;
  11629. range.setStartBefore(node);
  11630. //trace:937
  11631. //更新结束边界
  11632. if (range.startContainer === range.endContainer) {
  11633. range.endOffset--;
  11634. }
  11635. domUtils.remove(node);
  11636. node = tmp;
  11637. }
  11638. if (!collapsed) {
  11639. node = range.endContainer;
  11640. while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
  11641. tmp = node.parentNode;
  11642. range.setEndBefore(node);
  11643. domUtils.remove(node);
  11644. node = tmp;
  11645. }
  11646. }
  11647. }
  11648. range = this.selection.getRange();
  11649. doRemove(range);
  11650. range.select();
  11651. }
  11652. };
  11653. };
  11654. // plugins/blockquote.js
  11655. /**
  11656. * 添加引用
  11657. * @file
  11658. * @since 1.2.6.1
  11659. */
  11660. /**
  11661. * 添加引用
  11662. * @command blockquote
  11663. * @method execCommand
  11664. * @param { String } cmd 命令字符串
  11665. * @example
  11666. * ```javascript
  11667. * editor.execCommand( 'blockquote' );
  11668. * ```
  11669. */
  11670. /**
  11671. * 添加引用
  11672. * @command blockquote
  11673. * @method execCommand
  11674. * @param { String } cmd 命令字符串
  11675. * @param { Object } attrs 节点属性
  11676. * @example
  11677. * ```javascript
  11678. * editor.execCommand( 'blockquote',{
  11679. * style: "color: red;"
  11680. * } );
  11681. * ```
  11682. */
  11683. UE.plugins['blockquote'] = function () {
  11684. var me = this;
  11685. function getObj(editor) {
  11686. return domUtils.filterNodeList(editor.selection.getStartElementPath(), 'blockquote');
  11687. }
  11688. me.commands['blockquote'] = {
  11689. execCommand: function (cmdName, attrs) {
  11690. var range = this.selection.getRange(),
  11691. obj = getObj(this),
  11692. blockquote = dtd.blockquote,
  11693. bookmark = range.createBookmark();
  11694. if (obj) {
  11695. var start = range.startContainer,
  11696. startBlock = domUtils.isBlockElm(start) ? start : domUtils.findParent(start, function (node) {
  11697. return domUtils.isBlockElm(node)
  11698. }),
  11699. end = range.endContainer,
  11700. endBlock = domUtils.isBlockElm(end) ? end : domUtils.findParent(end, function (node) {
  11701. return domUtils.isBlockElm(node)
  11702. });
  11703. //处理一下li
  11704. startBlock = domUtils.findParentByTagName(startBlock, 'li', true) || startBlock;
  11705. endBlock = domUtils.findParentByTagName(endBlock, 'li', true) || endBlock;
  11706. if (startBlock.tagName == 'LI' || startBlock.tagName == 'TD' || startBlock === obj || domUtils.isBody(startBlock)) {
  11707. domUtils.remove(obj, true);
  11708. } else {
  11709. domUtils.breakParent(startBlock, obj);
  11710. }
  11711. if (startBlock !== endBlock) {
  11712. obj = domUtils.findParentByTagName(endBlock, 'blockquote');
  11713. if (obj) {
  11714. if (endBlock.tagName == 'LI' || endBlock.tagName == 'TD' || domUtils.isBody(endBlock)) {
  11715. obj.parentNode && domUtils.remove(obj, true);
  11716. } else {
  11717. domUtils.breakParent(endBlock, obj);
  11718. }
  11719. }
  11720. }
  11721. var blockquotes = domUtils.getElementsByTagName(this.document, 'blockquote');
  11722. for (var i = 0, bi; bi = blockquotes[i++];) {
  11723. if (!bi.childNodes.length) {
  11724. domUtils.remove(bi);
  11725. } else if (domUtils.getPosition(bi, startBlock) & domUtils.POSITION_FOLLOWING && domUtils.getPosition(bi, endBlock) & domUtils.POSITION_PRECEDING) {
  11726. domUtils.remove(bi, true);
  11727. }
  11728. }
  11729. } else {
  11730. var tmpRange = range.cloneRange(),
  11731. node = tmpRange.startContainer.nodeType == 1 ? tmpRange.startContainer : tmpRange.startContainer.parentNode,
  11732. preNode = node,
  11733. doEnd = 1;
  11734. //调整开始
  11735. while (1) {
  11736. if (domUtils.isBody(node)) {
  11737. if (preNode !== node) {
  11738. if (range.collapsed) {
  11739. tmpRange.selectNode(preNode);
  11740. doEnd = 0;
  11741. } else {
  11742. tmpRange.setStartBefore(preNode);
  11743. }
  11744. } else {
  11745. tmpRange.setStart(node, 0);
  11746. }
  11747. break;
  11748. }
  11749. if (!blockquote[node.tagName]) {
  11750. if (range.collapsed) {
  11751. tmpRange.selectNode(preNode);
  11752. } else {
  11753. tmpRange.setStartBefore(preNode);
  11754. }
  11755. break;
  11756. }
  11757. preNode = node;
  11758. node = node.parentNode;
  11759. }
  11760. //调整结束
  11761. if (doEnd) {
  11762. preNode = node = node = tmpRange.endContainer.nodeType == 1 ? tmpRange.endContainer : tmpRange.endContainer.parentNode;
  11763. while (1) {
  11764. if (domUtils.isBody(node)) {
  11765. if (preNode !== node) {
  11766. tmpRange.setEndAfter(preNode);
  11767. } else {
  11768. tmpRange.setEnd(node, node.childNodes.length);
  11769. }
  11770. break;
  11771. }
  11772. if (!blockquote[node.tagName]) {
  11773. tmpRange.setEndAfter(preNode);
  11774. break;
  11775. }
  11776. preNode = node;
  11777. node = node.parentNode;
  11778. }
  11779. }
  11780. node = range.document.createElement('blockquote');
  11781. domUtils.setAttributes(node, attrs);
  11782. node.appendChild(tmpRange.extractContents());
  11783. tmpRange.insertNode(node);
  11784. //去除重复的
  11785. var childs = domUtils.getElementsByTagName(node, 'blockquote');
  11786. for (var i = 0, ci; ci = childs[i++];) {
  11787. if (ci.parentNode) {
  11788. domUtils.remove(ci, true);
  11789. }
  11790. }
  11791. }
  11792. range.moveToBookmark(bookmark).select();
  11793. },
  11794. queryCommandState: function () {
  11795. return getObj(this) ? 1 : 0;
  11796. }
  11797. };
  11798. };
  11799. // plugins/convertcase.js
  11800. /**
  11801. * 大小写转换
  11802. * @file
  11803. * @since 1.2.6.1
  11804. */
  11805. /**
  11806. * 把选区内文本变大写,与“tolowercase”命令互斥
  11807. * @command touppercase
  11808. * @method execCommand
  11809. * @param { String } cmd 命令字符串
  11810. * @example
  11811. * ```javascript
  11812. * editor.execCommand( 'touppercase' );
  11813. * ```
  11814. */
  11815. /**
  11816. * 把选区内文本变小写,与“touppercase”命令互斥
  11817. * @command tolowercase
  11818. * @method execCommand
  11819. * @param { String } cmd 命令字符串
  11820. * @example
  11821. * ```javascript
  11822. * editor.execCommand( 'tolowercase' );
  11823. * ```
  11824. */
  11825. UE.commands['touppercase'] =
  11826. UE.commands['tolowercase'] = {
  11827. execCommand: function (cmd) {
  11828. var me = this;
  11829. var rng = me.selection.getRange();
  11830. if (rng.collapsed) {
  11831. return rng;
  11832. }
  11833. var bk = rng.createBookmark(),
  11834. bkEnd = bk.end,
  11835. filterFn = function (node) {
  11836. return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
  11837. },
  11838. curNode = domUtils.getNextDomNode(bk.start, false, filterFn);
  11839. while (curNode && (domUtils.getPosition(curNode, bkEnd) & domUtils.POSITION_PRECEDING)) {
  11840. if (curNode.nodeType == 3) {
  11841. curNode.nodeValue = curNode.nodeValue[cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase']();
  11842. }
  11843. curNode = domUtils.getNextDomNode(curNode, true, filterFn);
  11844. if (curNode === bkEnd) {
  11845. break;
  11846. }
  11847. }
  11848. rng.moveToBookmark(bk).select();
  11849. }
  11850. };
  11851. // plugins/indent.js
  11852. /**
  11853. * 首行缩进
  11854. * @file
  11855. * @since 1.2.6.1
  11856. */
  11857. /**
  11858. * 缩进
  11859. * @command indent
  11860. * @method execCommand
  11861. * @param { String } cmd 命令字符串
  11862. * @example
  11863. * ```javascript
  11864. * editor.execCommand( 'indent' );
  11865. * ```
  11866. */
  11867. UE.commands['indent'] = {
  11868. execCommand: function () {
  11869. var me = this, value = me.queryCommandState("indent") ? "0em" : (me.options.indentValue || '2em');
  11870. me.execCommand('Paragraph', 'p', {style: 'text-indent:' + value});
  11871. },
  11872. queryCommandState: function () {
  11873. var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), 'p h1 h2 h3 h4 h5 h6');
  11874. return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0;
  11875. }
  11876. };
  11877. // plugins/print.js
  11878. /**
  11879. * 打印
  11880. * @file
  11881. * @since 1.2.6.1
  11882. */
  11883. /**
  11884. * 打印
  11885. * @command print
  11886. * @method execCommand
  11887. * @param { String } cmd 命令字符串
  11888. * @example
  11889. * ```javascript
  11890. * editor.execCommand( 'print' );
  11891. * ```
  11892. */
  11893. UE.commands['print'] = {
  11894. execCommand: function () {
  11895. this.window.print();
  11896. },
  11897. notNeedUndo: 1
  11898. };
  11899. // plugins/preview.js
  11900. /**
  11901. * 预览
  11902. * @file
  11903. * @since 1.2.6.1
  11904. */
  11905. /**
  11906. * 预览
  11907. * @command preview
  11908. * @method execCommand
  11909. * @param { String } cmd 命令字符串
  11910. * @example
  11911. * ```javascript
  11912. * editor.execCommand( 'preview' );
  11913. * ```
  11914. */
  11915. UE.commands['preview'] = {
  11916. execCommand: function () {
  11917. var w = window.open('', '_blank', ''),
  11918. d = w.document;
  11919. d.open();
  11920. d.write('<!DOCTYPE html><html><head><meta charset="utf-8"/><script src="' + this.options.UEDITOR_HOME_URL + 'ueditor.parse.js"></script><script>' +
  11921. "setTimeout(function(){uParse('div',{rootPath: '" + this.options.UEDITOR_HOME_URL + "'})},300)" +
  11922. '</script></head><body><div>' + this.getContent(null, null, true) + '</div></body></html>');
  11923. d.close();
  11924. },
  11925. notNeedUndo: 1
  11926. };
  11927. // plugins/selectall.js
  11928. /**
  11929. * 全选
  11930. * @file
  11931. * @since 1.2.6.1
  11932. */
  11933. /**
  11934. * 选中所有内容
  11935. * @command selectall
  11936. * @method execCommand
  11937. * @param { String } cmd 命令字符串
  11938. * @example
  11939. * ```javascript
  11940. * editor.execCommand( 'selectall' );
  11941. * ```
  11942. */
  11943. UE.plugins['selectall'] = function () {
  11944. var me = this;
  11945. me.commands['selectall'] = {
  11946. execCommand: function () {
  11947. //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
  11948. var me = this, body = me.body,
  11949. range = me.selection.getRange();
  11950. range.selectNodeContents(body);
  11951. if (domUtils.isEmptyBlock(body)) {
  11952. //opera不能自动合并到元素的里边,要手动处理一下
  11953. if (browser.opera && body.firstChild && body.firstChild.nodeType == 1) {
  11954. range.setStartAtFirst(body.firstChild);
  11955. }
  11956. range.collapse(true);
  11957. }
  11958. range.select(true);
  11959. },
  11960. notNeedUndo: 1
  11961. };
  11962. //快捷键
  11963. me.addshortcutkey({
  11964. "selectAll": "ctrl+65"
  11965. });
  11966. };
  11967. // plugins/paragraph.js
  11968. /**
  11969. * 段落样式
  11970. * @file
  11971. * @since 1.2.6.1
  11972. */
  11973. /**
  11974. * 段落格式
  11975. * @command paragraph
  11976. * @method execCommand
  11977. * @param { String } cmd 命令字符串
  11978. * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
  11979. * @param {Object} attrs 标签的属性
  11980. * @example
  11981. * ```javascript
  11982. * editor.execCommand( 'Paragraph','h1','{
  11983. * class:'test'
  11984. * }' );
  11985. * ```
  11986. */
  11987. /**
  11988. * 返回选区内节点标签名
  11989. * @command paragraph
  11990. * @method queryCommandValue
  11991. * @param { String } cmd 命令字符串
  11992. * @return { String } 节点标签名
  11993. * @example
  11994. * ```javascript
  11995. * editor.queryCommandValue( 'Paragraph' );
  11996. * ```
  11997. */
  11998. UE.plugins['paragraph'] = function () {
  11999. var me = this,
  12000. block = domUtils.isBlockElm,
  12001. notExchange = ['TD', 'LI', 'PRE'],
  12002. doParagraph = function (range, style, attrs, sourceCmdName) {
  12003. var bookmark = range.createBookmark(),
  12004. filterFn = function (node) {
  12005. return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
  12006. },
  12007. para;
  12008. range.enlarge(true);
  12009. var bookmark2 = range.createBookmark(),
  12010. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  12011. tmpRange = range.cloneRange(),
  12012. tmpNode;
  12013. while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
  12014. if (current.nodeType == 3 || !block(current)) {
  12015. tmpRange.setStartBefore(current);
  12016. while (current && current !== bookmark2.end && !block(current)) {
  12017. tmpNode = current;
  12018. current = domUtils.getNextDomNode(current, false, null, function (node) {
  12019. return !block(node);
  12020. });
  12021. }
  12022. tmpRange.setEndAfter(tmpNode);
  12023. para = range.document.createElement(style);
  12024. if (attrs) {
  12025. domUtils.setAttributes(para, attrs);
  12026. if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
  12027. para.style.cssText = attrs.style;
  12028. }
  12029. }
  12030. para.appendChild(tmpRange.extractContents());
  12031. //需要内容占位
  12032. if (domUtils.isEmptyNode(para)) {
  12033. domUtils.fillChar(range.document, para);
  12034. }
  12035. tmpRange.insertNode(para);
  12036. var parent = para.parentNode;
  12037. //如果para上一级是一个block元素且不是body,td就删除它
  12038. if (block(parent) && !domUtils.isBody(para.parentNode) && utils.indexOf(notExchange, parent.tagName) == -1) {
  12039. //存储dir,style
  12040. if (!(sourceCmdName && sourceCmdName == 'customstyle')) {
  12041. parent.getAttribute('dir') && para.setAttribute('dir', parent.getAttribute('dir'));
  12042. //trace:1070
  12043. parent.style.cssText && (para.style.cssText = parent.style.cssText + ';' + para.style.cssText);
  12044. //trace:1030
  12045. parent.style.textAlign && !para.style.textAlign && (para.style.textAlign = parent.style.textAlign);
  12046. parent.style.textIndent && !para.style.textIndent && (para.style.textIndent = parent.style.textIndent);
  12047. parent.style.padding && !para.style.padding && (para.style.padding = parent.style.padding);
  12048. }
  12049. //trace:1706 选择的就是h1-6要删除
  12050. if (attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName)) {
  12051. domUtils.setAttributes(parent, attrs);
  12052. if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
  12053. parent.style.cssText = attrs.style;
  12054. }
  12055. domUtils.remove(para, true);
  12056. para = parent;
  12057. } else {
  12058. domUtils.remove(para.parentNode, true);
  12059. }
  12060. }
  12061. if (utils.indexOf(notExchange, parent.tagName) != -1) {
  12062. current = parent;
  12063. } else {
  12064. current = para;
  12065. }
  12066. current = domUtils.getNextDomNode(current, false, filterFn);
  12067. } else {
  12068. current = domUtils.getNextDomNode(current, true, filterFn);
  12069. }
  12070. }
  12071. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
  12072. };
  12073. me.setOpt('paragraph', {'p': '', 'h1': '', 'h2': '', 'h3': '', 'h4': '', 'h5': '', 'h6': ''});
  12074. me.commands['paragraph'] = {
  12075. execCommand: function (cmdName, style, attrs, sourceCmdName) {
  12076. var range = this.selection.getRange();
  12077. //闭合时单独处理
  12078. if (range.collapsed) {
  12079. var txt = this.document.createTextNode('p');
  12080. range.insertNode(txt);
  12081. //去掉冗余的fillchar
  12082. if (browser.ie) {
  12083. var node = txt.previousSibling;
  12084. if (node && domUtils.isWhitespace(node)) {
  12085. domUtils.remove(node);
  12086. }
  12087. node = txt.nextSibling;
  12088. if (node && domUtils.isWhitespace(node)) {
  12089. domUtils.remove(node);
  12090. }
  12091. }
  12092. }
  12093. range = doParagraph(range, style, attrs, sourceCmdName);
  12094. if (txt) {
  12095. range.setStartBefore(txt).collapse(true);
  12096. pN = txt.parentNode;
  12097. domUtils.remove(txt);
  12098. if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
  12099. domUtils.fillNode(this.document, pN);
  12100. }
  12101. }
  12102. if (browser.gecko && range.collapsed && range.startContainer.nodeType == 1) {
  12103. var child = range.startContainer.childNodes[range.startOffset];
  12104. if (child && child.nodeType == 1 && child.tagName.toLowerCase() == style) {
  12105. range.setStart(child, 0).collapse(true);
  12106. }
  12107. }
  12108. //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
  12109. range.select();
  12110. return true;
  12111. },
  12112. queryCommandValue: function () {
  12113. var node = domUtils.filterNodeList(this.selection.getStartElementPath(), 'p h1 h2 h3 h4 h5 h6');
  12114. return node ? node.tagName.toLowerCase() : '';
  12115. }
  12116. };
  12117. };
  12118. // plugins/directionality.js
  12119. /**
  12120. * 设置文字输入的方向的插件
  12121. * @file
  12122. * @since 1.2.6.1
  12123. */
  12124. (function () {
  12125. var block = domUtils.isBlockElm,
  12126. getObj = function (editor) {
  12127. // var startNode = editor.selection.getStart(),
  12128. // parents;
  12129. // if ( startNode ) {
  12130. // //查找所有的是block的父亲节点
  12131. // parents = domUtils.findParents( startNode, true, block, true );
  12132. // for ( var i = 0,ci; ci = parents[i++]; ) {
  12133. // if ( ci.getAttribute( 'dir' ) ) {
  12134. // return ci;
  12135. // }
  12136. // }
  12137. // }
  12138. return domUtils.filterNodeList(editor.selection.getStartElementPath(), function (n) {
  12139. return n && n.nodeType == 1 && n.getAttribute('dir')
  12140. });
  12141. },
  12142. doDirectionality = function (range, editor, forward) {
  12143. var bookmark,
  12144. filterFn = function (node) {
  12145. return node.nodeType == 1 ? !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node);
  12146. },
  12147. obj = getObj(editor);
  12148. if (obj && range.collapsed) {
  12149. obj.setAttribute('dir', forward);
  12150. return range;
  12151. }
  12152. bookmark = range.createBookmark();
  12153. range.enlarge(true);
  12154. var bookmark2 = range.createBookmark(),
  12155. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  12156. tmpRange = range.cloneRange(),
  12157. tmpNode;
  12158. while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
  12159. if (current.nodeType == 3 || !block(current)) {
  12160. tmpRange.setStartBefore(current);
  12161. while (current && current !== bookmark2.end && !block(current)) {
  12162. tmpNode = current;
  12163. current = domUtils.getNextDomNode(current, false, null, function (node) {
  12164. return !block(node);
  12165. });
  12166. }
  12167. tmpRange.setEndAfter(tmpNode);
  12168. var common = tmpRange.getCommonAncestor();
  12169. if (!domUtils.isBody(common) && block(common)) {
  12170. //遍历到了block节点
  12171. common.setAttribute('dir', forward);
  12172. current = common;
  12173. } else {
  12174. //没有遍历到,添加一个block节点
  12175. var p = range.document.createElement('p');
  12176. p.setAttribute('dir', forward);
  12177. var frag = tmpRange.extractContents();
  12178. p.appendChild(frag);
  12179. tmpRange.insertNode(p);
  12180. current = p;
  12181. }
  12182. current = domUtils.getNextDomNode(current, false, filterFn);
  12183. } else {
  12184. current = domUtils.getNextDomNode(current, true, filterFn);
  12185. }
  12186. }
  12187. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
  12188. };
  12189. /**
  12190. * 文字输入方向
  12191. * @command directionality
  12192. * @method execCommand
  12193. * @param { String } cmdName 命令字符串
  12194. * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
  12195. * @example
  12196. * ```javascript
  12197. * editor.execCommand( 'directionality', 'ltr');
  12198. * ```
  12199. */
  12200. /**
  12201. * 查询当前选区的文字输入方向
  12202. * @command directionality
  12203. * @method queryCommandValue
  12204. * @param { String } cmdName 命令字符串
  12205. * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
  12206. * @example
  12207. * ```javascript
  12208. * editor.queryCommandValue( 'directionality');
  12209. * ```
  12210. */
  12211. UE.commands['directionality'] = {
  12212. execCommand: function (cmdName, forward) {
  12213. var range = this.selection.getRange();
  12214. //闭合时单独处理
  12215. if (range.collapsed) {
  12216. var txt = this.document.createTextNode('d');
  12217. range.insertNode(txt);
  12218. }
  12219. doDirectionality(range, this, forward);
  12220. if (txt) {
  12221. range.setStartBefore(txt).collapse(true);
  12222. domUtils.remove(txt);
  12223. }
  12224. range.select();
  12225. return true;
  12226. },
  12227. queryCommandValue: function () {
  12228. var node = getObj(this);
  12229. return node ? node.getAttribute('dir') : 'ltr';
  12230. }
  12231. };
  12232. })();
  12233. // plugins/horizontal.js
  12234. /**
  12235. * 插入分割线插件
  12236. * @file
  12237. * @since 1.2.6.1
  12238. */
  12239. /**
  12240. * 插入分割线
  12241. * @command horizontal
  12242. * @method execCommand
  12243. * @param { String } cmdName 命令字符串
  12244. * @example
  12245. * ```javascript
  12246. * editor.execCommand( 'horizontal' );
  12247. * ```
  12248. */
  12249. UE.plugins['horizontal'] = function () {
  12250. var me = this;
  12251. me.commands['horizontal'] = {
  12252. execCommand: function (cmdName) {
  12253. var me = this;
  12254. if (me.queryCommandState(cmdName) !== -1) {
  12255. me.execCommand('insertHtml', '<hr>');
  12256. var range = me.selection.getRange(),
  12257. start = range.startContainer;
  12258. if (start.nodeType == 1 && !start.childNodes[range.startOffset]) {
  12259. var tmp;
  12260. if (tmp = start.childNodes[range.startOffset - 1]) {
  12261. if (tmp.nodeType == 1 && tmp.tagName == 'HR') {
  12262. if (me.options.enterTag == 'p') {
  12263. tmp = me.document.createElement('p');
  12264. range.insertNode(tmp);
  12265. range.setStart(tmp, 0).setCursor();
  12266. } else {
  12267. tmp = me.document.createElement('br');
  12268. range.insertNode(tmp);
  12269. range.setStartBefore(tmp).setCursor();
  12270. }
  12271. }
  12272. }
  12273. }
  12274. return true;
  12275. }
  12276. },
  12277. //边界在table里不能加分隔线
  12278. queryCommandState: function () {
  12279. return domUtils.filterNodeList(this.selection.getStartElementPath(), 'table') ? -1 : 0;
  12280. }
  12281. };
  12282. // me.addListener('delkeyup',function(){
  12283. // var rng = this.selection.getRange();
  12284. // if(browser.ie && browser.version > 8){
  12285. // rng.txtToElmBoundary(true);
  12286. // if(domUtils.isStartInblock(rng)){
  12287. // var tmpNode = rng.startContainer;
  12288. // var pre = tmpNode.previousSibling;
  12289. // if(pre && domUtils.isTagNode(pre,'hr')){
  12290. // domUtils.remove(pre);
  12291. // rng.select();
  12292. // return;
  12293. // }
  12294. // }
  12295. // }
  12296. // if(domUtils.isBody(rng.startContainer)){
  12297. // var hr = rng.startContainer.childNodes[rng.startOffset -1];
  12298. // if(hr && hr.nodeName == 'HR'){
  12299. // var next = hr.nextSibling;
  12300. // if(next){
  12301. // rng.setStart(next,0)
  12302. // }else if(hr.previousSibling){
  12303. // rng.setStartAtLast(hr.previousSibling)
  12304. // }else{
  12305. // var p = this.document.createElement('p');
  12306. // hr.parentNode.insertBefore(p,hr);
  12307. // domUtils.fillNode(this.document,p);
  12308. // rng.setStart(p,0);
  12309. // }
  12310. // domUtils.remove(hr);
  12311. // rng.setCursor(false,true);
  12312. // }
  12313. // }
  12314. // })
  12315. me.addListener('delkeydown', function (name, evt) {
  12316. var rng = this.selection.getRange();
  12317. rng.txtToElmBoundary(true);
  12318. if (domUtils.isStartInblock(rng)) {
  12319. var tmpNode = rng.startContainer;
  12320. var pre = tmpNode.previousSibling;
  12321. if (pre && domUtils.isTagNode(pre, 'hr')) {
  12322. domUtils.remove(pre);
  12323. rng.select();
  12324. domUtils.preventDefault(evt);
  12325. return true;
  12326. }
  12327. }
  12328. })
  12329. };
  12330. // plugins/time.js
  12331. /**
  12332. * 插入时间和日期
  12333. * @file
  12334. * @since 1.2.6.1
  12335. */
  12336. /**
  12337. * 插入时间,默认格式:12:59:59
  12338. * @command time
  12339. * @method execCommand
  12340. * @param { String } cmd 命令字符串
  12341. * @example
  12342. * ```javascript
  12343. * editor.execCommand( 'time');
  12344. * ```
  12345. */
  12346. /**
  12347. * 插入日期,默认格式:2013-08-30
  12348. * @command date
  12349. * @method execCommand
  12350. * @param { String } cmd 命令字符串
  12351. * @example
  12352. * ```javascript
  12353. * editor.execCommand( 'date');
  12354. * ```
  12355. */
  12356. UE.commands['time'] = UE.commands["date"] = {
  12357. execCommand: function (cmd, format) {
  12358. var date = new Date;
  12359. function formatTime(date, format) {
  12360. var hh = ('0' + date.getHours()).slice(-2),
  12361. ii = ('0' + date.getMinutes()).slice(-2),
  12362. ss = ('0' + date.getSeconds()).slice(-2);
  12363. format = format || 'hh:ii:ss';
  12364. return format.replace(/hh/ig, hh).replace(/ii/ig, ii).replace(/ss/ig, ss);
  12365. }
  12366. function formatDate(date, format) {
  12367. var yyyy = ('000' + date.getFullYear()).slice(-4),
  12368. yy = yyyy.slice(-2),
  12369. mm = ('0' + (date.getMonth() + 1)).slice(-2),
  12370. dd = ('0' + date.getDate()).slice(-2);
  12371. format = format || 'yyyy-mm-dd';
  12372. return format.replace(/yyyy/ig, yyyy).replace(/yy/ig, yy).replace(/mm/ig, mm).replace(/dd/ig, dd);
  12373. }
  12374. this.execCommand('insertHtml', cmd == "time" ? formatTime(date, format) : formatDate(date, format));
  12375. }
  12376. };
  12377. // plugins/rowspacing.js
  12378. /**
  12379. * 段前段后间距插件
  12380. * @file
  12381. * @since 1.2.6.1
  12382. */
  12383. /**
  12384. * 设置段间距
  12385. * @command rowspacing
  12386. * @method execCommand
  12387. * @param { String } cmd 命令字符串
  12388. * @param { String } value 段间距的值,以px为单位
  12389. * @param { String } dir 间距位置,top或bottom,分别表示段前和段后
  12390. * @example
  12391. * ```javascript
  12392. * editor.execCommand( 'rowspacing', '10', 'top' );
  12393. * ```
  12394. */
  12395. UE.plugins['rowspacing'] = function () {
  12396. var me = this;
  12397. me.setOpt({
  12398. 'rowspacingtop': ['5', '10', '15', '20', '25'],
  12399. 'rowspacingbottom': ['5', '10', '15', '20', '25']
  12400. });
  12401. me.commands['rowspacing'] = {
  12402. execCommand: function (cmdName, value, dir) {
  12403. this.execCommand('paragraph', 'p', {style: 'margin-' + dir + ':' + value + 'px'});
  12404. return true;
  12405. },
  12406. queryCommandValue: function (cmdName, dir) {
  12407. var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), function (node) {
  12408. return domUtils.isBlockElm(node)
  12409. }),
  12410. value;
  12411. //trace:1026
  12412. if (pN) {
  12413. value = domUtils.getComputedStyle(pN, 'margin-' + dir).replace(/[^\d]/g, '');
  12414. return !value ? 0 : value;
  12415. }
  12416. return 0;
  12417. }
  12418. };
  12419. };
  12420. // plugins/lineheight.js
  12421. /**
  12422. * 设置行内间距
  12423. * @file
  12424. * @since 1.2.6.1
  12425. */
  12426. UE.plugins['lineheight'] = function () {
  12427. var me = this;
  12428. me.setOpt({'lineheight': ['1', '1.5', '1.75', '2', '3', '4', '5']});
  12429. /**
  12430. * 行距
  12431. * @command lineheight
  12432. * @method execCommand
  12433. * @param { String } cmdName 命令字符串
  12434. * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75
  12435. * @example
  12436. * ```javascript
  12437. * editor.execCommand( 'lineheight', 1.5);
  12438. * ```
  12439. */
  12440. /**
  12441. * 查询当前选区内容的行高大小
  12442. * @command lineheight
  12443. * @method queryCommandValue
  12444. * @param { String } cmd 命令字符串
  12445. * @return { String } 返回当前行高大小
  12446. * @example
  12447. * ```javascript
  12448. * editor.queryCommandValue( 'lineheight' );
  12449. * ```
  12450. */
  12451. me.commands['lineheight'] = {
  12452. execCommand: function (cmdName, value) {
  12453. this.execCommand('paragraph', 'p', {style: 'line-height:' + (value == "1" ? "normal" : value + 'em')});
  12454. return true;
  12455. },
  12456. queryCommandValue: function () {
  12457. var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), function (node) {
  12458. return domUtils.isBlockElm(node)
  12459. });
  12460. if (pN) {
  12461. var value = domUtils.getComputedStyle(pN, 'line-height');
  12462. return value == 'normal' ? 1 : value.replace(/[^\d.]*/ig, "");
  12463. }
  12464. }
  12465. };
  12466. };
  12467. // plugins/insertcode.js
  12468. /**
  12469. * 插入代码插件
  12470. * @file
  12471. * @since 1.2.6.1
  12472. */
  12473. UE.plugins['insertcode'] = function () {
  12474. var me = this;
  12475. me.ready(function () {
  12476. utils.cssRule('pre', 'pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}',
  12477. me.document)
  12478. });
  12479. me.setOpt('insertcode', {
  12480. 'as3': 'ActionScript3',
  12481. 'bash': 'Bash/Shell',
  12482. 'cpp': 'C/C++',
  12483. 'css': 'Css',
  12484. 'cf': 'CodeFunction',
  12485. 'c#': 'C#',
  12486. 'delphi': 'Delphi',
  12487. 'diff': 'Diff',
  12488. 'erlang': 'Erlang',
  12489. 'groovy': 'Groovy',
  12490. 'html': 'Html',
  12491. 'java': 'Java',
  12492. 'jfx': 'JavaFx',
  12493. 'js': 'Javascript',
  12494. 'pl': 'Perl',
  12495. 'php': 'Php',
  12496. 'plain': 'Plain Text',
  12497. 'ps': 'PowerShell',
  12498. 'python': 'Python',
  12499. 'ruby': 'Ruby',
  12500. 'scala': 'Scala',
  12501. 'sql': 'Sql',
  12502. 'vb': 'Vb',
  12503. 'xml': 'Xml'
  12504. });
  12505. /**
  12506. * 插入代码
  12507. * @command insertcode
  12508. * @method execCommand
  12509. * @param { String } cmd 命令字符串
  12510. * @param { String } lang 插入代码的语言
  12511. * @example
  12512. * ```javascript
  12513. * editor.execCommand( 'insertcode', 'javascript' );
  12514. * ```
  12515. */
  12516. /**
  12517. * 如果选区所在位置是插入插入代码区域,返回代码的语言
  12518. * @command insertcode
  12519. * @method queryCommandValue
  12520. * @param { String } cmd 命令字符串
  12521. * @return { String } 返回代码的语言
  12522. * @example
  12523. * ```javascript
  12524. * editor.queryCommandValue( 'insertcode' );
  12525. * ```
  12526. */
  12527. me.commands['insertcode'] = {
  12528. execCommand: function (cmd, lang) {
  12529. var me = this,
  12530. rng = me.selection.getRange(),
  12531. pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true);
  12532. if (pre) {
  12533. pre.className = 'brush:' + lang + ';toolbar:false;';
  12534. } else {
  12535. var code = '';
  12536. if (rng.collapsed) {
  12537. code = browser.ie && browser.ie11below ? (browser.version <= 8 ? '&nbsp;' : '') : '<br/>';
  12538. } else {
  12539. var frag = rng.extractContents();
  12540. var div = me.document.createElement('div');
  12541. div.appendChild(frag);
  12542. utils.each(UE.filterNode(UE.htmlparser(div.innerHTML.replace(/[\r\t]/g, '')), me.options.filterTxtRules).children, function (node) {
  12543. if (browser.ie && browser.ie11below && browser.version > 8) {
  12544. if (node.type == 'element') {
  12545. if (node.tagName == 'br') {
  12546. code += '\n'
  12547. } else if (!dtd.$empty[node.tagName]) {
  12548. utils.each(node.children, function (cn) {
  12549. if (cn.type == 'element') {
  12550. if (cn.tagName == 'br') {
  12551. code += '\n'
  12552. } else if (!dtd.$empty[node.tagName]) {
  12553. code += cn.innerText();
  12554. }
  12555. } else {
  12556. code += cn.data
  12557. }
  12558. })
  12559. if (!/\n$/.test(code)) {
  12560. code += '\n';
  12561. }
  12562. }
  12563. } else {
  12564. code += node.data + '\n'
  12565. }
  12566. if (!node.nextSibling() && /\n$/.test(code)) {
  12567. code = code.replace(/\n$/, '');
  12568. }
  12569. } else {
  12570. if (browser.ie && browser.ie11below) {
  12571. if (node.type == 'element') {
  12572. if (node.tagName == 'br') {
  12573. code += '<br>'
  12574. } else if (!dtd.$empty[node.tagName]) {
  12575. utils.each(node.children, function (cn) {
  12576. if (cn.type == 'element') {
  12577. if (cn.tagName == 'br') {
  12578. code += '<br>'
  12579. } else if (!dtd.$empty[node.tagName]) {
  12580. code += cn.innerText();
  12581. }
  12582. } else {
  12583. code += cn.data
  12584. }
  12585. });
  12586. if (!/br>$/.test(code)) {
  12587. code += '<br>';
  12588. }
  12589. }
  12590. } else {
  12591. code += node.data + '<br>'
  12592. }
  12593. if (!node.nextSibling() && /<br>$/.test(code)) {
  12594. code = code.replace(/<br>$/, '');
  12595. }
  12596. } else {
  12597. code += (node.type == 'element' ? (dtd.$empty[node.tagName] ? '' : node.innerText()) : node.data);
  12598. if (!/br\/?\s*>$/.test(code)) {
  12599. if (!node.nextSibling())
  12600. return;
  12601. code += '<br>'
  12602. }
  12603. }
  12604. }
  12605. });
  12606. }
  12607. me.execCommand('inserthtml', '<pre id="coder"class="brush:' + lang + ';toolbar:false">' + code + '</pre>', true);
  12608. pre = me.document.getElementById('coder');
  12609. domUtils.removeAttributes(pre, 'id');
  12610. var tmpNode = pre.previousSibling;
  12611. if (tmpNode && (tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6 || domUtils.isEmptyBlock(tmpNode))) {
  12612. domUtils.remove(tmpNode)
  12613. }
  12614. var rng = me.selection.getRange();
  12615. if (domUtils.isEmptyBlock(pre)) {
  12616. rng.setStart(pre, 0).setCursor(false, true)
  12617. } else {
  12618. rng.selectNodeContents(pre).select()
  12619. }
  12620. }
  12621. },
  12622. queryCommandValue: function () {
  12623. var path = this.selection.getStartElementPath();
  12624. var lang = '';
  12625. utils.each(path, function (node) {
  12626. if (node.nodeName == 'PRE') {
  12627. var match = node.className.match(/brush:([^;]+)/);
  12628. lang = match && match[1] ? match[1] : '';
  12629. return false;
  12630. }
  12631. });
  12632. return lang;
  12633. }
  12634. };
  12635. me.addInputRule(function (root) {
  12636. utils.each(root.getNodesByTagName('pre'), function (pre) {
  12637. var brs = pre.getNodesByTagName('br');
  12638. if (brs.length) {
  12639. browser.ie && browser.ie11below && browser.version > 8 && utils.each(brs, function (br) {
  12640. var txt = UE.uNode.createText('\n');
  12641. br.parentNode.insertBefore(txt, br);
  12642. br.parentNode.removeChild(br);
  12643. });
  12644. return;
  12645. }
  12646. if (browser.ie && browser.ie11below && browser.version > 8)
  12647. return;
  12648. var code = pre.innerText().split(/\n/);
  12649. pre.innerHTML('');
  12650. utils.each(code, function (c) {
  12651. if (c.length) {
  12652. pre.appendChild(UE.uNode.createText(c));
  12653. }
  12654. pre.appendChild(UE.uNode.createElement('br'))
  12655. })
  12656. })
  12657. });
  12658. me.addOutputRule(function (root) {
  12659. utils.each(root.getNodesByTagName('pre'), function (pre) {
  12660. var code = '';
  12661. utils.each(pre.children, function (n) {
  12662. if (n.type == 'text') {
  12663. //在ie下文本内容有可能末尾带有\n要去掉
  12664. //trace:3396
  12665. code += n.data.replace(/[ ]/g, '&nbsp;').replace(/\n$/, '');
  12666. } else {
  12667. if (n.tagName == 'br') {
  12668. code += '\n'
  12669. } else {
  12670. code += (!dtd.$empty[n.tagName] ? '' : n.innerText());
  12671. }
  12672. }
  12673. });
  12674. pre.innerText(code.replace(/(&nbsp;|\n)+$/, ''))
  12675. })
  12676. });
  12677. //不需要判断highlight的command列表
  12678. me.notNeedCodeQuery = {
  12679. help: 1,
  12680. undo: 1,
  12681. redo: 1,
  12682. source: 1,
  12683. print: 1,
  12684. searchreplace: 1,
  12685. fullscreen: 1,
  12686. preview: 1,
  12687. insertparagraph: 1,
  12688. elementpath: 1,
  12689. insertcode: 1,
  12690. inserthtml: 1,
  12691. selectall: 1
  12692. };
  12693. //将queyCommamndState重置
  12694. var orgQuery = me.queryCommandState;
  12695. me.queryCommandState = function (cmd) {
  12696. var me = this;
  12697. if (!me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue('insertcode')) {
  12698. return -1;
  12699. }
  12700. return UE.Editor.prototype.queryCommandState.apply(this, arguments)
  12701. };
  12702. me.addListener('beforeenterkeydown', function () {
  12703. var rng = me.selection.getRange();
  12704. var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true);
  12705. if (pre) {
  12706. me.fireEvent('saveScene');
  12707. if (!rng.collapsed) {
  12708. rng.deleteContents();
  12709. }
  12710. if (!browser.ie || browser.ie9above) {
  12711. var tmpNode = me.document.createElement('br'), pre;
  12712. rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true);
  12713. var next = tmpNode.nextSibling;
  12714. if (!next && (!browser.ie || browser.version > 10)) {
  12715. rng.insertNode(tmpNode.cloneNode(false));
  12716. } else {
  12717. rng.setStartAfter(tmpNode);
  12718. }
  12719. pre = tmpNode.previousSibling;
  12720. var tmp;
  12721. while (pre) {
  12722. tmp = pre;
  12723. pre = pre.previousSibling;
  12724. if (!pre || pre.nodeName == 'BR') {
  12725. pre = tmp;
  12726. break;
  12727. }
  12728. }
  12729. if (pre) {
  12730. var str = '';
  12731. while (pre && pre.nodeName != 'BR' && new RegExp('^[\\s' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
  12732. str += pre.nodeValue;
  12733. pre = pre.nextSibling;
  12734. }
  12735. if (pre.nodeName != 'BR') {
  12736. var match = pre.nodeValue.match(new RegExp('^([\\s' + domUtils.fillChar + ']+)'));
  12737. if (match && match[1]) {
  12738. str += match[1]
  12739. }
  12740. }
  12741. if (str) {
  12742. str = me.document.createTextNode(str);
  12743. rng.insertNode(str).setStartAfter(str);
  12744. }
  12745. }
  12746. rng.collapse(true).select(true);
  12747. } else {
  12748. if (browser.version > 8) {
  12749. var txt = me.document.createTextNode('\n');
  12750. var start = rng.startContainer;
  12751. if (rng.startOffset == 0) {
  12752. var preNode = start.previousSibling;
  12753. if (preNode) {
  12754. rng.insertNode(txt);
  12755. var fillchar = me.document.createTextNode(' ');
  12756. rng.setStartAfter(txt).insertNode(fillchar).setStart(fillchar, 0).collapse(true).select(true)
  12757. }
  12758. } else {
  12759. rng.insertNode(txt).setStartAfter(txt);
  12760. var fillchar = me.document.createTextNode(' ');
  12761. start = rng.startContainer.childNodes[rng.startOffset];
  12762. if (start && !/^\n/.test(start.nodeValue)) {
  12763. rng.setStartBefore(txt)
  12764. }
  12765. rng.insertNode(fillchar).setStart(fillchar, 0).collapse(true).select(true)
  12766. }
  12767. } else {
  12768. var tmpNode = me.document.createElement('br');
  12769. rng.insertNode(tmpNode);
  12770. rng.insertNode(me.document.createTextNode(domUtils.fillChar));
  12771. rng.setStartAfter(tmpNode);
  12772. pre = tmpNode.previousSibling;
  12773. var tmp;
  12774. while (pre) {
  12775. tmp = pre;
  12776. pre = pre.previousSibling;
  12777. if (!pre || pre.nodeName == 'BR') {
  12778. pre = tmp;
  12779. break;
  12780. }
  12781. }
  12782. if (pre) {
  12783. var str = '';
  12784. while (pre && pre.nodeName != 'BR' && new RegExp('^[ ' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
  12785. str += pre.nodeValue;
  12786. pre = pre.nextSibling;
  12787. }
  12788. if (pre.nodeName != 'BR') {
  12789. var match = pre.nodeValue.match(new RegExp('^([ ' + domUtils.fillChar + ']+)'));
  12790. if (match && match[1]) {
  12791. str += match[1]
  12792. }
  12793. }
  12794. str = me.document.createTextNode(str);
  12795. rng.insertNode(str).setStartAfter(str);
  12796. }
  12797. rng.collapse(true).select();
  12798. }
  12799. }
  12800. me.fireEvent('saveScene');
  12801. return true;
  12802. }
  12803. });
  12804. me.addListener('tabkeydown', function (cmd, evt) {
  12805. var rng = me.selection.getRange();
  12806. var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true);
  12807. if (pre) {
  12808. me.fireEvent('saveScene');
  12809. if (evt.shiftKey) {
  12810. } else {
  12811. if (!rng.collapsed) {
  12812. var bk = rng.createBookmark();
  12813. var start = bk.start.previousSibling;
  12814. while (start) {
  12815. if (pre.firstChild === start && !domUtils.isBr(start)) {
  12816. pre.insertBefore(me.document.createTextNode(' '), start);
  12817. break;
  12818. }
  12819. if (domUtils.isBr(start)) {
  12820. pre.insertBefore(me.document.createTextNode(' '), start.nextSibling);
  12821. break;
  12822. }
  12823. start = start.previousSibling;
  12824. }
  12825. var end = bk.end;
  12826. start = bk.start.nextSibling;
  12827. if (pre.firstChild === bk.start) {
  12828. pre.insertBefore(me.document.createTextNode(' '), start.nextSibling)
  12829. }
  12830. while (start && start !== end) {
  12831. if (domUtils.isBr(start) && start.nextSibling) {
  12832. if (start.nextSibling === end) {
  12833. break;
  12834. }
  12835. pre.insertBefore(me.document.createTextNode(' '), start.nextSibling)
  12836. }
  12837. start = start.nextSibling;
  12838. }
  12839. rng.moveToBookmark(bk).select();
  12840. } else {
  12841. var tmpNode = me.document.createTextNode(' ');
  12842. rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true).select(true);
  12843. }
  12844. }
  12845. me.fireEvent('saveScene');
  12846. return true;
  12847. }
  12848. });
  12849. me.addListener('beforeinserthtml', function (evtName, html) {
  12850. var me = this,
  12851. rng = me.selection.getRange(),
  12852. pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true);
  12853. if (pre) {
  12854. if (!rng.collapsed) {
  12855. rng.deleteContents()
  12856. }
  12857. var htmlstr = '';
  12858. if (browser.ie && browser.version > 8) {
  12859. utils.each(UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children, function (node) {
  12860. if (node.type == 'element') {
  12861. if (node.tagName == 'br') {
  12862. htmlstr += '\n'
  12863. } else if (!dtd.$empty[node.tagName]) {
  12864. utils.each(node.children, function (cn) {
  12865. if (cn.type == 'element') {
  12866. if (cn.tagName == 'br') {
  12867. htmlstr += '\n'
  12868. } else if (!dtd.$empty[node.tagName]) {
  12869. htmlstr += cn.innerText();
  12870. }
  12871. } else {
  12872. htmlstr += cn.data
  12873. }
  12874. })
  12875. if (!/\n$/.test(htmlstr)) {
  12876. htmlstr += '\n';
  12877. }
  12878. }
  12879. } else {
  12880. htmlstr += node.data + '\n'
  12881. }
  12882. if (!node.nextSibling() && /\n$/.test(htmlstr)) {
  12883. htmlstr = htmlstr.replace(/\n$/, '');
  12884. }
  12885. });
  12886. var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/&nbsp;/g, ' ')));
  12887. rng.insertNode(tmpNode).selectNode(tmpNode).select();
  12888. } else {
  12889. var frag = me.document.createDocumentFragment();
  12890. utils.each(UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children, function (node) {
  12891. if (node.type == 'element') {
  12892. if (node.tagName == 'br') {
  12893. frag.appendChild(me.document.createElement('br'))
  12894. } else if (!dtd.$empty[node.tagName]) {
  12895. utils.each(node.children, function (cn) {
  12896. if (cn.type == 'element') {
  12897. if (cn.tagName == 'br') {
  12898. frag.appendChild(me.document.createElement('br'))
  12899. } else if (!dtd.$empty[node.tagName]) {
  12900. frag.appendChild(me.document.createTextNode(utils.html(cn.innerText().replace(/&nbsp;/g, ' '))));
  12901. }
  12902. } else {
  12903. frag.appendChild(me.document.createTextNode(utils.html(cn.data.replace(/&nbsp;/g, ' '))));
  12904. }
  12905. })
  12906. if (frag.lastChild.nodeName != 'BR') {
  12907. frag.appendChild(me.document.createElement('br'))
  12908. }
  12909. }
  12910. } else {
  12911. frag.appendChild(me.document.createTextNode(utils.html(node.data.replace(/&nbsp;/g, ' '))));
  12912. }
  12913. if (!node.nextSibling() && frag.lastChild.nodeName == 'BR') {
  12914. frag.removeChild(frag.lastChild)
  12915. }
  12916. });
  12917. rng.insertNode(frag).select();
  12918. }
  12919. return true;
  12920. }
  12921. });
  12922. //方向键的处理
  12923. me.addListener('keydown', function (cmd, evt) {
  12924. var me = this, keyCode = evt.keyCode || evt.which;
  12925. if (keyCode == 40) {
  12926. var rng = me.selection.getRange(), pre, start = rng.startContainer;
  12927. if (rng.collapsed && (pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)) && !pre.nextSibling) {
  12928. var last = pre.lastChild
  12929. while (last && last.nodeName == 'BR') {
  12930. last = last.previousSibling;
  12931. }
  12932. if (last === start || rng.startContainer === pre && rng.startOffset == pre.childNodes.length) {
  12933. me.execCommand('insertparagraph');
  12934. domUtils.preventDefault(evt)
  12935. }
  12936. }
  12937. }
  12938. });
  12939. //trace:3395
  12940. me.addListener('delkeydown', function (type, evt) {
  12941. var rng = this.selection.getRange();
  12942. rng.txtToElmBoundary(true);
  12943. var start = rng.startContainer;
  12944. if (domUtils.isTagNode(start, 'pre') && rng.collapsed && domUtils.isStartInblock(rng)) {
  12945. var p = me.document.createElement('p');
  12946. domUtils.fillNode(me.document, p);
  12947. start.parentNode.insertBefore(p, start);
  12948. domUtils.remove(start);
  12949. rng.setStart(p, 0).setCursor(false, true);
  12950. domUtils.preventDefault(evt);
  12951. return true;
  12952. }
  12953. })
  12954. };
  12955. // plugins/cleardoc.js
  12956. /**
  12957. * 清空文档插件
  12958. * @file
  12959. * @since 1.2.6.1
  12960. */
  12961. /**
  12962. * 清空文档
  12963. * @command cleardoc
  12964. * @method execCommand
  12965. * @param { String } cmd 命令字符串
  12966. * @example
  12967. * ```javascript
  12968. * //editor 是编辑器实例
  12969. * editor.execCommand('cleardoc');
  12970. * ```
  12971. */
  12972. UE.commands['cleardoc'] = {
  12973. execCommand: function (cmdName) {
  12974. var me = this,
  12975. enterTag = me.options.enterTag,
  12976. range = me.selection.getRange();
  12977. if (enterTag == "br") {
  12978. me.body.innerHTML = "<br/>";
  12979. range.setStart(me.body, 0).setCursor();
  12980. } else {
  12981. me.body.innerHTML = "<p>" + (ie ? "" : "<br/>") + "</p>";
  12982. range.setStart(me.body.firstChild, 0).setCursor(false, true);
  12983. }
  12984. setTimeout(function () {
  12985. me.fireEvent("clearDoc");
  12986. }, 0);
  12987. }
  12988. };
  12989. // plugins/anchor.js
  12990. /**
  12991. * 锚点插件,为UEditor提供插入锚点支持
  12992. * @file
  12993. * @since 1.2.6.1
  12994. */
  12995. UE.plugin.register('anchor', function () {
  12996. return {
  12997. bindEvents: {
  12998. 'ready': function () {
  12999. utils.cssRule('anchor',
  13000. '.anchorclass{background: url(\''
  13001. + this.options.themePath
  13002. + this.options.theme + '/images/anchor.gif\') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}',
  13003. this.document);
  13004. }
  13005. },
  13006. outputRule: function (root) {
  13007. utils.each(root.getNodesByTagName('img'), function (a) {
  13008. var val;
  13009. if (val = a.getAttr('anchorname')) {
  13010. a.tagName = 'a';
  13011. a.setAttr({
  13012. anchorname: '',
  13013. name: val,
  13014. 'class': ''
  13015. })
  13016. }
  13017. })
  13018. },
  13019. inputRule: function (root) {
  13020. utils.each(root.getNodesByTagName('a'), function (a) {
  13021. var val;
  13022. if ((val = a.getAttr('name')) && !a.getAttr('href')) {
  13023. a.tagName = 'img';
  13024. a.setAttr({
  13025. anchorname: a.getAttr('name'),
  13026. 'class': 'anchorclass'
  13027. });
  13028. a.setAttr('name')
  13029. }
  13030. })
  13031. },
  13032. commands: {
  13033. /**
  13034. * 插入锚点
  13035. * @command anchor
  13036. * @method execCommand
  13037. * @param { String } cmd 命令字符串
  13038. * @param { String } name 锚点名称字符串
  13039. * @example
  13040. * ```javascript
  13041. * //editor 是编辑器实例
  13042. * editor.execCommand('anchor', 'anchor1');
  13043. * ```
  13044. */
  13045. 'anchor': {
  13046. execCommand: function (cmd, name) {
  13047. var range = this.selection.getRange(), img = range.getClosedNode();
  13048. if (img && img.getAttribute('anchorname')) {
  13049. if (name) {
  13050. img.setAttribute('anchorname', name);
  13051. } else {
  13052. range.setStartBefore(img).setCursor();
  13053. domUtils.remove(img);
  13054. }
  13055. } else {
  13056. if (name) {
  13057. //只在选区的开始插入
  13058. var anchor = this.document.createElement('img');
  13059. range.collapse(true);
  13060. domUtils.setAttributes(anchor, {
  13061. 'anchorname': name,
  13062. 'class': 'anchorclass'
  13063. });
  13064. range.insertNode(anchor).setStartAfter(anchor).setCursor(false, true);
  13065. }
  13066. }
  13067. }
  13068. }
  13069. }
  13070. }
  13071. });
  13072. // plugins/wordcount.js
  13073. ///import core
  13074. ///commands 字数统计
  13075. ///commandsName WordCount,wordCount
  13076. ///commandsTitle 字数统计
  13077. /*
  13078. * Created by JetBrains WebStorm.
  13079. * User: taoqili
  13080. * Date: 11-9-7
  13081. * Time: 下午8:18
  13082. * To change this template use File | Settings | File Templates.
  13083. */
  13084. UE.plugins['wordcount'] = function () {
  13085. var me = this;
  13086. me.setOpt('wordCount', true);
  13087. me.addListener('contentchange', function () {
  13088. me.fireEvent('wordcount');
  13089. });
  13090. var timer;
  13091. me.addListener('ready', function () {
  13092. var me = this;
  13093. domUtils.on(me.body, "keyup", function (evt) {
  13094. var code = evt.keyCode || evt.which,
  13095. //忽略的按键,ctr,alt,shift,方向键
  13096. ignores = {"16": 1, "18": 1, "20": 1, "37": 1, "38": 1, "39": 1, "40": 1};
  13097. if (code in ignores) return;
  13098. clearTimeout(timer);
  13099. timer = setTimeout(function () {
  13100. me.fireEvent('wordcount');
  13101. }, 200)
  13102. })
  13103. });
  13104. };
  13105. // plugins/pagebreak.js
  13106. /**
  13107. * 分页功能插件
  13108. * @file
  13109. * @since 1.2.6.1
  13110. */
  13111. UE.plugins['pagebreak'] = function () {
  13112. var me = this,
  13113. notBreakTags = ['td'];
  13114. me.setOpt('pageBreakTag', '_ueditor_page_break_tag_');
  13115. function fillNode(node) {
  13116. if (domUtils.isEmptyBlock(node)) {
  13117. var firstChild = node.firstChild, tmpNode;
  13118. while (firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)) {
  13119. tmpNode = firstChild;
  13120. firstChild = firstChild.firstChild;
  13121. }
  13122. !tmpNode && (tmpNode = node);
  13123. domUtils.fillNode(me.document, tmpNode);
  13124. }
  13125. }
  13126. //分页符样式添加
  13127. me.ready(function () {
  13128. utils.cssRule('pagebreak', '.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}', me.document);
  13129. });
  13130. function isHr(node) {
  13131. return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak';
  13132. }
  13133. me.addInputRule(function (root) {
  13134. root.traversal(function (node) {
  13135. if (node.type == 'text' && node.data == me.options.pageBreakTag) {
  13136. var hr = UE.uNode.createElement('<hr class="pagebreak" noshade="noshade" size="5" style="-webkit-user-select: none;">');
  13137. node.parentNode.insertBefore(hr, node);
  13138. node.parentNode.removeChild(node)
  13139. }
  13140. })
  13141. });
  13142. me.addOutputRule(function (node) {
  13143. utils.each(node.getNodesByTagName('hr'), function (n) {
  13144. if (n.getAttr('class') == 'pagebreak') {
  13145. var txt = UE.uNode.createText(me.options.pageBreakTag);
  13146. n.parentNode.insertBefore(txt, n);
  13147. n.parentNode.removeChild(n);
  13148. }
  13149. })
  13150. });
  13151. /**
  13152. * 插入分页符
  13153. * @command pagebreak
  13154. * @method execCommand
  13155. * @param { String } cmd 命令字符串
  13156. * @remind 在表格中插入分页符会把表格切分成两部分
  13157. * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串,
  13158. * 以便于提交数据到服务器端后处理分页。
  13159. * @example
  13160. * ```javascript
  13161. * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak
  13162. * ```
  13163. */
  13164. me.commands['pagebreak'] = {
  13165. execCommand: function () {
  13166. var range = me.selection.getRange(), hr = me.document.createElement('hr');
  13167. domUtils.setAttributes(hr, {
  13168. 'class': 'pagebreak',
  13169. noshade: "noshade",
  13170. size: "5"
  13171. });
  13172. domUtils.unSelectable(hr);
  13173. //table单独处理
  13174. var node = domUtils.findParentByTagName(range.startContainer, notBreakTags, true),
  13175. parents = [], pN;
  13176. if (node) {
  13177. switch (node.tagName) {
  13178. case 'TD':
  13179. pN = node.parentNode;
  13180. if (!pN.previousSibling) {
  13181. var table = domUtils.findParentByTagName(pN, 'table');
  13182. // var tableWrapDiv = table.parentNode;
  13183. // if(tableWrapDiv && tableWrapDiv.nodeType == 1
  13184. // && tableWrapDiv.tagName == 'DIV'
  13185. // && tableWrapDiv.getAttribute('dropdrag')
  13186. // ){
  13187. // domUtils.remove(tableWrapDiv,true);
  13188. // }
  13189. table.parentNode.insertBefore(hr, table);
  13190. parents = domUtils.findParents(hr, true);
  13191. } else {
  13192. pN.parentNode.insertBefore(hr, pN);
  13193. parents = domUtils.findParents(hr);
  13194. }
  13195. pN = parents[1];
  13196. if (hr !== pN) {
  13197. domUtils.breakParent(hr, pN);
  13198. }
  13199. //table要重写绑定一下拖拽
  13200. me.fireEvent('afteradjusttable', me.document);
  13201. }
  13202. } else {
  13203. if (!range.collapsed) {
  13204. range.deleteContents();
  13205. var start = range.startContainer;
  13206. while (!domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start)) {
  13207. range.setStartBefore(start).collapse(true);
  13208. domUtils.remove(start);
  13209. start = range.startContainer;
  13210. }
  13211. }
  13212. range.insertNode(hr);
  13213. var pN = hr.parentNode, nextNode;
  13214. while (!domUtils.isBody(pN)) {
  13215. domUtils.breakParent(hr, pN);
  13216. nextNode = hr.nextSibling;
  13217. if (nextNode && domUtils.isEmptyBlock(nextNode)) {
  13218. domUtils.remove(nextNode);
  13219. }
  13220. pN = hr.parentNode;
  13221. }
  13222. nextNode = hr.nextSibling;
  13223. var pre = hr.previousSibling;
  13224. if (isHr(pre)) {
  13225. domUtils.remove(pre);
  13226. } else {
  13227. pre && fillNode(pre);
  13228. }
  13229. if (!nextNode) {
  13230. var p = me.document.createElement('p');
  13231. hr.parentNode.appendChild(p);
  13232. domUtils.fillNode(me.document, p);
  13233. range.setStart(p, 0).collapse(true);
  13234. } else {
  13235. if (isHr(nextNode)) {
  13236. domUtils.remove(nextNode);
  13237. } else {
  13238. fillNode(nextNode);
  13239. }
  13240. range.setEndAfter(hr).collapse(false);
  13241. }
  13242. range.select(true);
  13243. }
  13244. }
  13245. };
  13246. };
  13247. // plugins/wordimage.js
  13248. ///import core
  13249. ///commands 本地图片引导上传
  13250. ///commandsName WordImage
  13251. ///commandsTitle 本地图片引导上传
  13252. ///commandsDialog dialogs\wordimage
  13253. UE.plugin.register('wordimage', function () {
  13254. var me = this,
  13255. images = [];
  13256. return {
  13257. commands: {
  13258. 'wordimage': {
  13259. execCommand: function () {
  13260. var images = domUtils.getElementsByTagName(me.body, "img");
  13261. var urlList = [];
  13262. for (var i = 0, ci; ci = images[i++];) {
  13263. var url = ci.getAttribute("word_img");
  13264. url && urlList.push(url);
  13265. }
  13266. return urlList;
  13267. },
  13268. queryCommandState: function () {
  13269. images = domUtils.getElementsByTagName(me.body, "img");
  13270. for (var i = 0, ci; ci = images[i++];) {
  13271. if (ci.getAttribute("word_img")) {
  13272. return 1;
  13273. }
  13274. }
  13275. return -1;
  13276. },
  13277. notNeedUndo: true
  13278. }
  13279. },
  13280. inputRule: function (root) {
  13281. utils.each(root.getNodesByTagName('img'), function (img) {
  13282. var attrs = img.attrs,
  13283. flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
  13284. opt = me.options,
  13285. src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif';
  13286. if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) {
  13287. img.setAttr({
  13288. width: attrs.width,
  13289. height: attrs.height,
  13290. alt: attrs.alt,
  13291. word_img: attrs.src,
  13292. src: src,
  13293. 'style': 'background:url(' + (flag ? opt.themePath + opt.theme + '/images/word.gif' : opt.langPath + opt.lang + '/images/localimage.png') + ') no-repeat center center;border:1px solid #ddd'
  13294. })
  13295. }
  13296. })
  13297. }
  13298. }
  13299. });
  13300. // plugins/dragdrop.js
  13301. UE.plugins['dragdrop'] = function () {
  13302. var me = this;
  13303. me.ready(function () {
  13304. domUtils.on(this.body, 'dragend', function () {
  13305. var rng = me.selection.getRange();
  13306. var node = rng.getClosedNode() || me.selection.getStart();
  13307. if (node && node.tagName == 'IMG') {
  13308. var pre = node.previousSibling, next;
  13309. while (next = node.nextSibling) {
  13310. if (next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild) {
  13311. domUtils.remove(next)
  13312. } else {
  13313. break;
  13314. }
  13315. }
  13316. if ((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre) || !pre) && (!next || next && !domUtils.isEmptyBlock(next))) {
  13317. if (pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)) {
  13318. pre.appendChild(node);
  13319. domUtils.moveChild(next, pre);
  13320. domUtils.remove(next);
  13321. } else if (next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)) {
  13322. next.insertBefore(node, next.firstChild);
  13323. }
  13324. if (pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)) {
  13325. domUtils.remove(pre)
  13326. }
  13327. if (next && next.tagName == 'P' && domUtils.isEmptyBlock(next)) {
  13328. domUtils.remove(next)
  13329. }
  13330. rng.selectNode(node).select();
  13331. me.fireEvent('saveScene');
  13332. }
  13333. }
  13334. })
  13335. });
  13336. me.addListener('keyup', function (type, evt) {
  13337. var keyCode = evt.keyCode || evt.which;
  13338. if (keyCode == 13) {
  13339. var rng = me.selection.getRange(), node;
  13340. if (node = domUtils.findParentByTagName(rng.startContainer, 'p', true)) {
  13341. if (domUtils.getComputedStyle(node, 'text-align') == 'center') {
  13342. domUtils.removeStyle(node, 'text-align')
  13343. }
  13344. }
  13345. }
  13346. })
  13347. };
  13348. // plugins/undo.js
  13349. /**
  13350. * undo redo
  13351. * @file
  13352. * @since 1.2.6.1
  13353. */
  13354. /**
  13355. * 撤销上一次执行的命令
  13356. * @command undo
  13357. * @method execCommand
  13358. * @param { String } cmd 命令字符串
  13359. * @example
  13360. * ```javascript
  13361. * editor.execCommand( 'undo' );
  13362. * ```
  13363. */
  13364. /**
  13365. * 重做上一次执行的命令
  13366. * @command redo
  13367. * @method execCommand
  13368. * @param { String } cmd 命令字符串
  13369. * @example
  13370. * ```javascript
  13371. * editor.execCommand( 'redo' );
  13372. * ```
  13373. */
  13374. UE.plugins['undo'] = function () {
  13375. var saveSceneTimer;
  13376. var me = this,
  13377. maxUndoCount = me.options.maxUndoCount || 20,
  13378. maxInputCount = me.options.maxInputCount || 20,
  13379. fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
  13380. var noNeedFillCharTags = {
  13381. ol: 1, ul: 1, table: 1, tbody: 1, tr: 1, body: 1
  13382. };
  13383. var orgState = me.options.autoClearEmptyNode;
  13384. function compareAddr(indexA, indexB) {
  13385. if (indexA.length != indexB.length)
  13386. return 0;
  13387. for (var i = 0, l = indexA.length; i < l; i++) {
  13388. if (indexA[i] != indexB[i])
  13389. return 0
  13390. }
  13391. return 1;
  13392. }
  13393. function compareRangeAddress(rngAddrA, rngAddrB) {
  13394. if (rngAddrA.collapsed != rngAddrB.collapsed) {
  13395. return 0;
  13396. }
  13397. if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
  13398. return 0;
  13399. }
  13400. return 1;
  13401. }
  13402. function UndoManager() {
  13403. this.list = [];
  13404. this.index = 0;
  13405. this.hasUndo = false;
  13406. this.hasRedo = false;
  13407. this.undo = function () {
  13408. if (this.hasUndo) {
  13409. if (!this.list[this.index - 1] && this.list.length == 1) {
  13410. this.reset();
  13411. return;
  13412. }
  13413. while (this.list[this.index].content == this.list[this.index - 1].content) {
  13414. this.index--;
  13415. if (this.index == 0) {
  13416. return this.restore(0);
  13417. }
  13418. }
  13419. this.restore(--this.index);
  13420. }
  13421. };
  13422. this.redo = function () {
  13423. if (this.hasRedo) {
  13424. while (this.list[this.index].content == this.list[this.index + 1].content) {
  13425. this.index++;
  13426. if (this.index == this.list.length - 1) {
  13427. return this.restore(this.index);
  13428. }
  13429. }
  13430. this.restore(++this.index);
  13431. }
  13432. };
  13433. this.restore = function () {
  13434. var me = this.editor;
  13435. var scene = this.list[this.index];
  13436. var root = UE.htmlparser(scene.content.replace(fillchar, ''));
  13437. me.options.autoClearEmptyNode = false;
  13438. me.filterInputRule(root);
  13439. me.options.autoClearEmptyNode = orgState;
  13440. //trace:873
  13441. //去掉展位符
  13442. me.document.body.innerHTML = root.toHtml();
  13443. me.fireEvent('afterscencerestore');
  13444. //处理undo后空格不展位的问题
  13445. if (browser.ie) {
  13446. utils.each(domUtils.getElementsByTagName(me.document, 'td th caption p'), function (node) {
  13447. if (domUtils.isEmptyNode(node)) {
  13448. domUtils.fillNode(me.document, node);
  13449. }
  13450. })
  13451. }
  13452. try {
  13453. var rng = new dom.Range(me.document).moveToAddress(scene.address);
  13454. rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
  13455. } catch (e) {
  13456. }
  13457. this.update();
  13458. this.clearKey();
  13459. //不能把自己reset了
  13460. me.fireEvent('reset', true);
  13461. };
  13462. this.getScene = function () {
  13463. var me = this.editor;
  13464. var rng = me.selection.getRange(),
  13465. rngAddress = rng.createAddress(false, true);
  13466. me.fireEvent('beforegetscene');
  13467. var root = UE.htmlparser(me.body.innerHTML);
  13468. me.options.autoClearEmptyNode = false;
  13469. me.filterOutputRule(root);
  13470. me.options.autoClearEmptyNode = orgState;
  13471. var cont = root.toHtml();
  13472. //trace:3461
  13473. //这个会引起回退时导致空格丢失的情况
  13474. // browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
  13475. me.fireEvent('aftergetscene');
  13476. return {
  13477. address: rngAddress,
  13478. content: cont
  13479. }
  13480. };
  13481. this.save = function (notCompareRange, notSetCursor) {
  13482. clearTimeout(saveSceneTimer);
  13483. var currentScene = this.getScene(notSetCursor),
  13484. lastScene = this.list[this.index];
  13485. if (lastScene && lastScene.content != currentScene.content) {
  13486. me.trigger('contentchange')
  13487. }
  13488. //内容相同位置相同不存
  13489. if (lastScene && lastScene.content == currentScene.content &&
  13490. (notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address))
  13491. ) {
  13492. return;
  13493. }
  13494. this.list = this.list.slice(0, this.index + 1);
  13495. this.list.push(currentScene);
  13496. //如果大于最大数量了,就把最前的剔除
  13497. if (this.list.length > maxUndoCount) {
  13498. this.list.shift();
  13499. }
  13500. this.index = this.list.length - 1;
  13501. this.clearKey();
  13502. //跟新undo/redo状态
  13503. this.update();
  13504. };
  13505. this.update = function () {
  13506. this.hasRedo = !!this.list[this.index + 1];
  13507. this.hasUndo = !!this.list[this.index - 1];
  13508. };
  13509. this.reset = function () {
  13510. this.list = [];
  13511. this.index = 0;
  13512. this.hasUndo = false;
  13513. this.hasRedo = false;
  13514. this.clearKey();
  13515. };
  13516. this.clearKey = function () {
  13517. keycont = 0;
  13518. lastKeyCode = null;
  13519. };
  13520. }
  13521. me.undoManger = new UndoManager();
  13522. me.undoManger.editor = me;
  13523. function saveScene() {
  13524. this.undoManger.save();
  13525. }
  13526. me.addListener('saveScene', function () {
  13527. var args = Array.prototype.splice.call(arguments, 1);
  13528. this.undoManger.save.apply(this.undoManger, args);
  13529. });
  13530. // me.addListener('beforeexeccommand', saveScene);
  13531. // me.addListener('afterexeccommand', saveScene);
  13532. me.addListener('reset', function (type, exclude) {
  13533. if (!exclude) {
  13534. this.undoManger.reset();
  13535. }
  13536. });
  13537. me.commands['redo'] = me.commands['undo'] = {
  13538. execCommand: function (cmdName) {
  13539. this.undoManger[cmdName]();
  13540. },
  13541. queryCommandState: function (cmdName) {
  13542. return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
  13543. },
  13544. notNeedUndo: 1
  13545. };
  13546. var keys = {
  13547. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  13548. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  13549. 37: 1, 38: 1, 39: 1, 40: 1
  13550. },
  13551. keycont = 0,
  13552. lastKeyCode;
  13553. //输入法状态下不计算字符数
  13554. var inputType = false;
  13555. me.addListener('ready', function () {
  13556. domUtils.on(this.body, 'compositionstart', function () {
  13557. inputType = true;
  13558. });
  13559. domUtils.on(this.body, 'compositionend', function () {
  13560. inputType = false;
  13561. })
  13562. });
  13563. //快捷键
  13564. me.addshortcutkey({
  13565. "Undo": "ctrl+90", //undo
  13566. "Redo": "ctrl+89" //redo
  13567. });
  13568. var isCollapsed = true;
  13569. me.addListener('keydown', function (type, evt) {
  13570. var me = this;
  13571. var keyCode = evt.keyCode || evt.which;
  13572. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  13573. if (inputType)
  13574. return;
  13575. if (!me.selection.getRange().collapsed) {
  13576. me.undoManger.save(false, true);
  13577. isCollapsed = false;
  13578. return;
  13579. }
  13580. if (me.undoManger.list.length == 0) {
  13581. me.undoManger.save(true);
  13582. }
  13583. clearTimeout(saveSceneTimer);
  13584. function save(cont) {
  13585. cont.undoManger.save(false, true);
  13586. cont.fireEvent('selectionchange');
  13587. }
  13588. saveSceneTimer = setTimeout(function () {
  13589. if (inputType) {
  13590. var interalTimer = setInterval(function () {
  13591. if (!inputType) {
  13592. save(me);
  13593. clearInterval(interalTimer)
  13594. }
  13595. }, 300)
  13596. return;
  13597. }
  13598. save(me);
  13599. }, 200);
  13600. lastKeyCode = keyCode;
  13601. keycont++;
  13602. if (keycont >= maxInputCount) {
  13603. save(me)
  13604. }
  13605. }
  13606. });
  13607. me.addListener('keyup', function (type, evt) {
  13608. var keyCode = evt.keyCode || evt.which;
  13609. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  13610. if (inputType)
  13611. return;
  13612. if (!isCollapsed) {
  13613. this.undoManger.save(false, true);
  13614. isCollapsed = true;
  13615. }
  13616. }
  13617. });
  13618. //扩展实例,添加关闭和开启命令undo
  13619. me.stopCmdUndo = function () {
  13620. me.__hasEnterExecCommand = true;
  13621. };
  13622. me.startCmdUndo = function () {
  13623. me.__hasEnterExecCommand = false;
  13624. }
  13625. };
  13626. // plugins/copy.js
  13627. UE.plugin.register('copy', function () {
  13628. var me = this;
  13629. function initZeroClipboard() {
  13630. ZeroClipboard.config({
  13631. debug: false,
  13632. swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf'
  13633. });
  13634. var client = me.zeroclipboard = new ZeroClipboard();
  13635. // 复制内容
  13636. client.on('copy', function (e) {
  13637. var client = e.client,
  13638. rng = me.selection.getRange(),
  13639. div = document.createElement('div');
  13640. div.appendChild(rng.cloneContents());
  13641. client.setText(div.innerText || div.textContent);
  13642. client.setHtml(div.innerHTML);
  13643. rng.select();
  13644. });
  13645. // hover事件传递到target
  13646. client.on('mouseover mouseout', function (e) {
  13647. var target = e.target;
  13648. if (e.type == 'mouseover') {
  13649. domUtils.addClass(target, 'edui-state-hover');
  13650. } else if (e.type == 'mouseout') {
  13651. domUtils.removeClasses(target, 'edui-state-hover');
  13652. }
  13653. });
  13654. // flash加载不成功
  13655. client.on('wrongflash noflash', function () {
  13656. ZeroClipboard.destroy();
  13657. });
  13658. }
  13659. return {
  13660. bindEvents: {
  13661. 'ready': function () {
  13662. if (!browser.ie) {
  13663. if (window.ZeroClipboard) {
  13664. initZeroClipboard();
  13665. } else {
  13666. utils.loadFile(document, {
  13667. src: me.options.UEDITOR_HOME_URL + "third-party/zeroclipboard/ZeroClipboard.js",
  13668. tag: "script",
  13669. type: "text/javascript",
  13670. defer: "defer"
  13671. }, function () {
  13672. initZeroClipboard();
  13673. });
  13674. }
  13675. }
  13676. }
  13677. },
  13678. commands: {
  13679. 'copy': {
  13680. execCommand: function (cmd) {
  13681. if (!me.document.execCommand('copy')) {
  13682. alert(me.getLang('copymsg'));
  13683. }
  13684. }
  13685. }
  13686. }
  13687. }
  13688. });
  13689. // plugins/paste.js
  13690. ///import core
  13691. ///import plugins/inserthtml.js
  13692. ///import plugins/undo.js
  13693. ///import plugins/serialize.js
  13694. ///commands 粘贴
  13695. ///commandsName PastePlain
  13696. ///commandsTitle 纯文本粘贴模式
  13697. /**
  13698. * @description 粘贴
  13699. * @author zhanyi
  13700. */
  13701. UE.plugins['paste'] = function () {
  13702. function getClipboardData(callback) {
  13703. var doc = this.document;
  13704. if (doc.getElementById('baidu_pastebin')) {
  13705. return;
  13706. }
  13707. var range = this.selection.getRange(),
  13708. bk = range.createBookmark(),
  13709. //创建剪贴的容器div
  13710. pastebin = doc.createElement('div');
  13711. pastebin.id = 'baidu_pastebin';
  13712. // Safari 要求div必须有内容,才能粘贴内容进来
  13713. browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
  13714. doc.body.appendChild(pastebin);
  13715. //trace:717 隐藏的span不能得到top
  13716. //bk.start.innerHTML = '&nbsp;';
  13717. bk.start.style.display = '';
  13718. pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
  13719. //要在现在光标平行的位置加入,否则会出现跳动的问题
  13720. domUtils.getXY(bk.start).y + 'px';
  13721. range.selectNodeContents(pastebin).select(true);
  13722. setTimeout(function () {
  13723. if (browser.webkit) {
  13724. for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
  13725. if (domUtils.isEmptyNode(pi)) {
  13726. domUtils.remove(pi);
  13727. } else {
  13728. pastebin = pi;
  13729. break;
  13730. }
  13731. }
  13732. }
  13733. try {
  13734. pastebin.parentNode.removeChild(pastebin);
  13735. } catch (e) {
  13736. }
  13737. range.moveToBookmark(bk).select(true);
  13738. callback(pastebin);
  13739. }, 0);
  13740. }
  13741. var me = this;
  13742. me.setOpt({
  13743. retainOnlyLabelPasted: false
  13744. });
  13745. var txtContent, htmlContent, address;
  13746. function getPureHtml(html) {
  13747. return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (a, b, tagName, attrs) {
  13748. tagName = tagName.toLowerCase();
  13749. if ({img: 1}[tagName]) {
  13750. return a;
  13751. }
  13752. attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function (str, atr, val) {
  13753. if ({
  13754. 'src': 1,
  13755. 'href': 1,
  13756. 'name': 1
  13757. }[atr.toLowerCase()]) {
  13758. return atr + '=' + val + ' '
  13759. }
  13760. return ''
  13761. });
  13762. if ({
  13763. 'span': 1,
  13764. 'div': 1
  13765. }[tagName]) {
  13766. return ''
  13767. } else {
  13768. return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
  13769. }
  13770. });
  13771. }
  13772. function filter(div) {
  13773. var html;
  13774. if (div.firstChild) {
  13775. //去掉cut中添加的边界值
  13776. var nodes = domUtils.getElementsByTagName(div, 'span');
  13777. for (var i = 0, ni; ni = nodes[i++];) {
  13778. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  13779. domUtils.remove(ni);
  13780. }
  13781. }
  13782. if (browser.webkit) {
  13783. var brs = div.querySelectorAll('div br');
  13784. for (var i = 0, bi; bi = brs[i++];) {
  13785. var pN = bi.parentNode;
  13786. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  13787. pN.innerHTML = '<p><br/></p>';
  13788. domUtils.remove(pN);
  13789. }
  13790. }
  13791. var divs = div.querySelectorAll('#baidu_pastebin');
  13792. for (var i = 0, di; di = divs[i++];) {
  13793. var tmpP = me.document.createElement('p');
  13794. di.parentNode.insertBefore(tmpP, di);
  13795. while (di.firstChild) {
  13796. tmpP.appendChild(di.firstChild);
  13797. }
  13798. domUtils.remove(di);
  13799. }
  13800. var metas = div.querySelectorAll('meta');
  13801. for (var i = 0, ci; ci = metas[i++];) {
  13802. domUtils.remove(ci);
  13803. }
  13804. var brs = div.querySelectorAll('br');
  13805. for (i = 0; ci = brs[i++];) {
  13806. if (/^apple-/i.test(ci.className)) {
  13807. domUtils.remove(ci);
  13808. }
  13809. }
  13810. }
  13811. if (browser.gecko) {
  13812. var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
  13813. for (i = 0; ci = dirtyNodes[i++];) {
  13814. ci.removeAttribute('_moz_dirty');
  13815. }
  13816. }
  13817. if (!browser.ie) {
  13818. var spans = div.querySelectorAll('span.Apple-style-span');
  13819. for (var i = 0, ci; ci = spans[i++];) {
  13820. domUtils.remove(ci, true);
  13821. }
  13822. }
  13823. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  13824. html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  13825. //过滤word粘贴过来的冗余属性
  13826. html = UE.filterWord(html);
  13827. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  13828. var root = UE.htmlparser(html);
  13829. //如果给了过滤规则就先进行过滤
  13830. if (me.options.filterRules) {
  13831. UE.filterNode(root, me.options.filterRules);
  13832. }
  13833. //执行默认的处理
  13834. me.filterInputRule(root);
  13835. //针对chrome的处理
  13836. if (browser.webkit) {
  13837. var br = root.lastChild();
  13838. if (br && br.type == 'element' && br.tagName == 'br') {
  13839. root.removeChild(br)
  13840. }
  13841. utils.each(me.body.querySelectorAll('div'), function (node) {
  13842. if (domUtils.isEmptyBlock(node)) {
  13843. domUtils.remove(node, true)
  13844. }
  13845. })
  13846. }
  13847. html = {'html': root.toHtml()};
  13848. me.fireEvent('beforepaste', html, root);
  13849. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  13850. if (!html.html) {
  13851. return;
  13852. }
  13853. root = UE.htmlparser(html.html, true);
  13854. //如果开启了纯文本模式
  13855. if (me.queryCommandState('pasteplain') === 1) {
  13856. me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true);
  13857. } else {
  13858. //文本模式
  13859. UE.filterNode(root, me.options.filterTxtRules);
  13860. txtContent = root.toHtml();
  13861. //完全模式
  13862. htmlContent = html.html;
  13863. address = me.selection.getRange().createAddress(true);
  13864. me.execCommand('insertHtml', me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent, true);
  13865. }
  13866. me.fireEvent("afterpaste", html);
  13867. }
  13868. }
  13869. me.addListener('pasteTransfer', function (cmd, plainType) {
  13870. if (address && txtContent && htmlContent && txtContent != htmlContent) {
  13871. var range = me.selection.getRange();
  13872. range.moveToAddress(address, true);
  13873. if (!range.collapsed) {
  13874. while (!domUtils.isBody(range.startContainer)
  13875. ) {
  13876. var start = range.startContainer;
  13877. if (start.nodeType == 1) {
  13878. start = start.childNodes[range.startOffset];
  13879. if (!start) {
  13880. range.setStartBefore(range.startContainer);
  13881. continue;
  13882. }
  13883. var pre = start.previousSibling;
  13884. if (pre && pre.nodeType == 3 && new RegExp('^[\n\r\t ' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
  13885. range.setStartBefore(pre)
  13886. }
  13887. }
  13888. if (range.startOffset == 0) {
  13889. range.setStartBefore(range.startContainer);
  13890. } else {
  13891. break;
  13892. }
  13893. }
  13894. while (!domUtils.isBody(range.endContainer)
  13895. ) {
  13896. var end = range.endContainer;
  13897. if (end.nodeType == 1) {
  13898. end = end.childNodes[range.endOffset];
  13899. if (!end) {
  13900. range.setEndAfter(range.endContainer);
  13901. continue;
  13902. }
  13903. var next = end.nextSibling;
  13904. if (next && next.nodeType == 3 && new RegExp('^[\n\r\t' + domUtils.fillChar + ']*$').test(next.nodeValue)) {
  13905. range.setEndAfter(next)
  13906. }
  13907. }
  13908. if (range.endOffset == range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length) {
  13909. range.setEndAfter(range.endContainer);
  13910. } else {
  13911. break;
  13912. }
  13913. }
  13914. }
  13915. range.deleteContents();
  13916. range.select(true);
  13917. me.__hasEnterExecCommand = true;
  13918. var html = htmlContent;
  13919. if (plainType === 2) {
  13920. html = getPureHtml(html);
  13921. } else if (plainType) {
  13922. html = txtContent;
  13923. }
  13924. me.execCommand('inserthtml', html, true);
  13925. me.__hasEnterExecCommand = false;
  13926. var rng = me.selection.getRange();
  13927. while (!domUtils.isBody(rng.startContainer) && !rng.startOffset &&
  13928. rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  13929. ) {
  13930. rng.setStartBefore(rng.startContainer);
  13931. }
  13932. var tmpAddress = rng.createAddress(true);
  13933. address.endAddress = tmpAddress.startAddress;
  13934. }
  13935. });
  13936. me.addListener('ready', function () {
  13937. domUtils.on(me.body, 'cut', function () {
  13938. var range = me.selection.getRange();
  13939. if (!range.collapsed && me.undoManger) {
  13940. me.undoManger.save();
  13941. }
  13942. });
  13943. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  13944. domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
  13945. if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
  13946. return;
  13947. }
  13948. getClipboardData.call(me, function (div) {
  13949. filter(div);
  13950. });
  13951. });
  13952. });
  13953. me.commands['paste'] = {
  13954. execCommand: function (cmd) {
  13955. if (browser.ie) {
  13956. getClipboardData.call(me, function (div) {
  13957. filter(div);
  13958. });
  13959. me.document.execCommand('paste');
  13960. } else {
  13961. alert(me.getLang('pastemsg'));
  13962. }
  13963. }
  13964. }
  13965. };
  13966. // plugins/puretxtpaste.js
  13967. /**
  13968. * 纯文本粘贴插件
  13969. * @file
  13970. * @since 1.2.6.1
  13971. */
  13972. UE.plugins['pasteplain'] = function () {
  13973. var me = this;
  13974. me.setOpt({
  13975. 'pasteplain': false,
  13976. 'filterTxtRules': function () {
  13977. function transP(node) {
  13978. node.tagName = 'p';
  13979. node.setStyle();
  13980. }
  13981. function removeNode(node) {
  13982. node.parentNode.removeChild(node, true)
  13983. }
  13984. return {
  13985. //直接删除及其字节点内容
  13986. '-': 'script style object iframe embed input select',
  13987. 'p': {$: {}},
  13988. 'br': {$: {}},
  13989. div: function (node) {
  13990. var tmpNode, p = UE.uNode.createElement('p');
  13991. while (tmpNode = node.firstChild()) {
  13992. if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
  13993. p.appendChild(tmpNode);
  13994. } else {
  13995. if (p.firstChild()) {
  13996. node.parentNode.insertBefore(p, node);
  13997. p = UE.uNode.createElement('p');
  13998. } else {
  13999. node.parentNode.insertBefore(tmpNode, node);
  14000. }
  14001. }
  14002. }
  14003. if (p.firstChild()) {
  14004. node.parentNode.insertBefore(p, node);
  14005. }
  14006. node.parentNode.removeChild(node);
  14007. },
  14008. ol: removeNode,
  14009. ul: removeNode,
  14010. dl: removeNode,
  14011. dt: removeNode,
  14012. dd: removeNode,
  14013. 'li': removeNode,
  14014. 'caption': transP,
  14015. 'th': transP,
  14016. 'tr': transP,
  14017. 'h1': transP, 'h2': transP, 'h3': transP, 'h4': transP, 'h5': transP, 'h6': transP,
  14018. 'td': function (node) {
  14019. //没有内容的td直接删掉
  14020. var txt = !!node.innerText();
  14021. if (txt) {
  14022. node.parentNode.insertAfter(UE.uNode.createText(' &nbsp; &nbsp;'), node);
  14023. }
  14024. node.parentNode.removeChild(node, node.innerText())
  14025. }
  14026. }
  14027. }()
  14028. });
  14029. //暂时这里支持一下老版本的属性
  14030. var pasteplain = me.options.pasteplain;
  14031. /**
  14032. * 启用或取消纯文本粘贴模式
  14033. * @command pasteplain
  14034. * @method execCommand
  14035. * @param { String } cmd 命令字符串
  14036. * @example
  14037. * ```javascript
  14038. * editor.queryCommandState( 'pasteplain' );
  14039. * ```
  14040. */
  14041. /**
  14042. * 查询当前是否处于纯文本粘贴模式
  14043. * @command pasteplain
  14044. * @method queryCommandState
  14045. * @param { String } cmd 命令字符串
  14046. * @return { int } 如果处于纯文本模式,返回1,否则,返回0
  14047. * @example
  14048. * ```javascript
  14049. * editor.queryCommandState( 'pasteplain' );
  14050. * ```
  14051. */
  14052. me.commands['pasteplain'] = {
  14053. queryCommandState: function () {
  14054. return pasteplain ? 1 : 0;
  14055. },
  14056. execCommand: function () {
  14057. pasteplain = !pasteplain | 0;
  14058. },
  14059. notNeedUndo: 1
  14060. };
  14061. };
  14062. // plugins/list.js
  14063. /**
  14064. * 有序列表,无序列表插件
  14065. * @file
  14066. * @since 1.2.6.1
  14067. */
  14068. UE.plugins['list'] = function () {
  14069. var me = this,
  14070. notExchange = {
  14071. 'TD': 1,
  14072. 'PRE': 1,
  14073. 'BLOCKQUOTE': 1
  14074. };
  14075. var customStyle = {
  14076. 'cn': 'cn-1-',
  14077. 'cn1': 'cn-2-',
  14078. 'cn2': 'cn-3-',
  14079. 'num': 'num-1-',
  14080. 'num1': 'num-2-',
  14081. 'num2': 'num-3-',
  14082. 'dash': 'dash',
  14083. 'dot': 'dot'
  14084. };
  14085. me.setOpt({
  14086. 'autoTransWordToList': false,
  14087. 'insertorderedlist': {
  14088. 'num': '',
  14089. 'num1': '',
  14090. 'num2': '',
  14091. 'cn': '',
  14092. 'cn1': '',
  14093. 'cn2': '',
  14094. 'decimal': '',
  14095. 'lower-alpha': '',
  14096. 'lower-roman': '',
  14097. 'upper-alpha': '',
  14098. 'upper-roman': ''
  14099. },
  14100. 'insertunorderedlist': {
  14101. 'circle': '',
  14102. 'disc': '',
  14103. 'square': '',
  14104. 'dash': '',
  14105. 'dot': ''
  14106. },
  14107. listDefaultPaddingLeft: '30',
  14108. listiconpath: 'http://bs.baidu.com/listicon/',
  14109. maxListLevel: -1,//-1不限制
  14110. disablePInList: false
  14111. });
  14112. function listToArray(list) {
  14113. var arr = [];
  14114. for (var p in list) {
  14115. arr.push(p)
  14116. }
  14117. return arr;
  14118. }
  14119. var listStyle = {
  14120. 'OL': listToArray(me.options.insertorderedlist),
  14121. 'UL': listToArray(me.options.insertunorderedlist)
  14122. };
  14123. var liiconpath = me.options.listiconpath;
  14124. //根据用户配置,调整customStyle
  14125. for (var s in customStyle) {
  14126. if (!me.options.insertorderedlist.hasOwnProperty(s) && !me.options.insertunorderedlist.hasOwnProperty(s)) {
  14127. delete customStyle[s];
  14128. }
  14129. }
  14130. me.ready(function () {
  14131. var customCss = [];
  14132. for (var p in customStyle) {
  14133. if (p == 'dash' || p == 'dot') {
  14134. customCss.push('li.list-' + customStyle[p] + '{background-image:url(' + liiconpath + customStyle[p] + '.gif)}');
  14135. customCss.push('ul.custom_' + p + '{list-style:none;}ul.custom_' + p + ' li{background-position:0 3px;background-repeat:no-repeat}');
  14136. } else {
  14137. for (var i = 0; i < 99; i++) {
  14138. customCss.push('li.list-' + customStyle[p] + i + '{background-image:url(' + liiconpath + 'list-' + customStyle[p] + i + '.gif)}')
  14139. }
  14140. customCss.push('ol.custom_' + p + '{list-style:none;}ol.custom_' + p + ' li{background-position:0 3px;background-repeat:no-repeat}');
  14141. }
  14142. switch (p) {
  14143. case 'cn':
  14144. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}');
  14145. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}');
  14146. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}');
  14147. break;
  14148. case 'cn1':
  14149. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:30px}');
  14150. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}');
  14151. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}');
  14152. break;
  14153. case 'cn2':
  14154. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:40px}');
  14155. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:55px}');
  14156. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:68px}');
  14157. break;
  14158. case 'num':
  14159. case 'num1':
  14160. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}');
  14161. break;
  14162. case 'num2':
  14163. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:35px}');
  14164. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}');
  14165. break;
  14166. case 'dash':
  14167. customCss.push('li.list-' + p + '-paddingleft{padding-left:35px}');
  14168. break;
  14169. case 'dot':
  14170. customCss.push('li.list-' + p + '-paddingleft{padding-left:20px}');
  14171. }
  14172. }
  14173. customCss.push('.list-paddingleft-1{padding-left:0}');
  14174. customCss.push('.list-paddingleft-2{padding-left:' + me.options.listDefaultPaddingLeft + 'px}');
  14175. customCss.push('.list-paddingleft-3{padding-left:' + me.options.listDefaultPaddingLeft * 2 + 'px}');
  14176. //如果不给宽度会在自定应样式里出现滚动条
  14177. utils.cssRule('list', 'ol,ul{margin:0;pading:0;' + (browser.ie ? '' : 'width:95%') + '}li{clear:both;}' + customCss.join('\n'), me.document);
  14178. });
  14179. //单独处理剪切的问题
  14180. me.ready(function () {
  14181. domUtils.on(me.body, 'cut', function () {
  14182. setTimeout(function () {
  14183. var rng = me.selection.getRange(), li;
  14184. //trace:3416
  14185. if (!rng.collapsed) {
  14186. if (li = domUtils.findParentByTagName(rng.startContainer, 'li', true)) {
  14187. if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
  14188. var pn = li.parentNode, node;
  14189. if (node = pn.previousSibling) {
  14190. domUtils.remove(pn);
  14191. rng.setStartAtLast(node).collapse(true);
  14192. rng.select(true);
  14193. } else if (node = pn.nextSibling) {
  14194. domUtils.remove(pn);
  14195. rng.setStartAtFirst(node).collapse(true);
  14196. rng.select(true);
  14197. } else {
  14198. var tmpNode = me.document.createElement('p');
  14199. domUtils.fillNode(me.document, tmpNode);
  14200. pn.parentNode.insertBefore(tmpNode, pn);
  14201. domUtils.remove(pn);
  14202. rng.setStart(tmpNode, 0).collapse(true);
  14203. rng.select(true);
  14204. }
  14205. }
  14206. }
  14207. }
  14208. })
  14209. })
  14210. });
  14211. function getStyle(node) {
  14212. var cls = node.className;
  14213. if (domUtils.hasClass(node, /custom_/)) {
  14214. return cls.match(/custom_(\w+)/)[1]
  14215. }
  14216. return domUtils.getStyle(node, 'list-style-type')
  14217. }
  14218. me.addListener('beforepaste', function (type, html) {
  14219. var me = this,
  14220. rng = me.selection.getRange(), li;
  14221. var root = UE.htmlparser(html.html, true);
  14222. if (li = domUtils.findParentByTagName(rng.startContainer, 'li', true)) {
  14223. var list = li.parentNode, tagName = list.tagName == 'OL' ? 'ul' : 'ol';
  14224. utils.each(root.getNodesByTagName(tagName), function (n) {
  14225. n.tagName = list.tagName;
  14226. n.setAttr();
  14227. if (n.parentNode === root) {
  14228. type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
  14229. } else {
  14230. var className = n.parentNode.getAttr('class');
  14231. if (className && /custom_/.test(className)) {
  14232. type = className.match(/custom_(\w+)/)[1]
  14233. } else {
  14234. type = n.parentNode.getStyle('list-style-type');
  14235. }
  14236. if (!type) {
  14237. type = list.tagName == 'OL' ? 'decimal' : 'disc';
  14238. }
  14239. }
  14240. var index = utils.indexOf(listStyle[list.tagName], type);
  14241. if (n.parentNode !== root)
  14242. index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
  14243. var currentStyle = listStyle[list.tagName][index];
  14244. if (customStyle[currentStyle]) {
  14245. n.setAttr('class', 'custom_' + currentStyle)
  14246. } else {
  14247. n.setStyle('list-style-type', currentStyle)
  14248. }
  14249. })
  14250. }
  14251. html.html = root.toHtml();
  14252. });
  14253. //导出时,去掉p标签
  14254. me.getOpt('disablePInList') === true && me.addOutputRule(function (root) {
  14255. utils.each(root.getNodesByTagName('li'), function (li) {
  14256. var newChildrens = [], index = 0;
  14257. utils.each(li.children, function (n) {
  14258. if (n.tagName == 'p') {
  14259. var tmpNode;
  14260. while (tmpNode = n.children.pop()) {
  14261. newChildrens.splice(index, 0, tmpNode);
  14262. tmpNode.parentNode = li;
  14263. lastNode = tmpNode;
  14264. }
  14265. tmpNode = newChildrens[newChildrens.length - 1];
  14266. if (!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br') {
  14267. var br = UE.uNode.createElement('br');
  14268. br.parentNode = li;
  14269. newChildrens.push(br);
  14270. }
  14271. index = newChildrens.length;
  14272. }
  14273. });
  14274. if (newChildrens.length) {
  14275. li.children = newChildrens;
  14276. }
  14277. });
  14278. });
  14279. //进入编辑器的li要套p标签
  14280. me.addInputRule(function (root) {
  14281. utils.each(root.getNodesByTagName('li'), function (li) {
  14282. var tmpP = UE.uNode.createElement('p');
  14283. for (var i = 0, ci; ci = li.children[i];) {
  14284. if (ci.type == 'text' || dtd.p[ci.tagName]) {
  14285. tmpP.appendChild(ci);
  14286. } else {
  14287. if (tmpP.firstChild()) {
  14288. li.insertBefore(tmpP, ci);
  14289. tmpP = UE.uNode.createElement('p');
  14290. i = i + 2;
  14291. } else {
  14292. i++;
  14293. }
  14294. }
  14295. }
  14296. if (tmpP.firstChild() && !tmpP.parentNode || !li.firstChild()) {
  14297. li.appendChild(tmpP);
  14298. }
  14299. //trace:3357
  14300. //p不能为空
  14301. if (!tmpP.firstChild()) {
  14302. tmpP.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
  14303. }
  14304. //去掉末尾的空白
  14305. var p = li.firstChild();
  14306. var lastChild = p.lastChild();
  14307. if (lastChild && lastChild.type == 'text' && /^\s*$/.test(lastChild.data)) {
  14308. p.removeChild(lastChild)
  14309. }
  14310. });
  14311. if (me.options.autoTransWordToList) {
  14312. var orderlisttype = {
  14313. 'num1': /^\d+\)/,
  14314. 'decimal': /^\d+\./,
  14315. 'lower-alpha': /^[a-z]+\)/,
  14316. 'upper-alpha': /^[A-Z]+\./,
  14317. 'cn': /^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
  14318. 'cn2': /^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
  14319. },
  14320. unorderlisttype = {
  14321. 'square': 'n'
  14322. };
  14323. function checkListType(content, container) {
  14324. var span = container.firstChild();
  14325. if (span && span.type == 'element' && span.tagName == 'span' && /Wingdings|Symbol/.test(span.getStyle('font-family'))) {
  14326. for (var p in unorderlisttype) {
  14327. if (unorderlisttype[p] == span.data) {
  14328. return p
  14329. }
  14330. }
  14331. return 'disc'
  14332. }
  14333. for (var p in orderlisttype) {
  14334. if (orderlisttype[p].test(content)) {
  14335. return p;
  14336. }
  14337. }
  14338. }
  14339. utils.each(root.getNodesByTagName('p'), function (node) {
  14340. if (node.getAttr('class') != 'MsoListParagraph') {
  14341. return
  14342. }
  14343. //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视
  14344. node.setStyle('margin', '');
  14345. node.setStyle('margin-left', '');
  14346. node.setAttr('class', '');
  14347. function appendLi(list, p, type) {
  14348. if (list.tagName == 'ol') {
  14349. if (browser.ie) {
  14350. var first = p.firstChild();
  14351. if (first.type == 'element' && first.tagName == 'span' && orderlisttype[type].test(first.innerText())) {
  14352. p.removeChild(first);
  14353. }
  14354. } else {
  14355. p.innerHTML(p.innerHTML().replace(orderlisttype[type], ''));
  14356. }
  14357. } else {
  14358. p.removeChild(p.firstChild())
  14359. }
  14360. var li = UE.uNode.createElement('li');
  14361. li.appendChild(p);
  14362. list.appendChild(li);
  14363. }
  14364. var tmp = node, type, cacheNode = node;
  14365. if (node.parentNode.tagName != 'li' && (type = checkListType(node.innerText(), node))) {
  14366. var list = UE.uNode.createElement(me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul');
  14367. if (customStyle[type]) {
  14368. list.setAttr('class', 'custom_' + type)
  14369. } else {
  14370. list.setStyle('list-style-type', type)
  14371. }
  14372. while (node && node.parentNode.tagName != 'li' && checkListType(node.innerText(), node)) {
  14373. tmp = node.nextSibling();
  14374. if (!tmp) {
  14375. node.parentNode.insertBefore(list, node)
  14376. }
  14377. appendLi(list, node, type);
  14378. node = tmp;
  14379. }
  14380. if (!list.parentNode && node && node.parentNode) {
  14381. node.parentNode.insertBefore(list, node)
  14382. }
  14383. }
  14384. var span = cacheNode.firstChild();
  14385. if (span && span.type == 'element' && span.tagName == 'span' && /^\s*(&nbsp;)+\s*$/.test(span.innerText())) {
  14386. span.parentNode.removeChild(span)
  14387. }
  14388. })
  14389. }
  14390. });
  14391. //调整索引标签
  14392. me.addListener('contentchange', function () {
  14393. adjustListStyle(me.document)
  14394. });
  14395. function adjustListStyle(doc, ignore) {
  14396. utils.each(domUtils.getElementsByTagName(doc, 'ol ul'), function (node) {
  14397. if (!domUtils.inDoc(node, doc))
  14398. return;
  14399. var parent = node.parentNode;
  14400. if (parent.tagName == node.tagName) {
  14401. var nodeStyleType = getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc'),
  14402. parentStyleType = getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc');
  14403. if (nodeStyleType == parentStyleType) {
  14404. var styleIndex = utils.indexOf(listStyle[node.tagName], nodeStyleType);
  14405. styleIndex = styleIndex + 1 == listStyle[node.tagName].length ? 0 : styleIndex + 1;
  14406. setListStyle(node, listStyle[node.tagName][styleIndex])
  14407. }
  14408. }
  14409. var index = 0, type = 2;
  14410. if (domUtils.hasClass(node, /custom_/)) {
  14411. if (!(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/))) {
  14412. type = 1;
  14413. }
  14414. } else {
  14415. if (/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/)) {
  14416. type = 3;
  14417. }
  14418. }
  14419. var style = domUtils.getStyle(node, 'list-style-type');
  14420. style && (node.style.cssText = 'list-style-type:' + style);
  14421. node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/, '')) + ' list-paddingleft-' + type;
  14422. utils.each(domUtils.getElementsByTagName(node, 'li'), function (li) {
  14423. li.style.cssText && (li.style.cssText = '');
  14424. if (!li.firstChild) {
  14425. domUtils.remove(li);
  14426. return;
  14427. }
  14428. if (li.parentNode !== node) {
  14429. return;
  14430. }
  14431. index++;
  14432. if (domUtils.hasClass(node, /custom_/)) {
  14433. var paddingLeft = 1, currentStyle = getStyle(node);
  14434. if (node.tagName == 'OL') {
  14435. if (currentStyle) {
  14436. switch (currentStyle) {
  14437. case 'cn' :
  14438. case 'cn1':
  14439. case 'cn2':
  14440. if (index > 10 && (index % 10 == 0 || index > 10 && index < 20)) {
  14441. paddingLeft = 2
  14442. } else if (index > 20) {
  14443. paddingLeft = 3
  14444. }
  14445. break;
  14446. case 'num2' :
  14447. if (index > 9) {
  14448. paddingLeft = 2
  14449. }
  14450. }
  14451. }
  14452. li.className = 'list-' + customStyle[currentStyle] + index + ' ' + 'list-' + currentStyle + '-paddingleft-' + paddingLeft;
  14453. } else {
  14454. li.className = 'list-' + customStyle[currentStyle] + ' ' + 'list-' + currentStyle + '-paddingleft';
  14455. }
  14456. } else {
  14457. li.className = li.className.replace(/list-[\w\-]+/gi, '');
  14458. }
  14459. var className = li.getAttribute('class');
  14460. if (className !== null && !className.replace(/\s/g, '')) {
  14461. domUtils.removeAttributes(li, 'class')
  14462. }
  14463. });
  14464. !ignore && adjustList(node, node.tagName.toLowerCase(), getStyle(node) || domUtils.getStyle(node, 'list-style-type'), true);
  14465. })
  14466. }
  14467. function adjustList(list, tag, style, ignoreEmpty) {
  14468. var nextList = list.nextSibling;
  14469. if (nextList && nextList.nodeType == 1 && nextList.tagName.toLowerCase() == tag && (getStyle(nextList) || domUtils.getStyle(nextList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
  14470. domUtils.moveChild(nextList, list);
  14471. if (nextList.childNodes.length == 0) {
  14472. domUtils.remove(nextList);
  14473. }
  14474. }
  14475. if (nextList && domUtils.isFillChar(nextList)) {
  14476. domUtils.remove(nextList);
  14477. }
  14478. var preList = list.previousSibling;
  14479. if (preList && preList.nodeType == 1 && preList.tagName.toLowerCase() == tag && (getStyle(preList) || domUtils.getStyle(preList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) == style) {
  14480. domUtils.moveChild(list, preList);
  14481. }
  14482. if (preList && domUtils.isFillChar(preList)) {
  14483. domUtils.remove(preList);
  14484. }
  14485. !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list);
  14486. if (getStyle(list)) {
  14487. adjustListStyle(list.ownerDocument, true)
  14488. }
  14489. }
  14490. function setListStyle(list, style) {
  14491. if (customStyle[style]) {
  14492. list.className = 'custom_' + style;
  14493. }
  14494. try {
  14495. domUtils.setStyle(list, 'list-style-type', style);
  14496. } catch (e) {
  14497. }
  14498. }
  14499. function clearEmptySibling(node) {
  14500. var tmpNode = node.previousSibling;
  14501. if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
  14502. domUtils.remove(tmpNode);
  14503. }
  14504. tmpNode = node.nextSibling;
  14505. if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
  14506. domUtils.remove(tmpNode);
  14507. }
  14508. }
  14509. me.addListener('keydown', function (type, evt) {
  14510. function preventAndSave() {
  14511. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  14512. me.fireEvent('contentchange');
  14513. me.undoManger && me.undoManger.save();
  14514. }
  14515. function findList(node, filterFn) {
  14516. while (node && !domUtils.isBody(node)) {
  14517. if (filterFn(node)) {
  14518. return null
  14519. }
  14520. if (node.nodeType == 1 && /[ou]l/i.test(node.tagName)) {
  14521. return node;
  14522. }
  14523. node = node.parentNode;
  14524. }
  14525. return null;
  14526. }
  14527. var keyCode = evt.keyCode || evt.which;
  14528. if (keyCode == 13 && !evt.shiftKey) {//回车
  14529. var rng = me.selection.getRange(),
  14530. parent = domUtils.findParent(rng.startContainer, function (node) {
  14531. return domUtils.isBlockElm(node)
  14532. }, true),
  14533. li = domUtils.findParentByTagName(rng.startContainer, 'li', true);
  14534. if (parent && parent.tagName != 'PRE' && !li) {
  14535. var html = parent.innerHTML.replace(new RegExp(domUtils.fillChar, 'g'), '');
  14536. if (/^\s*1\s*\.[^\d]/.test(html)) {
  14537. parent.innerHTML = html.replace(/^\s*1\s*\./, '');
  14538. rng.setStartAtLast(parent).collapse(true).select();
  14539. me.__hasEnterExecCommand = true;
  14540. me.execCommand('insertorderedlist');
  14541. me.__hasEnterExecCommand = false;
  14542. }
  14543. }
  14544. var range = me.selection.getRange(),
  14545. start = findList(range.startContainer, function (node) {
  14546. return node.tagName == 'TABLE';
  14547. }),
  14548. end = range.collapsed ? start : findList(range.endContainer, function (node) {
  14549. return node.tagName == 'TABLE';
  14550. });
  14551. if (start && end && start === end) {
  14552. if (!range.collapsed) {
  14553. start = domUtils.findParentByTagName(range.startContainer, 'li', true);
  14554. end = domUtils.findParentByTagName(range.endContainer, 'li', true);
  14555. if (start && end && start === end) {
  14556. range.deleteContents();
  14557. li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  14558. if (li && domUtils.isEmptyBlock(li)) {
  14559. pre = li.previousSibling;
  14560. next = li.nextSibling;
  14561. p = me.document.createElement('p');
  14562. domUtils.fillNode(me.document, p);
  14563. parentList = li.parentNode;
  14564. if (pre && next) {
  14565. range.setStart(next, 0).collapse(true).select(true);
  14566. domUtils.remove(li);
  14567. } else {
  14568. if (!pre && !next || !pre) {
  14569. parentList.parentNode.insertBefore(p, parentList);
  14570. } else {
  14571. li.parentNode.parentNode.insertBefore(p, parentList.nextSibling);
  14572. }
  14573. domUtils.remove(li);
  14574. if (!parentList.firstChild) {
  14575. domUtils.remove(parentList);
  14576. }
  14577. range.setStart(p, 0).setCursor();
  14578. }
  14579. preventAndSave();
  14580. return;
  14581. }
  14582. } else {
  14583. var tmpRange = range.cloneRange(),
  14584. bk = tmpRange.collapse(false).createBookmark();
  14585. range.deleteContents();
  14586. tmpRange.moveToBookmark(bk);
  14587. var li = domUtils.findParentByTagName(tmpRange.startContainer, 'li', true);
  14588. clearEmptySibling(li);
  14589. tmpRange.select();
  14590. preventAndSave();
  14591. return;
  14592. }
  14593. }
  14594. li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  14595. if (li) {
  14596. if (domUtils.isEmptyBlock(li)) {
  14597. bk = range.createBookmark();
  14598. var parentList = li.parentNode;
  14599. if (li !== parentList.lastChild) {
  14600. domUtils.breakParent(li, parentList);
  14601. clearEmptySibling(li);
  14602. } else {
  14603. parentList.parentNode.insertBefore(li, parentList.nextSibling);
  14604. if (domUtils.isEmptyNode(parentList)) {
  14605. domUtils.remove(parentList);
  14606. }
  14607. }
  14608. //嵌套不处理
  14609. if (!dtd.$list[li.parentNode.tagName]) {
  14610. if (!domUtils.isBlockElm(li.firstChild)) {
  14611. p = me.document.createElement('p');
  14612. li.parentNode.insertBefore(p, li);
  14613. while (li.firstChild) {
  14614. p.appendChild(li.firstChild);
  14615. }
  14616. domUtils.remove(li);
  14617. } else {
  14618. domUtils.remove(li, true);
  14619. }
  14620. }
  14621. range.moveToBookmark(bk).select();
  14622. } else {
  14623. var first = li.firstChild;
  14624. if (!first || !domUtils.isBlockElm(first)) {
  14625. var p = me.document.createElement('p');
  14626. !li.firstChild && domUtils.fillNode(me.document, p);
  14627. while (li.firstChild) {
  14628. p.appendChild(li.firstChild);
  14629. }
  14630. li.appendChild(p);
  14631. first = p;
  14632. }
  14633. var span = me.document.createElement('span');
  14634. range.insertNode(span);
  14635. domUtils.breakParent(span, li);
  14636. var nextLi = span.nextSibling;
  14637. first = nextLi.firstChild;
  14638. if (!first) {
  14639. p = me.document.createElement('p');
  14640. domUtils.fillNode(me.document, p);
  14641. nextLi.appendChild(p);
  14642. first = p;
  14643. }
  14644. if (domUtils.isEmptyNode(first)) {
  14645. first.innerHTML = '';
  14646. domUtils.fillNode(me.document, first);
  14647. }
  14648. range.setStart(first, 0).collapse(true).shrinkBoundary().select();
  14649. domUtils.remove(span);
  14650. var pre = nextLi.previousSibling;
  14651. if (pre && domUtils.isEmptyBlock(pre)) {
  14652. pre.innerHTML = '<p></p>';
  14653. domUtils.fillNode(me.document, pre.firstChild);
  14654. }
  14655. }
  14656. // }
  14657. preventAndSave();
  14658. }
  14659. }
  14660. }
  14661. if (keyCode == 8) {
  14662. //修中ie中li下的问题
  14663. range = me.selection.getRange();
  14664. if (range.collapsed && domUtils.isStartInblock(range)) {
  14665. tmpRange = range.cloneRange().trimBoundary();
  14666. li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  14667. //要在li的最左边,才能处理
  14668. if (li && domUtils.isStartInblock(tmpRange)) {
  14669. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  14670. if (start && start !== li.firstChild) {
  14671. var parentList = domUtils.findParentByTagName(start, ['ol', 'ul']);
  14672. domUtils.breakParent(start, parentList);
  14673. clearEmptySibling(start);
  14674. me.fireEvent('contentchange');
  14675. range.setStart(start, 0).setCursor(false, true);
  14676. me.fireEvent('saveScene');
  14677. domUtils.preventDefault(evt);
  14678. return;
  14679. }
  14680. if (li && (pre = li.previousSibling)) {
  14681. if (keyCode == 46 && li.childNodes.length) {
  14682. return;
  14683. }
  14684. //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
  14685. if (dtd.$list[pre.tagName]) {
  14686. pre = pre.lastChild;
  14687. }
  14688. me.undoManger && me.undoManger.save();
  14689. first = li.firstChild;
  14690. if (domUtils.isBlockElm(first)) {
  14691. if (domUtils.isEmptyNode(first)) {
  14692. // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
  14693. pre.appendChild(first);
  14694. range.setStart(first, 0).setCursor(false, true);
  14695. //first不是唯一的节点
  14696. while (li.firstChild) {
  14697. pre.appendChild(li.firstChild);
  14698. }
  14699. } else {
  14700. span = me.document.createElement('span');
  14701. range.insertNode(span);
  14702. //判断pre是否是空的节点,如果是<p><br/></p>类型的空节点,干掉p标签防止它占位
  14703. if (domUtils.isEmptyBlock(pre)) {
  14704. pre.innerHTML = '';
  14705. }
  14706. domUtils.moveChild(li, pre);
  14707. range.setStartBefore(span).collapse(true).select(true);
  14708. domUtils.remove(span);
  14709. }
  14710. } else {
  14711. if (domUtils.isEmptyNode(li)) {
  14712. var p = me.document.createElement('p');
  14713. pre.appendChild(p);
  14714. range.setStart(p, 0).setCursor();
  14715. // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
  14716. } else {
  14717. range.setEnd(pre, pre.childNodes.length).collapse().select(true);
  14718. while (li.firstChild) {
  14719. pre.appendChild(li.firstChild);
  14720. }
  14721. }
  14722. }
  14723. domUtils.remove(li);
  14724. me.fireEvent('contentchange');
  14725. me.fireEvent('saveScene');
  14726. domUtils.preventDefault(evt);
  14727. return;
  14728. }
  14729. //trace:980
  14730. if (li && !li.previousSibling) {
  14731. var parentList = li.parentNode;
  14732. var bk = range.createBookmark();
  14733. if (domUtils.isTagNode(parentList.parentNode, 'ol ul')) {
  14734. parentList.parentNode.insertBefore(li, parentList);
  14735. if (domUtils.isEmptyNode(parentList)) {
  14736. domUtils.remove(parentList)
  14737. }
  14738. } else {
  14739. while (li.firstChild) {
  14740. parentList.parentNode.insertBefore(li.firstChild, parentList);
  14741. }
  14742. domUtils.remove(li);
  14743. if (domUtils.isEmptyNode(parentList)) {
  14744. domUtils.remove(parentList)
  14745. }
  14746. }
  14747. range.moveToBookmark(bk).setCursor(false, true);
  14748. me.fireEvent('contentchange');
  14749. me.fireEvent('saveScene');
  14750. domUtils.preventDefault(evt);
  14751. return;
  14752. }
  14753. }
  14754. }
  14755. }
  14756. });
  14757. me.addListener('keyup', function (type, evt) {
  14758. var keyCode = evt.keyCode || evt.which;
  14759. if (keyCode == 8) {
  14760. var rng = me.selection.getRange(), list;
  14761. if (list = domUtils.findParentByTagName(rng.startContainer, ['ol', 'ul'], true)) {
  14762. adjustList(list, list.tagName.toLowerCase(), getStyle(list) || domUtils.getComputedStyle(list, 'list-style-type'), true)
  14763. }
  14764. }
  14765. });
  14766. //处理tab键
  14767. me.addListener('tabkeydown', function () {
  14768. var range = me.selection.getRange();
  14769. //控制级数
  14770. function checkLevel(li) {
  14771. if (me.options.maxListLevel != -1) {
  14772. var level = li.parentNode, levelNum = 0;
  14773. while (/[ou]l/i.test(level.tagName)) {
  14774. levelNum++;
  14775. level = level.parentNode;
  14776. }
  14777. if (levelNum >= me.options.maxListLevel) {
  14778. return true;
  14779. }
  14780. }
  14781. }
  14782. //只以开始为准
  14783. //todo 后续改进
  14784. var li = domUtils.findParentByTagName(range.startContainer, 'li', true);
  14785. if (li) {
  14786. var bk;
  14787. if (range.collapsed) {
  14788. if (checkLevel(li))
  14789. return true;
  14790. var parentLi = li.parentNode,
  14791. list = me.document.createElement(parentLi.tagName),
  14792. index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type'));
  14793. index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
  14794. var currentStyle = listStyle[list.tagName][index];
  14795. setListStyle(list, currentStyle);
  14796. if (domUtils.isStartInblock(range)) {
  14797. me.fireEvent('saveScene');
  14798. bk = range.createBookmark();
  14799. parentLi.insertBefore(list, li);
  14800. list.appendChild(li);
  14801. adjustList(list, list.tagName.toLowerCase(), currentStyle);
  14802. me.fireEvent('contentchange');
  14803. range.moveToBookmark(bk).select(true);
  14804. return true;
  14805. }
  14806. } else {
  14807. me.fireEvent('saveScene');
  14808. bk = range.createBookmark();
  14809. for (var i = 0, closeList, parents = domUtils.findParents(li), ci; ci = parents[i++];) {
  14810. if (domUtils.isTagNode(ci, 'ol ul')) {
  14811. closeList = ci;
  14812. break;
  14813. }
  14814. }
  14815. var current = li;
  14816. if (bk.end) {
  14817. while (current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)) {
  14818. if (checkLevel(current)) {
  14819. current = domUtils.getNextDomNode(current, false, null, function (node) {
  14820. return node !== closeList
  14821. });
  14822. continue;
  14823. }
  14824. var parentLi = current.parentNode,
  14825. list = me.document.createElement(parentLi.tagName),
  14826. index = utils.indexOf(listStyle[list.tagName], getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type'));
  14827. var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
  14828. var currentStyle = listStyle[list.tagName][currentIndex];
  14829. setListStyle(list, currentStyle);
  14830. parentLi.insertBefore(list, current);
  14831. while (current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)) {
  14832. li = current.nextSibling;
  14833. list.appendChild(current);
  14834. if (!li || domUtils.isTagNode(li, 'ol ul')) {
  14835. if (li) {
  14836. while (li = li.firstChild) {
  14837. if (li.tagName == 'LI') {
  14838. break;
  14839. }
  14840. }
  14841. } else {
  14842. li = domUtils.getNextDomNode(current, false, null, function (node) {
  14843. return node !== closeList
  14844. });
  14845. }
  14846. break;
  14847. }
  14848. current = li;
  14849. }
  14850. adjustList(list, list.tagName.toLowerCase(), currentStyle);
  14851. current = li;
  14852. }
  14853. }
  14854. me.fireEvent('contentchange');
  14855. range.moveToBookmark(bk).select();
  14856. return true;
  14857. }
  14858. }
  14859. });
  14860. function getLi(start) {
  14861. while (start && !domUtils.isBody(start)) {
  14862. if (start.nodeName == 'TABLE') {
  14863. return null;
  14864. }
  14865. if (start.nodeName == 'LI') {
  14866. return start
  14867. }
  14868. start = start.parentNode;
  14869. }
  14870. }
  14871. /**
  14872. * 有序列表,与“insertunorderedlist”命令互斥
  14873. * @command insertorderedlist
  14874. * @method execCommand
  14875. * @param { String } command 命令字符串
  14876. * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
  14877. * @example
  14878. * ```javascript
  14879. * editor.execCommand( 'insertorderedlist','decimal');
  14880. * ```
  14881. */
  14882. /**
  14883. * 查询当前选区内容是否有序列表
  14884. * @command insertorderedlist
  14885. * @method queryCommandState
  14886. * @param { String } cmd 命令字符串
  14887. * @return { int } 如果当前选区是有序列表返回1,否则返回0
  14888. * @example
  14889. * ```javascript
  14890. * editor.queryCommandState( 'insertorderedlist' );
  14891. * ```
  14892. */
  14893. /**
  14894. * 查询当前选区内容是否有序列表
  14895. * @command insertorderedlist
  14896. * @method queryCommandValue
  14897. * @param { String } cmd 命令字符串
  14898. * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
  14899. * @example
  14900. * ```javascript
  14901. * editor.queryCommandValue( 'insertorderedlist' );
  14902. * ```
  14903. */
  14904. /**
  14905. * 无序列表,与“insertorderedlist”命令互斥
  14906. * @command insertunorderedlist
  14907. * @method execCommand
  14908. * @param { String } command 命令字符串
  14909. * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
  14910. * @example
  14911. * ```javascript
  14912. * editor.execCommand( 'insertunorderedlist','circle');
  14913. * ```
  14914. */
  14915. /**
  14916. * 查询当前是否有word文档粘贴进来的图片
  14917. * @command insertunorderedlist
  14918. * @method insertunorderedlist
  14919. * @param { String } command 命令字符串
  14920. * @return { int } 如果当前选区是无序列表返回1,否则返回0
  14921. * @example
  14922. * ```javascript
  14923. * editor.queryCommandState( 'insertunorderedlist' );
  14924. * ```
  14925. */
  14926. /**
  14927. * 查询当前选区内容是否有序列表
  14928. * @command insertunorderedlist
  14929. * @method queryCommandValue
  14930. * @param { String } command 命令字符串
  14931. * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
  14932. * @example
  14933. * ```javascript
  14934. * editor.queryCommandValue( 'insertunorderedlist' );
  14935. * ```
  14936. */
  14937. me.commands['insertorderedlist'] =
  14938. me.commands['insertunorderedlist'] = {
  14939. execCommand: function (command, style) {
  14940. if (!style) {
  14941. style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc';
  14942. }
  14943. var me = this,
  14944. range = this.selection.getRange(),
  14945. filterFn = function (node) {
  14946. return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
  14947. },
  14948. tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul',
  14949. frag = me.document.createDocumentFragment();
  14950. //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
  14951. //range.shrinkBoundary();//.adjustmentBoundary();
  14952. range.adjustmentBoundary().shrinkBoundary();
  14953. var bko = range.createBookmark(true),
  14954. start = getLi(me.document.getElementById(bko.start)),
  14955. modifyStart = 0,
  14956. end = getLi(me.document.getElementById(bko.end)),
  14957. modifyEnd = 0,
  14958. startParent, endParent,
  14959. list, tmp;
  14960. if (start || end) {
  14961. start && (startParent = start.parentNode);
  14962. if (!bko.end) {
  14963. end = start;
  14964. }
  14965. end && (endParent = end.parentNode);
  14966. if (startParent === endParent) {
  14967. while (start !== end) {
  14968. tmp = start;
  14969. start = start.nextSibling;
  14970. if (!domUtils.isBlockElm(tmp.firstChild)) {
  14971. var p = me.document.createElement('p');
  14972. while (tmp.firstChild) {
  14973. p.appendChild(tmp.firstChild);
  14974. }
  14975. tmp.appendChild(p);
  14976. }
  14977. frag.appendChild(tmp);
  14978. }
  14979. tmp = me.document.createElement('span');
  14980. startParent.insertBefore(tmp, end);
  14981. if (!domUtils.isBlockElm(end.firstChild)) {
  14982. p = me.document.createElement('p');
  14983. while (end.firstChild) {
  14984. p.appendChild(end.firstChild);
  14985. }
  14986. end.appendChild(p);
  14987. }
  14988. frag.appendChild(end);
  14989. domUtils.breakParent(tmp, startParent);
  14990. if (domUtils.isEmptyNode(tmp.previousSibling)) {
  14991. domUtils.remove(tmp.previousSibling);
  14992. }
  14993. if (domUtils.isEmptyNode(tmp.nextSibling)) {
  14994. domUtils.remove(tmp.nextSibling)
  14995. }
  14996. var nodeStyle = getStyle(startParent) || domUtils.getComputedStyle(startParent, 'list-style-type') || (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc');
  14997. if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
  14998. for (var i = 0, ci, tmpFrag = me.document.createDocumentFragment(); ci = frag.firstChild;) {
  14999. if (domUtils.isTagNode(ci, 'ol ul')) {
  15000. // 删除时,子列表不处理
  15001. // utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
  15002. // while(li.firstChild){
  15003. // tmpFrag.appendChild(li.firstChild);
  15004. // }
  15005. //
  15006. // });
  15007. tmpFrag.appendChild(ci);
  15008. } else {
  15009. while (ci.firstChild) {
  15010. tmpFrag.appendChild(ci.firstChild);
  15011. domUtils.remove(ci);
  15012. }
  15013. }
  15014. }
  15015. tmp.parentNode.insertBefore(tmpFrag, tmp);
  15016. } else {
  15017. list = me.document.createElement(tag);
  15018. setListStyle(list, style);
  15019. list.appendChild(frag);
  15020. tmp.parentNode.insertBefore(list, tmp);
  15021. }
  15022. domUtils.remove(tmp);
  15023. list && adjustList(list, tag, style);
  15024. range.moveToBookmark(bko).select();
  15025. return;
  15026. }
  15027. //开始
  15028. if (start) {
  15029. while (start) {
  15030. tmp = start.nextSibling;
  15031. if (domUtils.isTagNode(start, 'ol ul')) {
  15032. frag.appendChild(start);
  15033. } else {
  15034. var tmpfrag = me.document.createDocumentFragment(),
  15035. hasBlock = 0;
  15036. while (start.firstChild) {
  15037. if (domUtils.isBlockElm(start.firstChild)) {
  15038. hasBlock = 1;
  15039. }
  15040. tmpfrag.appendChild(start.firstChild);
  15041. }
  15042. if (!hasBlock) {
  15043. var tmpP = me.document.createElement('p');
  15044. tmpP.appendChild(tmpfrag);
  15045. frag.appendChild(tmpP);
  15046. } else {
  15047. frag.appendChild(tmpfrag);
  15048. }
  15049. domUtils.remove(start);
  15050. }
  15051. start = tmp;
  15052. }
  15053. startParent.parentNode.insertBefore(frag, startParent.nextSibling);
  15054. if (domUtils.isEmptyNode(startParent)) {
  15055. range.setStartBefore(startParent);
  15056. domUtils.remove(startParent);
  15057. } else {
  15058. range.setStartAfter(startParent);
  15059. }
  15060. modifyStart = 1;
  15061. }
  15062. if (end && domUtils.inDoc(endParent, me.document)) {
  15063. //结束
  15064. start = endParent.firstChild;
  15065. while (start && start !== end) {
  15066. tmp = start.nextSibling;
  15067. if (domUtils.isTagNode(start, 'ol ul')) {
  15068. frag.appendChild(start);
  15069. } else {
  15070. tmpfrag = me.document.createDocumentFragment();
  15071. hasBlock = 0;
  15072. while (start.firstChild) {
  15073. if (domUtils.isBlockElm(start.firstChild)) {
  15074. hasBlock = 1;
  15075. }
  15076. tmpfrag.appendChild(start.firstChild);
  15077. }
  15078. if (!hasBlock) {
  15079. tmpP = me.document.createElement('p');
  15080. tmpP.appendChild(tmpfrag);
  15081. frag.appendChild(tmpP);
  15082. } else {
  15083. frag.appendChild(tmpfrag);
  15084. }
  15085. domUtils.remove(start);
  15086. }
  15087. start = tmp;
  15088. }
  15089. var tmpDiv = domUtils.createElement(me.document, 'div', {
  15090. 'tmpDiv': 1
  15091. });
  15092. domUtils.moveChild(end, tmpDiv);
  15093. frag.appendChild(tmpDiv);
  15094. domUtils.remove(end);
  15095. endParent.parentNode.insertBefore(frag, endParent);
  15096. range.setEndBefore(endParent);
  15097. if (domUtils.isEmptyNode(endParent)) {
  15098. domUtils.remove(endParent);
  15099. }
  15100. modifyEnd = 1;
  15101. }
  15102. }
  15103. if (!modifyStart) {
  15104. range.setStartBefore(me.document.getElementById(bko.start));
  15105. }
  15106. if (bko.end && !modifyEnd) {
  15107. range.setEndAfter(me.document.getElementById(bko.end));
  15108. }
  15109. range.enlarge(true, function (node) {
  15110. return notExchange[node.tagName];
  15111. });
  15112. frag = me.document.createDocumentFragment();
  15113. var bk = range.createBookmark(),
  15114. current = domUtils.getNextDomNode(bk.start, false, filterFn),
  15115. tmpRange = range.cloneRange(),
  15116. tmpNode,
  15117. block = domUtils.isBlockElm;
  15118. while (current && current !== bk.end && (domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING)) {
  15119. if (current.nodeType == 3 || dtd.li[current.tagName]) {
  15120. if (current.nodeType == 1 && dtd.$list[current.tagName]) {
  15121. while (current.firstChild) {
  15122. frag.appendChild(current.firstChild);
  15123. }
  15124. tmpNode = domUtils.getNextDomNode(current, false, filterFn);
  15125. domUtils.remove(current);
  15126. current = tmpNode;
  15127. continue;
  15128. }
  15129. tmpNode = current;
  15130. tmpRange.setStartBefore(current);
  15131. while (current && current !== bk.end && (!block(current) || domUtils.isBookmarkNode(current))) {
  15132. tmpNode = current;
  15133. current = domUtils.getNextDomNode(current, false, null, function (node) {
  15134. return !notExchange[node.tagName];
  15135. });
  15136. }
  15137. if (current && block(current)) {
  15138. tmp = domUtils.getNextDomNode(tmpNode, false, filterFn);
  15139. if (tmp && domUtils.isBookmarkNode(tmp)) {
  15140. current = domUtils.getNextDomNode(tmp, false, filterFn);
  15141. tmpNode = tmp;
  15142. }
  15143. }
  15144. tmpRange.setEndAfter(tmpNode);
  15145. current = domUtils.getNextDomNode(tmpNode, false, filterFn);
  15146. var li = range.document.createElement('li');
  15147. li.appendChild(tmpRange.extractContents());
  15148. if (domUtils.isEmptyNode(li)) {
  15149. var tmpNode = range.document.createElement('p');
  15150. while (li.firstChild) {
  15151. tmpNode.appendChild(li.firstChild)
  15152. }
  15153. li.appendChild(tmpNode);
  15154. }
  15155. frag.appendChild(li);
  15156. } else {
  15157. current = domUtils.getNextDomNode(current, true, filterFn);
  15158. }
  15159. }
  15160. range.moveToBookmark(bk).collapse(true);
  15161. list = me.document.createElement(tag);
  15162. setListStyle(list, style);
  15163. list.appendChild(frag);
  15164. range.insertNode(list);
  15165. //当前list上下看能否合并
  15166. adjustList(list, tag, style);
  15167. //去掉冗余的tmpDiv
  15168. for (var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div'); ci = tmpDivs[i++];) {
  15169. if (ci.getAttribute('tmpDiv')) {
  15170. domUtils.remove(ci, true)
  15171. }
  15172. }
  15173. range.moveToBookmark(bko).select();
  15174. },
  15175. queryCommandState: function (command) {
  15176. var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
  15177. var path = this.selection.getStartElementPath();
  15178. for (var i = 0, ci; ci = path[i++];) {
  15179. if (ci.nodeName == 'TABLE') {
  15180. return 0
  15181. }
  15182. if (tag == ci.nodeName.toLowerCase()) {
  15183. return 1
  15184. }
  15185. ;
  15186. }
  15187. return 0;
  15188. },
  15189. queryCommandValue: function (command) {
  15190. var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul';
  15191. var path = this.selection.getStartElementPath(),
  15192. node;
  15193. for (var i = 0, ci; ci = path[i++];) {
  15194. if (ci.nodeName == 'TABLE') {
  15195. node = null;
  15196. break;
  15197. }
  15198. if (tag == ci.nodeName.toLowerCase()) {
  15199. node = ci;
  15200. break;
  15201. }
  15202. ;
  15203. }
  15204. return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null;
  15205. }
  15206. };
  15207. };
  15208. // plugins/source.js
  15209. /**
  15210. * 源码编辑插件
  15211. * @file
  15212. * @since 1.2.6.1
  15213. */
  15214. (function () {
  15215. var sourceEditors = {
  15216. textarea: function (editor, holder) {
  15217. var textarea = holder.ownerDocument.createElement('textarea');
  15218. textarea.style.cssText = 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;';
  15219. // todo: IE下只有onresize属性可用... 很纠结
  15220. if (browser.ie && browser.version < 8) {
  15221. textarea.style.width = holder.offsetWidth + 'px';
  15222. textarea.style.height = holder.offsetHeight + 'px';
  15223. holder.onresize = function () {
  15224. textarea.style.width = holder.offsetWidth + 'px';
  15225. textarea.style.height = holder.offsetHeight + 'px';
  15226. };
  15227. }
  15228. holder.appendChild(textarea);
  15229. return {
  15230. setContent: function (content) {
  15231. textarea.value = content;
  15232. },
  15233. getContent: function () {
  15234. return textarea.value;
  15235. },
  15236. select: function () {
  15237. var range;
  15238. if (browser.ie) {
  15239. range = textarea.createTextRange();
  15240. range.collapse(true);
  15241. range.select();
  15242. } else {
  15243. //todo: chrome下无法设置焦点
  15244. textarea.setSelectionRange(0, 0);
  15245. textarea.focus();
  15246. }
  15247. },
  15248. dispose: function () {
  15249. holder.removeChild(textarea);
  15250. // todo
  15251. holder.onresize = null;
  15252. textarea = null;
  15253. holder = null;
  15254. }
  15255. };
  15256. },
  15257. codemirror: function (editor, holder) {
  15258. var codeEditor = window.CodeMirror(holder, {
  15259. mode: "text/html",
  15260. tabMode: "indent",
  15261. lineNumbers: true,
  15262. lineWrapping: true
  15263. });
  15264. var dom = codeEditor.getWrapperElement();
  15265. dom.style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
  15266. codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;';
  15267. codeEditor.refresh();
  15268. return {
  15269. getCodeMirror: function () {
  15270. return codeEditor;
  15271. },
  15272. setContent: function (content) {
  15273. codeEditor.setValue(content);
  15274. },
  15275. getContent: function () {
  15276. return codeEditor.getValue();
  15277. },
  15278. select: function () {
  15279. codeEditor.focus();
  15280. },
  15281. dispose: function () {
  15282. holder.removeChild(dom);
  15283. dom = null;
  15284. codeEditor = null;
  15285. }
  15286. };
  15287. }
  15288. };
  15289. UE.plugins['source'] = function () {
  15290. var me = this;
  15291. var opt = this.options;
  15292. var sourceMode = false;
  15293. var sourceEditor;
  15294. var orgSetContent;
  15295. opt.sourceEditor = browser.ie ? 'textarea' : (opt.sourceEditor || 'codemirror');
  15296. me.setOpt({
  15297. sourceEditorFirst: false
  15298. });
  15299. function createSourceEditor(holder) {
  15300. return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](me, holder);
  15301. }
  15302. var bakCssText;
  15303. //解决在源码模式下getContent不能得到最新的内容问题
  15304. var oldGetContent,
  15305. bakAddress;
  15306. /**
  15307. * 切换源码模式和编辑模式
  15308. * @command source
  15309. * @method execCommand
  15310. * @param { String } cmd 命令字符串
  15311. * @example
  15312. * ```javascript
  15313. * editor.execCommand( 'source');
  15314. * ```
  15315. */
  15316. /**
  15317. * 查询当前编辑区域的状态是源码模式还是可视化模式
  15318. * @command source
  15319. * @method queryCommandState
  15320. * @param { String } cmd 命令字符串
  15321. * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
  15322. * @example
  15323. * ```javascript
  15324. * editor.queryCommandState( 'source' );
  15325. * ```
  15326. */
  15327. me.commands['source'] = {
  15328. execCommand: function () {
  15329. sourceMode = !sourceMode;
  15330. if (sourceMode) {
  15331. bakAddress = me.selection.getRange().createAddress(false, true);
  15332. me.undoManger && me.undoManger.save(true);
  15333. if (browser.gecko) {
  15334. me.body.contentEditable = false;
  15335. }
  15336. bakCssText = me.iframe.style.cssText;
  15337. me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;';
  15338. me.fireEvent('beforegetcontent');
  15339. var root = UE.htmlparser(me.body.innerHTML);
  15340. me.filterOutputRule(root);
  15341. root.traversal(function (node) {
  15342. if (node.type == 'element') {
  15343. switch (node.tagName) {
  15344. case 'td':
  15345. case 'th':
  15346. case 'caption':
  15347. if (node.children && node.children.length == 1) {
  15348. if (node.firstChild().tagName == 'br') {
  15349. node.removeChild(node.firstChild())
  15350. }
  15351. }
  15352. ;
  15353. break;
  15354. case 'pre':
  15355. node.innerText(node.innerText().replace(/&nbsp;/g, ' '))
  15356. }
  15357. }
  15358. });
  15359. me.fireEvent('aftergetcontent');
  15360. var content = root.toHtml(true);
  15361. sourceEditor = createSourceEditor(me.iframe.parentNode);
  15362. sourceEditor.setContent(content);
  15363. orgSetContent = me.setContent;
  15364. me.setContent = function (html) {
  15365. //这里暂时不触发事件,防止报错
  15366. var root = UE.htmlparser(html);
  15367. me.filterInputRule(root);
  15368. html = root.toHtml();
  15369. sourceEditor.setContent(html);
  15370. };
  15371. setTimeout(function () {
  15372. sourceEditor.select();
  15373. me.addListener('fullscreenchanged', function () {
  15374. try {
  15375. sourceEditor.getCodeMirror().refresh()
  15376. } catch (e) {
  15377. }
  15378. });
  15379. });
  15380. //重置getContent,源码模式下取值也能是最新的数据
  15381. oldGetContent = me.getContent;
  15382. me.getContent = function () {
  15383. return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  15384. };
  15385. } else {
  15386. me.iframe.style.cssText = bakCssText;
  15387. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  15388. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  15389. cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>', 'g'), function (a, b) {
  15390. if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
  15391. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, '');
  15392. }
  15393. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, '')
  15394. });
  15395. me.setContent = orgSetContent;
  15396. me.setContent(cont);
  15397. sourceEditor.dispose();
  15398. sourceEditor = null;
  15399. //还原getContent方法
  15400. me.getContent = oldGetContent;
  15401. var first = me.body.firstChild;
  15402. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  15403. if (!first) {
  15404. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  15405. first = me.body.firstChild;
  15406. }
  15407. //要在ifm为显示时ff才能取到selection,否则报错
  15408. //这里不能比较位置了
  15409. me.undoManger && me.undoManger.save(true);
  15410. if (browser.gecko) {
  15411. var input = document.createElement('input');
  15412. input.style.cssText = 'position:absolute;left:0;top:-32768px';
  15413. document.body.appendChild(input);
  15414. me.body.contentEditable = false;
  15415. setTimeout(function () {
  15416. domUtils.setViewportOffset(input, {left: -32768, top: 0});
  15417. input.focus();
  15418. setTimeout(function () {
  15419. me.body.contentEditable = true;
  15420. me.selection.getRange().moveToAddress(bakAddress).select(true);
  15421. domUtils.remove(input);
  15422. });
  15423. });
  15424. } else {
  15425. //ie下有可能报错,比如在代码顶头的情况
  15426. try {
  15427. me.selection.getRange().moveToAddress(bakAddress).select(true);
  15428. } catch (e) {
  15429. }
  15430. }
  15431. }
  15432. this.fireEvent('sourcemodechanged', sourceMode);
  15433. },
  15434. queryCommandState: function () {
  15435. return sourceMode | 0;
  15436. },
  15437. notNeedUndo: 1
  15438. };
  15439. var oldQueryCommandState = me.queryCommandState;
  15440. me.queryCommandState = function (cmdName) {
  15441. cmdName = cmdName.toLowerCase();
  15442. if (sourceMode) {
  15443. //源码模式下可以开启的命令
  15444. return cmdName in {
  15445. 'source': 1,
  15446. 'fullscreen': 1
  15447. } ? 1 : -1
  15448. }
  15449. return oldQueryCommandState.apply(this, arguments);
  15450. };
  15451. if (opt.sourceEditor == "codemirror") {
  15452. me.addListener("ready", function () {
  15453. utils.loadFile(document, {
  15454. src: opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
  15455. tag: "script",
  15456. type: "text/javascript",
  15457. defer: "defer"
  15458. }, function () {
  15459. if (opt.sourceEditorFirst) {
  15460. setTimeout(function () {
  15461. me.execCommand("source");
  15462. }, 0);
  15463. }
  15464. });
  15465. utils.loadFile(document, {
  15466. tag: "link",
  15467. rel: "stylesheet",
  15468. type: "text/css",
  15469. href: opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
  15470. });
  15471. });
  15472. }
  15473. };
  15474. })();
  15475. // plugins/enterkey.js
  15476. ///import core
  15477. ///import plugins/undo.js
  15478. ///commands 设置回车标签p或br
  15479. ///commandsName EnterKey
  15480. ///commandsTitle 设置回车标签p或br
  15481. /**
  15482. * @description 处理回车
  15483. * @author zhanyi
  15484. */
  15485. UE.plugins['enterkey'] = function () {
  15486. var hTag,
  15487. me = this,
  15488. tag = me.options.enterTag;
  15489. me.addListener('keyup', function (type, evt) {
  15490. var keyCode = evt.keyCode || evt.which;
  15491. if (keyCode == 13) {
  15492. var range = me.selection.getRange(),
  15493. start = range.startContainer,
  15494. doSave;
  15495. //修正在h1-h6里边回车后不能嵌套p的问题
  15496. if (!browser.ie) {
  15497. if (/h\d/i.test(hTag)) {
  15498. if (browser.gecko) {
  15499. var h = domUtils.findParentByTagName(start, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption', 'table'], true);
  15500. if (!h) {
  15501. me.document.execCommand('formatBlock', false, '<p>');
  15502. doSave = 1;
  15503. }
  15504. } else {
  15505. //chrome remove div
  15506. if (start.nodeType == 1) {
  15507. var tmp = me.document.createTextNode(''), div;
  15508. range.insertNode(tmp);
  15509. div = domUtils.findParentByTagName(tmp, 'div', true);
  15510. if (div) {
  15511. var p = me.document.createElement('p');
  15512. while (div.firstChild) {
  15513. p.appendChild(div.firstChild);
  15514. }
  15515. div.parentNode.insertBefore(p, div);
  15516. domUtils.remove(div);
  15517. range.setStartBefore(tmp).setCursor();
  15518. doSave = 1;
  15519. }
  15520. domUtils.remove(tmp);
  15521. }
  15522. }
  15523. if (me.undoManger && doSave) {
  15524. me.undoManger.save();
  15525. }
  15526. }
  15527. //没有站位符,会出现多行的问题
  15528. browser.opera && range.select();
  15529. } else {
  15530. me.fireEvent('saveScene', true, true)
  15531. }
  15532. }
  15533. });
  15534. me.addListener('keydown', function (type, evt) {
  15535. var keyCode = evt.keyCode || evt.which;
  15536. if (keyCode == 13) {//回车
  15537. if (me.fireEvent('beforeenterkeydown')) {
  15538. domUtils.preventDefault(evt);
  15539. return;
  15540. }
  15541. me.fireEvent('saveScene', true, true);
  15542. hTag = '';
  15543. var range = me.selection.getRange();
  15544. if (!range.collapsed) {
  15545. //跨td不能删
  15546. var start = range.startContainer,
  15547. end = range.endContainer,
  15548. startTd = domUtils.findParentByTagName(start, 'td', true),
  15549. endTd = domUtils.findParentByTagName(end, 'td', true);
  15550. if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
  15551. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  15552. return;
  15553. }
  15554. }
  15555. if (tag == 'p') {
  15556. if (!browser.ie) {
  15557. start = domUtils.findParentByTagName(range.startContainer, ['ol', 'ul', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption'], true);
  15558. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  15559. //trace:2431
  15560. if (!start && !browser.opera) {
  15561. me.document.execCommand('formatBlock', false, '<p>');
  15562. if (browser.gecko) {
  15563. range = me.selection.getRange();
  15564. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  15565. start && domUtils.removeDirtyAttr(start);
  15566. }
  15567. } else {
  15568. hTag = start.tagName;
  15569. start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
  15570. }
  15571. }
  15572. } else {
  15573. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  15574. if (!range.collapsed) {
  15575. range.deleteContents();
  15576. start = range.startContainer;
  15577. if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
  15578. while (start.nodeType == 1) {
  15579. if (dtd.$empty[start.tagName]) {
  15580. range.setStartBefore(start).setCursor();
  15581. if (me.undoManger) {
  15582. me.undoManger.save();
  15583. }
  15584. return false;
  15585. }
  15586. if (!start.firstChild) {
  15587. var br = range.document.createElement('br');
  15588. start.appendChild(br);
  15589. range.setStart(start, 0).setCursor();
  15590. if (me.undoManger) {
  15591. me.undoManger.save();
  15592. }
  15593. return false;
  15594. }
  15595. start = start.firstChild;
  15596. }
  15597. if (start === range.startContainer.childNodes[range.startOffset]) {
  15598. br = range.document.createElement('br');
  15599. range.insertNode(br).setCursor();
  15600. } else {
  15601. range.setStart(start, 0).setCursor();
  15602. }
  15603. } else {
  15604. br = range.document.createElement('br');
  15605. range.insertNode(br).setStartAfter(br).setCursor();
  15606. }
  15607. } else {
  15608. br = range.document.createElement('br');
  15609. range.insertNode(br);
  15610. var parent = br.parentNode;
  15611. if (parent.lastChild === br) {
  15612. br.parentNode.insertBefore(br.cloneNode(true), br);
  15613. range.setStartBefore(br);
  15614. } else {
  15615. range.setStartAfter(br);
  15616. }
  15617. range.setCursor();
  15618. }
  15619. }
  15620. }
  15621. });
  15622. };
  15623. // plugins/keystrokes.js
  15624. /* 处理特殊键的兼容性问题 */
  15625. UE.plugins['keystrokes'] = function () {
  15626. var me = this;
  15627. var collapsed = true;
  15628. me.addListener('keydown', function (type, evt) {
  15629. var keyCode = evt.keyCode || evt.which,
  15630. rng = me.selection.getRange();
  15631. //处理全选的情况
  15632. if (!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <= 90
  15633. || keyCode >= 48 && keyCode <= 57 ||
  15634. keyCode >= 96 && keyCode <= 111 || {
  15635. 13: 1,
  15636. 8: 1,
  15637. 46: 1
  15638. }[keyCode])
  15639. ) {
  15640. var tmpNode = rng.startContainer;
  15641. if (domUtils.isFillChar(tmpNode)) {
  15642. rng.setStartBefore(tmpNode)
  15643. }
  15644. tmpNode = rng.endContainer;
  15645. if (domUtils.isFillChar(tmpNode)) {
  15646. rng.setEndAfter(tmpNode)
  15647. }
  15648. rng.txtToElmBoundary();
  15649. //结束边界可能放到了br的前边,要把br包含进来
  15650. // x[xxx]<br/>
  15651. if (rng.endContainer && rng.endContainer.nodeType == 1) {
  15652. tmpNode = rng.endContainer.childNodes[rng.endOffset];
  15653. if (tmpNode && domUtils.isBr(tmpNode)) {
  15654. rng.setEndAfter(tmpNode);
  15655. }
  15656. }
  15657. if (rng.startOffset == 0) {
  15658. tmpNode = rng.startContainer;
  15659. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  15660. tmpNode = rng.endContainer;
  15661. if (rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode, 'lastChild')) {
  15662. me.fireEvent('saveScene');
  15663. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  15664. rng.setStart(me.body.firstChild, 0).setCursor(false, true);
  15665. me._selectionChange();
  15666. return;
  15667. }
  15668. }
  15669. }
  15670. }
  15671. //处理backspace
  15672. if (keyCode == keymap.Backspace) {
  15673. rng = me.selection.getRange();
  15674. collapsed = rng.collapsed;
  15675. if (me.fireEvent('delkeydown', evt)) {
  15676. return;
  15677. }
  15678. var start, end;
  15679. //避免按两次删除才能生效的问题
  15680. if (rng.collapsed && rng.inFillChar()) {
  15681. start = rng.startContainer;
  15682. if (domUtils.isFillChar(start)) {
  15683. rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
  15684. domUtils.remove(start)
  15685. } else {
  15686. start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '');
  15687. rng.startOffset--;
  15688. rng.collapse(true).select(true)
  15689. }
  15690. }
  15691. //解决选中control元素不能删除的问题
  15692. if (start = rng.getClosedNode()) {
  15693. me.fireEvent('saveScene');
  15694. rng.setStartBefore(start);
  15695. domUtils.remove(start);
  15696. rng.setCursor();
  15697. me.fireEvent('saveScene');
  15698. domUtils.preventDefault(evt);
  15699. return;
  15700. }
  15701. //阻止在table上的删除
  15702. if (!browser.ie) {
  15703. start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
  15704. end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
  15705. if (start && !end || !start && end || start !== end) {
  15706. evt.preventDefault();
  15707. return;
  15708. }
  15709. }
  15710. }
  15711. //处理tab键的逻辑
  15712. if (keyCode == keymap.Tab) {
  15713. //不处理以下标签
  15714. var excludeTagNameForTabKey = {
  15715. 'ol': 1,
  15716. 'ul': 1,
  15717. 'table': 1
  15718. };
  15719. //处理组件里的tab按下事件
  15720. if (me.fireEvent('tabkeydown', evt)) {
  15721. domUtils.preventDefault(evt);
  15722. return;
  15723. }
  15724. var range = me.selection.getRange();
  15725. me.fireEvent('saveScene');
  15726. for (var i = 0, txt = '', tabSize = me.options.tabSize || 4, tabNode = me.options.tabNode || '&nbsp;'; i < tabSize; i++) {
  15727. txt += tabNode;
  15728. }
  15729. var span = me.document.createElement('span');
  15730. span.innerHTML = txt + domUtils.fillChar;
  15731. if (range.collapsed) {
  15732. range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
  15733. } else {
  15734. var filterFn = function (node) {
  15735. return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
  15736. };
  15737. //普通的情况
  15738. start = domUtils.findParent(range.startContainer, filterFn, true);
  15739. end = domUtils.findParent(range.endContainer, filterFn, true);
  15740. if (start && end && start === end) {
  15741. range.deleteContents();
  15742. range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
  15743. } else {
  15744. var bookmark = range.createBookmark();
  15745. range.enlarge(true);
  15746. var bookmark2 = range.createBookmark(),
  15747. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
  15748. while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
  15749. current.insertBefore(span.cloneNode(true).firstChild, current.firstChild);
  15750. current = domUtils.getNextDomNode(current, false, filterFn);
  15751. }
  15752. range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
  15753. }
  15754. }
  15755. domUtils.preventDefault(evt)
  15756. }
  15757. //trace:1634
  15758. //ff的del键在容器空的时候,也会删除
  15759. if (browser.gecko && keyCode == 46) {
  15760. range = me.selection.getRange();
  15761. if (range.collapsed) {
  15762. start = range.startContainer;
  15763. if (domUtils.isEmptyBlock(start)) {
  15764. var parent = start.parentNode;
  15765. while (domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)) {
  15766. start = parent;
  15767. parent = parent.parentNode;
  15768. }
  15769. if (start === parent.lastChild)
  15770. evt.preventDefault();
  15771. return;
  15772. }
  15773. }
  15774. }
  15775. });
  15776. me.addListener('keyup', function (type, evt) {
  15777. var keyCode = evt.keyCode || evt.which,
  15778. rng, me = this;
  15779. if (keyCode == keymap.Backspace) {
  15780. if (me.fireEvent('delkeyup')) {
  15781. return;
  15782. }
  15783. rng = me.selection.getRange();
  15784. if (rng.collapsed) {
  15785. var tmpNode,
  15786. autoClearTagName = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
  15787. if (tmpNode = domUtils.findParentByTagName(rng.startContainer, autoClearTagName, true)) {
  15788. if (domUtils.isEmptyBlock(tmpNode)) {
  15789. var pre = tmpNode.previousSibling;
  15790. if (pre && pre.nodeName != 'TABLE') {
  15791. domUtils.remove(tmpNode);
  15792. rng.setStartAtLast(pre).setCursor(false, true);
  15793. return;
  15794. } else {
  15795. var next = tmpNode.nextSibling;
  15796. if (next && next.nodeName != 'TABLE') {
  15797. domUtils.remove(tmpNode);
  15798. rng.setStartAtFirst(next).setCursor(false, true);
  15799. return;
  15800. }
  15801. }
  15802. }
  15803. }
  15804. //处理当删除到body时,要重新给p标签展位
  15805. if (domUtils.isBody(rng.startContainer)) {
  15806. var tmpNode = domUtils.createElement(me.document, 'p', {
  15807. 'innerHTML': browser.ie ? domUtils.fillChar : '<br/>'
  15808. });
  15809. rng.insertNode(tmpNode).setStart(tmpNode, 0).setCursor(false, true);
  15810. }
  15811. }
  15812. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  15813. if (!collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))) {
  15814. if (browser.ie) {
  15815. var span = rng.document.createElement('span');
  15816. rng.insertNode(span).setStartBefore(span).collapse(true);
  15817. rng.select();
  15818. domUtils.remove(span)
  15819. } else {
  15820. rng.select()
  15821. }
  15822. }
  15823. }
  15824. })
  15825. };
  15826. // plugins/fiximgclick.js
  15827. ///import core
  15828. ///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
  15829. ///commandsName FixImgClick
  15830. ///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小
  15831. //修复chrome下图片不能点击的问题,出现八个角可改变大小
  15832. UE.plugins['fiximgclick'] = (function () {
  15833. var elementUpdated = false;
  15834. function Scale() {
  15835. this.editor = null;
  15836. this.resizer = null;
  15837. this.cover = null;
  15838. this.doc = document;
  15839. this.prePos = {x: 0, y: 0};
  15840. this.startPos = {x: 0, y: 0};
  15841. }
  15842. (function () {
  15843. var rect = [
  15844. //[left, top, width, height]
  15845. [0, 0, -1, -1],
  15846. [0, 0, 0, -1],
  15847. [0, 0, 1, -1],
  15848. [0, 0, -1, 0],
  15849. [0, 0, 1, 0],
  15850. [0, 0, -1, 1],
  15851. [0, 0, 0, 1],
  15852. [0, 0, 1, 1]
  15853. ];
  15854. Scale.prototype = {
  15855. init: function (editor) {
  15856. var me = this;
  15857. me.editor = editor;
  15858. me.startPos = this.prePos = {x: 0, y: 0};
  15859. me.dragId = -1;
  15860. var hands = [],
  15861. cover = me.cover = document.createElement('div'),
  15862. resizer = me.resizer = document.createElement('div');
  15863. cover.id = me.editor.ui.id + '_imagescale_cover';
  15864. cover.style.cssText = 'position:absolute;display:none;z-index:' + (me.editor.options.zIndex) + ';filter:alpha(opacity=0); opacity:0;background:#CCC;';
  15865. domUtils.on(cover, 'mousedown click', function () {
  15866. me.hide();
  15867. });
  15868. for (i = 0; i < 8; i++) {
  15869. hands.push('<span class="edui-editor-imagescale-hand' + i + '"></span>');
  15870. }
  15871. resizer.id = me.editor.ui.id + '_imagescale';
  15872. resizer.className = 'edui-editor-imagescale';
  15873. resizer.innerHTML = hands.join('');
  15874. resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + (me.editor.options.zIndex) + ';';
  15875. me.editor.ui.getDom().appendChild(cover);
  15876. me.editor.ui.getDom().appendChild(resizer);
  15877. me.initStyle();
  15878. me.initEvents();
  15879. },
  15880. initStyle: function () {
  15881. 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;}' +
  15882. '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
  15883. + '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
  15884. + '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
  15885. + '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
  15886. + '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
  15887. + '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
  15888. + '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
  15889. + '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
  15890. + '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
  15891. },
  15892. initEvents: function () {
  15893. var me = this;
  15894. me.startPos.x = me.startPos.y = 0;
  15895. me.isDraging = false;
  15896. },
  15897. _eventHandler: function (e) {
  15898. var me = this;
  15899. switch (e.type) {
  15900. case 'mousedown':
  15901. var hand = e.target || e.srcElement, hand;
  15902. if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) {
  15903. me.dragId = hand.className.slice(-1);
  15904. me.startPos.x = me.prePos.x = e.clientX;
  15905. me.startPos.y = me.prePos.y = e.clientY;
  15906. domUtils.on(me.doc, 'mousemove', me.proxy(me._eventHandler, me));
  15907. }
  15908. break;
  15909. case 'mousemove':
  15910. if (me.dragId != -1) {
  15911. me.updateContainerStyle(me.dragId, {
  15912. x: e.clientX - me.prePos.x,
  15913. y: e.clientY - me.prePos.y
  15914. });
  15915. me.prePos.x = e.clientX;
  15916. me.prePos.y = e.clientY;
  15917. elementUpdated = true;
  15918. me.updateTargetElement();
  15919. }
  15920. break;
  15921. case 'mouseup':
  15922. if (me.dragId != -1) {
  15923. me.updateContainerStyle(me.dragId, {
  15924. x: e.clientX - me.prePos.x,
  15925. y: e.clientY - me.prePos.y
  15926. });
  15927. me.updateTargetElement();
  15928. if (me.target.parentNode) me.attachTo(me.target);
  15929. me.dragId = -1;
  15930. }
  15931. domUtils.un(me.doc, 'mousemove', me.proxy(me._eventHandler, me));
  15932. //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
  15933. if (elementUpdated) {
  15934. elementUpdated = false;
  15935. me.editor.fireEvent('contentchange');
  15936. }
  15937. break;
  15938. default:
  15939. break;
  15940. }
  15941. },
  15942. updateTargetElement: function () {
  15943. var me = this;
  15944. domUtils.setStyles(me.target, {
  15945. 'width': me.resizer.style.width,
  15946. 'height': me.resizer.style.height
  15947. });
  15948. me.target.width = parseInt(me.resizer.style.width);
  15949. me.target.height = parseInt(me.resizer.style.height);
  15950. me.attachTo(me.target);
  15951. },
  15952. updateContainerStyle: function (dir, offset) {
  15953. var me = this,
  15954. dom = me.resizer, tmp;
  15955. if (rect[dir][0] != 0) {
  15956. tmp = parseInt(dom.style.left) + offset.x;
  15957. dom.style.left = me._validScaledProp('left', tmp) + 'px';
  15958. }
  15959. if (rect[dir][1] != 0) {
  15960. tmp = parseInt(dom.style.top) + offset.y;
  15961. dom.style.top = me._validScaledProp('top', tmp) + 'px';
  15962. }
  15963. if (rect[dir][2] != 0) {
  15964. tmp = dom.clientWidth + rect[dir][2] * offset.x;
  15965. dom.style.width = me._validScaledProp('width', tmp) + 'px';
  15966. }
  15967. if (rect[dir][3] != 0) {
  15968. tmp = dom.clientHeight + rect[dir][3] * offset.y;
  15969. dom.style.height = me._validScaledProp('height', tmp) + 'px';
  15970. }
  15971. },
  15972. _validScaledProp: function (prop, value) {
  15973. var ele = this.resizer,
  15974. wrap = document;
  15975. value = isNaN(value) ? 0 : value;
  15976. switch (prop) {
  15977. case 'left':
  15978. return value < 0 ? 0 : (value + ele.clientWidth) > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value;
  15979. case 'top':
  15980. return value < 0 ? 0 : (value + ele.clientHeight) > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value;
  15981. case 'width':
  15982. return value <= 0 ? 1 : (value + ele.offsetLeft) > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value;
  15983. case 'height':
  15984. return value <= 0 ? 1 : (value + ele.offsetTop) > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value;
  15985. }
  15986. },
  15987. hideCover: function () {
  15988. this.cover.style.display = 'none';
  15989. },
  15990. showCover: function () {
  15991. var me = this,
  15992. editorPos = domUtils.getXY(me.editor.ui.getDom()),
  15993. iframePos = domUtils.getXY(me.editor.iframe);
  15994. domUtils.setStyles(me.cover, {
  15995. 'width': me.editor.iframe.offsetWidth + 'px',
  15996. 'height': me.editor.iframe.offsetHeight + 'px',
  15997. 'top': iframePos.y - editorPos.y + 'px',
  15998. 'left': iframePos.x - editorPos.x + 'px',
  15999. 'position': 'absolute',
  16000. 'display': ''
  16001. })
  16002. },
  16003. show: function (targetObj) {
  16004. var me = this;
  16005. me.resizer.style.display = 'block';
  16006. if (targetObj) me.attachTo(targetObj);
  16007. domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me));
  16008. domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
  16009. me.showCover();
  16010. me.editor.fireEvent('afterscaleshow', me);
  16011. me.editor.fireEvent('saveScene');
  16012. },
  16013. hide: function () {
  16014. var me = this;
  16015. me.hideCover();
  16016. me.resizer.style.display = 'none';
  16017. domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me));
  16018. domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me));
  16019. me.editor.fireEvent('afterscalehide', me);
  16020. },
  16021. proxy: function (fn, context) {
  16022. return function (e) {
  16023. return fn.apply(context || this, arguments);
  16024. };
  16025. },
  16026. attachTo: function (targetObj) {
  16027. var me = this,
  16028. target = me.target = targetObj,
  16029. resizer = this.resizer,
  16030. imgPos = domUtils.getXY(target),
  16031. iframePos = domUtils.getXY(me.editor.iframe),
  16032. editorPos = domUtils.getXY(resizer.parentNode);
  16033. domUtils.setStyles(resizer, {
  16034. 'width': target.width + 'px',
  16035. 'height': target.height + 'px',
  16036. 'left': iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + 'px',
  16037. 'top': iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + 'px'
  16038. });
  16039. }
  16040. }
  16041. })();
  16042. return function () {
  16043. var me = this,
  16044. imageScale;
  16045. me.setOpt('imageScaleEnabled', true);
  16046. if (!browser.ie && me.options.imageScaleEnabled) {
  16047. me.addListener('click', function (type, e) {
  16048. var range = me.selection.getRange(),
  16049. img = range.getClosedNode();
  16050. if (img && img.tagName == 'IMG' && me.body.contentEditable != "false") {
  16051. if (img.className.indexOf("edui-faked-music") != -1 ||
  16052. img.getAttribute("anchorname") ||
  16053. domUtils.hasClass(img, 'loadingclass') ||
  16054. domUtils.hasClass(img, 'loaderrorclass')) {
  16055. return
  16056. }
  16057. if (!imageScale) {
  16058. imageScale = new Scale();
  16059. imageScale.init(me);
  16060. me.ui.getDom().appendChild(imageScale.resizer);
  16061. var _keyDownHandler = function (e) {
  16062. imageScale.hide();
  16063. if (imageScale.target) me.selection.getRange().selectNode(imageScale.target).select();
  16064. }, _mouseDownHandler = function (e) {
  16065. var ele = e.target || e.srcElement;
  16066. if (ele && (ele.className === undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) {
  16067. _keyDownHandler(e);
  16068. }
  16069. }, timer;
  16070. me.addListener('afterscaleshow', function (e) {
  16071. me.addListener('beforekeydown', _keyDownHandler);
  16072. me.addListener('beforemousedown', _mouseDownHandler);
  16073. domUtils.on(document, 'keydown', _keyDownHandler);
  16074. domUtils.on(document, 'mousedown', _mouseDownHandler);
  16075. me.selection.getNative().removeAllRanges();
  16076. });
  16077. me.addListener('afterscalehide', function (e) {
  16078. me.removeListener('beforekeydown', _keyDownHandler);
  16079. me.removeListener('beforemousedown', _mouseDownHandler);
  16080. domUtils.un(document, 'keydown', _keyDownHandler);
  16081. domUtils.un(document, 'mousedown', _mouseDownHandler);
  16082. var target = imageScale.target;
  16083. if (target.parentNode) {
  16084. me.selection.getRange().selectNode(target).select();
  16085. }
  16086. });
  16087. //TODO 有iframe的情况,mousedown不能往下传。。
  16088. domUtils.on(imageScale.resizer, 'mousedown', function (e) {
  16089. me.selection.getNative().removeAllRanges();
  16090. var ele = e.target || e.srcElement;
  16091. if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
  16092. timer = setTimeout(function () {
  16093. imageScale.hide();
  16094. if (imageScale.target) me.selection.getRange().selectNode(ele).select();
  16095. }, 200);
  16096. }
  16097. });
  16098. domUtils.on(imageScale.resizer, 'mouseup', function (e) {
  16099. var ele = e.target || e.srcElement;
  16100. if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
  16101. clearTimeout(timer);
  16102. }
  16103. });
  16104. }
  16105. imageScale.show(img);
  16106. } else {
  16107. if (imageScale && imageScale.resizer.style.display != 'none') imageScale.hide();
  16108. }
  16109. });
  16110. }
  16111. if (browser.webkit) {
  16112. me.addListener('click', function (type, e) {
  16113. if (e.target.tagName == 'IMG' && me.body.contentEditable != "false") {
  16114. var range = new dom.Range(me.document);
  16115. range.selectNode(e.target).select();
  16116. }
  16117. });
  16118. }
  16119. }
  16120. })();
  16121. // plugins/autolink.js
  16122. ///import core
  16123. ///commands 为非ie浏览器自动添加a标签
  16124. ///commandsName AutoLink
  16125. ///commandsTitle 自动增加链接
  16126. /**
  16127. * @description 为非ie浏览器自动添加a标签
  16128. * @author zhanyi
  16129. */
  16130. UE.plugin.register('autolink', function () {
  16131. var cont = 0;
  16132. return !browser.ie ? {
  16133. bindEvents: {
  16134. 'reset': function () {
  16135. cont = 0;
  16136. },
  16137. 'keydown': function (type, evt) {
  16138. var me = this;
  16139. var keyCode = evt.keyCode || evt.which;
  16140. if (keyCode == 32 || keyCode == 13) {
  16141. var sel = me.selection.getNative(),
  16142. range = sel.getRangeAt(0).cloneRange(),
  16143. offset,
  16144. charCode;
  16145. var start = range.startContainer;
  16146. while (start.nodeType == 1 && range.startOffset > 0) {
  16147. start = range.startContainer.childNodes[range.startOffset - 1];
  16148. if (!start) {
  16149. break;
  16150. }
  16151. range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length);
  16152. range.collapse(true);
  16153. start = range.startContainer;
  16154. }
  16155. do {
  16156. if (range.startOffset == 0) {
  16157. start = range.startContainer.previousSibling;
  16158. while (start && start.nodeType == 1) {
  16159. start = start.lastChild;
  16160. }
  16161. if (!start || domUtils.isFillChar(start)) {
  16162. break;
  16163. }
  16164. offset = start.nodeValue.length;
  16165. } else {
  16166. start = range.startContainer;
  16167. offset = range.startOffset;
  16168. }
  16169. range.setStart(start, offset - 1);
  16170. charCode = range.toString().charCodeAt(0);
  16171. } while (charCode != 160 && charCode != 32);
  16172. if (range.toString().replace(new RegExp(domUtils.fillChar, 'g'), '').match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)) {
  16173. while (range.toString().length) {
  16174. if (/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())) {
  16175. break;
  16176. }
  16177. try {
  16178. range.setStart(range.startContainer, range.startOffset + 1);
  16179. } catch (e) {
  16180. //trace:2121
  16181. var start = range.startContainer;
  16182. while (!(next = start.nextSibling)) {
  16183. if (domUtils.isBody(start)) {
  16184. return;
  16185. }
  16186. start = start.parentNode;
  16187. }
  16188. range.setStart(next, 0);
  16189. }
  16190. }
  16191. //range的开始边界已经在a标签里的不再处理
  16192. if (domUtils.findParentByTagName(range.startContainer, 'a', true)) {
  16193. return;
  16194. }
  16195. var a = me.document.createElement('a'), text = me.document.createTextNode(' '), href;
  16196. me.undoManger && me.undoManger.save();
  16197. a.appendChild(range.extractContents());
  16198. a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, '');
  16199. href = a.getAttribute("href").replace(new RegExp(domUtils.fillChar, 'g'), '');
  16200. href = /^(?:https?:\/\/)/ig.test(href) ? href : "http://" + href;
  16201. a.setAttribute('_src', utils.html(href));
  16202. a.href = utils.html(href);
  16203. range.insertNode(a);
  16204. a.parentNode.insertBefore(text, a.nextSibling);
  16205. range.setStart(text, 0);
  16206. range.collapse(true);
  16207. sel.removeAllRanges();
  16208. sel.addRange(range);
  16209. me.undoManger && me.undoManger.save();
  16210. }
  16211. }
  16212. }
  16213. }
  16214. } : {}
  16215. }, function () {
  16216. var keyCodes = {
  16217. 37: 1, 38: 1, 39: 1, 40: 1,
  16218. 13: 1, 32: 1
  16219. };
  16220. function checkIsCludeLink(node) {
  16221. if (node.nodeType == 3) {
  16222. return null
  16223. }
  16224. if (node.nodeName == 'A') {
  16225. return node;
  16226. }
  16227. var lastChild = node.lastChild;
  16228. while (lastChild) {
  16229. if (lastChild.nodeName == 'A') {
  16230. return lastChild;
  16231. }
  16232. if (lastChild.nodeType == 3) {
  16233. if (domUtils.isWhitespace(lastChild)) {
  16234. lastChild = lastChild.previousSibling;
  16235. continue;
  16236. }
  16237. return null
  16238. }
  16239. lastChild = lastChild.lastChild;
  16240. }
  16241. }
  16242. browser.ie && this.addListener('keyup', function (cmd, evt) {
  16243. var me = this, keyCode = evt.keyCode;
  16244. if (keyCodes[keyCode]) {
  16245. var rng = me.selection.getRange();
  16246. var start = rng.startContainer;
  16247. if (keyCode == 13) {
  16248. while (start && !domUtils.isBody(start) && !domUtils.isBlockElm(start)) {
  16249. start = start.parentNode;
  16250. }
  16251. if (start && !domUtils.isBody(start) && start.nodeName == 'P') {
  16252. var pre = start.previousSibling;
  16253. if (pre && pre.nodeType == 1) {
  16254. var pre = checkIsCludeLink(pre);
  16255. if (pre && !pre.getAttribute('_href')) {
  16256. domUtils.remove(pre, true);
  16257. }
  16258. }
  16259. }
  16260. } else if (keyCode == 32) {
  16261. if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) {
  16262. start = start.previousSibling;
  16263. if (start && start.nodeName == 'A' && !start.getAttribute('_href')) {
  16264. domUtils.remove(start, true);
  16265. }
  16266. }
  16267. } else {
  16268. start = domUtils.findParentByTagName(start, 'a', true);
  16269. if (start && !start.getAttribute('_href')) {
  16270. var bk = rng.createBookmark();
  16271. domUtils.remove(start, true);
  16272. rng.moveToBookmark(bk).select(true)
  16273. }
  16274. }
  16275. }
  16276. });
  16277. }
  16278. );
  16279. // plugins/autoheight.js
  16280. ///import core
  16281. ///commands 当输入内容超过编辑器高度时,编辑器自动增高
  16282. ///commandsName AutoHeight,autoHeightEnabled
  16283. ///commandsTitle 自动增高
  16284. /**
  16285. * @description 自动伸展
  16286. * @author zhanyi
  16287. */
  16288. UE.plugins['autoheight'] = function () {
  16289. var me = this;
  16290. //提供开关,就算加载也可以关闭
  16291. me.autoHeightEnabled = me.options.autoHeightEnabled !== false;
  16292. if (!me.autoHeightEnabled) {
  16293. return;
  16294. }
  16295. var bakOverflow,
  16296. lastHeight = 0,
  16297. options = me.options,
  16298. currentHeight,
  16299. timer;
  16300. function adjustHeight() {
  16301. var me = this;
  16302. clearTimeout(timer);
  16303. if (isFullscreen) return;
  16304. if (!me.queryCommandState || me.queryCommandState && me.queryCommandState('source') != 1) {
  16305. timer = setTimeout(function () {
  16306. var node = me.body.lastChild;
  16307. while (node && node.nodeType != 1) {
  16308. node = node.previousSibling;
  16309. }
  16310. if (node && node.nodeType == 1) {
  16311. node.style.clear = 'both';
  16312. currentHeight = Math.max(domUtils.getXY(node).y + node.offsetHeight + 25, Math.max(options.minFrameHeight, options.initialFrameHeight));
  16313. if (currentHeight != lastHeight) {
  16314. if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
  16315. me.iframe.parentNode.style.height = currentHeight + 'px';
  16316. }
  16317. me.body.style.height = currentHeight + 'px';
  16318. lastHeight = currentHeight;
  16319. }
  16320. domUtils.removeStyle(node, 'clear');
  16321. }
  16322. }, 50)
  16323. }
  16324. }
  16325. var isFullscreen;
  16326. me.addListener('fullscreenchanged', function (cmd, f) {
  16327. isFullscreen = f
  16328. });
  16329. me.addListener('destroy', function () {
  16330. me.removeListener('contentchange afterinserthtml keyup mouseup', adjustHeight)
  16331. });
  16332. me.enableAutoHeight = function () {
  16333. var me = this;
  16334. if (!me.autoHeightEnabled) {
  16335. return;
  16336. }
  16337. var doc = me.document;
  16338. me.autoHeightEnabled = true;
  16339. bakOverflow = doc.body.style.overflowY;
  16340. doc.body.style.overflowY = 'hidden';
  16341. me.addListener('contentchange afterinserthtml keyup mouseup', adjustHeight);
  16342. //ff不给事件算得不对
  16343. setTimeout(function () {
  16344. adjustHeight.call(me);
  16345. }, browser.gecko ? 100 : 0);
  16346. me.fireEvent('autoheightchanged', me.autoHeightEnabled);
  16347. };
  16348. me.disableAutoHeight = function () {
  16349. me.body.style.overflowY = bakOverflow || '';
  16350. me.removeListener('contentchange', adjustHeight);
  16351. me.removeListener('keyup', adjustHeight);
  16352. me.removeListener('mouseup', adjustHeight);
  16353. me.autoHeightEnabled = false;
  16354. me.fireEvent('autoheightchanged', me.autoHeightEnabled);
  16355. };
  16356. me.on('setHeight', function () {
  16357. me.disableAutoHeight()
  16358. });
  16359. me.addListener('ready', function () {
  16360. me.enableAutoHeight();
  16361. //trace:1764
  16362. var timer;
  16363. domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function () {
  16364. clearTimeout(timer);
  16365. timer = setTimeout(function () {
  16366. //trace:3681
  16367. adjustHeight.call(me);
  16368. }, 100);
  16369. });
  16370. //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
  16371. var lastScrollY;
  16372. window.onscroll = function () {
  16373. if (lastScrollY === null) {
  16374. lastScrollY = this.scrollY
  16375. } else if (this.scrollY == 0 && lastScrollY != 0) {
  16376. me.window.scrollTo(0, 0);
  16377. lastScrollY = null;
  16378. }
  16379. }
  16380. });
  16381. };
  16382. // plugins/autofloat.js
  16383. ///import core
  16384. ///commands 悬浮工具栏
  16385. ///commandsName AutoFloat,autoFloatEnabled
  16386. ///commandsTitle 悬浮工具栏
  16387. /**
  16388. * modified by chengchao01
  16389. * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉!
  16390. */
  16391. UE.plugins['autofloat'] = function () {
  16392. var me = this,
  16393. lang = me.getLang();
  16394. me.setOpt({
  16395. topOffset: 0
  16396. });
  16397. // var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
  16398. var optsAutoFloatEnabled = false,
  16399. topOffset = me.options.topOffset;
  16400. //如果不固定toolbar的位置,则直接退出
  16401. if (!optsAutoFloatEnabled) {
  16402. return;
  16403. }
  16404. var uiUtils = UE.ui.uiUtils,
  16405. LteIE6 = browser.ie && browser.version <= 6,
  16406. quirks = browser.quirks;
  16407. function checkHasUI() {
  16408. if (!UE.ui) {
  16409. alert(lang.autofloatMsg);
  16410. return 0;
  16411. }
  16412. return 1;
  16413. }
  16414. function fixIE6FixedPos() {
  16415. var docStyle = document.body.style;
  16416. docStyle.backgroundImage = 'url("about:blank")';
  16417. docStyle.backgroundAttachment = 'fixed';
  16418. }
  16419. var bakCssText,
  16420. placeHolder = document.createElement('div'),
  16421. toolbarBox, orgTop,
  16422. getPosition,
  16423. flag = true; //ie7模式下需要偏移
  16424. function setFloating() {
  16425. var toobarBoxPos = domUtils.getXY(toolbarBox),
  16426. origalFloat = domUtils.getComputedStyle(toolbarBox, 'position'),
  16427. origalLeft = domUtils.getComputedStyle(toolbarBox, 'left');
  16428. toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
  16429. toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
  16430. toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
  16431. if (LteIE6 || (quirks && browser.ie)) {
  16432. if (toolbarBox.style.position != 'absolute') {
  16433. toolbarBox.style.position = 'absolute';
  16434. }
  16435. toolbarBox.style.top = (document.body.scrollTop || document.documentElement.scrollTop) - orgTop + topOffset + 'px';
  16436. } else {
  16437. if (browser.ie7Compat && flag) {
  16438. flag = false;
  16439. toolbarBox.style.left = domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left + 2 + 'px';
  16440. }
  16441. if (toolbarBox.style.position != 'fixed') {
  16442. toolbarBox.style.position = 'fixed';
  16443. toolbarBox.style.top = topOffset + "px";
  16444. ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
  16445. }
  16446. }
  16447. }
  16448. function unsetFloating() {
  16449. flag = true;
  16450. if (placeHolder.parentNode) {
  16451. placeHolder.parentNode.removeChild(placeHolder);
  16452. }
  16453. toolbarBox.style.cssText = bakCssText;
  16454. }
  16455. function updateFloating() {
  16456. var rect3 = getPosition(me.container);
  16457. var offset = me.options.toolbarTopOffset || 0;
  16458. if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
  16459. setFloating();
  16460. } else {
  16461. unsetFloating();
  16462. }
  16463. }
  16464. var defer_updateFloating = utils.defer(function () {
  16465. updateFloating();
  16466. }, browser.ie ? 200 : 100, true);
  16467. me.addListener('destroy', function () {
  16468. domUtils.un(window, ['scroll', 'resize'], updateFloating);
  16469. me.removeListener('keydown', defer_updateFloating);
  16470. });
  16471. me.addListener('ready', function () {
  16472. if (checkHasUI(me)) {
  16473. //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断
  16474. if (!me.ui) {
  16475. return;
  16476. }
  16477. getPosition = uiUtils.getClientRect;
  16478. toolbarBox = me.ui.getDom('toolbarbox');
  16479. orgTop = getPosition(toolbarBox).top;
  16480. bakCssText = toolbarBox.style.cssText;
  16481. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  16482. if (LteIE6) {
  16483. fixIE6FixedPos();
  16484. }
  16485. domUtils.on(window, ['scroll', 'resize'], updateFloating);
  16486. me.addListener('keydown', defer_updateFloating);
  16487. me.addListener('beforefullscreenchange', function (t, enabled) {
  16488. if (enabled) {
  16489. unsetFloating();
  16490. }
  16491. });
  16492. me.addListener('fullscreenchanged', function (t, enabled) {
  16493. if (!enabled) {
  16494. updateFloating();
  16495. }
  16496. });
  16497. me.addListener('sourcemodechanged', function (t, enabled) {
  16498. setTimeout(function () {
  16499. updateFloating();
  16500. }, 0);
  16501. });
  16502. me.addListener("clearDoc", function () {
  16503. setTimeout(function () {
  16504. updateFloating();
  16505. }, 0);
  16506. })
  16507. }
  16508. });
  16509. };
  16510. // plugins/video.js
  16511. /**
  16512. * video插件, 为UEditor提供视频插入支持
  16513. * @file
  16514. * @since 1.2.6.1
  16515. */
  16516. UE.plugins['video'] = function () {
  16517. var me = this;
  16518. /**
  16519. * 创建插入视频字符窜
  16520. * @param url 视频地址
  16521. * @param width 视频宽度
  16522. * @param height 视频高度
  16523. * @param align 视频对齐
  16524. * @param toEmbed 是否以flash代替显示
  16525. * @param addParagraph 是否需要添加P 标签
  16526. */
  16527. function creatInsertStr(url, width, height, id, align, classname, type) {
  16528. url = utils.unhtmlForUrl(url);
  16529. align = utils.unhtml(align);
  16530. classname = utils.unhtml(classname);
  16531. width = parseInt(width, 10) || 0;
  16532. height = parseInt(height, 10) || 0;
  16533. var str;
  16534. switch (type) {
  16535. case 'image':
  16536. str = '<img ' + (id ? 'id="' + id + '"' : '') + ' width="' + width + '" height="' + height + '" _url="' + url + '" class="' + classname.replace(/\bvideo-js\b/, '') + '"' +
  16537. ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" style="background:url(' + me.options.UEDITOR_HOME_URL + 'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;' + (align ? 'float:' + align + ';' : '') + '" />'
  16538. break;
  16539. case 'embed':
  16540. str = '<embed type="application/x-shockwave-flash" class="' + classname + '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
  16541. ' src="' + utils.html(url) + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"' : '') +
  16542. ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
  16543. break;
  16544. case 'video':
  16545. var ext = url.substr(url.lastIndexOf('.') + 1);
  16546. if (ext == 'ogv') ext = 'ogg';
  16547. str = '<video' + (id ? ' id="' + id + '"' : '') + ' class="' + classname + ' video-js" ' + (align ? ' style="float:' + align + '"' : '') +
  16548. ' controls preload="none" width="' + width + '" height="' + height + '" src="' + ns.img(url) + '" data-setup="{}">' +
  16549. '<source src="' + url + '" type="video/' + ext + '" style="background:url(' + me.options.UEDITOR_HOME_URL + 'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;' + (align ? 'float:' + align + ';' : '') + '" /></video>';
  16550. break;
  16551. }
  16552. // console.log("type:",type,"str:",str);
  16553. return str;
  16554. }
  16555. function switchImgAndVideo(root, img2video) {
  16556. utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'), function (node) {
  16557. var className = node.getAttr('class');
  16558. if (className && className.indexOf('edui-faked-video') != -1) {
  16559. var html = creatInsertStr(img2video ? node.getAttr('_url') : node.getAttr('src'), node.getAttr('width'), node.getAttr('height'), null, node.getStyle('float') || '', className, img2video ? 'embed' : 'image');
  16560. node.parentNode.replaceChild(UE.uNode.createElement(html), node);
  16561. }
  16562. if (className && className.indexOf('edui-upload-video') != -1) {
  16563. var html = creatInsertStr(img2video ? node.getAttr('_url') : node.getAttr('src'), node.getAttr('width'), node.getAttr('height'), null, node.getStyle('float') || '', className, img2video ? 'video' : 'image');
  16564. node.parentNode.replaceChild(UE.uNode.createElement(html), node);
  16565. }
  16566. })
  16567. }
  16568. me.addOutputRule(function (root) {
  16569. switchImgAndVideo(root, true)
  16570. });
  16571. me.addInputRule(function (root) {
  16572. switchImgAndVideo(root)
  16573. });
  16574. /**
  16575. * 插入视频
  16576. * @command insertvideo
  16577. * @method execCommand
  16578. * @param { String } cmd 命令字符串
  16579. * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性
  16580. * @example
  16581. * ```javascript
  16582. *
  16583. * var videoAttr = {
  16584. * //视频地址
  16585. * url: 'http://www.youku.com/xxx',
  16586. * //视频宽高值, 单位px
  16587. * width: 200,
  16588. * height: 100
  16589. * };
  16590. *
  16591. * //editor 是编辑器实例
  16592. * //向编辑器插入单个视频
  16593. * editor.execCommand( 'insertvideo', videoAttr );
  16594. * ```
  16595. */
  16596. /**
  16597. * 插入视频
  16598. * @command insertvideo
  16599. * @method execCommand
  16600. * @param { String } cmd 命令字符串
  16601. * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性
  16602. * @example
  16603. * ```javascript
  16604. *
  16605. * var videoAttr1 = {
  16606. * //视频地址
  16607. * url: 'http://www.youku.com/xxx',
  16608. * //视频宽高值, 单位px
  16609. * width: 200,
  16610. * height: 100
  16611. * },
  16612. * videoAttr2 = {
  16613. * //视频地址
  16614. * url: 'http://www.youku.com/xxx',
  16615. * //视频宽高值, 单位px
  16616. * width: 200,
  16617. * height: 100
  16618. * }
  16619. *
  16620. * //editor 是编辑器实例
  16621. * //该方法将会向编辑器内插入两个视频
  16622. * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] );
  16623. * ```
  16624. */
  16625. /**
  16626. * 查询当前光标所在处是否是一个视频
  16627. * @command insertvideo
  16628. * @method queryCommandState
  16629. * @param { String } cmd 需要查询的命令字符串
  16630. * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0
  16631. * @example
  16632. * ```javascript
  16633. *
  16634. * //editor 是编辑器实例
  16635. * editor.queryCommandState( 'insertvideo' );
  16636. * ```
  16637. */
  16638. me.commands["insertvideo"] = {
  16639. execCommand: function (cmd, videoObjs, type) {
  16640. videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs];
  16641. var html = [], id = 'tmpVedio', cl;
  16642. for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
  16643. vi = videoObjs[i];
  16644. cl = (type == 'upload' ? 'edui-upload-video video-js vjs-default-skin' : 'edui-faked-video');
  16645. html.push(creatInsertStr(vi.url, vi.width || 420, vi.height || 280, id + i, null, cl, 'image'));
  16646. }
  16647. me.execCommand("inserthtml", html.join(""), true);
  16648. var rng = this.selection.getRange();
  16649. for (var i = 0, len = videoObjs.length; i < len; i++) {
  16650. var img = this.document.getElementById('tmpVedio' + i);
  16651. domUtils.removeAttributes(img, 'id');
  16652. rng.selectNode(img).select();
  16653. me.execCommand('imagefloat', videoObjs[i].align)
  16654. }
  16655. },
  16656. queryCommandState: function () {
  16657. var img = me.selection.getRange().getClosedNode(),
  16658. flag = img && (img.className == "edui-faked-video" || img.className.indexOf("edui-upload-video") != -1);
  16659. return flag ? 1 : 0;
  16660. }
  16661. };
  16662. };
  16663. // plugins/table.core.js
  16664. /**
  16665. * Created with JetBrains WebStorm.
  16666. * User: taoqili
  16667. * Date: 13-1-18
  16668. * Time: 上午11:09
  16669. * To change this template use File | Settings | File Templates.
  16670. */
  16671. /**
  16672. * UE表格操作类
  16673. * @param table
  16674. * @constructor
  16675. */
  16676. (function () {
  16677. var UETable = UE.UETable = function (table) {
  16678. this.table = table;
  16679. this.indexTable = [];
  16680. this.selectedTds = [];
  16681. this.cellsRange = {};
  16682. this.update(table);
  16683. };
  16684. //===以下为静态工具方法===
  16685. UETable.removeSelectedClass = function (cells) {
  16686. utils.each(cells, function (cell) {
  16687. domUtils.removeClasses(cell, "selectTdClass");
  16688. })
  16689. };
  16690. UETable.addSelectedClass = function (cells) {
  16691. utils.each(cells, function (cell) {
  16692. domUtils.addClass(cell, "selectTdClass");
  16693. })
  16694. };
  16695. UETable.isEmptyBlock = function (node) {
  16696. var reg = new RegExp(domUtils.fillChar, 'g');
  16697. if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
  16698. return 0;
  16699. }
  16700. for (var i in dtd.$isNotEmpty) if (dtd.$isNotEmpty.hasOwnProperty(i)) {
  16701. if (node.getElementsByTagName(i).length) {
  16702. return 0;
  16703. }
  16704. }
  16705. return 1;
  16706. };
  16707. UETable.getWidth = function (cell) {
  16708. if (!cell) return 0;
  16709. return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
  16710. };
  16711. /**
  16712. * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的
  16713. * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态;
  16714. * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组
  16715. * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null
  16716. */
  16717. UETable.getTableCellAlignState = function (cells) {
  16718. !utils.isArray(cells) && (cells = [cells]);
  16719. var result = {},
  16720. status = ['align', 'valign'],
  16721. tempStatus = null,
  16722. isSame = true;//状态是否相同
  16723. utils.each(cells, function (cellNode) {
  16724. utils.each(status, function (currentState) {
  16725. tempStatus = cellNode.getAttribute(currentState);
  16726. if (!result[currentState] && tempStatus) {
  16727. result[currentState] = tempStatus;
  16728. } else if (!result[currentState] || (tempStatus !== result[currentState])) {
  16729. isSame = false;
  16730. return false;
  16731. }
  16732. });
  16733. return isSame;
  16734. });
  16735. return isSame ? result : null;
  16736. };
  16737. /**
  16738. * 根据当前选区获取相关的table信息
  16739. * @return {Object}
  16740. */
  16741. UETable.getTableItemsByRange = function (editor) {
  16742. var start = editor.selection.getStart();
  16743. //ff下会选中bookmark
  16744. if (start && start.id && start.id.indexOf('_baidu_bookmark_start_') === 0 && start.nextSibling) {
  16745. start = start.nextSibling;
  16746. }
  16747. //在table或者td边缘有可能存在选中tr的情况
  16748. var cell = start && domUtils.findParentByTagName(start, ["td", "th"], true),
  16749. tr = cell && cell.parentNode,
  16750. caption = start && domUtils.findParentByTagName(start, 'caption', true),
  16751. table = caption ? caption.parentNode : tr && tr.parentNode.parentNode;
  16752. return {
  16753. cell: cell,
  16754. tr: tr,
  16755. table: table,
  16756. caption: caption
  16757. }
  16758. };
  16759. UETable.getUETableBySelected = function (editor) {
  16760. var table = UETable.getTableItemsByRange(editor).table;
  16761. if (table && table.ueTable && table.ueTable.selectedTds.length) {
  16762. return table.ueTable;
  16763. }
  16764. return null;
  16765. };
  16766. UETable.getDefaultValue = function (editor, table) {
  16767. var borderMap = {
  16768. thin: '0px',
  16769. medium: '1px',
  16770. thick: '2px'
  16771. },
  16772. tableBorder, tdPadding, tdBorder, tmpValue;
  16773. if (!table) {
  16774. table = editor.document.createElement('table');
  16775. table.insertRow(0).insertCell(0).innerHTML = 'xxx';
  16776. editor.body.appendChild(table);
  16777. var td = table.getElementsByTagName('td')[0];
  16778. tmpValue = domUtils.getComputedStyle(table, 'border-left-width');
  16779. tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16780. tmpValue = domUtils.getComputedStyle(td, 'padding-left');
  16781. tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16782. tmpValue = domUtils.getComputedStyle(td, 'border-left-width');
  16783. tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16784. domUtils.remove(table);
  16785. return {
  16786. tableBorder: tableBorder,
  16787. tdPadding: tdPadding,
  16788. tdBorder: tdBorder
  16789. };
  16790. } else {
  16791. td = table.getElementsByTagName('td')[0];
  16792. tmpValue = domUtils.getComputedStyle(table, 'border-left-width');
  16793. tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16794. tmpValue = domUtils.getComputedStyle(td, 'padding-left');
  16795. tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16796. tmpValue = domUtils.getComputedStyle(td, 'border-left-width');
  16797. tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
  16798. return {
  16799. tableBorder: tableBorder,
  16800. tdPadding: tdPadding,
  16801. tdBorder: tdBorder
  16802. };
  16803. }
  16804. };
  16805. /**
  16806. * 根据当前点击的td或者table获取索引对象
  16807. * @param tdOrTable
  16808. */
  16809. UETable.getUETable = function (tdOrTable) {
  16810. var tag = tdOrTable.tagName.toLowerCase();
  16811. tdOrTable = (tag == "td" || tag == "th" || tag == 'caption') ? domUtils.findParentByTagName(tdOrTable, "table", true) : tdOrTable;
  16812. if (!tdOrTable.ueTable) {
  16813. tdOrTable.ueTable = new UETable(tdOrTable);
  16814. }
  16815. return tdOrTable.ueTable;
  16816. };
  16817. UETable.cloneCell = function (cell, ignoreMerge, keepPro) {
  16818. if (!cell || utils.isString(cell)) {
  16819. return this.table.ownerDocument.createElement(cell || 'td');
  16820. }
  16821. var flag = domUtils.hasClass(cell, "selectTdClass");
  16822. flag && domUtils.removeClasses(cell, "selectTdClass");
  16823. var tmpCell = cell.cloneNode(true);
  16824. if (ignoreMerge) {
  16825. tmpCell.rowSpan = tmpCell.colSpan = 1;
  16826. }
  16827. //去掉宽高
  16828. !keepPro && domUtils.removeAttributes(tmpCell, 'width height');
  16829. !keepPro && domUtils.removeAttributes(tmpCell, 'style');
  16830. tmpCell.style.borderLeftStyle = "";
  16831. tmpCell.style.borderTopStyle = "";
  16832. tmpCell.style.borderLeftColor = cell.style.borderRightColor;
  16833. tmpCell.style.borderLeftWidth = cell.style.borderRightWidth;
  16834. tmpCell.style.borderTopColor = cell.style.borderBottomColor;
  16835. tmpCell.style.borderTopWidth = cell.style.borderBottomWidth;
  16836. flag && domUtils.addClass(cell, "selectTdClass");
  16837. return tmpCell;
  16838. }
  16839. UETable.prototype = {
  16840. getMaxRows: function () {
  16841. var rows = this.table.rows, maxLen = 1;
  16842. for (var i = 0, row; row = rows[i]; i++) {
  16843. var currentMax = 1;
  16844. for (var j = 0, cj; cj = row.cells[j++];) {
  16845. currentMax = Math.max(cj.rowSpan || 1, currentMax);
  16846. }
  16847. maxLen = Math.max(currentMax + i, maxLen);
  16848. }
  16849. return maxLen;
  16850. },
  16851. /**
  16852. * 获取当前表格的最大列数
  16853. */
  16854. getMaxCols: function () {
  16855. var rows = this.table.rows, maxLen = 0, cellRows = {};
  16856. for (var i = 0, row; row = rows[i]; i++) {
  16857. var cellsNum = 0;
  16858. for (var j = 0, cj; cj = row.cells[j++];) {
  16859. cellsNum += (cj.colSpan || 1);
  16860. if (cj.rowSpan && cj.rowSpan > 1) {
  16861. for (var k = 1; k < cj.rowSpan; k++) {
  16862. if (!cellRows['row_' + (i + k)]) {
  16863. cellRows['row_' + (i + k)] = (cj.colSpan || 1);
  16864. } else {
  16865. cellRows['row_' + (i + k)]++
  16866. }
  16867. }
  16868. }
  16869. }
  16870. cellsNum += cellRows['row_' + i] || 0;
  16871. maxLen = Math.max(cellsNum, maxLen);
  16872. }
  16873. return maxLen;
  16874. },
  16875. getCellColIndex: function (cell) {
  16876. },
  16877. /**
  16878. * 获取当前cell旁边的单元格,
  16879. * @param cell
  16880. * @param right
  16881. */
  16882. getHSideCell: function (cell, right) {
  16883. try {
  16884. var cellInfo = this.getCellInfo(cell),
  16885. previewRowIndex, previewColIndex;
  16886. var len = this.selectedTds.length,
  16887. range = this.cellsRange;
  16888. //首行或者首列没有前置单元格
  16889. if ((!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (right && (!len ? (cellInfo.colIndex == (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null;
  16890. previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex;
  16891. previewColIndex = !right ? (!len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1)
  16892. : (!len ? cellInfo.colIndex + 1 : range.endColIndex + 1);
  16893. return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex);
  16894. } catch (e) {
  16895. showError(e);
  16896. }
  16897. },
  16898. getTabNextCell: function (cell, preRowIndex) {
  16899. var cellInfo = this.getCellInfo(cell),
  16900. rowIndex = preRowIndex || cellInfo.rowIndex,
  16901. colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1),
  16902. nextCell;
  16903. try {
  16904. nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex);
  16905. } catch (e) {
  16906. try {
  16907. rowIndex = rowIndex * 1 + 1;
  16908. colIndex = 0;
  16909. nextCell = this.getCell(this.indexTable[rowIndex][colIndex].rowIndex, this.indexTable[rowIndex][colIndex].cellIndex);
  16910. } catch (e) {
  16911. }
  16912. }
  16913. return nextCell;
  16914. },
  16915. /**
  16916. * 获取视觉上的后置单元格
  16917. * @param cell
  16918. * @param bottom
  16919. */
  16920. getVSideCell: function (cell, bottom, ignoreRange) {
  16921. try {
  16922. var cellInfo = this.getCellInfo(cell),
  16923. nextRowIndex, nextColIndex;
  16924. var len = this.selectedTds.length && !ignoreRange,
  16925. range = this.cellsRange;
  16926. //末行或者末列没有后置单元格
  16927. if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null;
  16928. nextRowIndex = !bottom ? (!len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1)
  16929. : (!len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1);
  16930. nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
  16931. return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex);
  16932. } catch (e) {
  16933. showError(e);
  16934. }
  16935. },
  16936. /**
  16937. * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同
  16938. */
  16939. getSameEndPosCells: function (cell, xOrY) {
  16940. try {
  16941. var flag = (xOrY.toLowerCase() === "x"),
  16942. end = domUtils.getXY(cell)[flag ? 'x' : 'y'] + cell["offset" + (flag ? 'Width' : 'Height')],
  16943. rows = this.table.rows,
  16944. cells = null, returns = [];
  16945. for (var i = 0; i < this.rowsNum; i++) {
  16946. cells = rows[i].cells;
  16947. for (var j = 0, tmpCell; tmpCell = cells[j++];) {
  16948. var tmpEnd = domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] + tmpCell["offset" + (flag ? 'Width' : 'Height')];
  16949. //对应行的td已经被上面行rowSpan了
  16950. if (tmpEnd > end && flag) break;
  16951. if (cell == tmpCell || end == tmpEnd) {
  16952. //只获取单一的单元格
  16953. //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能
  16954. if (tmpCell[flag ? "colSpan" : "rowSpan"] == 1) {
  16955. returns.push(tmpCell);
  16956. }
  16957. if (flag) break;
  16958. }
  16959. }
  16960. }
  16961. return returns;
  16962. } catch (e) {
  16963. showError(e);
  16964. }
  16965. },
  16966. setCellContent: function (cell, content) {
  16967. cell.innerHTML = content || (browser.ie ? domUtils.fillChar : "<br />");
  16968. },
  16969. cloneCell: UETable.cloneCell,
  16970. /**
  16971. * 获取跟当前单元格的右边竖线为左边的所有未合并单元格
  16972. */
  16973. getSameStartPosXCells: function (cell) {
  16974. try {
  16975. var start = domUtils.getXY(cell).x + cell.offsetWidth,
  16976. rows = this.table.rows, cells, returns = [];
  16977. for (var i = 0; i < this.rowsNum; i++) {
  16978. cells = rows[i].cells;
  16979. for (var j = 0, tmpCell; tmpCell = cells[j++];) {
  16980. var tmpStart = domUtils.getXY(tmpCell).x;
  16981. if (tmpStart > start) break;
  16982. if (tmpStart == start && tmpCell.colSpan == 1) {
  16983. returns.push(tmpCell);
  16984. break;
  16985. }
  16986. }
  16987. }
  16988. return returns;
  16989. } catch (e) {
  16990. showError(e);
  16991. }
  16992. },
  16993. /**
  16994. * 更新table对应的索引表
  16995. */
  16996. update: function (table) {
  16997. this.table = table || this.table;
  16998. this.selectedTds = [];
  16999. this.cellsRange = {};
  17000. this.indexTable = [];
  17001. var rows = this.table.rows,
  17002. rowsNum = this.getMaxRows(),
  17003. dNum = rowsNum - rows.length,
  17004. colsNum = this.getMaxCols();
  17005. while (dNum--) {
  17006. this.table.insertRow(rows.length);
  17007. }
  17008. this.rowsNum = rowsNum;
  17009. this.colsNum = colsNum;
  17010. for (var i = 0, len = rows.length; i < len; i++) {
  17011. this.indexTable[i] = new Array(colsNum);
  17012. }
  17013. //填充索引表
  17014. for (var rowIndex = 0, row; row = rows[rowIndex]; rowIndex++) {
  17015. for (var cellIndex = 0, cell, cells = row.cells; cell = cells[cellIndex]; cellIndex++) {
  17016. //修正整行被rowSpan时导致的行数计算错误
  17017. if (cell.rowSpan > rowsNum) {
  17018. cell.rowSpan = rowsNum;
  17019. }
  17020. var colIndex = cellIndex,
  17021. rowSpan = cell.rowSpan || 1,
  17022. colSpan = cell.colSpan || 1;
  17023. //当已经被上一行rowSpan或者被前一列colSpan了,则跳到下一个单元格进行
  17024. while (this.indexTable[rowIndex][colIndex]) colIndex++;
  17025. for (var j = 0; j < rowSpan; j++) {
  17026. for (var k = 0; k < colSpan; k++) {
  17027. this.indexTable[rowIndex + j][colIndex + k] = {
  17028. rowIndex: rowIndex,
  17029. cellIndex: cellIndex,
  17030. colIndex: colIndex,
  17031. rowSpan: rowSpan,
  17032. colSpan: colSpan
  17033. }
  17034. }
  17035. }
  17036. }
  17037. }
  17038. //修复残缺td
  17039. for (j = 0; j < rowsNum; j++) {
  17040. for (k = 0; k < colsNum; k++) {
  17041. if (this.indexTable[j][k] === undefined) {
  17042. row = rows[j];
  17043. cell = row.cells[row.cells.length - 1];
  17044. cell = cell ? cell.cloneNode(true) : this.table.ownerDocument.createElement("td");
  17045. this.setCellContent(cell);
  17046. if (cell.colSpan !== 1) cell.colSpan = 1;
  17047. if (cell.rowSpan !== 1) cell.rowSpan = 1;
  17048. row.appendChild(cell);
  17049. this.indexTable[j][k] = {
  17050. rowIndex: j,
  17051. cellIndex: cell.cellIndex,
  17052. colIndex: k,
  17053. rowSpan: 1,
  17054. colSpan: 1
  17055. }
  17056. }
  17057. }
  17058. }
  17059. //当框选后删除行或者列后撤销,需要重建选区。
  17060. var tds = domUtils.getElementsByTagName(this.table, "td"),
  17061. selectTds = [];
  17062. utils.each(tds, function (td) {
  17063. if (domUtils.hasClass(td, "selectTdClass")) {
  17064. selectTds.push(td);
  17065. }
  17066. });
  17067. if (selectTds.length) {
  17068. var start = selectTds[0],
  17069. end = selectTds[selectTds.length - 1],
  17070. startInfo = this.getCellInfo(start),
  17071. endInfo = this.getCellInfo(end);
  17072. this.selectedTds = selectTds;
  17073. this.cellsRange = {
  17074. beginRowIndex: startInfo.rowIndex,
  17075. beginColIndex: startInfo.colIndex,
  17076. endRowIndex: endInfo.rowIndex + endInfo.rowSpan - 1,
  17077. endColIndex: endInfo.colIndex + endInfo.colSpan - 1
  17078. };
  17079. }
  17080. //给第一行设置firstRow的样式名称,在排序图标的样式上使用到
  17081. if (!domUtils.hasClass(this.table.rows[0], "firstRow")) {
  17082. domUtils.addClass(this.table.rows[0], "firstRow");
  17083. for (var i = 1; i < this.table.rows.length; i++) {
  17084. domUtils.removeClasses(this.table.rows[i], "firstRow");
  17085. }
  17086. }
  17087. },
  17088. /**
  17089. * 获取单元格的索引信息
  17090. */
  17091. getCellInfo: function (cell) {
  17092. if (!cell) return;
  17093. var cellIndex = cell.cellIndex,
  17094. rowIndex = cell.parentNode.rowIndex,
  17095. rowInfo = this.indexTable[rowIndex],
  17096. numCols = this.colsNum;
  17097. for (var colIndex = cellIndex; colIndex < numCols; colIndex++) {
  17098. var cellInfo = rowInfo[colIndex];
  17099. if (cellInfo.rowIndex === rowIndex && cellInfo.cellIndex === cellIndex) {
  17100. return cellInfo;
  17101. }
  17102. }
  17103. },
  17104. /**
  17105. * 根据行列号获取单元格
  17106. */
  17107. getCell: function (rowIndex, cellIndex) {
  17108. return rowIndex < this.rowsNum && this.table.rows[rowIndex].cells[cellIndex] || null;
  17109. },
  17110. /**
  17111. * 删除单元格
  17112. */
  17113. deleteCell: function (cell, rowIndex) {
  17114. rowIndex = typeof rowIndex == 'number' ? rowIndex : cell.parentNode.rowIndex;
  17115. var row = this.table.rows[rowIndex];
  17116. row.deleteCell(cell.cellIndex);
  17117. },
  17118. /**
  17119. * 根据始末两个单元格获取被框选的所有单元格范围
  17120. */
  17121. getCellsRange: function (cellA, cellB) {
  17122. function checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex) {
  17123. var tmpBeginRowIndex = beginRowIndex,
  17124. tmpBeginColIndex = beginColIndex,
  17125. tmpEndRowIndex = endRowIndex,
  17126. tmpEndColIndex = endColIndex,
  17127. cellInfo, colIndex, rowIndex;
  17128. // 通过indexTable检查是否存在超出TableRange上边界的情况
  17129. if (beginRowIndex > 0) {
  17130. for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
  17131. cellInfo = me.indexTable[beginRowIndex][colIndex];
  17132. rowIndex = cellInfo.rowIndex;
  17133. if (rowIndex < beginRowIndex) {
  17134. tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex);
  17135. }
  17136. }
  17137. }
  17138. // 通过indexTable检查是否存在超出TableRange右边界的情况
  17139. if (endColIndex < me.colsNum) {
  17140. for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
  17141. cellInfo = me.indexTable[rowIndex][endColIndex];
  17142. colIndex = cellInfo.colIndex + cellInfo.colSpan - 1;
  17143. if (colIndex > endColIndex) {
  17144. tmpEndColIndex = Math.max(colIndex, tmpEndColIndex);
  17145. }
  17146. }
  17147. }
  17148. // 检查是否有超出TableRange下边界的情况
  17149. if (endRowIndex < me.rowsNum) {
  17150. for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
  17151. cellInfo = me.indexTable[endRowIndex][colIndex];
  17152. rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1;
  17153. if (rowIndex > endRowIndex) {
  17154. tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex);
  17155. }
  17156. }
  17157. }
  17158. // 检查是否有超出TableRange左边界的情况
  17159. if (beginColIndex > 0) {
  17160. for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
  17161. cellInfo = me.indexTable[rowIndex][beginColIndex];
  17162. colIndex = cellInfo.colIndex;
  17163. if (colIndex < beginColIndex) {
  17164. tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex);
  17165. }
  17166. }
  17167. }
  17168. //递归调用直至所有完成所有框选单元格的扩展
  17169. if (tmpBeginRowIndex != beginRowIndex || tmpBeginColIndex != beginColIndex || tmpEndRowIndex != endRowIndex || tmpEndColIndex != endColIndex) {
  17170. return checkRange(tmpBeginRowIndex, tmpBeginColIndex, tmpEndRowIndex, tmpEndColIndex);
  17171. } else {
  17172. // 不需要扩展TableRange的情况
  17173. return {
  17174. beginRowIndex: beginRowIndex,
  17175. beginColIndex: beginColIndex,
  17176. endRowIndex: endRowIndex,
  17177. endColIndex: endColIndex
  17178. };
  17179. }
  17180. }
  17181. try {
  17182. var me = this,
  17183. cellAInfo = me.getCellInfo(cellA);
  17184. if (cellA === cellB) {
  17185. return {
  17186. beginRowIndex: cellAInfo.rowIndex,
  17187. beginColIndex: cellAInfo.colIndex,
  17188. endRowIndex: cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
  17189. endColIndex: cellAInfo.colIndex + cellAInfo.colSpan - 1
  17190. };
  17191. }
  17192. var cellBInfo = me.getCellInfo(cellB);
  17193. // 计算TableRange的四个边
  17194. var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex),
  17195. beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex),
  17196. endRowIndex = Math.max(cellAInfo.rowIndex + cellAInfo.rowSpan - 1, cellBInfo.rowIndex + cellBInfo.rowSpan - 1),
  17197. endColIndex = Math.max(cellAInfo.colIndex + cellAInfo.colSpan - 1, cellBInfo.colIndex + cellBInfo.colSpan - 1);
  17198. return checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex);
  17199. } catch (e) {
  17200. //throw e;
  17201. }
  17202. },
  17203. /**
  17204. * 依据cellsRange获取对应的单元格集合
  17205. */
  17206. getCells: function (range) {
  17207. //每次获取cells之前必须先清除上次的选择,否则会对后续获取操作造成影响
  17208. this.clearSelected();
  17209. var beginRowIndex = range.beginRowIndex,
  17210. beginColIndex = range.beginColIndex,
  17211. endRowIndex = range.endRowIndex,
  17212. endColIndex = range.endColIndex,
  17213. cellInfo, rowIndex, colIndex, tdHash = {}, returnTds = [];
  17214. for (var i = beginRowIndex; i <= endRowIndex; i++) {
  17215. for (var j = beginColIndex; j <= endColIndex; j++) {
  17216. cellInfo = this.indexTable[i][j];
  17217. rowIndex = cellInfo.rowIndex;
  17218. colIndex = cellInfo.colIndex;
  17219. // 如果Cells里已经包含了此Cell则跳过
  17220. var key = rowIndex + '|' + colIndex;
  17221. if (tdHash[key]) continue;
  17222. tdHash[key] = 1;
  17223. if (rowIndex < i || colIndex < j || rowIndex + cellInfo.rowSpan - 1 > endRowIndex || colIndex + cellInfo.colSpan - 1 > endColIndex) {
  17224. return null;
  17225. }
  17226. returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex));
  17227. }
  17228. }
  17229. return returnTds;
  17230. },
  17231. /**
  17232. * 清理已经选中的单元格
  17233. */
  17234. clearSelected: function () {
  17235. UETable.removeSelectedClass(this.selectedTds);
  17236. this.selectedTds = [];
  17237. this.cellsRange = {};
  17238. },
  17239. /**
  17240. * 根据range设置已经选中的单元格
  17241. */
  17242. setSelected: function (range) {
  17243. var cells = this.getCells(range);
  17244. UETable.addSelectedClass(cells);
  17245. this.selectedTds = cells;
  17246. this.cellsRange = range;
  17247. },
  17248. isFullRow: function () {
  17249. var range = this.cellsRange;
  17250. return (range.endColIndex - range.beginColIndex + 1) == this.colsNum;
  17251. },
  17252. isFullCol: function () {
  17253. var range = this.cellsRange,
  17254. table = this.table,
  17255. ths = table.getElementsByTagName("th"),
  17256. rows = range.endRowIndex - range.beginRowIndex + 1;
  17257. return !ths.length ? rows == this.rowsNum : rows == this.rowsNum || (rows == this.rowsNum - 1);
  17258. },
  17259. /**
  17260. * 获取视觉上的前置单元格,默认是左边,top传入时
  17261. * @param cell
  17262. * @param top
  17263. */
  17264. getNextCell: function (cell, bottom, ignoreRange) {
  17265. try {
  17266. var cellInfo = this.getCellInfo(cell),
  17267. nextRowIndex, nextColIndex;
  17268. var len = this.selectedTds.length && !ignoreRange,
  17269. range = this.cellsRange;
  17270. //末行或者末列没有后置单元格
  17271. if ((!bottom && (cellInfo.rowIndex == 0)) || (bottom && (!len ? (cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1) : (range.endRowIndex == this.rowsNum - 1)))) return null;
  17272. nextRowIndex = !bottom ? (!len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1)
  17273. : (!len ? (cellInfo.rowIndex + cellInfo.rowSpan) : range.endRowIndex + 1);
  17274. nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
  17275. return this.getCell(this.indexTable[nextRowIndex][nextColIndex].rowIndex, this.indexTable[nextRowIndex][nextColIndex].cellIndex);
  17276. } catch (e) {
  17277. showError(e);
  17278. }
  17279. },
  17280. getPreviewCell: function (cell, top) {
  17281. try {
  17282. var cellInfo = this.getCellInfo(cell),
  17283. previewRowIndex, previewColIndex;
  17284. var len = this.selectedTds.length,
  17285. range = this.cellsRange;
  17286. //首行或者首列没有前置单元格
  17287. if ((!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) || (top && (!len ? (cellInfo.rowIndex > (this.colsNum - 1)) : (range.endColIndex == this.colsNum - 1)))) return null;
  17288. previewRowIndex = !top ? (!len ? cellInfo.rowIndex : range.beginRowIndex)
  17289. : (!len ? (cellInfo.rowIndex < 1 ? 0 : (cellInfo.rowIndex - 1)) : range.beginRowIndex);
  17290. previewColIndex = !top ? (!len ? (cellInfo.colIndex < 1 ? 0 : (cellInfo.colIndex - 1)) : range.beginColIndex - 1)
  17291. : (!len ? cellInfo.colIndex : range.endColIndex + 1);
  17292. return this.getCell(this.indexTable[previewRowIndex][previewColIndex].rowIndex, this.indexTable[previewRowIndex][previewColIndex].cellIndex);
  17293. } catch (e) {
  17294. showError(e);
  17295. }
  17296. },
  17297. /**
  17298. * 移动单元格中的内容
  17299. */
  17300. moveContent: function (cellTo, cellFrom) {
  17301. if (UETable.isEmptyBlock(cellFrom)) return;
  17302. if (UETable.isEmptyBlock(cellTo)) {
  17303. cellTo.innerHTML = cellFrom.innerHTML;
  17304. return;
  17305. }
  17306. var child = cellTo.lastChild;
  17307. if (child.nodeType == 3 || !dtd.$block[child.tagName]) {
  17308. cellTo.appendChild(cellTo.ownerDocument.createElement('br'))
  17309. }
  17310. while (child = cellFrom.firstChild) {
  17311. cellTo.appendChild(child);
  17312. }
  17313. },
  17314. /**
  17315. * 向右合并单元格
  17316. */
  17317. mergeRight: function (cell) {
  17318. var cellInfo = this.getCellInfo(cell),
  17319. rightColIndex = cellInfo.colIndex + cellInfo.colSpan,
  17320. rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex],
  17321. rightCell = this.getCell(rightCellInfo.rowIndex, rightCellInfo.cellIndex);
  17322. //合并
  17323. cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan;
  17324. //被合并的单元格不应存在宽度属性
  17325. cell.removeAttribute("width");
  17326. //移动内容
  17327. this.moveContent(cell, rightCell);
  17328. //删掉被合并的Cell
  17329. this.deleteCell(rightCell, rightCellInfo.rowIndex);
  17330. this.update();
  17331. },
  17332. /**
  17333. * 向下合并单元格
  17334. */
  17335. mergeDown: function (cell) {
  17336. var cellInfo = this.getCellInfo(cell),
  17337. downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan,
  17338. downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex],
  17339. downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex);
  17340. cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan;
  17341. cell.removeAttribute("height");
  17342. this.moveContent(cell, downCell);
  17343. this.deleteCell(downCell, downCellInfo.rowIndex);
  17344. this.update();
  17345. },
  17346. /**
  17347. * 合并整个range中的内容
  17348. */
  17349. mergeRange: function () {
  17350. //由于合并操作可以在任意时刻进行,所以无法通过鼠标位置等信息实时生成range,只能通过缓存实例中的cellsRange对象来访问
  17351. var range = this.cellsRange,
  17352. leftTopCell = this.getCell(range.beginRowIndex, this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex);
  17353. if (leftTopCell.tagName == "TH" && range.endRowIndex !== range.beginRowIndex) {
  17354. var index = this.indexTable,
  17355. info = this.getCellInfo(leftTopCell);
  17356. leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex);
  17357. range = this.getCellsRange(leftTopCell, this.getCell(index[this.rowsNum - 1][info.colIndex].rowIndex, index[this.rowsNum - 1][info.colIndex].cellIndex));
  17358. }
  17359. // 删除剩余的Cells
  17360. var cells = this.getCells(range);
  17361. for (var i = 0, ci; ci = cells[i++];) {
  17362. if (ci !== leftTopCell) {
  17363. this.moveContent(leftTopCell, ci);
  17364. this.deleteCell(ci);
  17365. }
  17366. }
  17367. // 修改左上角Cell的rowSpan和colSpan,并调整宽度属性设置
  17368. leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1;
  17369. leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute("height");
  17370. leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1;
  17371. leftTopCell.colSpan > 1 && leftTopCell.removeAttribute("width");
  17372. if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) {
  17373. leftTopCell.colSpan = 1;
  17374. }
  17375. if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) {
  17376. var rowIndex = leftTopCell.parentNode.rowIndex;
  17377. //解决IE下的表格操作问题
  17378. if (this.table.deleteRow) {
  17379. for (var i = rowIndex + 1, curIndex = rowIndex + 1, len = leftTopCell.rowSpan; i < len; i++) {
  17380. this.table.deleteRow(curIndex);
  17381. }
  17382. } else {
  17383. for (var i = 0, len = leftTopCell.rowSpan - 1; i < len; i++) {
  17384. var row = this.table.rows[rowIndex + 1];
  17385. row.parentNode.removeChild(row);
  17386. }
  17387. }
  17388. leftTopCell.rowSpan = 1;
  17389. }
  17390. this.update();
  17391. },
  17392. /**
  17393. * 插入一行单元格
  17394. */
  17395. insertRow: function (rowIndex, sourceCell) {
  17396. var numCols = this.colsNum,
  17397. table = this.table,
  17398. row = table.insertRow(rowIndex), cell,
  17399. isInsertTitle = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH';
  17400. function replaceTdToTh(colIndex, cell, tableRow) {
  17401. if (colIndex == 0) {
  17402. var tr = tableRow.nextSibling || tableRow.previousSibling,
  17403. th = tr.cells[colIndex];
  17404. if (th.tagName == 'TH') {
  17405. th = cell.ownerDocument.createElement("th");
  17406. th.appendChild(cell.firstChild);
  17407. tableRow.insertBefore(th, cell);
  17408. domUtils.remove(cell)
  17409. }
  17410. } else {
  17411. if (cell.tagName == 'TH') {
  17412. var td = cell.ownerDocument.createElement("td");
  17413. td.appendChild(cell.firstChild);
  17414. tableRow.insertBefore(td, cell);
  17415. domUtils.remove(cell)
  17416. }
  17417. }
  17418. }
  17419. //首行直接插入,无需考虑部分单元格被rowspan的情况
  17420. if (rowIndex == 0 || rowIndex == this.rowsNum) {
  17421. for (var colIndex = 0; colIndex < numCols; colIndex++) {
  17422. cell = this.cloneCell(sourceCell, true);
  17423. this.setCellContent(cell);
  17424. cell.getAttribute('vAlign') && cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17425. row.appendChild(cell);
  17426. if (!isInsertTitle) replaceTdToTh(colIndex, cell, row);
  17427. }
  17428. } else {
  17429. var infoRow = this.indexTable[rowIndex],
  17430. cellIndex = 0;
  17431. for (colIndex = 0; colIndex < numCols; colIndex++) {
  17432. var cellInfo = infoRow[colIndex];
  17433. //如果存在某个单元格的rowspan穿过待插入行的位置,则修改该单元格的rowspan即可,无需插入单元格
  17434. if (cellInfo.rowIndex < rowIndex) {
  17435. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
  17436. cell.rowSpan = cellInfo.rowSpan + 1;
  17437. } else {
  17438. cell = this.cloneCell(sourceCell, true);
  17439. this.setCellContent(cell);
  17440. row.appendChild(cell);
  17441. }
  17442. if (!isInsertTitle) replaceTdToTh(colIndex, cell, row);
  17443. }
  17444. }
  17445. //框选时插入不触发contentchange,需要手动更新索引。
  17446. this.update();
  17447. return row;
  17448. },
  17449. /**
  17450. * 删除一行单元格
  17451. * @param rowIndex
  17452. */
  17453. deleteRow: function (rowIndex) {
  17454. var row = this.table.rows[rowIndex],
  17455. infoRow = this.indexTable[rowIndex],
  17456. colsNum = this.colsNum,
  17457. count = 0; //处理计数
  17458. for (var colIndex = 0; colIndex < colsNum;) {
  17459. var cellInfo = infoRow[colIndex],
  17460. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
  17461. if (cell.rowSpan > 1) {
  17462. if (cellInfo.rowIndex == rowIndex) {
  17463. var clone = cell.cloneNode(true);
  17464. clone.rowSpan = cell.rowSpan - 1;
  17465. clone.innerHTML = "";
  17466. cell.rowSpan = 1;
  17467. var nextRowIndex = rowIndex + 1,
  17468. nextRow = this.table.rows[nextRowIndex],
  17469. insertCellIndex,
  17470. preMerged = this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count;
  17471. if (preMerged < colIndex) {
  17472. insertCellIndex = colIndex - preMerged - 1;
  17473. //nextRow.insertCell(insertCellIndex);
  17474. domUtils.insertAfter(nextRow.cells[insertCellIndex], clone);
  17475. } else {
  17476. if (nextRow.cells.length) nextRow.insertBefore(clone, nextRow.cells[0])
  17477. }
  17478. count += 1;
  17479. //cell.parentNode.removeChild(cell);
  17480. }
  17481. }
  17482. colIndex += cell.colSpan || 1;
  17483. }
  17484. var deleteTds = [], cacheMap = {};
  17485. for (colIndex = 0; colIndex < colsNum; colIndex++) {
  17486. var tmpRowIndex = infoRow[colIndex].rowIndex,
  17487. tmpCellIndex = infoRow[colIndex].cellIndex,
  17488. key = tmpRowIndex + "_" + tmpCellIndex;
  17489. if (cacheMap[key]) continue;
  17490. cacheMap[key] = 1;
  17491. cell = this.getCell(tmpRowIndex, tmpCellIndex);
  17492. deleteTds.push(cell);
  17493. }
  17494. var mergeTds = [];
  17495. utils.each(deleteTds, function (td) {
  17496. if (td.rowSpan == 1) {
  17497. td.parentNode.removeChild(td);
  17498. } else {
  17499. mergeTds.push(td);
  17500. }
  17501. });
  17502. utils.each(mergeTds, function (td) {
  17503. td.rowSpan--;
  17504. });
  17505. row.parentNode.removeChild(row);
  17506. //浏览器方法本身存在bug,采用自定义方法删除
  17507. //this.table.deleteRow(rowIndex);
  17508. this.update();
  17509. },
  17510. insertCol: function (colIndex, sourceCell, defaultValue) {
  17511. var rowsNum = this.rowsNum,
  17512. rowIndex = 0,
  17513. tableRow, cell,
  17514. backWidth = parseInt((this.table.offsetWidth - (this.colsNum + 1) * 20 - (this.colsNum + 1)) / (this.colsNum + 1), 10),
  17515. isInsertTitleCol = typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH';
  17516. function replaceTdToTh(rowIndex, cell, tableRow) {
  17517. if (rowIndex == 0) {
  17518. var th = cell.nextSibling || cell.previousSibling;
  17519. if (th.tagName == 'TH') {
  17520. th = cell.ownerDocument.createElement("th");
  17521. th.appendChild(cell.firstChild);
  17522. tableRow.insertBefore(th, cell);
  17523. domUtils.remove(cell)
  17524. }
  17525. } else {
  17526. if (cell.tagName == 'TH') {
  17527. var td = cell.ownerDocument.createElement("td");
  17528. td.appendChild(cell.firstChild);
  17529. tableRow.insertBefore(td, cell);
  17530. domUtils.remove(cell)
  17531. }
  17532. }
  17533. }
  17534. var preCell;
  17535. if (colIndex == 0 || colIndex == this.colsNum) {
  17536. for (; rowIndex < rowsNum; rowIndex++) {
  17537. tableRow = this.table.rows[rowIndex];
  17538. preCell = tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length];
  17539. cell = this.cloneCell(sourceCell, true); //tableRow.insertCell(colIndex == 0 ? colIndex : tableRow.cells.length);
  17540. this.setCellContent(cell);
  17541. cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17542. preCell && cell.setAttribute('width', preCell.getAttribute('width'));
  17543. if (!colIndex) {
  17544. tableRow.insertBefore(cell, tableRow.cells[0]);
  17545. } else {
  17546. domUtils.insertAfter(tableRow.cells[tableRow.cells.length - 1], cell);
  17547. }
  17548. if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow)
  17549. }
  17550. } else {
  17551. for (; rowIndex < rowsNum; rowIndex++) {
  17552. var cellInfo = this.indexTable[rowIndex][colIndex];
  17553. if (cellInfo.colIndex < colIndex) {
  17554. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
  17555. cell.colSpan = cellInfo.colSpan + 1;
  17556. } else {
  17557. tableRow = this.table.rows[rowIndex];
  17558. preCell = tableRow.cells[cellInfo.cellIndex];
  17559. cell = this.cloneCell(sourceCell, true);//tableRow.insertCell(cellInfo.cellIndex);
  17560. this.setCellContent(cell);
  17561. cell.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17562. preCell && cell.setAttribute('width', preCell.getAttribute('width'));
  17563. //防止IE下报错
  17564. preCell ? tableRow.insertBefore(cell, preCell) : tableRow.appendChild(cell);
  17565. }
  17566. if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow);
  17567. }
  17568. }
  17569. //框选时插入不触发contentchange,需要手动更新索引
  17570. this.update();
  17571. this.updateWidth(backWidth, defaultValue || {tdPadding: 10, tdBorder: 1});
  17572. },
  17573. updateWidth: function (width, defaultValue) {
  17574. var table = this.table,
  17575. tmpWidth = UETable.getWidth(table) - defaultValue.tdPadding * 2 - defaultValue.tdBorder + width;
  17576. if (tmpWidth < table.ownerDocument.body.offsetWidth) {
  17577. table.setAttribute("width", tmpWidth);
  17578. return;
  17579. }
  17580. var tds = domUtils.getElementsByTagName(this.table, "td th");
  17581. utils.each(tds, function (td) {
  17582. td.setAttribute("width", width);
  17583. })
  17584. },
  17585. deleteCol: function (colIndex) {
  17586. var indexTable = this.indexTable,
  17587. tableRows = this.table.rows,
  17588. backTableWidth = this.table.getAttribute("width"),
  17589. backTdWidth = 0,
  17590. rowsNum = this.rowsNum,
  17591. cacheMap = {};
  17592. for (var rowIndex = 0; rowIndex < rowsNum;) {
  17593. var infoRow = indexTable[rowIndex],
  17594. cellInfo = infoRow[colIndex],
  17595. key = cellInfo.rowIndex + '_' + cellInfo.colIndex;
  17596. // 跳过已经处理过的Cell
  17597. if (cacheMap[key]) continue;
  17598. cacheMap[key] = 1;
  17599. var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
  17600. if (!backTdWidth) backTdWidth = cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0);
  17601. // 如果Cell的colSpan大于1, 就修改colSpan, 否则就删掉这个Cell
  17602. if (cell.colSpan > 1) {
  17603. cell.colSpan--;
  17604. } else {
  17605. tableRows[rowIndex].deleteCell(cellInfo.cellIndex);
  17606. }
  17607. rowIndex += cellInfo.rowSpan || 1;
  17608. }
  17609. this.table.setAttribute("width", backTableWidth - backTdWidth);
  17610. this.update();
  17611. },
  17612. splitToCells: function (cell) {
  17613. var me = this,
  17614. cells = this.splitToRows(cell);
  17615. utils.each(cells, function (cell) {
  17616. me.splitToCols(cell);
  17617. })
  17618. },
  17619. splitToRows: function (cell) {
  17620. var cellInfo = this.getCellInfo(cell),
  17621. rowIndex = cellInfo.rowIndex,
  17622. colIndex = cellInfo.colIndex,
  17623. results = [];
  17624. // 修改Cell的rowSpan
  17625. cell.rowSpan = 1;
  17626. results.push(cell);
  17627. // 补齐单元格
  17628. for (var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan; i < endRow; i++) {
  17629. if (i == rowIndex) continue;
  17630. var tableRow = this.table.rows[i],
  17631. tmpCell = tableRow.insertCell(colIndex - this.getPreviewMergedCellsNum(i, colIndex));
  17632. tmpCell.colSpan = cellInfo.colSpan;
  17633. this.setCellContent(tmpCell);
  17634. tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17635. tmpCell.setAttribute('align', cell.getAttribute('align'));
  17636. if (cell.style.cssText) {
  17637. tmpCell.style.cssText = cell.style.cssText;
  17638. }
  17639. results.push(tmpCell);
  17640. }
  17641. this.update();
  17642. return results;
  17643. },
  17644. getPreviewMergedCellsNum: function (rowIndex, colIndex) {
  17645. var indexRow = this.indexTable[rowIndex],
  17646. num = 0;
  17647. for (var i = 0; i < colIndex;) {
  17648. var colSpan = indexRow[i].colSpan,
  17649. tmpRowIndex = indexRow[i].rowIndex;
  17650. num += (colSpan - (tmpRowIndex == rowIndex ? 1 : 0));
  17651. i += colSpan;
  17652. }
  17653. return num;
  17654. },
  17655. splitToCols: function (cell) {
  17656. var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0),
  17657. cellInfo = this.getCellInfo(cell),
  17658. rowIndex = cellInfo.rowIndex,
  17659. colIndex = cellInfo.colIndex,
  17660. results = [];
  17661. // 修改Cell的rowSpan
  17662. cell.colSpan = 1;
  17663. cell.setAttribute("width", backWidth);
  17664. results.push(cell);
  17665. // 补齐单元格
  17666. for (var j = colIndex, endCol = colIndex + cellInfo.colSpan; j < endCol; j++) {
  17667. if (j == colIndex) continue;
  17668. var tableRow = this.table.rows[rowIndex],
  17669. tmpCell = tableRow.insertCell(this.indexTable[rowIndex][j].cellIndex + 1);
  17670. tmpCell.rowSpan = cellInfo.rowSpan;
  17671. this.setCellContent(tmpCell);
  17672. tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17673. tmpCell.setAttribute('align', cell.getAttribute('align'));
  17674. tmpCell.setAttribute('width', backWidth);
  17675. if (cell.style.cssText) {
  17676. tmpCell.style.cssText = cell.style.cssText;
  17677. }
  17678. //处理th的情况
  17679. if (cell.tagName == 'TH') {
  17680. var th = cell.ownerDocument.createElement('th');
  17681. th.appendChild(tmpCell.firstChild);
  17682. th.setAttribute('vAlign', cell.getAttribute('vAlign'));
  17683. th.rowSpan = tmpCell.rowSpan;
  17684. tableRow.insertBefore(th, tmpCell);
  17685. domUtils.remove(tmpCell);
  17686. }
  17687. results.push(tmpCell);
  17688. }
  17689. this.update();
  17690. return results;
  17691. },
  17692. isLastCell: function (cell, rowsNum, colsNum) {
  17693. rowsNum = rowsNum || this.rowsNum;
  17694. colsNum = colsNum || this.colsNum;
  17695. var cellInfo = this.getCellInfo(cell);
  17696. return ((cellInfo.rowIndex + cellInfo.rowSpan) == rowsNum) &&
  17697. ((cellInfo.colIndex + cellInfo.colSpan) == colsNum);
  17698. },
  17699. getLastCell: function (cells) {
  17700. cells = cells || this.table.getElementsByTagName("td");
  17701. var firstInfo = this.getCellInfo(cells[0]);
  17702. var me = this, last = cells[0],
  17703. tr = last.parentNode,
  17704. cellsNum = 0, cols = 0, rows;
  17705. utils.each(cells, function (cell) {
  17706. if (cell.parentNode == tr) cols += cell.colSpan || 1;
  17707. cellsNum += cell.rowSpan * cell.colSpan || 1;
  17708. });
  17709. rows = cellsNum / cols;
  17710. utils.each(cells, function (cell) {
  17711. if (me.isLastCell(cell, rows, cols)) {
  17712. last = cell;
  17713. return false;
  17714. }
  17715. });
  17716. return last;
  17717. },
  17718. selectRow: function (rowIndex) {
  17719. var indexRow = this.indexTable[rowIndex],
  17720. start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex),
  17721. end = this.getCell(indexRow[this.colsNum - 1].rowIndex, indexRow[this.colsNum - 1].cellIndex),
  17722. range = this.getCellsRange(start, end);
  17723. this.setSelected(range);
  17724. },
  17725. selectTable: function () {
  17726. var tds = this.table.getElementsByTagName("td"),
  17727. range = this.getCellsRange(tds[0], tds[tds.length - 1]);
  17728. this.setSelected(range);
  17729. },
  17730. setBackground: function (cells, value) {
  17731. if (typeof value === "string") {
  17732. utils.each(cells, function (cell) {
  17733. cell.style.backgroundColor = value;
  17734. })
  17735. } else if (typeof value === "object") {
  17736. value = utils.extend({
  17737. repeat: true,
  17738. colorList: ["#ddd", "#fff"]
  17739. }, value);
  17740. var rowIndex = this.getCellInfo(cells[0]).rowIndex,
  17741. count = 0,
  17742. colors = value.colorList,
  17743. getColor = function (list, index, repeat) {
  17744. return list[index] ? list[index] : repeat ? list[index % list.length] : "";
  17745. };
  17746. for (var i = 0, cell; cell = cells[i++];) {
  17747. var cellInfo = this.getCellInfo(cell);
  17748. cell.style.backgroundColor = getColor(colors, ((rowIndex + count) == cellInfo.rowIndex) ? count : ++count, value.repeat);
  17749. }
  17750. }
  17751. },
  17752. removeBackground: function (cells) {
  17753. utils.each(cells, function (cell) {
  17754. cell.style.backgroundColor = "";
  17755. })
  17756. }
  17757. };
  17758. function showError(e) {
  17759. }
  17760. })();
  17761. // plugins/table.cmds.js
  17762. /**
  17763. * Created with JetBrains PhpStorm.
  17764. * User: taoqili
  17765. * Date: 13-2-20
  17766. * Time: 下午6:25
  17767. * To change this template use File | Settings | File Templates.
  17768. */
  17769. ;
  17770. (function () {
  17771. var UT = UE.UETable,
  17772. getTableItemsByRange = function (editor) {
  17773. return UT.getTableItemsByRange(editor);
  17774. },
  17775. getUETableBySelected = function (editor) {
  17776. return UT.getUETableBySelected(editor)
  17777. },
  17778. getDefaultValue = function (editor, table) {
  17779. return UT.getDefaultValue(editor, table);
  17780. },
  17781. getUETable = function (tdOrTable) {
  17782. return UT.getUETable(tdOrTable);
  17783. };
  17784. UE.commands['inserttable'] = {
  17785. queryCommandState: function () {
  17786. return getTableItemsByRange(this).table ? -1 : 0;
  17787. },
  17788. execCommand: function (cmd, opt) {
  17789. function createTable(opt, tdWidth) {
  17790. var html = [],
  17791. rowsNum = opt.numRows,
  17792. colsNum = opt.numCols;
  17793. for (var r = 0; r < rowsNum; r++) {
  17794. html.push('<tr' + (r == 0 ? ' class="firstRow"' : '') + '>');
  17795. for (var c = 0; c < colsNum; c++) {
  17796. html.push('<td width="' + tdWidth + '" vAlign="' + opt.tdvalign + '" >' + (browser.ie && browser.version < 11 ? domUtils.fillChar : '<br/>') + '</td>')
  17797. }
  17798. html.push('</tr>')
  17799. }
  17800. //禁止指定table-width
  17801. return '<table><tbody>' + html.join('') + '</tbody></table>'
  17802. }
  17803. if (!opt) {
  17804. opt = utils.extend({}, {
  17805. numCols: this.options.defaultCols,
  17806. numRows: this.options.defaultRows,
  17807. tdvalign: this.options.tdvalign
  17808. })
  17809. }
  17810. var me = this;
  17811. var range = this.selection.getRange(),
  17812. start = range.startContainer,
  17813. firstParentBlock = domUtils.findParent(start, function (node) {
  17814. return domUtils.isBlockElm(node);
  17815. }, true) || me.body;
  17816. var defaultValue = getDefaultValue(me),
  17817. tableWidth = firstParentBlock.offsetWidth,
  17818. tdWidth = Math.floor(tableWidth / opt.numCols - defaultValue.tdPadding * 2 - defaultValue.tdBorder);
  17819. //todo其他属性
  17820. !opt.tdvalign && (opt.tdvalign = me.options.tdvalign);
  17821. me.execCommand("inserthtml", createTable(opt, tdWidth));
  17822. }
  17823. };
  17824. UE.commands['insertparagraphbeforetable'] = {
  17825. queryCommandState: function () {
  17826. return getTableItemsByRange(this).cell ? 0 : -1;
  17827. },
  17828. execCommand: function () {
  17829. var table = getTableItemsByRange(this).table;
  17830. if (table) {
  17831. var p = this.document.createElement("p");
  17832. p.innerHTML = browser.ie ? '&nbsp;' : '<br />';
  17833. table.parentNode.insertBefore(p, table);
  17834. this.selection.getRange().setStart(p, 0).setCursor();
  17835. }
  17836. }
  17837. };
  17838. UE.commands['deletetable'] = {
  17839. queryCommandState: function () {
  17840. var rng = this.selection.getRange();
  17841. return domUtils.findParentByTagName(rng.startContainer, 'table', true) ? 0 : -1;
  17842. },
  17843. execCommand: function (cmd, table) {
  17844. var rng = this.selection.getRange();
  17845. table = table || domUtils.findParentByTagName(rng.startContainer, 'table', true);
  17846. if (table) {
  17847. var next = table.nextSibling;
  17848. if (!next) {
  17849. next = domUtils.createElement(this.document, 'p', {
  17850. 'innerHTML': browser.ie ? domUtils.fillChar : '<br/>'
  17851. });
  17852. table.parentNode.insertBefore(next, table);
  17853. }
  17854. domUtils.remove(table);
  17855. rng = this.selection.getRange();
  17856. if (next.nodeType == 3) {
  17857. rng.setStartBefore(next)
  17858. } else {
  17859. rng.setStart(next, 0)
  17860. }
  17861. rng.setCursor(false, true)
  17862. this.fireEvent("tablehasdeleted")
  17863. }
  17864. }
  17865. };
  17866. UE.commands['cellalign'] = {
  17867. queryCommandState: function () {
  17868. return getSelectedArr(this).length ? 0 : -1
  17869. },
  17870. execCommand: function (cmd, align) {
  17871. var selectedTds = getSelectedArr(this);
  17872. if (selectedTds.length) {
  17873. for (var i = 0, ci; ci = selectedTds[i++];) {
  17874. ci.setAttribute('align', align);
  17875. }
  17876. }
  17877. }
  17878. };
  17879. UE.commands['cellvalign'] = {
  17880. queryCommandState: function () {
  17881. return getSelectedArr(this).length ? 0 : -1;
  17882. },
  17883. execCommand: function (cmd, valign) {
  17884. var selectedTds = getSelectedArr(this);
  17885. if (selectedTds.length) {
  17886. for (var i = 0, ci; ci = selectedTds[i++];) {
  17887. ci.setAttribute('vAlign', valign);
  17888. }
  17889. }
  17890. }
  17891. };
  17892. UE.commands['insertcaption'] = {
  17893. queryCommandState: function () {
  17894. var table = getTableItemsByRange(this).table;
  17895. if (table) {
  17896. return table.getElementsByTagName('caption').length == 0 ? 1 : -1;
  17897. }
  17898. return -1;
  17899. },
  17900. execCommand: function () {
  17901. var table = getTableItemsByRange(this).table;
  17902. if (table) {
  17903. var caption = this.document.createElement('caption');
  17904. caption.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  17905. table.insertBefore(caption, table.firstChild);
  17906. var range = this.selection.getRange();
  17907. range.setStart(caption, 0).setCursor();
  17908. }
  17909. }
  17910. };
  17911. UE.commands['deletecaption'] = {
  17912. queryCommandState: function () {
  17913. var rng = this.selection.getRange(),
  17914. table = domUtils.findParentByTagName(rng.startContainer, 'table');
  17915. if (table) {
  17916. return table.getElementsByTagName('caption').length == 0 ? -1 : 1;
  17917. }
  17918. return -1;
  17919. },
  17920. execCommand: function () {
  17921. var rng = this.selection.getRange(),
  17922. table = domUtils.findParentByTagName(rng.startContainer, 'table');
  17923. if (table) {
  17924. domUtils.remove(table.getElementsByTagName('caption')[0]);
  17925. var range = this.selection.getRange();
  17926. range.setStart(table.rows[0].cells[0], 0).setCursor();
  17927. }
  17928. }
  17929. };
  17930. UE.commands['inserttitle'] = {
  17931. queryCommandState: function () {
  17932. var table = getTableItemsByRange(this).table;
  17933. if (table) {
  17934. var firstRow = table.rows[0];
  17935. return firstRow.cells[firstRow.cells.length - 1].tagName.toLowerCase() != 'th' ? 0 : -1
  17936. }
  17937. return -1;
  17938. },
  17939. execCommand: function () {
  17940. var table = getTableItemsByRange(this).table;
  17941. if (table) {
  17942. getUETable(table).insertRow(0, 'th');
  17943. }
  17944. var th = table.getElementsByTagName('th')[0];
  17945. this.selection.getRange().setStart(th, 0).setCursor(false, true);
  17946. }
  17947. };
  17948. UE.commands['deletetitle'] = {
  17949. queryCommandState: function () {
  17950. var table = getTableItemsByRange(this).table;
  17951. if (table) {
  17952. var firstRow = table.rows[0];
  17953. return firstRow.cells[firstRow.cells.length - 1].tagName.toLowerCase() == 'th' ? 0 : -1
  17954. }
  17955. return -1;
  17956. },
  17957. execCommand: function () {
  17958. var table = getTableItemsByRange(this).table;
  17959. if (table) {
  17960. domUtils.remove(table.rows[0])
  17961. }
  17962. var td = table.getElementsByTagName('td')[0];
  17963. this.selection.getRange().setStart(td, 0).setCursor(false, true);
  17964. }
  17965. };
  17966. UE.commands['inserttitlecol'] = {
  17967. queryCommandState: function () {
  17968. var table = getTableItemsByRange(this).table;
  17969. if (table) {
  17970. var lastRow = table.rows[table.rows.length - 1];
  17971. return lastRow.getElementsByTagName('th').length ? -1 : 0;
  17972. }
  17973. return -1;
  17974. },
  17975. execCommand: function (cmd) {
  17976. var table = getTableItemsByRange(this).table;
  17977. if (table) {
  17978. getUETable(table).insertCol(0, 'th');
  17979. }
  17980. resetTdWidth(table, this);
  17981. var th = table.getElementsByTagName('th')[0];
  17982. this.selection.getRange().setStart(th, 0).setCursor(false, true);
  17983. }
  17984. };
  17985. UE.commands['deletetitlecol'] = {
  17986. queryCommandState: function () {
  17987. var table = getTableItemsByRange(this).table;
  17988. if (table) {
  17989. var lastRow = table.rows[table.rows.length - 1];
  17990. return lastRow.getElementsByTagName('th').length ? 0 : -1;
  17991. }
  17992. return -1;
  17993. },
  17994. execCommand: function () {
  17995. var table = getTableItemsByRange(this).table;
  17996. if (table) {
  17997. for (var i = 0; i < table.rows.length; i++) {
  17998. domUtils.remove(table.rows[i].children[0])
  17999. }
  18000. }
  18001. resetTdWidth(table, this);
  18002. var td = table.getElementsByTagName('td')[0];
  18003. this.selection.getRange().setStart(td, 0).setCursor(false, true);
  18004. }
  18005. };
  18006. UE.commands["mergeright"] = {
  18007. queryCommandState: function (cmd) {
  18008. var tableItems = getTableItemsByRange(this),
  18009. table = tableItems.table,
  18010. cell = tableItems.cell;
  18011. if (!table || !cell) return -1;
  18012. var ut = getUETable(table);
  18013. if (ut.selectedTds.length) return -1;
  18014. var cellInfo = ut.getCellInfo(cell),
  18015. rightColIndex = cellInfo.colIndex + cellInfo.colSpan;
  18016. if (rightColIndex >= ut.colsNum) return -1; // 如果处于最右边则不能向右合并
  18017. var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex],
  18018. rightCell = table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex];
  18019. if (!rightCell || cell.tagName != rightCell.tagName) return -1; // TH和TD不能相互合并
  18020. // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
  18021. return (rightCellInfo.rowIndex == cellInfo.rowIndex && rightCellInfo.rowSpan == cellInfo.rowSpan) ? 0 : -1;
  18022. },
  18023. execCommand: function (cmd) {
  18024. var rng = this.selection.getRange(),
  18025. bk = rng.createBookmark(true);
  18026. var cell = getTableItemsByRange(this).cell,
  18027. ut = getUETable(cell);
  18028. ut.mergeRight(cell);
  18029. rng.moveToBookmark(bk).select();
  18030. }
  18031. };
  18032. UE.commands["mergedown"] = {
  18033. queryCommandState: function (cmd) {
  18034. var tableItems = getTableItemsByRange(this),
  18035. table = tableItems.table,
  18036. cell = tableItems.cell;
  18037. if (!table || !cell) return -1;
  18038. var ut = getUETable(table);
  18039. if (ut.selectedTds.length) return -1;
  18040. var cellInfo = ut.getCellInfo(cell),
  18041. downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan;
  18042. if (downRowIndex >= ut.rowsNum) return -1; // 如果处于最下边则不能向下合并
  18043. var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex],
  18044. downCell = table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex];
  18045. if (!downCell || cell.tagName != downCell.tagName) return -1; // TH和TD不能相互合并
  18046. // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
  18047. return (downCellInfo.colIndex == cellInfo.colIndex && downCellInfo.colSpan == cellInfo.colSpan) ? 0 : -1;
  18048. },
  18049. execCommand: function () {
  18050. var rng = this.selection.getRange(),
  18051. bk = rng.createBookmark(true);
  18052. var cell = getTableItemsByRange(this).cell,
  18053. ut = getUETable(cell);
  18054. ut.mergeDown(cell);
  18055. rng.moveToBookmark(bk).select();
  18056. }
  18057. };
  18058. UE.commands["mergecells"] = {
  18059. queryCommandState: function () {
  18060. return getUETableBySelected(this) ? 0 : -1;
  18061. },
  18062. execCommand: function () {
  18063. var ut = getUETableBySelected(this);
  18064. if (ut && ut.selectedTds.length) {
  18065. var cell = ut.selectedTds[0];
  18066. ut.mergeRange();
  18067. var rng = this.selection.getRange();
  18068. if (domUtils.isEmptyBlock(cell)) {
  18069. rng.setStart(cell, 0).collapse(true)
  18070. } else {
  18071. rng.selectNodeContents(cell)
  18072. }
  18073. rng.select();
  18074. }
  18075. }
  18076. };
  18077. UE.commands["insertrow"] = {
  18078. queryCommandState: function () {
  18079. var tableItems = getTableItemsByRange(this),
  18080. cell = tableItems.cell;
  18081. return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && tableItems.tr !== tableItems.table.rows[0])) &&
  18082. getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1;
  18083. },
  18084. execCommand: function () {
  18085. var rng = this.selection.getRange(),
  18086. bk = rng.createBookmark(true);
  18087. var tableItems = getTableItemsByRange(this),
  18088. cell = tableItems.cell,
  18089. table = tableItems.table,
  18090. ut = getUETable(table),
  18091. cellInfo = ut.getCellInfo(cell);
  18092. //ut.insertRow(!ut.selectedTds.length ? cellInfo.rowIndex:ut.cellsRange.beginRowIndex,'');
  18093. if (!ut.selectedTds.length) {
  18094. ut.insertRow(cellInfo.rowIndex, cell);
  18095. } else {
  18096. var range = ut.cellsRange;
  18097. for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
  18098. ut.insertRow(range.beginRowIndex, cell);
  18099. }
  18100. }
  18101. rng.moveToBookmark(bk).select();
  18102. if (table.getAttribute("interlaced") === "enabled") this.fireEvent("interlacetable", table);
  18103. }
  18104. };
  18105. //后插入行
  18106. UE.commands["insertrownext"] = {
  18107. queryCommandState: function () {
  18108. var tableItems = getTableItemsByRange(this),
  18109. cell = tableItems.cell;
  18110. return cell && (cell.tagName == "TD") && getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1;
  18111. },
  18112. execCommand: function () {
  18113. var rng = this.selection.getRange(),
  18114. bk = rng.createBookmark(true);
  18115. var tableItems = getTableItemsByRange(this),
  18116. cell = tableItems.cell,
  18117. table = tableItems.table,
  18118. ut = getUETable(table),
  18119. cellInfo = ut.getCellInfo(cell);
  18120. //ut.insertRow(!ut.selectedTds.length? cellInfo.rowIndex + cellInfo.rowSpan : ut.cellsRange.endRowIndex + 1,'');
  18121. if (!ut.selectedTds.length) {
  18122. ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell);
  18123. } else {
  18124. var range = ut.cellsRange;
  18125. for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
  18126. ut.insertRow(range.endRowIndex + 1, cell);
  18127. }
  18128. }
  18129. rng.moveToBookmark(bk).select();
  18130. if (table.getAttribute("interlaced") === "enabled") this.fireEvent("interlacetable", table);
  18131. }
  18132. };
  18133. UE.commands["deleterow"] = {
  18134. queryCommandState: function () {
  18135. var tableItems = getTableItemsByRange(this);
  18136. return tableItems.cell ? 0 : -1;
  18137. },
  18138. execCommand: function () {
  18139. var cell = getTableItemsByRange(this).cell,
  18140. ut = getUETable(cell),
  18141. cellsRange = ut.cellsRange,
  18142. cellInfo = ut.getCellInfo(cell),
  18143. preCell = ut.getVSideCell(cell),
  18144. nextCell = ut.getVSideCell(cell, true),
  18145. rng = this.selection.getRange();
  18146. if (utils.isEmptyObject(cellsRange)) {
  18147. ut.deleteRow(cellInfo.rowIndex);
  18148. } else {
  18149. for (var i = cellsRange.beginRowIndex; i < cellsRange.endRowIndex + 1; i++) {
  18150. ut.deleteRow(cellsRange.beginRowIndex);
  18151. }
  18152. }
  18153. var table = ut.table;
  18154. if (!table.getElementsByTagName('td').length) {
  18155. var nextSibling = table.nextSibling;
  18156. domUtils.remove(table);
  18157. if (nextSibling) {
  18158. rng.setStart(nextSibling, 0).setCursor(false, true);
  18159. }
  18160. } else {
  18161. if (cellInfo.rowSpan == 1 || cellInfo.rowSpan == cellsRange.endRowIndex - cellsRange.beginRowIndex + 1) {
  18162. if (nextCell || preCell) rng.selectNodeContents(nextCell || preCell).setCursor(false, true);
  18163. } else {
  18164. var newCell = ut.getCell(cellInfo.rowIndex, ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex);
  18165. if (newCell) rng.selectNodeContents(newCell).setCursor(false, true);
  18166. }
  18167. }
  18168. if (table.getAttribute("interlaced") === "enabled") this.fireEvent("interlacetable", table);
  18169. }
  18170. };
  18171. UE.commands["insertcol"] = {
  18172. queryCommandState: function (cmd) {
  18173. var tableItems = getTableItemsByRange(this),
  18174. cell = tableItems.cell;
  18175. return cell && (cell.tagName == "TD" || (cell.tagName == 'TH' && cell !== tableItems.tr.cells[0])) &&
  18176. getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1;
  18177. },
  18178. execCommand: function (cmd) {
  18179. var rng = this.selection.getRange(),
  18180. bk = rng.createBookmark(true);
  18181. if (this.queryCommandState(cmd) == -1) return;
  18182. var cell = getTableItemsByRange(this).cell,
  18183. ut = getUETable(cell),
  18184. cellInfo = ut.getCellInfo(cell);
  18185. //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex:ut.cellsRange.beginColIndex);
  18186. if (!ut.selectedTds.length) {
  18187. ut.insertCol(cellInfo.colIndex, cell);
  18188. } else {
  18189. var range = ut.cellsRange;
  18190. for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
  18191. ut.insertCol(range.beginColIndex, cell);
  18192. }
  18193. }
  18194. rng.moveToBookmark(bk).select(true);
  18195. }
  18196. };
  18197. UE.commands["insertcolnext"] = {
  18198. queryCommandState: function () {
  18199. var tableItems = getTableItemsByRange(this),
  18200. cell = tableItems.cell;
  18201. return cell && getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1;
  18202. },
  18203. execCommand: function () {
  18204. var rng = this.selection.getRange(),
  18205. bk = rng.createBookmark(true);
  18206. var cell = getTableItemsByRange(this).cell,
  18207. ut = getUETable(cell),
  18208. cellInfo = ut.getCellInfo(cell);
  18209. //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex + cellInfo.colSpan:ut.cellsRange.endColIndex +1);
  18210. if (!ut.selectedTds.length) {
  18211. ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell);
  18212. } else {
  18213. var range = ut.cellsRange;
  18214. for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
  18215. ut.insertCol(range.endColIndex + 1, cell);
  18216. }
  18217. }
  18218. rng.moveToBookmark(bk).select();
  18219. }
  18220. };
  18221. UE.commands["deletecol"] = {
  18222. queryCommandState: function () {
  18223. var tableItems = getTableItemsByRange(this);
  18224. return tableItems.cell ? 0 : -1;
  18225. },
  18226. execCommand: function () {
  18227. var cell = getTableItemsByRange(this).cell,
  18228. ut = getUETable(cell),
  18229. range = ut.cellsRange,
  18230. cellInfo = ut.getCellInfo(cell),
  18231. preCell = ut.getHSideCell(cell),
  18232. nextCell = ut.getHSideCell(cell, true);
  18233. if (utils.isEmptyObject(range)) {
  18234. ut.deleteCol(cellInfo.colIndex);
  18235. } else {
  18236. for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) {
  18237. ut.deleteCol(range.beginColIndex);
  18238. }
  18239. }
  18240. var table = ut.table,
  18241. rng = this.selection.getRange();
  18242. if (!table.getElementsByTagName('td').length) {
  18243. var nextSibling = table.nextSibling;
  18244. domUtils.remove(table);
  18245. if (nextSibling) {
  18246. rng.setStart(nextSibling, 0).setCursor(false, true);
  18247. }
  18248. } else {
  18249. if (domUtils.inDoc(cell, this.document)) {
  18250. rng.setStart(cell, 0).setCursor(false, true);
  18251. } else {
  18252. if (nextCell && domUtils.inDoc(nextCell, this.document)) {
  18253. rng.selectNodeContents(nextCell).setCursor(false, true);
  18254. } else {
  18255. if (preCell && domUtils.inDoc(preCell, this.document)) {
  18256. rng.selectNodeContents(preCell).setCursor(true, true);
  18257. }
  18258. }
  18259. }
  18260. }
  18261. }
  18262. };
  18263. UE.commands["splittocells"] = {
  18264. queryCommandState: function () {
  18265. var tableItems = getTableItemsByRange(this),
  18266. cell = tableItems.cell;
  18267. if (!cell) return -1;
  18268. var ut = getUETable(tableItems.table);
  18269. if (ut.selectedTds.length > 0) return -1;
  18270. return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1;
  18271. },
  18272. execCommand: function () {
  18273. var rng = this.selection.getRange(),
  18274. bk = rng.createBookmark(true);
  18275. var cell = getTableItemsByRange(this).cell,
  18276. ut = getUETable(cell);
  18277. ut.splitToCells(cell);
  18278. rng.moveToBookmark(bk).select();
  18279. }
  18280. };
  18281. UE.commands["splittorows"] = {
  18282. queryCommandState: function () {
  18283. var tableItems = getTableItemsByRange(this),
  18284. cell = tableItems.cell;
  18285. if (!cell) return -1;
  18286. var ut = getUETable(tableItems.table);
  18287. if (ut.selectedTds.length > 0) return -1;
  18288. return cell && cell.rowSpan > 1 ? 0 : -1;
  18289. },
  18290. execCommand: function () {
  18291. var rng = this.selection.getRange(),
  18292. bk = rng.createBookmark(true);
  18293. var cell = getTableItemsByRange(this).cell,
  18294. ut = getUETable(cell);
  18295. ut.splitToRows(cell);
  18296. rng.moveToBookmark(bk).select();
  18297. }
  18298. };
  18299. UE.commands["splittocols"] = {
  18300. queryCommandState: function () {
  18301. var tableItems = getTableItemsByRange(this),
  18302. cell = tableItems.cell;
  18303. if (!cell) return -1;
  18304. var ut = getUETable(tableItems.table);
  18305. if (ut.selectedTds.length > 0) return -1;
  18306. return cell && cell.colSpan > 1 ? 0 : -1;
  18307. },
  18308. execCommand: function () {
  18309. var rng = this.selection.getRange(),
  18310. bk = rng.createBookmark(true);
  18311. var cell = getTableItemsByRange(this).cell,
  18312. ut = getUETable(cell);
  18313. ut.splitToCols(cell);
  18314. rng.moveToBookmark(bk).select();
  18315. }
  18316. };
  18317. UE.commands["adaptbytext"] =
  18318. UE.commands["adaptbywindow"] = {
  18319. queryCommandState: function () {
  18320. return getTableItemsByRange(this).table ? 0 : -1
  18321. },
  18322. execCommand: function (cmd) {
  18323. var tableItems = getTableItemsByRange(this),
  18324. table = tableItems.table;
  18325. if (table) {
  18326. if (cmd == 'adaptbywindow') {
  18327. resetTdWidth(table, this);
  18328. } else {
  18329. var cells = domUtils.getElementsByTagName(table, "td th");
  18330. utils.each(cells, function (cell) {
  18331. cell.removeAttribute("width");
  18332. });
  18333. table.removeAttribute("width");
  18334. }
  18335. }
  18336. }
  18337. };
  18338. //平均分配各列
  18339. UE.commands['averagedistributecol'] = {
  18340. queryCommandState: function () {
  18341. var ut = getUETableBySelected(this);
  18342. if (!ut) return -1;
  18343. return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
  18344. },
  18345. execCommand: function (cmd) {
  18346. var me = this,
  18347. ut = getUETableBySelected(me);
  18348. function getAverageWidth() {
  18349. var tb = ut.table,
  18350. averageWidth, sumWidth = 0, colsNum = 0,
  18351. tbAttr = getDefaultValue(me, tb);
  18352. if (ut.isFullRow()) {
  18353. sumWidth = tb.offsetWidth;
  18354. colsNum = ut.colsNum;
  18355. } else {
  18356. var begin = ut.cellsRange.beginColIndex,
  18357. end = ut.cellsRange.endColIndex,
  18358. node;
  18359. for (var i = begin; i <= end;) {
  18360. node = ut.selectedTds[i];
  18361. sumWidth += node.offsetWidth;
  18362. i += node.colSpan;
  18363. colsNum += 1;
  18364. }
  18365. }
  18366. averageWidth = Math.ceil(sumWidth / colsNum) - tbAttr.tdBorder * 2 - tbAttr.tdPadding * 2;
  18367. return averageWidth;
  18368. }
  18369. function setAverageWidth(averageWidth) {
  18370. utils.each(domUtils.getElementsByTagName(ut.table, "th"), function (node) {
  18371. node.setAttribute("width", "");
  18372. });
  18373. var cells = ut.isFullRow() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds;
  18374. utils.each(cells, function (node) {
  18375. if (node.colSpan == 1) {
  18376. node.setAttribute("width", averageWidth);
  18377. }
  18378. });
  18379. }
  18380. if (ut && ut.selectedTds.length) {
  18381. setAverageWidth(getAverageWidth());
  18382. }
  18383. }
  18384. };
  18385. //平均分配各行
  18386. UE.commands['averagedistributerow'] = {
  18387. queryCommandState: function () {
  18388. var ut = getUETableBySelected(this);
  18389. if (!ut) return -1;
  18390. if (ut.selectedTds && /th/ig.test(ut.selectedTds[0].tagName)) return -1;
  18391. return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
  18392. },
  18393. execCommand: function (cmd) {
  18394. var me = this,
  18395. ut = getUETableBySelected(me);
  18396. function getAverageHeight() {
  18397. var averageHeight, rowNum, sumHeight = 0,
  18398. tb = ut.table,
  18399. tbAttr = getDefaultValue(me, tb),
  18400. tdpadding = parseInt(domUtils.getComputedStyle(tb.getElementsByTagName('td')[0], "padding-top"));
  18401. if (ut.isFullCol()) {
  18402. var captionArr = domUtils.getElementsByTagName(tb, "caption"),
  18403. thArr = domUtils.getElementsByTagName(tb, "th"),
  18404. captionHeight, thHeight;
  18405. if (captionArr.length > 0) {
  18406. captionHeight = captionArr[0].offsetHeight;
  18407. }
  18408. if (thArr.length > 0) {
  18409. thHeight = thArr[0].offsetHeight;
  18410. }
  18411. sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0);
  18412. rowNum = thArr.length == 0 ? ut.rowsNum : (ut.rowsNum - 1);
  18413. } else {
  18414. var begin = ut.cellsRange.beginRowIndex,
  18415. end = ut.cellsRange.endRowIndex,
  18416. count = 0,
  18417. trs = domUtils.getElementsByTagName(tb, "tr");
  18418. for (var i = begin; i <= end; i++) {
  18419. sumHeight += trs[i].offsetHeight;
  18420. count += 1;
  18421. }
  18422. rowNum = count;
  18423. }
  18424. //ie8下是混杂模式
  18425. if (browser.ie && browser.version < 9) {
  18426. averageHeight = Math.ceil(sumHeight / rowNum);
  18427. } else {
  18428. averageHeight = Math.ceil(sumHeight / rowNum) - tbAttr.tdBorder * 2 - tdpadding * 2;
  18429. }
  18430. return averageHeight;
  18431. }
  18432. function setAverageHeight(averageHeight) {
  18433. var cells = ut.isFullCol() ? domUtils.getElementsByTagName(ut.table, "td") : ut.selectedTds;
  18434. utils.each(cells, function (node) {
  18435. if (node.rowSpan == 1) {
  18436. node.setAttribute("height", averageHeight);
  18437. }
  18438. });
  18439. }
  18440. if (ut && ut.selectedTds.length) {
  18441. setAverageHeight(getAverageHeight());
  18442. }
  18443. }
  18444. };
  18445. //单元格对齐方式
  18446. UE.commands['cellalignment'] = {
  18447. queryCommandState: function () {
  18448. return getTableItemsByRange(this).table ? 0 : -1
  18449. },
  18450. execCommand: function (cmd, data) {
  18451. var me = this,
  18452. ut = getUETableBySelected(me);
  18453. if (!ut) {
  18454. var start = me.selection.getStart(),
  18455. cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
  18456. if (!/caption/ig.test(cell.tagName)) {
  18457. domUtils.setAttributes(cell, data);
  18458. } else {
  18459. cell.style.textAlign = data.align;
  18460. cell.style.verticalAlign = data.vAlign;
  18461. }
  18462. me.selection.getRange().setCursor(true);
  18463. } else {
  18464. utils.each(ut.selectedTds, function (cell) {
  18465. domUtils.setAttributes(cell, data);
  18466. });
  18467. }
  18468. },
  18469. /**
  18470. * 查询当前点击的单元格的对齐状态, 如果当前已经选择了多个单元格, 则会返回所有单元格经过统一协调过后的状态
  18471. * @see UE.UETable.getTableCellAlignState
  18472. */
  18473. queryCommandValue: function (cmd) {
  18474. var activeMenuCell = getTableItemsByRange(this).cell;
  18475. if (!activeMenuCell) {
  18476. activeMenuCell = getSelectedArr(this)[0];
  18477. }
  18478. if (!activeMenuCell) {
  18479. return null;
  18480. } else {
  18481. //获取同时选中的其他单元格
  18482. var cells = UE.UETable.getUETable(activeMenuCell).selectedTds;
  18483. !cells.length && (cells = activeMenuCell);
  18484. return UE.UETable.getTableCellAlignState(cells);
  18485. }
  18486. }
  18487. };
  18488. //表格对齐方式
  18489. UE.commands['tablealignment'] = {
  18490. queryCommandState: function () {
  18491. if (browser.ie && browser.version < 8) {
  18492. return -1;
  18493. }
  18494. return getTableItemsByRange(this).table ? 0 : -1
  18495. },
  18496. execCommand: function (cmd, value) {
  18497. var me = this,
  18498. start = me.selection.getStart(),
  18499. table = start && domUtils.findParentByTagName(start, ["table"], true);
  18500. if (table) {
  18501. table.setAttribute("align", value);
  18502. }
  18503. }
  18504. };
  18505. //表格属性
  18506. UE.commands['edittable'] = {
  18507. queryCommandState: function () {
  18508. return getTableItemsByRange(this).table ? 0 : -1
  18509. },
  18510. execCommand: function (cmd, color) {
  18511. var rng = this.selection.getRange(),
  18512. table = domUtils.findParentByTagName(rng.startContainer, 'table');
  18513. if (table) {
  18514. var arr = domUtils.getElementsByTagName(table, "td").concat(
  18515. domUtils.getElementsByTagName(table, "th"),
  18516. domUtils.getElementsByTagName(table, "caption")
  18517. );
  18518. utils.each(arr, function (node) {
  18519. node.style.borderColor = color;
  18520. });
  18521. }
  18522. }
  18523. };
  18524. //单元格属性
  18525. UE.commands['edittd'] = {
  18526. queryCommandState: function () {
  18527. return getTableItemsByRange(this).table ? 0 : -1
  18528. },
  18529. execCommand: function (cmd, bkColor) {
  18530. var me = this,
  18531. ut = getUETableBySelected(me);
  18532. if (!ut) {
  18533. var start = me.selection.getStart(),
  18534. cell = start && domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
  18535. if (cell) {
  18536. cell.style.backgroundColor = bkColor;
  18537. }
  18538. } else {
  18539. utils.each(ut.selectedTds, function (cell) {
  18540. cell.style.backgroundColor = bkColor;
  18541. });
  18542. }
  18543. }
  18544. };
  18545. UE.commands["settablebackground"] = {
  18546. queryCommandState: function () {
  18547. return getSelectedArr(this).length > 1 ? 0 : -1;
  18548. },
  18549. execCommand: function (cmd, value) {
  18550. var cells, ut;
  18551. cells = getSelectedArr(this);
  18552. ut = getUETable(cells[0]);
  18553. ut.setBackground(cells, value);
  18554. }
  18555. };
  18556. UE.commands["cleartablebackground"] = {
  18557. queryCommandState: function () {
  18558. var cells = getSelectedArr(this);
  18559. if (!cells.length) return -1;
  18560. for (var i = 0, cell; cell = cells[i++];) {
  18561. if (cell.style.backgroundColor !== "") return 0;
  18562. }
  18563. return -1;
  18564. },
  18565. execCommand: function () {
  18566. var cells = getSelectedArr(this),
  18567. ut = getUETable(cells[0]);
  18568. ut.removeBackground(cells);
  18569. }
  18570. };
  18571. UE.commands["interlacetable"] = UE.commands["uninterlacetable"] = {
  18572. queryCommandState: function (cmd) {
  18573. var table = getTableItemsByRange(this).table;
  18574. if (!table) return -1;
  18575. var interlaced = table.getAttribute("interlaced");
  18576. if (cmd == "interlacetable") {
  18577. //TODO 待定
  18578. //是否需要待定,如果设置,则命令只能单次执行成功,但反射具备toggle效果;否则可以覆盖前次命令,但反射将不存在toggle效果
  18579. return (interlaced === "enabled") ? -1 : 0;
  18580. } else {
  18581. return (!interlaced || interlaced === "disabled") ? -1 : 0;
  18582. }
  18583. },
  18584. execCommand: function (cmd, classList) {
  18585. var table = getTableItemsByRange(this).table;
  18586. if (cmd == "interlacetable") {
  18587. table.setAttribute("interlaced", "enabled");
  18588. this.fireEvent("interlacetable", table, classList);
  18589. } else {
  18590. table.setAttribute("interlaced", "disabled");
  18591. this.fireEvent("uninterlacetable", table);
  18592. }
  18593. }
  18594. };
  18595. UE.commands["setbordervisible"] = {
  18596. queryCommandState: function (cmd) {
  18597. var table = getTableItemsByRange(this).table;
  18598. if (!table) return -1;
  18599. return 0;
  18600. },
  18601. execCommand: function () {
  18602. var table = getTableItemsByRange(this).table;
  18603. utils.each(domUtils.getElementsByTagName(table, 'td'), function (td) {
  18604. td.style.borderWidth = '1px';
  18605. td.style.borderStyle = 'solid';
  18606. })
  18607. }
  18608. };
  18609. function resetTdWidth(table, editor) {
  18610. var tds = domUtils.getElementsByTagName(table, 'td th');
  18611. utils.each(tds, function (td) {
  18612. td.removeAttribute("width");
  18613. });
  18614. table.setAttribute('width', getTableWidth(editor, true, getDefaultValue(editor, table)));
  18615. var tdsWidths = [];
  18616. setTimeout(function () {
  18617. utils.each(tds, function (td) {
  18618. (td.colSpan == 1) && tdsWidths.push(td.offsetWidth)
  18619. })
  18620. utils.each(tds, function (td, i) {
  18621. (td.colSpan == 1) && td.setAttribute("width", tdsWidths[i] + "");
  18622. })
  18623. }, 0);
  18624. }
  18625. function getTableWidth(editor, needIEHack, defaultValue) {
  18626. var body = editor.body;
  18627. return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0);
  18628. }
  18629. function getSelectedArr(editor) {
  18630. var cell = getTableItemsByRange(editor).cell;
  18631. if (cell) {
  18632. var ut = getUETable(cell);
  18633. return ut.selectedTds.length ? ut.selectedTds : [cell];
  18634. } else {
  18635. return [];
  18636. }
  18637. }
  18638. })();
  18639. // plugins/table.action.js
  18640. /**
  18641. * Created with JetBrains PhpStorm.
  18642. * User: taoqili
  18643. * Date: 12-10-12
  18644. * Time: 上午10:05
  18645. * To change this template use File | Settings | File Templates.
  18646. */
  18647. UE.plugins['table'] = function () {
  18648. var me = this,
  18649. tabTimer = null,
  18650. //拖动计时器
  18651. tableDragTimer = null,
  18652. //双击计时器
  18653. tableResizeTimer = null,
  18654. //单元格最小宽度
  18655. cellMinWidth = 5,
  18656. isInResizeBuffer = false,
  18657. //单元格边框大小
  18658. cellBorderWidth = 5,
  18659. //鼠标偏移距离
  18660. offsetOfTableCell = 10,
  18661. //记录在有限时间内的点击状态, 共有3个取值, 0, 1, 2。 0代表未初始化, 1代表单击了1次,2代表2次
  18662. singleClickState = 0,
  18663. userActionStatus = null,
  18664. //双击允许的时间范围
  18665. dblclickTime = 360,
  18666. UT = UE.UETable,
  18667. getUETable = function (tdOrTable) {
  18668. return UT.getUETable(tdOrTable);
  18669. },
  18670. getUETableBySelected = function (editor) {
  18671. return UT.getUETableBySelected(editor);
  18672. },
  18673. getDefaultValue = function (editor, table) {
  18674. return UT.getDefaultValue(editor, table);
  18675. },
  18676. removeSelectedClass = function (cells) {
  18677. return UT.removeSelectedClass(cells);
  18678. };
  18679. function showError(e) {
  18680. // throw e;
  18681. }
  18682. me.ready(function () {
  18683. var me = this;
  18684. var orgGetText = me.selection.getText;
  18685. me.selection.getText = function () {
  18686. var table = getUETableBySelected(me);
  18687. if (table) {
  18688. var str = '';
  18689. utils.each(table.selectedTds, function (td) {
  18690. str += td[browser.ie ? 'innerText' : 'textContent'];
  18691. })
  18692. return str;
  18693. } else {
  18694. return orgGetText.call(me.selection)
  18695. }
  18696. }
  18697. })
  18698. //处理拖动及框选相关方法
  18699. var startTd = null, //鼠标按下时的锚点td
  18700. currentTd = null, //当前鼠标经过时的td
  18701. onDrag = "", //指示当前拖动状态,其值可为"","h","v" ,分别表示未拖动状态,横向拖动状态,纵向拖动状态,用于鼠标移动过程中的判断
  18702. onBorder = false, //检测鼠标按下时是否处在单元格边缘位置
  18703. dragButton = null,
  18704. dragOver = false,
  18705. dragLine = null, //模拟的拖动线
  18706. dragTd = null; //发生拖动的目标td
  18707. var mousedown = false,
  18708. //todo 判断混乱模式
  18709. needIEHack = true;
  18710. me.setOpt({
  18711. 'maxColNum': 20,
  18712. 'maxRowNum': 100,
  18713. 'defaultCols': 5,
  18714. 'defaultRows': 5,
  18715. 'tdvalign': 'top',
  18716. 'cursorpath': me.options.UEDITOR_HOME_URL + "themes/default/images/cursor_",
  18717. 'tableDragable': false,
  18718. 'classList': ["ue-table-interlace-color-single", "ue-table-interlace-color-double"]
  18719. });
  18720. me.getUETable = getUETable;
  18721. var commands = {
  18722. 'deletetable': 1,
  18723. 'inserttable': 1,
  18724. 'cellvalign': 1,
  18725. 'insertcaption': 1,
  18726. 'deletecaption': 1,
  18727. 'inserttitle': 1,
  18728. 'deletetitle': 1,
  18729. "mergeright": 1,
  18730. "mergedown": 1,
  18731. "mergecells": 1,
  18732. "insertrow": 1,
  18733. "insertrownext": 1,
  18734. "deleterow": 1,
  18735. "insertcol": 1,
  18736. "insertcolnext": 1,
  18737. "deletecol": 1,
  18738. "splittocells": 1,
  18739. "splittorows": 1,
  18740. "splittocols": 1,
  18741. "adaptbytext": 1,
  18742. "adaptbywindow": 1,
  18743. "adaptbycustomer": 1,
  18744. "insertparagraph": 1,
  18745. "insertparagraphbeforetable": 1,
  18746. "averagedistributecol": 1,
  18747. "averagedistributerow": 1
  18748. };
  18749. me.ready(function () {
  18750. utils.cssRule('table',
  18751. //选中的td上的样式
  18752. '.selectTdClass{background-color:#edf5fa !important}' +
  18753. 'table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}' +
  18754. //插入的表格的默认样式
  18755. 'table{margin-bottom:10px;border-collapse:collapse;display:table;}' +
  18756. 'td,th{padding: 5px 10px;border: 1px solid #DDD;}' +
  18757. 'caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}' +
  18758. 'th{border-top:1px solid #BBB;background-color:#F7F7F7;}' +
  18759. 'table tr.firstRow th{border-top-width:2px;}' +
  18760. '.ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }' +
  18761. 'td p{margin:0;padding:0;}', me.document);
  18762. var tableCopyList, isFullCol, isFullRow;
  18763. //注册del/backspace事件
  18764. me.addListener('keydown', function (cmd, evt) {
  18765. var me = this;
  18766. var keyCode = evt.keyCode || evt.which;
  18767. if (keyCode == 8) {
  18768. var ut = getUETableBySelected(me);
  18769. if (ut && ut.selectedTds.length) {
  18770. if (ut.isFullCol()) {
  18771. me.execCommand('deletecol')
  18772. } else if (ut.isFullRow()) {
  18773. me.execCommand('deleterow')
  18774. } else {
  18775. me.fireEvent('delcells');
  18776. }
  18777. domUtils.preventDefault(evt);
  18778. }
  18779. var caption = domUtils.findParentByTagName(me.selection.getStart(), 'caption', true),
  18780. range = me.selection.getRange();
  18781. if (range.collapsed && caption && isEmptyBlock(caption)) {
  18782. me.fireEvent('saveScene');
  18783. var table = caption.parentNode;
  18784. domUtils.remove(caption);
  18785. if (table) {
  18786. range.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
  18787. }
  18788. me.fireEvent('saveScene');
  18789. }
  18790. }
  18791. if (keyCode == 46) {
  18792. ut = getUETableBySelected(me);
  18793. if (ut) {
  18794. me.fireEvent('saveScene');
  18795. for (var i = 0, ci; ci = ut.selectedTds[i++];) {
  18796. domUtils.fillNode(me.document, ci)
  18797. }
  18798. me.fireEvent('saveScene');
  18799. domUtils.preventDefault(evt);
  18800. }
  18801. }
  18802. if (keyCode == 13) {
  18803. var rng = me.selection.getRange(),
  18804. caption = domUtils.findParentByTagName(rng.startContainer, 'caption', true);
  18805. if (caption) {
  18806. var table = domUtils.findParentByTagName(caption, 'table');
  18807. if (!rng.collapsed) {
  18808. rng.deleteContents();
  18809. me.fireEvent('saveScene');
  18810. } else {
  18811. if (caption) {
  18812. rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
  18813. }
  18814. }
  18815. domUtils.preventDefault(evt);
  18816. return;
  18817. }
  18818. if (rng.collapsed) {
  18819. var table = domUtils.findParentByTagName(rng.startContainer, 'table');
  18820. if (table) {
  18821. var cell = table.rows[0].cells[0],
  18822. start = domUtils.findParentByTagName(me.selection.getStart(), ['td', 'th'], true),
  18823. preNode = table.previousSibling;
  18824. if (cell === start && (!preNode || preNode.nodeType == 1 && preNode.tagName == 'TABLE') && domUtils.isStartInblock(rng)) {
  18825. var first = domUtils.findParent(me.selection.getStart(), function (n) {
  18826. return domUtils.isBlockElm(n)
  18827. }, true);
  18828. if (first && (/t(h|d)/i.test(first.tagName) || first === start.firstChild)) {
  18829. me.execCommand('insertparagraphbeforetable');
  18830. domUtils.preventDefault(evt);
  18831. }
  18832. }
  18833. }
  18834. }
  18835. }
  18836. if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == '67') {
  18837. tableCopyList = null;
  18838. var ut = getUETableBySelected(me);
  18839. if (ut) {
  18840. var tds = ut.selectedTds;
  18841. isFullCol = ut.isFullCol();
  18842. isFullRow = ut.isFullRow();
  18843. tableCopyList = [
  18844. [ut.cloneCell(tds[0], null, true)]
  18845. ];
  18846. for (var i = 1, ci; ci = tds[i]; i++) {
  18847. if (ci.parentNode !== tds[i - 1].parentNode) {
  18848. tableCopyList.push([ut.cloneCell(ci, null, true)]);
  18849. } else {
  18850. tableCopyList[tableCopyList.length - 1].push(ut.cloneCell(ci, null, true));
  18851. }
  18852. }
  18853. }
  18854. }
  18855. });
  18856. me.addListener("tablehasdeleted", function () {
  18857. toggleDraggableState(this, false, "", null);
  18858. if (dragButton) domUtils.remove(dragButton);
  18859. });
  18860. me.addListener('beforepaste', function (cmd, html) {
  18861. var me = this;
  18862. var rng = me.selection.getRange();
  18863. if (domUtils.findParentByTagName(rng.startContainer, 'caption', true)) {
  18864. var div = me.document.createElement("div");
  18865. div.innerHTML = html.html;
  18866. //trace:3729
  18867. html.html = div[browser.ie9below ? 'innerText' : 'textContent'];
  18868. return;
  18869. }
  18870. var table = getUETableBySelected(me);
  18871. if (tableCopyList) {
  18872. me.fireEvent('saveScene');
  18873. var rng = me.selection.getRange();
  18874. var td = domUtils.findParentByTagName(rng.startContainer, ['td', 'th'], true), tmpNode, preNode;
  18875. if (td) {
  18876. var ut = getUETable(td);
  18877. if (isFullRow) {
  18878. var rowIndex = ut.getCellInfo(td).rowIndex;
  18879. if (td.tagName == 'TH') {
  18880. rowIndex++;
  18881. }
  18882. for (var i = 0, ci; ci = tableCopyList[i++];) {
  18883. var tr = ut.insertRow(rowIndex++, "td");
  18884. for (var j = 0, cj; cj = ci[j]; j++) {
  18885. var cell = tr.cells[j];
  18886. if (!cell) {
  18887. cell = tr.insertCell(j)
  18888. }
  18889. cell.innerHTML = cj.innerHTML;
  18890. cj.getAttribute('width') && cell.setAttribute('width', cj.getAttribute('width'));
  18891. cj.getAttribute('vAlign') && cell.setAttribute('vAlign', cj.getAttribute('vAlign'));
  18892. cj.getAttribute('align') && cell.setAttribute('align', cj.getAttribute('align'));
  18893. cj.style.cssText && (cell.style.cssText = cj.style.cssText)
  18894. }
  18895. for (var j = 0, cj; cj = tr.cells[j]; j++) {
  18896. if (!ci[j])
  18897. break;
  18898. cj.innerHTML = ci[j].innerHTML;
  18899. ci[j].getAttribute('width') && cj.setAttribute('width', ci[j].getAttribute('width'));
  18900. ci[j].getAttribute('vAlign') && cj.setAttribute('vAlign', ci[j].getAttribute('vAlign'));
  18901. ci[j].getAttribute('align') && cj.setAttribute('align', ci[j].getAttribute('align'));
  18902. ci[j].style.cssText && (cj.style.cssText = ci[j].style.cssText)
  18903. }
  18904. }
  18905. } else {
  18906. if (isFullCol) {
  18907. cellInfo = ut.getCellInfo(td);
  18908. var maxColNum = 0;
  18909. for (var j = 0, ci = tableCopyList[0], cj; cj = ci[j++];) {
  18910. maxColNum += cj.colSpan || 1;
  18911. }
  18912. me.__hasEnterExecCommand = true;
  18913. for (i = 0; i < maxColNum; i++) {
  18914. me.execCommand('insertcol');
  18915. }
  18916. me.__hasEnterExecCommand = false;
  18917. td = ut.table.rows[0].cells[cellInfo.cellIndex];
  18918. if (td.tagName == 'TH') {
  18919. td = ut.table.rows[1].cells[cellInfo.cellIndex];
  18920. }
  18921. }
  18922. for (var i = 0, ci; ci = tableCopyList[i++];) {
  18923. tmpNode = td;
  18924. for (var j = 0, cj; cj = ci[j++];) {
  18925. if (td) {
  18926. td.innerHTML = cj.innerHTML;
  18927. //todo 定制处理
  18928. cj.getAttribute('width') && td.setAttribute('width', cj.getAttribute('width'));
  18929. cj.getAttribute('vAlign') && td.setAttribute('vAlign', cj.getAttribute('vAlign'));
  18930. cj.getAttribute('align') && td.setAttribute('align', cj.getAttribute('align'));
  18931. cj.style.cssText && (td.style.cssText = cj.style.cssText);
  18932. preNode = td;
  18933. td = td.nextSibling;
  18934. } else {
  18935. var cloneTd = cj.cloneNode(true);
  18936. domUtils.removeAttributes(cloneTd, ['class', 'rowSpan', 'colSpan']);
  18937. preNode.parentNode.appendChild(cloneTd)
  18938. }
  18939. }
  18940. td = ut.getNextCell(tmpNode, true, true);
  18941. if (!tableCopyList[i])
  18942. break;
  18943. if (!td) {
  18944. var cellInfo = ut.getCellInfo(tmpNode);
  18945. ut.table.insertRow(ut.table.rows.length);
  18946. ut.update();
  18947. td = ut.getVSideCell(tmpNode, true);
  18948. }
  18949. }
  18950. }
  18951. ut.update();
  18952. } else {
  18953. table = me.document.createElement('table');
  18954. for (var i = 0, ci; ci = tableCopyList[i++];) {
  18955. var tr = table.insertRow(table.rows.length);
  18956. for (var j = 0, cj; cj = ci[j++];) {
  18957. cloneTd = UT.cloneCell(cj, null, true);
  18958. domUtils.removeAttributes(cloneTd, ['class']);
  18959. tr.appendChild(cloneTd)
  18960. }
  18961. if (j == 2 && cloneTd.rowSpan > 1) {
  18962. cloneTd.rowSpan = 1;
  18963. }
  18964. }
  18965. var defaultValue = getDefaultValue(me),
  18966. width = me.body.offsetWidth -
  18967. (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0);
  18968. me.execCommand('insertHTML', '<table ' +
  18969. (isFullCol && isFullRow ? 'width="' + width + '"' : '') +
  18970. '>' + table.innerHTML.replace(/>\s*</g, '><').replace(/\bth\b/gi, "td") + '</table>')
  18971. }
  18972. me.fireEvent('contentchange');
  18973. me.fireEvent('saveScene');
  18974. html.html = '';
  18975. return true;
  18976. } else {
  18977. var div = me.document.createElement("div"), tables;
  18978. div.innerHTML = html.html;
  18979. tables = div.getElementsByTagName("table");
  18980. if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) {
  18981. utils.each(tables, function (t) {
  18982. domUtils.remove(t)
  18983. });
  18984. if (domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)) {
  18985. div.innerHTML = div[browser.ie ? 'innerText' : 'textContent'];
  18986. }
  18987. } else {
  18988. utils.each(tables, function (table) {
  18989. removeStyleSize(table, true);
  18990. domUtils.removeAttributes(table, ['style', 'border']);
  18991. utils.each(domUtils.getElementsByTagName(table, "td"), function (td) {
  18992. if (isEmptyBlock(td)) {
  18993. domUtils.fillNode(me.document, td);
  18994. }
  18995. removeStyleSize(td, true);
  18996. // domUtils.removeAttributes(td, ['style'])
  18997. });
  18998. });
  18999. }
  19000. html.html = div.innerHTML;
  19001. }
  19002. });
  19003. me.addListener('afterpaste', function () {
  19004. utils.each(domUtils.getElementsByTagName(me.body, "table"), function (table) {
  19005. if (table.offsetWidth > me.body.offsetWidth) {
  19006. var defaultValue = getDefaultValue(me, table);
  19007. table.style.width = me.body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (me.options.offsetWidth || 0) + 'px'
  19008. }
  19009. })
  19010. });
  19011. me.addListener('blur', function () {
  19012. tableCopyList = null;
  19013. });
  19014. var timer;
  19015. me.addListener('keydown', function () {
  19016. clearTimeout(timer);
  19017. timer = setTimeout(function () {
  19018. var rng = me.selection.getRange(),
  19019. cell = domUtils.findParentByTagName(rng.startContainer, ['th', 'td'], true);
  19020. if (cell) {
  19021. var table = cell.parentNode.parentNode.parentNode;
  19022. if (table.offsetWidth > table.getAttribute("width")) {
  19023. cell.style.wordBreak = "break-all";
  19024. }
  19025. }
  19026. }, 100);
  19027. });
  19028. me.addListener("selectionchange", function () {
  19029. toggleDraggableState(me, false, "", null);
  19030. });
  19031. //内容变化时触发索引更新
  19032. //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新
  19033. me.addListener("contentchange", function () {
  19034. var me = this;
  19035. //尽可能排除一些不需要更新的状况
  19036. hideDragLine(me);
  19037. if (getUETableBySelected(me)) return;
  19038. var rng = me.selection.getRange();
  19039. var start = rng.startContainer;
  19040. start = domUtils.findParentByTagName(start, ['td', 'th'], true);
  19041. utils.each(domUtils.getElementsByTagName(me.document, 'table'), function (table) {
  19042. if (me.fireEvent("excludetable", table) === true) return;
  19043. table.ueTable = new UT(table);
  19044. //trace:3742
  19045. // utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) {
  19046. //
  19047. // if (domUtils.isEmptyBlock(td) && td !== start) {
  19048. // domUtils.fillNode(me.document, td);
  19049. // if (browser.ie && browser.version == 6) {
  19050. // td.innerHTML = '&nbsp;'
  19051. // }
  19052. // }
  19053. // });
  19054. // utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) {
  19055. // if (domUtils.isEmptyBlock(th) && th !== start) {
  19056. // domUtils.fillNode(me.document, th);
  19057. // if (browser.ie && browser.version == 6) {
  19058. // th.innerHTML = '&nbsp;'
  19059. // }
  19060. // }
  19061. // });
  19062. table.onmouseover = function () {
  19063. me.fireEvent('tablemouseover', table);
  19064. };
  19065. table.onmousemove = function () {
  19066. me.fireEvent('tablemousemove', table);
  19067. me.options.tableDragable && toggleDragButton(true, this, me);
  19068. utils.defer(function () {
  19069. me.fireEvent('contentchange', 50)
  19070. }, true)
  19071. };
  19072. table.onmouseout = function () {
  19073. me.fireEvent('tablemouseout', table);
  19074. toggleDraggableState(me, false, "", null);
  19075. hideDragLine(me);
  19076. };
  19077. table.onclick = function (evt) {
  19078. evt = me.window.event || evt;
  19079. var target = getParentTdOrTh(evt.target || evt.srcElement);
  19080. if (!target) return;
  19081. var ut = getUETable(target),
  19082. table = ut.table,
  19083. cellInfo = ut.getCellInfo(target),
  19084. cellsRange,
  19085. rng = me.selection.getRange();
  19086. // if ("topLeft" == inPosition(table, mouseCoords(evt))) {
  19087. // cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell());
  19088. // ut.setSelected(cellsRange);
  19089. // return;
  19090. // }
  19091. // if ("bottomRight" == inPosition(table, mouseCoords(evt))) {
  19092. //
  19093. // return;
  19094. // }
  19095. if (inTableSide(table, target, evt, true)) {
  19096. var endTdCol = ut.getCell(ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex, ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex);
  19097. if (evt.shiftKey && ut.selectedTds.length) {
  19098. if (ut.selectedTds[0] !== endTdCol) {
  19099. cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol);
  19100. ut.setSelected(cellsRange);
  19101. } else {
  19102. rng && rng.selectNodeContents(endTdCol).select();
  19103. }
  19104. } else {
  19105. if (target !== endTdCol) {
  19106. cellsRange = ut.getCellsRange(target, endTdCol);
  19107. ut.setSelected(cellsRange);
  19108. } else {
  19109. rng && rng.selectNodeContents(endTdCol).select();
  19110. }
  19111. }
  19112. return;
  19113. }
  19114. if (inTableSide(table, target, evt)) {
  19115. var endTdRow = ut.getCell(ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex, ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex);
  19116. if (evt.shiftKey && ut.selectedTds.length) {
  19117. if (ut.selectedTds[0] !== endTdRow) {
  19118. cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow);
  19119. ut.setSelected(cellsRange);
  19120. } else {
  19121. rng && rng.selectNodeContents(endTdRow).select();
  19122. }
  19123. } else {
  19124. if (target !== endTdRow) {
  19125. cellsRange = ut.getCellsRange(target, endTdRow);
  19126. ut.setSelected(cellsRange);
  19127. } else {
  19128. rng && rng.selectNodeContents(endTdRow).select();
  19129. }
  19130. }
  19131. }
  19132. };
  19133. });
  19134. switchBorderColor(me, true);
  19135. });
  19136. domUtils.on(me.document, "mousemove", mouseMoveEvent);
  19137. domUtils.on(me.document, "mouseout", function (evt) {
  19138. var target = evt.target || evt.srcElement;
  19139. if (target.tagName == "TABLE") {
  19140. toggleDraggableState(me, false, "", null);
  19141. }
  19142. });
  19143. /**
  19144. * 表格隔行变色
  19145. */
  19146. me.addListener("interlacetable", function (type, table, classList) {
  19147. if (!table) return;
  19148. var me = this,
  19149. rows = table.rows,
  19150. len = rows.length,
  19151. getClass = function (list, index, repeat) {
  19152. return list[index] ? list[index] : repeat ? list[index % list.length] : "";
  19153. };
  19154. for (var i = 0; i < len; i++) {
  19155. rows[i].className = getClass(classList || me.options.classList, i, true);
  19156. }
  19157. });
  19158. me.addListener("uninterlacetable", function (type, table) {
  19159. if (!table) return;
  19160. var me = this,
  19161. rows = table.rows,
  19162. classList = me.options.classList,
  19163. len = rows.length;
  19164. for (var i = 0; i < len; i++) {
  19165. domUtils.removeClasses(rows[i], classList);
  19166. }
  19167. });
  19168. me.addListener("mousedown", mouseDownEvent);
  19169. me.addListener("mouseup", mouseUpEvent);
  19170. //拖动的时候触发mouseup
  19171. domUtils.on(me.body, 'dragstart', function (evt) {
  19172. mouseUpEvent.call(me, 'dragstart', evt);
  19173. });
  19174. me.addOutputRule(function (root) {
  19175. utils.each(root.getNodesByTagName('div'), function (n) {
  19176. if (n.getAttr('id') == 'ue_tableDragLine') {
  19177. n.parentNode.removeChild(n);
  19178. }
  19179. });
  19180. });
  19181. var currentRowIndex = 0;
  19182. me.addListener("mousedown", function () {
  19183. currentRowIndex = 0;
  19184. });
  19185. me.addListener('tabkeydown', function () {
  19186. var range = this.selection.getRange(),
  19187. common = range.getCommonAncestor(true, true),
  19188. table = domUtils.findParentByTagName(common, 'table');
  19189. if (table) {
  19190. if (domUtils.findParentByTagName(common, 'caption', true)) {
  19191. var cell = domUtils.getElementsByTagName(table, 'th td');
  19192. if (cell && cell.length) {
  19193. range.setStart(cell[0], 0).setCursor(false, true)
  19194. }
  19195. } else {
  19196. var cell = domUtils.findParentByTagName(common, ['td', 'th'], true),
  19197. ua = getUETable(cell);
  19198. currentRowIndex = cell.rowSpan > 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex;
  19199. var nextCell = ua.getTabNextCell(cell, currentRowIndex);
  19200. if (nextCell) {
  19201. if (isEmptyBlock(nextCell)) {
  19202. range.setStart(nextCell, 0).setCursor(false, true)
  19203. } else {
  19204. range.selectNodeContents(nextCell).select()
  19205. }
  19206. } else {
  19207. me.fireEvent('saveScene');
  19208. me.__hasEnterExecCommand = true;
  19209. this.execCommand('insertrownext');
  19210. me.__hasEnterExecCommand = false;
  19211. range = this.selection.getRange();
  19212. range.setStart(table.rows[table.rows.length - 1].cells[0], 0).setCursor();
  19213. me.fireEvent('saveScene');
  19214. }
  19215. }
  19216. return true;
  19217. }
  19218. });
  19219. browser.ie && me.addListener('selectionchange', function () {
  19220. toggleDraggableState(this, false, "", null);
  19221. });
  19222. me.addListener("keydown", function (type, evt) {
  19223. var me = this;
  19224. //处理在表格的最后一个输入tab产生新的表格
  19225. var keyCode = evt.keyCode || evt.which;
  19226. if (keyCode == 8 || keyCode == 46) {
  19227. return;
  19228. }
  19229. var notCtrlKey = !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey;
  19230. notCtrlKey && removeSelectedClass(domUtils.getElementsByTagName(me.body, "td"));
  19231. var ut = getUETableBySelected(me);
  19232. if (!ut) return;
  19233. notCtrlKey && ut.clearSelected();
  19234. });
  19235. me.addListener("beforegetcontent", function () {
  19236. switchBorderColor(this, false);
  19237. browser.ie && utils.each(this.document.getElementsByTagName('caption'), function (ci) {
  19238. if (domUtils.isEmptyNode(ci)) {
  19239. ci.innerHTML = '&nbsp;'
  19240. }
  19241. });
  19242. });
  19243. me.addListener("aftergetcontent", function () {
  19244. switchBorderColor(this, true);
  19245. });
  19246. me.addListener("getAllHtml", function () {
  19247. removeSelectedClass(me.document.getElementsByTagName("td"));
  19248. });
  19249. //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况
  19250. me.addListener("fullscreenchanged", function (type, fullscreen) {
  19251. if (!fullscreen) {
  19252. var ratio = this.body.offsetWidth / document.body.offsetWidth,
  19253. tables = domUtils.getElementsByTagName(this.body, "table");
  19254. utils.each(tables, function (table) {
  19255. if (table.offsetWidth < me.body.offsetWidth) return false;
  19256. var tds = domUtils.getElementsByTagName(table, "td"),
  19257. backWidths = [];
  19258. utils.each(tds, function (td) {
  19259. backWidths.push(td.offsetWidth);
  19260. });
  19261. for (var i = 0, td; td = tds[i]; i++) {
  19262. td.setAttribute("width", Math.floor(backWidths[i] * ratio));
  19263. }
  19264. table.setAttribute("width", Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me))))
  19265. });
  19266. }
  19267. });
  19268. //重写execCommand命令,用于处理框选时的处理
  19269. var oldExecCommand = me.execCommand;
  19270. me.execCommand = function (cmd, datatat) {
  19271. var me = this,
  19272. args = arguments;
  19273. cmd = cmd.toLowerCase();
  19274. var ut = getUETableBySelected(me), tds,
  19275. range = new dom.Range(me.document),
  19276. cmdFun = me.commands[cmd] || UE.commands[cmd],
  19277. result;
  19278. if (!cmdFun) return;
  19279. if (ut && !commands[cmd] && !cmdFun.notNeedUndo && !me.__hasEnterExecCommand) {
  19280. me.__hasEnterExecCommand = true;
  19281. me.fireEvent("beforeexeccommand", cmd);
  19282. tds = ut.selectedTds;
  19283. var lastState = -2, lastValue = -2, value, state;
  19284. for (var i = 0, td; td = tds[i]; i++) {
  19285. if (isEmptyBlock(td)) {
  19286. range.setStart(td, 0).setCursor(false, true)
  19287. } else {
  19288. range.selectNode(td).select(true);
  19289. }
  19290. state = me.queryCommandState(cmd);
  19291. value = me.queryCommandValue(cmd);
  19292. if (state != -1) {
  19293. if (lastState !== state || lastValue !== value) {
  19294. me._ignoreContentChange = true;
  19295. result = oldExecCommand.apply(me, arguments);
  19296. me._ignoreContentChange = false;
  19297. }
  19298. lastState = me.queryCommandState(cmd);
  19299. lastValue = me.queryCommandValue(cmd);
  19300. if (domUtils.isEmptyBlock(td)) {
  19301. domUtils.fillNode(me.document, td)
  19302. }
  19303. }
  19304. }
  19305. range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true);
  19306. me.fireEvent('contentchange');
  19307. me.fireEvent("afterexeccommand", cmd);
  19308. me.__hasEnterExecCommand = false;
  19309. me._selectionChange();
  19310. } else {
  19311. result = oldExecCommand.apply(me, arguments);
  19312. }
  19313. return result;
  19314. };
  19315. });
  19316. /**
  19317. * 删除obj的宽高style,改成属性宽高
  19318. * @param obj
  19319. * @param replaceToProperty
  19320. */
  19321. function removeStyleSize(obj, replaceToProperty) {
  19322. removeStyle(obj, "width", true);
  19323. removeStyle(obj, "height", true);
  19324. }
  19325. function removeStyle(obj, styleName, replaceToProperty) {
  19326. if (obj.style[styleName]) {
  19327. replaceToProperty && obj.setAttribute(styleName, parseInt(obj.style[styleName], 10));
  19328. obj.style[styleName] = "";
  19329. }
  19330. }
  19331. function getParentTdOrTh(ele) {
  19332. if (ele.tagName == "TD" || ele.tagName == "TH") return ele;
  19333. var td;
  19334. if (td = domUtils.findParentByTagName(ele, "td", true) || domUtils.findParentByTagName(ele, "th", true)) return td;
  19335. return null;
  19336. }
  19337. function isEmptyBlock(node) {
  19338. var reg = new RegExp(domUtils.fillChar, 'g');
  19339. if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
  19340. return 0;
  19341. }
  19342. for (var n in dtd.$isNotEmpty) {
  19343. if (node.getElementsByTagName(n).length) {
  19344. return 0;
  19345. }
  19346. }
  19347. return 1;
  19348. }
  19349. function mouseCoords(evt) {
  19350. if (evt.pageX || evt.pageY) {
  19351. return {x: evt.pageX, y: evt.pageY};
  19352. }
  19353. return {
  19354. x: evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft,
  19355. y: evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
  19356. };
  19357. }
  19358. function mouseMoveEvent(evt) {
  19359. if (isEditorDisabled()) {
  19360. return;
  19361. }
  19362. try {
  19363. //普通状态下鼠标移动
  19364. var target = getParentTdOrTh(evt.target || evt.srcElement),
  19365. pos;
  19366. //区分用户的行为是拖动还是双击
  19367. if (isInResizeBuffer) {
  19368. me.body.style.webkitUserSelect = 'none';
  19369. if (Math.abs(userActionStatus.x - evt.clientX) > offsetOfTableCell || Math.abs(userActionStatus.y - evt.clientY) > offsetOfTableCell) {
  19370. clearTableDragTimer();
  19371. isInResizeBuffer = false;
  19372. singleClickState = 0;
  19373. //drag action
  19374. tableBorderDrag(evt);
  19375. }
  19376. }
  19377. //修改单元格大小时的鼠标移动
  19378. if (onDrag && dragTd) {
  19379. singleClickState = 0;
  19380. me.body.style.webkitUserSelect = 'none';
  19381. me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
  19382. pos = mouseCoords(evt);
  19383. toggleDraggableState(me, true, onDrag, pos, target);
  19384. if (onDrag == "h") {
  19385. dragLine.style.left = getPermissionX(dragTd, evt) + "px";
  19386. } else if (onDrag == "v") {
  19387. dragLine.style.top = getPermissionY(dragTd, evt) + "px";
  19388. }
  19389. return;
  19390. }
  19391. //当鼠标处于table上时,修改移动过程中的光标状态
  19392. if (target) {
  19393. //针对使用table作为容器的组件不触发拖拽效果
  19394. if (me.fireEvent('excludetable', target) === true)
  19395. return;
  19396. pos = mouseCoords(evt);
  19397. var state = getRelation(target, pos),
  19398. table = domUtils.findParentByTagName(target, "table", true);
  19399. if (inTableSide(table, target, evt, true)) {
  19400. if (me.fireEvent("excludetable", table) === true) return;
  19401. me.body.style.cursor = "url(" + me.options.cursorpath + "h.png),pointer";
  19402. } else if (inTableSide(table, target, evt)) {
  19403. if (me.fireEvent("excludetable", table) === true) return;
  19404. me.body.style.cursor = "url(" + me.options.cursorpath + "v.png),pointer";
  19405. } else {
  19406. me.body.style.cursor = "text";
  19407. var curCell = target;
  19408. if (/\d/.test(state)) {
  19409. state = state.replace(/\d/, '');
  19410. target = getUETable(target).getPreviewCell(target, state == "v");
  19411. }
  19412. //位于第一行的顶部或者第一列的左边时不可拖动
  19413. toggleDraggableState(me, target ? !!state : false, target ? state : '', pos, target);
  19414. }
  19415. } else {
  19416. toggleDragButton(false, table, me);
  19417. }
  19418. } catch (e) {
  19419. showError(e);
  19420. }
  19421. }
  19422. var dragButtonTimer;
  19423. function toggleDragButton(show, table, editor) {
  19424. if (!show) {
  19425. if (dragOver) return;
  19426. dragButtonTimer = setTimeout(function () {
  19427. !dragOver && dragButton && dragButton.parentNode && dragButton.parentNode.removeChild(dragButton);
  19428. }, 2000);
  19429. } else {
  19430. createDragButton(table, editor);
  19431. }
  19432. }
  19433. function createDragButton(table, editor) {
  19434. var pos = domUtils.getXY(table),
  19435. doc = table.ownerDocument;
  19436. if (dragButton && dragButton.parentNode) return dragButton;
  19437. dragButton = doc.createElement("div");
  19438. dragButton.contentEditable = false;
  19439. dragButton.innerHTML = "";
  19440. dragButton.style.cssText = "width:15px;height:15px;background-image:url(" + editor.options.UEDITOR_HOME_URL + "dialogs/table/dragicon.png);position: absolute;cursor:move;top:" + (pos.y - 15) + "px;left:" + (pos.x) + "px;";
  19441. domUtils.unSelectable(dragButton);
  19442. dragButton.onmouseover = function (evt) {
  19443. dragOver = true;
  19444. };
  19445. dragButton.onmouseout = function (evt) {
  19446. dragOver = false;
  19447. };
  19448. domUtils.on(dragButton, 'click', function (type, evt) {
  19449. doClick(evt, this);
  19450. });
  19451. domUtils.on(dragButton, 'dblclick', function (type, evt) {
  19452. doDblClick(evt);
  19453. });
  19454. domUtils.on(dragButton, 'dragstart', function (type, evt) {
  19455. domUtils.preventDefault(evt);
  19456. });
  19457. var timer;
  19458. function doClick(evt, button) {
  19459. // 部分浏览器下需要清理
  19460. clearTimeout(timer);
  19461. timer = setTimeout(function () {
  19462. editor.fireEvent("tableClicked", table, button);
  19463. }, 300);
  19464. }
  19465. function doDblClick(evt) {
  19466. clearTimeout(timer);
  19467. var ut = getUETable(table),
  19468. start = table.rows[0].cells[0],
  19469. end = ut.getLastCell(),
  19470. range = ut.getCellsRange(start, end);
  19471. editor.selection.getRange().setStart(start, 0).setCursor(false, true);
  19472. ut.setSelected(range);
  19473. }
  19474. doc.body.appendChild(dragButton);
  19475. }
  19476. // function inPosition(table, pos) {
  19477. // var tablePos = domUtils.getXY(table),
  19478. // width = table.offsetWidth,
  19479. // height = table.offsetHeight;
  19480. // if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) {
  19481. // return "topLeft";
  19482. // } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) {
  19483. // return "bottomRight";
  19484. // }
  19485. // }
  19486. function inTableSide(table, cell, evt, top) {
  19487. var pos = mouseCoords(evt),
  19488. state = getRelation(cell, pos);
  19489. if (top) {
  19490. var caption = table.getElementsByTagName("caption")[0],
  19491. capHeight = caption ? caption.offsetHeight : 0;
  19492. return (state == "v1") && ((pos.y - domUtils.getXY(table).y - capHeight) < 8);
  19493. } else {
  19494. return (state == "h1") && ((pos.x - domUtils.getXY(table).x) < 8);
  19495. }
  19496. }
  19497. /**
  19498. * 获取拖动时允许的X轴坐标
  19499. * @param dragTd
  19500. * @param evt
  19501. */
  19502. function getPermissionX(dragTd, evt) {
  19503. var ut = getUETable(dragTd);
  19504. if (ut) {
  19505. var preTd = ut.getSameEndPosCells(dragTd, "x")[0],
  19506. nextTd = ut.getSameStartPosXCells(dragTd)[0],
  19507. mouseX = mouseCoords(evt).x,
  19508. left = (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20,
  19509. right = nextTd ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20 : (me.body.offsetWidth + 5 || parseInt(domUtils.getComputedStyle(me.body, "width"), 10));
  19510. left += cellMinWidth;
  19511. right -= cellMinWidth;
  19512. return mouseX < left ? left : mouseX > right ? right : mouseX;
  19513. }
  19514. }
  19515. /**
  19516. * 获取拖动时允许的Y轴坐标
  19517. */
  19518. function getPermissionY(dragTd, evt) {
  19519. try {
  19520. var top = domUtils.getXY(dragTd).y,
  19521. mousePosY = mouseCoords(evt).y;
  19522. return mousePosY < top ? top : mousePosY;
  19523. } catch (e) {
  19524. showError(e);
  19525. }
  19526. }
  19527. /**
  19528. * 移动状态切换
  19529. */
  19530. function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
  19531. try {
  19532. editor.body.style.cursor = dir == "h" ? "col-resize" : dir == "v" ? "row-resize" : "text";
  19533. if (browser.ie) {
  19534. if (dir && !mousedown && !getUETableBySelected(editor)) {
  19535. getDragLine(editor, editor.document);
  19536. showDragLineAt(dir, cell);
  19537. } else {
  19538. hideDragLine(editor)
  19539. }
  19540. }
  19541. onBorder = draggable;
  19542. } catch (e) {
  19543. showError(e);
  19544. }
  19545. }
  19546. /**
  19547. * 获取与UETable相关的resize line
  19548. * @param uetable UETable对象
  19549. */
  19550. function getResizeLineByUETable() {
  19551. var lineId = '_UETableResizeLine',
  19552. line = this.document.getElementById(lineId);
  19553. if (!line) {
  19554. line = this.document.createElement("div");
  19555. line.id = lineId;
  19556. line.contnetEditable = false;
  19557. line.setAttribute("unselectable", "on");
  19558. var styles = {
  19559. width: 2 * cellBorderWidth + 1 + 'px',
  19560. position: 'absolute',
  19561. 'z-index': 100000,
  19562. cursor: 'col-resize',
  19563. background: 'red',
  19564. display: 'none'
  19565. };
  19566. //切换状态
  19567. line.onmouseout = function () {
  19568. this.style.display = 'none';
  19569. };
  19570. utils.extend(line.style, styles);
  19571. this.document.body.appendChild(line);
  19572. }
  19573. return line;
  19574. }
  19575. /**
  19576. * 更新resize-line
  19577. */
  19578. function updateResizeLine(cell, uetable) {
  19579. var line = getResizeLineByUETable.call(this),
  19580. table = uetable.table,
  19581. styles = {
  19582. top: domUtils.getXY(table).y + 'px',
  19583. left: domUtils.getXY(cell).x + cell.offsetWidth - cellBorderWidth + 'px',
  19584. display: 'block',
  19585. height: table.offsetHeight + 'px'
  19586. };
  19587. utils.extend(line.style, styles);
  19588. }
  19589. /**
  19590. * 显示resize-line
  19591. */
  19592. function showResizeLine(cell) {
  19593. var uetable = getUETable(cell);
  19594. updateResizeLine.call(this, cell, uetable);
  19595. }
  19596. /**
  19597. * 获取鼠标与当前单元格的相对位置
  19598. * @param ele
  19599. * @param mousePos
  19600. */
  19601. function getRelation(ele, mousePos) {
  19602. var elePos = domUtils.getXY(ele);
  19603. if (!elePos) {
  19604. return '';
  19605. }
  19606. if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
  19607. return "h";
  19608. }
  19609. if (mousePos.x - elePos.x < cellBorderWidth) {
  19610. return 'h1'
  19611. }
  19612. if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
  19613. return "v";
  19614. }
  19615. if (mousePos.y - elePos.y < cellBorderWidth) {
  19616. return 'v1'
  19617. }
  19618. return '';
  19619. }
  19620. function mouseDownEvent(type, evt) {
  19621. if (isEditorDisabled()) {
  19622. return;
  19623. }
  19624. userActionStatus = {
  19625. x: evt.clientX,
  19626. y: evt.clientY
  19627. };
  19628. //右键菜单单独处理
  19629. if (evt.button == 2) {
  19630. var ut = getUETableBySelected(me),
  19631. flag = false;
  19632. if (ut) {
  19633. var td = getTargetTd(me, evt);
  19634. utils.each(ut.selectedTds, function (ti) {
  19635. if (ti === td) {
  19636. flag = true;
  19637. }
  19638. });
  19639. if (!flag) {
  19640. removeSelectedClass(domUtils.getElementsByTagName(me.body, "th td"));
  19641. ut.clearSelected()
  19642. } else {
  19643. td = ut.selectedTds[0];
  19644. setTimeout(function () {
  19645. me.selection.getRange().setStart(td, 0).setCursor(false, true);
  19646. }, 0);
  19647. }
  19648. }
  19649. } else {
  19650. tableClickHander(evt);
  19651. }
  19652. }
  19653. //清除表格的计时器
  19654. function clearTableTimer() {
  19655. tabTimer && clearTimeout(tabTimer);
  19656. tabTimer = null;
  19657. }
  19658. //双击收缩
  19659. function tableDbclickHandler(evt) {
  19660. singleClickState = 0;
  19661. evt = evt || me.window.event;
  19662. var target = getParentTdOrTh(evt.target || evt.srcElement);
  19663. if (target) {
  19664. var h;
  19665. if (h = getRelation(target, mouseCoords(evt))) {
  19666. hideDragLine(me);
  19667. if (h == 'h1') {
  19668. h = 'h';
  19669. if (inTableSide(domUtils.findParentByTagName(target, "table"), target, evt)) {
  19670. me.execCommand('adaptbywindow');
  19671. } else {
  19672. target = getUETable(target).getPreviewCell(target);
  19673. if (target) {
  19674. var rng = me.selection.getRange();
  19675. rng.selectNodeContents(target).setCursor(true, true)
  19676. }
  19677. }
  19678. }
  19679. if (h == 'h') {
  19680. var ut = getUETable(target),
  19681. table = ut.table,
  19682. cells = getCellsByMoveBorder(target, table, true);
  19683. cells = extractArray(cells, 'left');
  19684. ut.width = ut.offsetWidth;
  19685. var oldWidth = [],
  19686. newWidth = [];
  19687. utils.each(cells, function (cell) {
  19688. oldWidth.push(cell.offsetWidth);
  19689. });
  19690. utils.each(cells, function (cell) {
  19691. cell.removeAttribute("width");
  19692. });
  19693. window.setTimeout(function () {
  19694. //是否允许改变
  19695. var changeable = true;
  19696. utils.each(cells, function (cell, index) {
  19697. var width = cell.offsetWidth;
  19698. if (width > oldWidth[index]) {
  19699. changeable = false;
  19700. return false;
  19701. }
  19702. newWidth.push(width);
  19703. });
  19704. var change = changeable ? newWidth : oldWidth;
  19705. utils.each(cells, function (cell, index) {
  19706. cell.width = change[index] - getTabcellSpace();
  19707. });
  19708. }, 0);
  19709. // minWidth -= cellMinWidth;
  19710. //
  19711. // table.removeAttribute("width");
  19712. // utils.each(cells, function (cell) {
  19713. // cell.style.width = "";
  19714. // cell.width -= minWidth;
  19715. // });
  19716. }
  19717. }
  19718. }
  19719. }
  19720. function tableClickHander(evt) {
  19721. removeSelectedClass(domUtils.getElementsByTagName(me.body, "td th"));
  19722. //trace:3113
  19723. //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值
  19724. utils.each(me.document.getElementsByTagName('table'), function (t) {
  19725. t.ueTable = null;
  19726. });
  19727. startTd = getTargetTd(me, evt);
  19728. if (!startTd) return;
  19729. var table = domUtils.findParentByTagName(startTd, "table", true);
  19730. ut = getUETable(table);
  19731. ut && ut.clearSelected();
  19732. //判断当前鼠标状态
  19733. if (!onBorder) {
  19734. me.document.body.style.webkitUserSelect = '';
  19735. mousedown = true;
  19736. me.addListener('mouseover', mouseOverEvent);
  19737. } else {
  19738. //边框上的动作处理
  19739. borderActionHandler(evt);
  19740. }
  19741. }
  19742. //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响
  19743. function borderActionHandler(evt) {
  19744. if (browser.ie) {
  19745. evt = reconstruct(evt);
  19746. }
  19747. clearTableDragTimer();
  19748. //是否正在等待resize的缓冲中
  19749. isInResizeBuffer = true;
  19750. tableDragTimer = setTimeout(function () {
  19751. tableBorderDrag(evt);
  19752. }, dblclickTime);
  19753. }
  19754. function extractArray(originArr, key) {
  19755. var result = [],
  19756. tmp = null;
  19757. for (var i = 0, len = originArr.length; i < len; i++) {
  19758. tmp = originArr[i][key];
  19759. if (tmp) {
  19760. result.push(tmp);
  19761. }
  19762. }
  19763. return result;
  19764. }
  19765. function clearTableDragTimer() {
  19766. tableDragTimer && clearTimeout(tableDragTimer);
  19767. tableDragTimer = null;
  19768. }
  19769. function reconstruct(obj) {
  19770. var attrs = ['pageX', 'pageY', 'clientX', 'clientY', 'srcElement', 'target'],
  19771. newObj = {};
  19772. if (obj) {
  19773. for (var i = 0, key, val; key = attrs[i]; i++) {
  19774. val = obj[key];
  19775. val && (newObj[key] = val);
  19776. }
  19777. }
  19778. return newObj;
  19779. }
  19780. //边框拖动
  19781. function tableBorderDrag(evt) {
  19782. isInResizeBuffer = false;
  19783. startTd = evt.target || evt.srcElement;
  19784. if (!startTd) return;
  19785. var state = getRelation(startTd, mouseCoords(evt));
  19786. if (/\d/.test(state)) {
  19787. state = state.replace(/\d/, '');
  19788. startTd = getUETable(startTd).getPreviewCell(startTd, state == 'v');
  19789. }
  19790. hideDragLine(me);
  19791. getDragLine(me, me.document);
  19792. me.fireEvent('saveScene');
  19793. showDragLineAt(state, startTd);
  19794. mousedown = true;
  19795. //拖动开始
  19796. onDrag = state;
  19797. dragTd = startTd;
  19798. }
  19799. function mouseUpEvent(type, evt) {
  19800. if (isEditorDisabled()) {
  19801. return;
  19802. }
  19803. clearTableDragTimer();
  19804. isInResizeBuffer = false;
  19805. if (onBorder) {
  19806. singleClickState = ++singleClickState % 3;
  19807. userActionStatus = {
  19808. x: evt.clientX,
  19809. y: evt.clientY
  19810. };
  19811. tableResizeTimer = setTimeout(function () {
  19812. singleClickState > 0 && singleClickState--;
  19813. }, dblclickTime);
  19814. if (singleClickState === 2) {
  19815. singleClickState = 0;
  19816. tableDbclickHandler(evt);
  19817. return;
  19818. }
  19819. }
  19820. if (evt.button == 2) return;
  19821. var me = this;
  19822. //清除表格上原生跨选问题
  19823. var range = me.selection.getRange(),
  19824. start = domUtils.findParentByTagName(range.startContainer, 'table', true),
  19825. end = domUtils.findParentByTagName(range.endContainer, 'table', true);
  19826. if (start || end) {
  19827. if (start === end) {
  19828. start = domUtils.findParentByTagName(range.startContainer, ['td', 'th', 'caption'], true);
  19829. end = domUtils.findParentByTagName(range.endContainer, ['td', 'th', 'caption'], true);
  19830. if (start !== end) {
  19831. me.selection.clearRange()
  19832. }
  19833. } else {
  19834. me.selection.clearRange()
  19835. }
  19836. }
  19837. mousedown = false;
  19838. me.document.body.style.webkitUserSelect = '';
  19839. //拖拽状态下的mouseUP
  19840. if (onDrag && dragTd) {
  19841. me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
  19842. singleClickState = 0;
  19843. dragLine = me.document.getElementById('ue_tableDragLine');
  19844. // trace 3973
  19845. if (dragLine) {
  19846. var dragTdPos = domUtils.getXY(dragTd),
  19847. dragLinePos = domUtils.getXY(dragLine);
  19848. switch (onDrag) {
  19849. case "h":
  19850. changeColWidth(dragTd, dragLinePos.x - dragTdPos.x);
  19851. break;
  19852. case "v":
  19853. changeRowHeight(dragTd, dragLinePos.y - dragTdPos.y - dragTd.offsetHeight);
  19854. break;
  19855. default:
  19856. }
  19857. onDrag = "";
  19858. dragTd = null;
  19859. hideDragLine(me);
  19860. me.fireEvent('saveScene');
  19861. return;
  19862. }
  19863. }
  19864. //正常状态下的mouseup
  19865. if (!startTd) {
  19866. var target = domUtils.findParentByTagName(evt.target || evt.srcElement, "td", true);
  19867. if (!target) target = domUtils.findParentByTagName(evt.target || evt.srcElement, "th", true);
  19868. if (target && (target.tagName == "TD" || target.tagName == "TH")) {
  19869. if (me.fireEvent("excludetable", target) === true) return;
  19870. range = new dom.Range(me.document);
  19871. range.setStart(target, 0).setCursor(false, true);
  19872. }
  19873. } else {
  19874. var ut = getUETable(startTd),
  19875. cell = ut ? ut.selectedTds[0] : null;
  19876. if (cell) {
  19877. range = new dom.Range(me.document);
  19878. if (domUtils.isEmptyBlock(cell)) {
  19879. range.setStart(cell, 0).setCursor(false, true);
  19880. } else {
  19881. range.selectNodeContents(cell).shrinkBoundary().setCursor(false, true);
  19882. }
  19883. } else {
  19884. range = me.selection.getRange().shrinkBoundary();
  19885. if (!range.collapsed) {
  19886. var start = domUtils.findParentByTagName(range.startContainer, ['td', 'th'], true),
  19887. end = domUtils.findParentByTagName(range.endContainer, ['td', 'th'], true);
  19888. //在table里边的不能清除
  19889. if (start && !end || !start && end || start && end && start !== end) {
  19890. range.setCursor(false, true);
  19891. }
  19892. }
  19893. }
  19894. startTd = null;
  19895. me.removeListener('mouseover', mouseOverEvent);
  19896. }
  19897. me._selectionChange(250, evt);
  19898. }
  19899. function mouseOverEvent(type, evt) {
  19900. if (isEditorDisabled()) {
  19901. return;
  19902. }
  19903. var me = this,
  19904. tar = evt.target || evt.srcElement;
  19905. currentTd = domUtils.findParentByTagName(tar, "td", true) || domUtils.findParentByTagName(tar, "th", true);
  19906. //需要判断两个TD是否位于同一个表格内
  19907. if (startTd && currentTd &&
  19908. ((startTd.tagName == "TD" && currentTd.tagName == "TD") || (startTd.tagName == "TH" && currentTd.tagName == "TH")) &&
  19909. domUtils.findParentByTagName(startTd, 'table') == domUtils.findParentByTagName(currentTd, 'table')) {
  19910. var ut = getUETable(currentTd);
  19911. if (startTd != currentTd) {
  19912. me.document.body.style.webkitUserSelect = 'none';
  19913. me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
  19914. var range = ut.getCellsRange(startTd, currentTd);
  19915. ut.setSelected(range);
  19916. } else {
  19917. me.document.body.style.webkitUserSelect = '';
  19918. ut.clearSelected();
  19919. }
  19920. }
  19921. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  19922. }
  19923. function setCellHeight(cell, height, backHeight) {
  19924. var lineHight = parseInt(domUtils.getComputedStyle(cell, "line-height"), 10),
  19925. tmpHeight = backHeight + height;
  19926. height = tmpHeight < lineHight ? lineHight : tmpHeight;
  19927. if (cell.style.height) cell.style.height = "";
  19928. cell.rowSpan == 1 ? cell.setAttribute("height", height) : (cell.removeAttribute && cell.removeAttribute("height"));
  19929. }
  19930. function getWidth(cell) {
  19931. if (!cell) return 0;
  19932. return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
  19933. }
  19934. function changeColWidth(cell, changeValue) {
  19935. var ut = getUETable(cell);
  19936. if (ut) {
  19937. //根据当前移动的边框获取相关的单元格
  19938. var table = ut.table,
  19939. cells = getCellsByMoveBorder(cell, table);
  19940. table.style.width = "";
  19941. table.removeAttribute("width");
  19942. //修正改变量
  19943. changeValue = correctChangeValue(changeValue, cell, cells);
  19944. if (cell.nextSibling) {
  19945. var i = 0;
  19946. utils.each(cells, function (cellGroup) {
  19947. cellGroup.left.width = (+cellGroup.left.width) + changeValue;
  19948. cellGroup.right && (cellGroup.right.width = (+cellGroup.right.width) - changeValue);
  19949. });
  19950. } else {
  19951. utils.each(cells, function (cellGroup) {
  19952. cellGroup.left.width -= -changeValue;
  19953. });
  19954. }
  19955. }
  19956. }
  19957. function isEditorDisabled() {
  19958. return me.body.contentEditable === "false";
  19959. }
  19960. function changeRowHeight(td, changeValue) {
  19961. if (Math.abs(changeValue) < 10) return;
  19962. var ut = getUETable(td);
  19963. if (ut) {
  19964. var cells = ut.getSameEndPosCells(td, "y"),
  19965. //备份需要连带变化的td的原始高度,否则后期无法获取正确的值
  19966. backHeight = cells[0] ? cells[0].offsetHeight : 0;
  19967. for (var i = 0, cell; cell = cells[i++];) {
  19968. setCellHeight(cell, changeValue, backHeight);
  19969. }
  19970. }
  19971. }
  19972. /**
  19973. * 获取调整单元格大小的相关单元格
  19974. * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格
  19975. */
  19976. function getCellsByMoveBorder(cell, table, isContainMergeCell) {
  19977. if (!table) {
  19978. table = domUtils.findParentByTagName(cell, 'table');
  19979. }
  19980. if (!table) {
  19981. return null;
  19982. }
  19983. //获取到该单元格所在行的序列号
  19984. var index = domUtils.getNodeIndex(cell),
  19985. temp = cell,
  19986. rows = table.rows,
  19987. colIndex = 0;
  19988. while (temp) {
  19989. //获取到当前单元格在未发生单元格合并时的序列
  19990. if (temp.nodeType === 1) {
  19991. colIndex += (temp.colSpan || 1);
  19992. }
  19993. temp = temp.previousSibling;
  19994. }
  19995. temp = null;
  19996. //记录想关的单元格
  19997. var borderCells = [];
  19998. utils.each(rows, function (tabRow) {
  19999. var cells = tabRow.cells,
  20000. currIndex = 0;
  20001. utils.each(cells, function (tabCell) {
  20002. currIndex += (tabCell.colSpan || 1);
  20003. if (currIndex === colIndex) {
  20004. borderCells.push({
  20005. left: tabCell,
  20006. right: tabCell.nextSibling || null
  20007. });
  20008. return false;
  20009. } else if (currIndex > colIndex) {
  20010. if (isContainMergeCell) {
  20011. borderCells.push({
  20012. left: tabCell
  20013. });
  20014. }
  20015. return false;
  20016. }
  20017. });
  20018. });
  20019. return borderCells;
  20020. }
  20021. /**
  20022. * 通过给定的单元格集合获取最小的单元格width
  20023. */
  20024. function getMinWidthByTableCells(cells) {
  20025. var minWidth = Number.MAX_VALUE;
  20026. for (var i = 0, curCell; curCell = cells[i]; i++) {
  20027. minWidth = Math.min(minWidth, curCell.width || getTableCellWidth(curCell));
  20028. }
  20029. return minWidth;
  20030. }
  20031. function correctChangeValue(changeValue, relatedCell, cells) {
  20032. //为单元格的paading预留空间
  20033. changeValue -= getTabcellSpace();
  20034. if (changeValue < 0) {
  20035. return 0;
  20036. }
  20037. changeValue -= getTableCellWidth(relatedCell);
  20038. //确定方向
  20039. var direction = changeValue < 0 ? 'left' : 'right';
  20040. changeValue = Math.abs(changeValue);
  20041. //只关心非最后一个单元格就可以
  20042. utils.each(cells, function (cellGroup) {
  20043. var curCell = cellGroup[direction];
  20044. //为单元格保留最小空间
  20045. if (curCell) {
  20046. changeValue = Math.min(changeValue, getTableCellWidth(curCell) - cellMinWidth);
  20047. }
  20048. });
  20049. //修正越界
  20050. changeValue = changeValue < 0 ? 0 : changeValue;
  20051. return direction === 'left' ? -changeValue : changeValue;
  20052. }
  20053. function getTableCellWidth(cell) {
  20054. var width = 0,
  20055. //偏移纠正量
  20056. offset = 0,
  20057. width = cell.offsetWidth - getTabcellSpace();
  20058. //最后一个节点纠正一下
  20059. if (!cell.nextSibling) {
  20060. width -= getTableCellOffset(cell);
  20061. }
  20062. width = width < 0 ? 0 : width;
  20063. try {
  20064. cell.width = width;
  20065. } catch (e) {
  20066. }
  20067. return width;
  20068. }
  20069. /**
  20070. * 获取单元格所在表格的最末单元格的偏移量
  20071. */
  20072. function getTableCellOffset(cell) {
  20073. tab = domUtils.findParentByTagName(cell, "table", false);
  20074. if (tab.offsetVal === undefined) {
  20075. var prev = cell.previousSibling;
  20076. if (prev) {
  20077. //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立
  20078. tab.offsetVal = cell.offsetWidth - prev.offsetWidth === UT.borderWidth ? UT.borderWidth : 0;
  20079. } else {
  20080. tab.offsetVal = 0;
  20081. }
  20082. }
  20083. return tab.offsetVal;
  20084. }
  20085. function getTabcellSpace() {
  20086. if (UT.tabcellSpace === undefined) {
  20087. var cell = null,
  20088. tab = me.document.createElement("table"),
  20089. tbody = me.document.createElement("tbody"),
  20090. trow = me.document.createElement("tr"),
  20091. tabcell = me.document.createElement("td"),
  20092. mirror = null;
  20093. tabcell.style.cssText = 'border: 0;';
  20094. tabcell.width = 1;
  20095. trow.appendChild(tabcell);
  20096. trow.appendChild(mirror = tabcell.cloneNode(false));
  20097. tbody.appendChild(trow);
  20098. tab.appendChild(tbody);
  20099. tab.style.cssText = "visibility: hidden;";
  20100. me.body.appendChild(tab);
  20101. UT.paddingSpace = tabcell.offsetWidth - 1;
  20102. var tmpTabWidth = tab.offsetWidth;
  20103. tabcell.style.cssText = '';
  20104. mirror.style.cssText = '';
  20105. UT.borderWidth = (tab.offsetWidth - tmpTabWidth) / 3;
  20106. UT.tabcellSpace = UT.paddingSpace + UT.borderWidth;
  20107. me.body.removeChild(tab);
  20108. }
  20109. getTabcellSpace = function () {
  20110. return UT.tabcellSpace;
  20111. };
  20112. return UT.tabcellSpace;
  20113. }
  20114. function getDragLine(editor, doc) {
  20115. if (mousedown) return;
  20116. dragLine = editor.document.createElement("div");
  20117. domUtils.setAttributes(dragLine, {
  20118. id: "ue_tableDragLine",
  20119. unselectable: 'on',
  20120. contenteditable: false,
  20121. 'onresizestart': 'return false',
  20122. 'ondragstart': 'return false',
  20123. 'onselectstart': 'return false',
  20124. style: "background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)"
  20125. });
  20126. editor.body.appendChild(dragLine);
  20127. }
  20128. function hideDragLine(editor) {
  20129. if (mousedown) return;
  20130. var line;
  20131. while (line = editor.document.getElementById('ue_tableDragLine')) {
  20132. domUtils.remove(line)
  20133. }
  20134. }
  20135. /**
  20136. * 依据state(v|h)在cell位置显示横线
  20137. * @param state
  20138. * @param cell
  20139. */
  20140. function showDragLineAt(state, cell) {
  20141. if (!cell) return;
  20142. var table = domUtils.findParentByTagName(cell, "table"),
  20143. caption = table.getElementsByTagName('caption'),
  20144. width = table.offsetWidth,
  20145. height = table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0),
  20146. tablePos = domUtils.getXY(table),
  20147. cellPos = domUtils.getXY(cell), css;
  20148. switch (state) {
  20149. case "h":
  20150. css = 'height:' + height + 'px;top:' + (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) + 'px;left:' + (cellPos.x + cell.offsetWidth);
  20151. dragLine.style.cssText = css + 'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)';
  20152. break;
  20153. case "v":
  20154. css = 'width:' + width + 'px;left:' + tablePos.x + 'px;top:' + (cellPos.y + cell.offsetHeight);
  20155. //必须加上border:0和color:blue,否则低版ie不支持背景色显示
  20156. dragLine.style.cssText = css + 'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)';
  20157. break;
  20158. default:
  20159. }
  20160. }
  20161. /**
  20162. * 当表格边框颜色为白色时设置为虚线,true为添加虚线
  20163. * @param editor
  20164. * @param flag
  20165. */
  20166. function switchBorderColor(editor, flag) {
  20167. var tableArr = domUtils.getElementsByTagName(editor.body, "table"), color;
  20168. for (var i = 0, node; node = tableArr[i++];) {
  20169. var td = domUtils.getElementsByTagName(node, "td");
  20170. if (td[0]) {
  20171. if (flag) {
  20172. color = (td[0].style.borderColor).replace(/\s/g, "");
  20173. if (/(#ffffff)|(rgb\(255,255,255\))/ig.test(color))
  20174. domUtils.addClass(node, "noBorderTable")
  20175. } else {
  20176. domUtils.removeClasses(node, "noBorderTable")
  20177. }
  20178. }
  20179. }
  20180. }
  20181. function getTableWidth(editor, needIEHack, defaultValue) {
  20182. var body = editor.body;
  20183. return body.offsetWidth - (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) - defaultValue.tableBorder * 2 - (editor.options.offsetWidth || 0);
  20184. }
  20185. /**
  20186. * 获取当前拖动的单元格
  20187. */
  20188. function getTargetTd(editor, evt) {
  20189. var target = domUtils.findParentByTagName(evt.target || evt.srcElement, ["td", "th"], true),
  20190. dir = null;
  20191. if (!target) {
  20192. return null;
  20193. }
  20194. dir = getRelation(target, mouseCoords(evt));
  20195. //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td
  20196. if (!target) {
  20197. return null;
  20198. }
  20199. if (dir === 'h1' && target.previousSibling) {
  20200. var position = domUtils.getXY(target),
  20201. cellWidth = target.offsetWidth;
  20202. if (Math.abs(position.x + cellWidth - evt.clientX) > cellWidth / 3) {
  20203. target = target.previousSibling;
  20204. }
  20205. } else if (dir === 'v1' && target.parentNode.previousSibling) {
  20206. var position = domUtils.getXY(target),
  20207. cellHeight = target.offsetHeight;
  20208. if (Math.abs(position.y + cellHeight - evt.clientY) > cellHeight / 3) {
  20209. target = target.parentNode.previousSibling.firstChild;
  20210. }
  20211. }
  20212. //排除了非td内部以及用于代码高亮部分的td
  20213. return target && !(editor.fireEvent("excludetable", target) === true) ? target : null;
  20214. }
  20215. };
  20216. // plugins/table.sort.js
  20217. /**
  20218. * Created with JetBrains PhpStorm.
  20219. * User: Jinqn
  20220. * Date: 13-10-12
  20221. * Time: 上午10:20
  20222. * To change this template use File | Settings | File Templates.
  20223. */
  20224. UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) {
  20225. var table = this.table,
  20226. rows = table.rows,
  20227. trArray = [],
  20228. flag = rows[0].cells[0].tagName === "TH",
  20229. lastRowIndex = 0;
  20230. if (this.selectedTds.length) {
  20231. var range = this.cellsRange,
  20232. len = range.endRowIndex + 1;
  20233. for (var i = range.beginRowIndex; i < len; i++) {
  20234. trArray[i] = rows[i];
  20235. }
  20236. trArray.splice(0, range.beginRowIndex);
  20237. lastRowIndex = (range.endRowIndex + 1) === this.rowsNum ? 0 : range.endRowIndex + 1;
  20238. } else {
  20239. for (var i = 0, len = rows.length; i < len; i++) {
  20240. trArray[i] = rows[i];
  20241. }
  20242. }
  20243. var Fn = {
  20244. 'reversecurrent': function (td1, td2) {
  20245. return 1;
  20246. },
  20247. 'orderbyasc': function (td1, td2) {
  20248. var value1 = td1.innerText || td1.textContent,
  20249. value2 = td2.innerText || td2.textContent;
  20250. return value1.localeCompare(value2);
  20251. },
  20252. 'reversebyasc': function (td1, td2) {
  20253. var value1 = td1.innerHTML,
  20254. value2 = td2.innerHTML;
  20255. return value2.localeCompare(value1);
  20256. },
  20257. 'orderbynum': function (td1, td2) {
  20258. var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
  20259. value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/);
  20260. if (value1) value1 = +value1[0];
  20261. if (value2) value2 = +value2[0];
  20262. return (value1 || 0) - (value2 || 0);
  20263. },
  20264. 'reversebynum': function (td1, td2) {
  20265. var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
  20266. value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/);
  20267. if (value1) value1 = +value1[0];
  20268. if (value2) value2 = +value2[0];
  20269. return (value2 || 0) - (value1 || 0);
  20270. }
  20271. };
  20272. //对表格设置排序的标记data-sort-type
  20273. table.setAttribute('data-sort-type', compareFn && typeof compareFn === "string" && Fn[compareFn] ? compareFn : '');
  20274. //th不参与排序
  20275. flag && trArray.splice(0, 1);
  20276. trArray = utils.sort(trArray, function (tr1, tr2) {
  20277. var result;
  20278. if (compareFn && typeof compareFn === "function") {
  20279. result = compareFn.call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
  20280. } else if (compareFn && typeof compareFn === "number") {
  20281. result = 1;
  20282. } else if (compareFn && typeof compareFn === "string" && Fn[compareFn]) {
  20283. result = Fn[compareFn].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
  20284. } else {
  20285. result = Fn['orderbyasc'].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex]);
  20286. }
  20287. return result;
  20288. });
  20289. var fragment = table.ownerDocument.createDocumentFragment();
  20290. for (var j = 0, len = trArray.length; j < len; j++) {
  20291. fragment.appendChild(trArray[j]);
  20292. }
  20293. var tbody = table.getElementsByTagName("tbody")[0];
  20294. if (!lastRowIndex) {
  20295. tbody.appendChild(fragment);
  20296. } else {
  20297. tbody.insertBefore(fragment, rows[lastRowIndex - range.endRowIndex + range.beginRowIndex - 1])
  20298. }
  20299. };
  20300. UE.plugins['tablesort'] = function () {
  20301. var me = this,
  20302. UT = UE.UETable,
  20303. getUETable = function (tdOrTable) {
  20304. return UT.getUETable(tdOrTable);
  20305. },
  20306. getTableItemsByRange = function (editor) {
  20307. return UT.getTableItemsByRange(editor);
  20308. };
  20309. me.ready(function () {
  20310. //添加表格可排序的样式
  20311. utils.cssRule('tablesort',
  20312. 'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' +
  20313. ' background-image:url(' + me.options.themePath + me.options.theme + '/images/sortable.png);}',
  20314. me.document);
  20315. //做单元格合并操作时,清除可排序标识
  20316. me.addListener("afterexeccommand", function (type, cmd) {
  20317. if (cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') {
  20318. this.execCommand('disablesort');
  20319. }
  20320. });
  20321. });
  20322. //表格排序
  20323. UE.commands['sorttable'] = {
  20324. queryCommandState: function () {
  20325. var me = this,
  20326. tableItems = getTableItemsByRange(me);
  20327. if (!tableItems.cell) return -1;
  20328. var table = tableItems.table,
  20329. cells = table.getElementsByTagName("td");
  20330. for (var i = 0, cell; cell = cells[i++];) {
  20331. if (cell.rowSpan != 1 || cell.colSpan != 1) return -1;
  20332. }
  20333. return 0;
  20334. },
  20335. execCommand: function (cmd, fn) {
  20336. var me = this,
  20337. range = me.selection.getRange(),
  20338. bk = range.createBookmark(true),
  20339. tableItems = getTableItemsByRange(me),
  20340. cell = tableItems.cell,
  20341. ut = getUETable(tableItems.table),
  20342. cellInfo = ut.getCellInfo(cell);
  20343. ut.sortTable(cellInfo.cellIndex, fn);
  20344. range.moveToBookmark(bk);
  20345. try {
  20346. range.select();
  20347. } catch (e) {
  20348. }
  20349. }
  20350. };
  20351. //设置表格可排序,清除表格可排序
  20352. UE.commands["enablesort"] = UE.commands["disablesort"] = {
  20353. queryCommandState: function (cmd) {
  20354. var table = getTableItemsByRange(this).table;
  20355. if (table && cmd == 'enablesort') {
  20356. var cells = domUtils.getElementsByTagName(table, 'th td');
  20357. for (var i = 0; i < cells.length; i++) {
  20358. if (cells[i].getAttribute('colspan') > 1 || cells[i].getAttribute('rowspan') > 1) return -1;
  20359. }
  20360. }
  20361. return !table ? -1 : cmd == 'enablesort' ^ table.getAttribute('data-sort') != 'sortEnabled' ? -1 : 0;
  20362. },
  20363. execCommand: function (cmd) {
  20364. var table = getTableItemsByRange(this).table;
  20365. table.setAttribute("data-sort", cmd == "enablesort" ? "sortEnabled" : "sortDisabled");
  20366. cmd == "enablesort" ? domUtils.addClass(table, "sortEnabled") : domUtils.removeClasses(table, "sortEnabled");
  20367. }
  20368. };
  20369. };
  20370. // plugins/contextmenu.js
  20371. ///import core
  20372. ///commands 右键菜单
  20373. ///commandsName ContextMenu
  20374. ///commandsTitle 右键菜单
  20375. /**
  20376. * 右键菜单
  20377. * @function
  20378. * @name baidu.editor.plugins.contextmenu
  20379. * @author zhanyi
  20380. */
  20381. UE.plugins['contextmenu'] = function () {
  20382. var me = this;
  20383. me.setOpt('enableContextMenu', true);
  20384. if (me.getOpt('enableContextMenu') === false) {
  20385. return;
  20386. }
  20387. var lang = me.getLang("contextMenu"),
  20388. menu,
  20389. items = me.options.contextMenu || [
  20390. {label: lang['selectall'], cmdName: 'selectall'},
  20391. {
  20392. label: lang.cleardoc,
  20393. cmdName: 'cleardoc',
  20394. exec: function () {
  20395. if (confirm(lang.confirmclear)) {
  20396. this.execCommand('cleardoc');
  20397. }
  20398. }
  20399. },
  20400. '-',
  20401. {
  20402. label: lang.unlink,
  20403. cmdName: 'unlink'
  20404. },
  20405. '-',
  20406. {
  20407. group: lang.paragraph,
  20408. icon: 'justifyjustify',
  20409. subMenu: [
  20410. {
  20411. label: lang.justifyleft,
  20412. cmdName: 'justify',
  20413. value: 'left'
  20414. },
  20415. {
  20416. label: lang.justifyright,
  20417. cmdName: 'justify',
  20418. value: 'right'
  20419. },
  20420. {
  20421. label: lang.justifycenter,
  20422. cmdName: 'justify',
  20423. value: 'center'
  20424. },
  20425. {
  20426. label: lang.justifyjustify,
  20427. cmdName: 'justify',
  20428. value: 'justify'
  20429. }
  20430. ]
  20431. },
  20432. '-',
  20433. {
  20434. group: lang.table,
  20435. icon: 'table',
  20436. subMenu: [
  20437. {
  20438. label: lang.inserttable,
  20439. cmdName: 'inserttable'
  20440. },
  20441. {
  20442. label: lang.deletetable,
  20443. cmdName: 'deletetable'
  20444. },
  20445. '-',
  20446. {
  20447. label: lang.deleterow,
  20448. cmdName: 'deleterow'
  20449. },
  20450. {
  20451. label: lang.deletecol,
  20452. cmdName: 'deletecol'
  20453. },
  20454. {
  20455. label: lang.insertcol,
  20456. cmdName: 'insertcol'
  20457. },
  20458. {
  20459. label: lang.insertcolnext,
  20460. cmdName: 'insertcolnext'
  20461. },
  20462. {
  20463. label: lang.insertrow,
  20464. cmdName: 'insertrow'
  20465. },
  20466. {
  20467. label: lang.insertrownext,
  20468. cmdName: 'insertrownext'
  20469. },
  20470. '-',
  20471. {
  20472. label: lang.insertcaption,
  20473. cmdName: 'insertcaption'
  20474. },
  20475. {
  20476. label: lang.deletecaption,
  20477. cmdName: 'deletecaption'
  20478. },
  20479. {
  20480. label: lang.inserttitle,
  20481. cmdName: 'inserttitle'
  20482. },
  20483. {
  20484. label: lang.deletetitle,
  20485. cmdName: 'deletetitle'
  20486. },
  20487. {
  20488. label: lang.inserttitlecol,
  20489. cmdName: 'inserttitlecol'
  20490. },
  20491. {
  20492. label: lang.deletetitlecol,
  20493. cmdName: 'deletetitlecol'
  20494. },
  20495. '-',
  20496. {
  20497. label: lang.mergecells,
  20498. cmdName: 'mergecells'
  20499. },
  20500. {
  20501. label: lang.mergeright,
  20502. cmdName: 'mergeright'
  20503. },
  20504. {
  20505. label: lang.mergedown,
  20506. cmdName: 'mergedown'
  20507. },
  20508. '-',
  20509. {
  20510. label: lang.splittorows,
  20511. cmdName: 'splittorows'
  20512. },
  20513. {
  20514. label: lang.splittocols,
  20515. cmdName: 'splittocols'
  20516. },
  20517. {
  20518. label: lang.splittocells,
  20519. cmdName: 'splittocells'
  20520. },
  20521. '-',
  20522. {
  20523. label: lang.averageDiseRow,
  20524. cmdName: 'averagedistributerow'
  20525. },
  20526. {
  20527. label: lang.averageDisCol,
  20528. cmdName: 'averagedistributecol'
  20529. },
  20530. '-',
  20531. {
  20532. label: lang.edittd,
  20533. cmdName: 'edittd',
  20534. exec: function () {
  20535. if (UE.ui['edittd']) {
  20536. new UE.ui['edittd'](this);
  20537. }
  20538. this.getDialog('edittd').open();
  20539. }
  20540. },
  20541. {
  20542. label: lang.edittable,
  20543. cmdName: 'edittable',
  20544. exec: function () {
  20545. if (UE.ui['edittable']) {
  20546. new UE.ui['edittable'](this);
  20547. }
  20548. this.getDialog('edittable').open();
  20549. }
  20550. },
  20551. {
  20552. label: lang.setbordervisible,
  20553. cmdName: 'setbordervisible'
  20554. }
  20555. ]
  20556. },
  20557. {
  20558. group: lang.tablesort,
  20559. icon: 'tablesort',
  20560. subMenu: [
  20561. {
  20562. label: lang.enablesort,
  20563. cmdName: 'enablesort'
  20564. },
  20565. {
  20566. label: lang.disablesort,
  20567. cmdName: 'disablesort'
  20568. },
  20569. '-',
  20570. {
  20571. label: lang.reversecurrent,
  20572. cmdName: 'sorttable',
  20573. value: 'reversecurrent'
  20574. },
  20575. {
  20576. label: lang.orderbyasc,
  20577. cmdName: 'sorttable',
  20578. value: 'orderbyasc'
  20579. },
  20580. {
  20581. label: lang.reversebyasc,
  20582. cmdName: 'sorttable',
  20583. value: 'reversebyasc'
  20584. },
  20585. {
  20586. label: lang.orderbynum,
  20587. cmdName: 'sorttable',
  20588. value: 'orderbynum'
  20589. },
  20590. {
  20591. label: lang.reversebynum,
  20592. cmdName: 'sorttable',
  20593. value: 'reversebynum'
  20594. }
  20595. ]
  20596. },
  20597. {
  20598. group: lang.borderbk,
  20599. icon: 'borderBack',
  20600. subMenu: [
  20601. {
  20602. label: lang.setcolor,
  20603. cmdName: "interlacetable",
  20604. exec: function () {
  20605. this.execCommand("interlacetable");
  20606. }
  20607. },
  20608. {
  20609. label: lang.unsetcolor,
  20610. cmdName: "uninterlacetable",
  20611. exec: function () {
  20612. this.execCommand("uninterlacetable");
  20613. }
  20614. },
  20615. {
  20616. label: lang.setbackground,
  20617. cmdName: "settablebackground",
  20618. exec: function () {
  20619. this.execCommand("settablebackground", {repeat: true, colorList: ["#bbb", "#ccc"]});
  20620. }
  20621. },
  20622. {
  20623. label: lang.unsetbackground,
  20624. cmdName: "cleartablebackground",
  20625. exec: function () {
  20626. this.execCommand("cleartablebackground");
  20627. }
  20628. },
  20629. {
  20630. label: lang.redandblue,
  20631. cmdName: "settablebackground",
  20632. exec: function () {
  20633. this.execCommand("settablebackground", {repeat: true, colorList: ["red", "blue"]});
  20634. }
  20635. },
  20636. {
  20637. label: lang.threecolorgradient,
  20638. cmdName: "settablebackground",
  20639. exec: function () {
  20640. this.execCommand("settablebackground", {
  20641. repeat: true,
  20642. colorList: ["#aaa", "#bbb", "#ccc"]
  20643. });
  20644. }
  20645. }
  20646. ]
  20647. },
  20648. {
  20649. group: lang.aligntd,
  20650. icon: 'aligntd',
  20651. subMenu: [
  20652. {
  20653. cmdName: 'cellalignment',
  20654. value: {align: 'left', vAlign: 'top'}
  20655. },
  20656. {
  20657. cmdName: 'cellalignment',
  20658. value: {align: 'center', vAlign: 'top'}
  20659. },
  20660. {
  20661. cmdName: 'cellalignment',
  20662. value: {align: 'right', vAlign: 'top'}
  20663. },
  20664. {
  20665. cmdName: 'cellalignment',
  20666. value: {align: 'left', vAlign: 'middle'}
  20667. },
  20668. {
  20669. cmdName: 'cellalignment',
  20670. value: {align: 'center', vAlign: 'middle'}
  20671. },
  20672. {
  20673. cmdName: 'cellalignment',
  20674. value: {align: 'right', vAlign: 'middle'}
  20675. },
  20676. {
  20677. cmdName: 'cellalignment',
  20678. value: {align: 'left', vAlign: 'bottom'}
  20679. },
  20680. {
  20681. cmdName: 'cellalignment',
  20682. value: {align: 'center', vAlign: 'bottom'}
  20683. },
  20684. {
  20685. cmdName: 'cellalignment',
  20686. value: {align: 'right', vAlign: 'bottom'}
  20687. }
  20688. ]
  20689. },
  20690. {
  20691. group: lang.aligntable,
  20692. icon: 'aligntable',
  20693. subMenu: [
  20694. {
  20695. cmdName: 'tablealignment',
  20696. className: 'left',
  20697. label: lang.tableleft,
  20698. value: "left"
  20699. },
  20700. {
  20701. cmdName: 'tablealignment',
  20702. className: 'center',
  20703. label: lang.tablecenter,
  20704. value: "center"
  20705. },
  20706. {
  20707. cmdName: 'tablealignment',
  20708. className: 'right',
  20709. label: lang.tableright,
  20710. value: "right"
  20711. }
  20712. ]
  20713. },
  20714. '-',
  20715. {
  20716. label: lang.insertparagraphbefore,
  20717. cmdName: 'insertparagraph',
  20718. value: true
  20719. },
  20720. {
  20721. label: lang.insertparagraphafter,
  20722. cmdName: 'insertparagraph'
  20723. },
  20724. {
  20725. label: lang['copy'],
  20726. cmdName: 'copy'
  20727. },
  20728. {
  20729. label: lang['paste'],
  20730. cmdName: 'paste'
  20731. }
  20732. ];
  20733. if (!items.length) {
  20734. return;
  20735. }
  20736. var uiUtils = UE.ui.uiUtils;
  20737. me.addListener('contextmenu', function (type, evt) {
  20738. var offset = uiUtils.getViewportOffsetByEvent(evt);
  20739. me.fireEvent('beforeselectionchange');
  20740. if (menu) {
  20741. menu.destroy();
  20742. }
  20743. for (var i = 0, ti, contextItems = []; ti = items[i]; i++) {
  20744. var last;
  20745. (function (item) {
  20746. if (item == '-') {
  20747. if ((last = contextItems[contextItems.length - 1]) && last !== '-') {
  20748. contextItems.push('-');
  20749. }
  20750. } else if (item.hasOwnProperty("group")) {
  20751. for (var j = 0, cj, subMenu = []; cj = item.subMenu[j]; j++) {
  20752. (function (subItem) {
  20753. if (subItem == '-') {
  20754. if ((last = subMenu[subMenu.length - 1]) && last !== '-') {
  20755. subMenu.push('-');
  20756. } else {
  20757. subMenu.splice(subMenu.length - 1);
  20758. }
  20759. } else {
  20760. if ((me.commands[subItem.cmdName] || UE.commands[subItem.cmdName] || subItem.query) &&
  20761. (subItem.query ? subItem.query() : me.queryCommandState(subItem.cmdName)) > -1) {
  20762. subMenu.push({
  20763. 'label': subItem.label || me.getLang("contextMenu." + subItem.cmdName + (subItem.value || '')) || "",
  20764. 'className': 'edui-for-' + subItem.cmdName + (subItem.className ? (' edui-for-' + subItem.cmdName + '-' + subItem.className) : ''),
  20765. onclick: subItem.exec ? function () {
  20766. subItem.exec.call(me);
  20767. } : function () {
  20768. me.execCommand(subItem.cmdName, subItem.value);
  20769. }
  20770. });
  20771. }
  20772. }
  20773. })(cj);
  20774. }
  20775. if (subMenu.length) {
  20776. function getLabel() {
  20777. switch (item.icon) {
  20778. case "table":
  20779. return me.getLang("contextMenu.table");
  20780. case "justifyjustify":
  20781. return me.getLang("contextMenu.paragraph");
  20782. case "aligntd":
  20783. return me.getLang("contextMenu.aligntd");
  20784. case "aligntable":
  20785. return me.getLang("contextMenu.aligntable");
  20786. case "tablesort":
  20787. return lang.tablesort;
  20788. case "borderBack":
  20789. return lang.borderbk;
  20790. default :
  20791. return '';
  20792. }
  20793. }
  20794. contextItems.push({
  20795. //todo 修正成自动获取方式
  20796. 'label': getLabel(),
  20797. className: 'edui-for-' + item.icon,
  20798. 'subMenu': {
  20799. items: subMenu,
  20800. editor: me
  20801. }
  20802. });
  20803. }
  20804. } else {
  20805. //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法
  20806. if ((me.commands[item.cmdName] || UE.commands[item.cmdName] || item.query) &&
  20807. (item.query ? item.query.call(me) : me.queryCommandState(item.cmdName)) > -1) {
  20808. contextItems.push({
  20809. 'label': item.label || me.getLang("contextMenu." + item.cmdName),
  20810. className: 'edui-for-' + (item.icon ? item.icon : item.cmdName + (item.value || '')),
  20811. onclick: item.exec ? function () {
  20812. item.exec.call(me);
  20813. } : function () {
  20814. me.execCommand(item.cmdName, item.value);
  20815. }
  20816. });
  20817. }
  20818. }
  20819. })(ti);
  20820. }
  20821. if (contextItems[contextItems.length - 1] == '-') {
  20822. contextItems.pop();
  20823. }
  20824. menu = new UE.ui.Menu({
  20825. items: contextItems,
  20826. className: "edui-contextmenu",
  20827. editor: me
  20828. });
  20829. menu.render();
  20830. menu.showAt(offset);
  20831. me.fireEvent("aftershowcontextmenu", menu);
  20832. domUtils.preventDefault(evt);
  20833. if (browser.ie) {
  20834. var ieRange;
  20835. try {
  20836. ieRange = me.selection.getNative().createRange();
  20837. } catch (e) {
  20838. return;
  20839. }
  20840. if (ieRange.item) {
  20841. var range = new dom.Range(me.document);
  20842. range.selectNode(ieRange.item(0)).select(true, true);
  20843. }
  20844. }
  20845. });
  20846. // 添加复制的flash按钮
  20847. me.addListener('aftershowcontextmenu', function (type, menu) {
  20848. if (me.zeroclipboard) {
  20849. var items = menu.items;
  20850. for (var key in items) {
  20851. if (items[key].className == 'edui-for-copy') {
  20852. me.zeroclipboard.clip(items[key].getDom());
  20853. }
  20854. }
  20855. }
  20856. });
  20857. };
  20858. // plugins/shortcutmenu.js
  20859. ///import core
  20860. ///commands 弹出菜单
  20861. // commandsName popupmenu
  20862. ///commandsTitle 弹出菜单
  20863. /**
  20864. * 弹出菜单
  20865. * @function
  20866. * @name baidu.editor.plugins.popupmenu
  20867. * @author xuheng
  20868. */
  20869. UE.plugins['shortcutmenu'] = function () {
  20870. var me = this,
  20871. menu,
  20872. items = me.options.shortcutMenu || [];
  20873. if (!items.length) {
  20874. return;
  20875. }
  20876. me.addListener('contextmenu mouseup', function (type, e) {
  20877. var me = this,
  20878. customEvt = {
  20879. type: type,
  20880. target: e.target || e.srcElement,
  20881. screenX: e.screenX,
  20882. screenY: e.screenY,
  20883. clientX: e.clientX,
  20884. clientY: e.clientY
  20885. };
  20886. setTimeout(function () {
  20887. var rng = me.selection.getRange();
  20888. if (rng.collapsed === false || type == "contextmenu") {
  20889. if (!menu) {
  20890. menu = new baidu.editor.ui.ShortCutMenu({
  20891. editor: me,
  20892. items: items,
  20893. theme: me.options.theme,
  20894. className: 'edui-shortcutmenu'
  20895. });
  20896. menu.render();
  20897. me.fireEvent("afterrendershortcutmenu", menu);
  20898. }
  20899. menu.show(customEvt, !!UE.plugins['contextmenu']);
  20900. }
  20901. });
  20902. if (type == 'contextmenu') {
  20903. domUtils.preventDefault(e);
  20904. if (browser.ie9below) {
  20905. var ieRange;
  20906. try {
  20907. ieRange = me.selection.getNative().createRange();
  20908. } catch (e) {
  20909. return;
  20910. }
  20911. if (ieRange.item) {
  20912. var range = new dom.Range(me.document);
  20913. range.selectNode(ieRange.item(0)).select(true, true);
  20914. }
  20915. }
  20916. }
  20917. });
  20918. me.addListener('keydown', function (type) {
  20919. if (type == "keydown") {
  20920. menu && !menu.isHidden && menu.hide();
  20921. }
  20922. });
  20923. };
  20924. // plugins/basestyle.js
  20925. /**
  20926. * B、I、sub、super命令支持
  20927. * @file
  20928. * @since 1.2.6.1
  20929. */
  20930. UE.plugins['basestyle'] = function () {
  20931. /**
  20932. * 字体加粗
  20933. * @command bold
  20934. * @param { String } cmd 命令字符串
  20935. * @remind 对已加粗的文本内容执行该命令, 将取消加粗
  20936. * @method execCommand
  20937. * @example
  20938. * ```javascript
  20939. * //editor是编辑器实例
  20940. * //对当前选中的文本内容执行加粗操作
  20941. * //第一次执行, 文本内容加粗
  20942. * editor.execCommand( 'bold' );
  20943. *
  20944. * //第二次执行, 文本内容取消加粗
  20945. * editor.execCommand( 'bold' );
  20946. * ```
  20947. */
  20948. /**
  20949. * 字体倾斜
  20950. * @command italic
  20951. * @method execCommand
  20952. * @param { String } cmd 命令字符串
  20953. * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
  20954. * @example
  20955. * ```javascript
  20956. * //editor是编辑器实例
  20957. * //对当前选中的文本内容执行斜体操作
  20958. * //第一次操作, 文本内容将变成斜体
  20959. * editor.execCommand( 'italic' );
  20960. *
  20961. * //再次对同一文本内容执行, 则文本内容将恢复正常
  20962. * editor.execCommand( 'italic' );
  20963. * ```
  20964. */
  20965. /**
  20966. * 下标文本,与“superscript”命令互斥
  20967. * @command subscript
  20968. * @method execCommand
  20969. * @remind 把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
  20970. * @param { String } cmd 命令字符串
  20971. * @example
  20972. * ```javascript
  20973. * //editor是编辑器实例
  20974. * //对当前选中的文本内容执行下标操作
  20975. * //第一次操作, 文本内容将变成下标文本
  20976. * editor.execCommand( 'subscript' );
  20977. *
  20978. * //再次对同一文本内容执行, 则文本内容将恢复正常
  20979. * editor.execCommand( 'subscript' );
  20980. * ```
  20981. */
  20982. /**
  20983. * 上标文本,与“subscript”命令互斥
  20984. * @command superscript
  20985. * @method execCommand
  20986. * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
  20987. * @param { String } cmd 命令字符串
  20988. * @example
  20989. * ```javascript
  20990. * //editor是编辑器实例
  20991. * //对当前选中的文本内容执行上标操作
  20992. * //第一次操作, 文本内容将变成上标文本
  20993. * editor.execCommand( 'superscript' );
  20994. *
  20995. * //再次对同一文本内容执行, 则文本内容将恢复正常
  20996. * editor.execCommand( 'superscript' );
  20997. * ```
  20998. */
  20999. var basestyles = {
  21000. 'bold': ['strong', 'b'],
  21001. 'italic': ['em', 'i'],
  21002. 'subscript': ['sub'],
  21003. 'superscript': ['sup']
  21004. },
  21005. getObj = function (editor, tagNames) {
  21006. return domUtils.filterNodeList(editor.selection.getStartElementPath(), tagNames);
  21007. },
  21008. me = this;
  21009. //添加快捷键
  21010. me.addshortcutkey({
  21011. "Bold": "ctrl+66",//^B
  21012. "Italic": "ctrl+73", //^I
  21013. "Underline": "ctrl+85"//^U
  21014. });
  21015. me.addInputRule(function (root) {
  21016. utils.each(root.getNodesByTagName('b i'), function (node) {
  21017. switch (node.tagName) {
  21018. case 'b':
  21019. node.tagName = 'strong';
  21020. break;
  21021. case 'i':
  21022. node.tagName = 'em';
  21023. }
  21024. });
  21025. });
  21026. for (var style in basestyles) {
  21027. (function (cmd, tagNames) {
  21028. me.commands[cmd] = {
  21029. execCommand: function (cmdName) {
  21030. var range = me.selection.getRange(), obj = getObj(this, tagNames);
  21031. if (range.collapsed) {
  21032. if (obj) {
  21033. var tmpText = me.document.createTextNode('');
  21034. range.insertNode(tmpText).removeInlineStyle(tagNames);
  21035. range.setStartBefore(tmpText);
  21036. domUtils.remove(tmpText);
  21037. } else {
  21038. var tmpNode = range.document.createElement(tagNames[0]);
  21039. if (cmdName == 'superscript' || cmdName == 'subscript') {
  21040. tmpText = me.document.createTextNode('');
  21041. range.insertNode(tmpText)
  21042. .removeInlineStyle(['sub', 'sup'])
  21043. .setStartBefore(tmpText)
  21044. .collapse(true);
  21045. }
  21046. range.insertNode(tmpNode).setStart(tmpNode, 0);
  21047. }
  21048. range.collapse(true);
  21049. } else {
  21050. if (cmdName == 'superscript' || cmdName == 'subscript') {
  21051. if (!obj || obj.tagName.toLowerCase() != cmdName) {
  21052. range.removeInlineStyle(['sub', 'sup']);
  21053. }
  21054. }
  21055. obj ? range.removeInlineStyle(tagNames) : range.applyInlineStyle(tagNames[0]);
  21056. }
  21057. range.select();
  21058. },
  21059. queryCommandState: function () {
  21060. return getObj(this, tagNames) ? 1 : 0;
  21061. }
  21062. };
  21063. })(style, basestyles[style]);
  21064. }
  21065. };
  21066. // plugins/elementpath.js
  21067. /**
  21068. * 选取路径命令
  21069. * @file
  21070. */
  21071. UE.plugins['elementpath'] = function () {
  21072. var currentLevel,
  21073. tagNames,
  21074. me = this;
  21075. me.setOpt('elementPathEnabled', true);
  21076. if (!me.options.elementPathEnabled) {
  21077. return;
  21078. }
  21079. me.commands['elementpath'] = {
  21080. execCommand: function (cmdName, level) {
  21081. var start = tagNames[level],
  21082. range = me.selection.getRange();
  21083. currentLevel = level * 1;
  21084. range.selectNode(start).select();
  21085. },
  21086. queryCommandValue: function () {
  21087. //产生一个副本,不能修改原来的startElementPath;
  21088. var parents = [].concat(this.selection.getStartElementPath()).reverse(),
  21089. names = [];
  21090. tagNames = parents;
  21091. for (var i = 0, ci; ci = parents[i]; i++) {
  21092. if (ci.nodeType == 3) {
  21093. continue;
  21094. }
  21095. var name = ci.tagName.toLowerCase();
  21096. if (name == 'img' && ci.getAttribute('anchorname')) {
  21097. name = 'anchor';
  21098. }
  21099. names[i] = name;
  21100. if (currentLevel == i) {
  21101. currentLevel = -1;
  21102. break;
  21103. }
  21104. }
  21105. return names;
  21106. }
  21107. };
  21108. };
  21109. // plugins/formatmatch.js
  21110. /**
  21111. * 格式刷,只格式inline的
  21112. * @file
  21113. * @since 1.2.6.1
  21114. */
  21115. /**
  21116. * 格式刷
  21117. * @command formatmatch
  21118. * @method execCommand
  21119. * @remind 该操作不能复制段落格式
  21120. * @param { String } cmd 命令字符串
  21121. * @example
  21122. * ```javascript
  21123. * //editor是编辑器实例
  21124. * //获取格式刷
  21125. * editor.execCommand( 'formatmatch' );
  21126. * ```
  21127. */
  21128. UE.plugins['formatmatch'] = function () {
  21129. var me = this,
  21130. list = [], img,
  21131. flag = 0;
  21132. me.addListener('reset', function () {
  21133. list = [];
  21134. flag = 0;
  21135. });
  21136. function addList(type, evt) {
  21137. if (browser.webkit) {
  21138. var target = evt.target.tagName == 'IMG' ? evt.target : null;
  21139. }
  21140. function addFormat(range) {
  21141. if (text) {
  21142. range.selectNode(text);
  21143. }
  21144. return range.applyInlineStyle(list[list.length - 1].tagName, null, list);
  21145. }
  21146. me.undoManger && me.undoManger.save();
  21147. var range = me.selection.getRange(),
  21148. imgT = target || range.getClosedNode();
  21149. if (img && imgT && imgT.tagName == 'IMG') {
  21150. //trace:964
  21151. imgT.style.cssText += ';float:' + (img.style.cssFloat || img.style.styleFloat || 'none') + ';display:' + (img.style.display || 'inline');
  21152. img = null;
  21153. } else {
  21154. if (!img) {
  21155. var collapsed = range.collapsed;
  21156. if (collapsed) {
  21157. var text = me.document.createTextNode('match');
  21158. range.insertNode(text).select();
  21159. }
  21160. me.__hasEnterExecCommand = true;
  21161. //不能把block上的属性干掉
  21162. //trace:1553
  21163. var removeFormatAttributes = me.options.removeFormatAttributes;
  21164. me.options.removeFormatAttributes = '';
  21165. me.execCommand('removeformat');
  21166. me.options.removeFormatAttributes = removeFormatAttributes;
  21167. me.__hasEnterExecCommand = false;
  21168. //trace:969
  21169. range = me.selection.getRange();
  21170. if (list.length) {
  21171. addFormat(range);
  21172. }
  21173. if (text) {
  21174. range.setStartBefore(text).collapse(true);
  21175. }
  21176. range.select();
  21177. text && domUtils.remove(text);
  21178. }
  21179. }
  21180. me.undoManger && me.undoManger.save();
  21181. me.removeListener('mouseup', addList);
  21182. flag = 0;
  21183. }
  21184. me.commands['formatmatch'] = {
  21185. execCommand: function (cmdName) {
  21186. if (flag) {
  21187. flag = 0;
  21188. list = [];
  21189. me.removeListener('mouseup', addList);
  21190. return;
  21191. }
  21192. var range = me.selection.getRange();
  21193. img = range.getClosedNode();
  21194. if (!img || img.tagName != 'IMG') {
  21195. range.collapse(true).shrinkBoundary();
  21196. var start = range.startContainer;
  21197. list = domUtils.findParents(start, true, function (node) {
  21198. return !domUtils.isBlockElm(node) && node.nodeType == 1;
  21199. });
  21200. //a不能加入格式刷, 并且克隆节点
  21201. for (var i = 0, ci; ci = list[i]; i++) {
  21202. if (ci.tagName == 'A') {
  21203. list.splice(i, 1);
  21204. break;
  21205. }
  21206. }
  21207. }
  21208. me.addListener('mouseup', addList);
  21209. flag = 1;
  21210. },
  21211. queryCommandState: function () {
  21212. return flag;
  21213. },
  21214. notNeedUndo: 1
  21215. };
  21216. };
  21217. // plugins/searchreplace.js
  21218. ///import core
  21219. ///commands 查找替换
  21220. ///commandsName SearchReplace
  21221. ///commandsTitle 查询替换
  21222. ///commandsDialog dialogs\searchreplace
  21223. /**
  21224. * @description 查找替换
  21225. * @author zhanyi
  21226. */
  21227. UE.plugin.register('searchreplace', function () {
  21228. var me = this;
  21229. var _blockElm = {'table': 1, 'tbody': 1, 'tr': 1, 'ol': 1, 'ul': 1};
  21230. function findTextInString(textContent, opt, currentIndex) {
  21231. var str = opt.searchStr;
  21232. if (opt.dir == -1) {
  21233. textContent = textContent.split('').reverse().join('');
  21234. str = str.split('').reverse().join('');
  21235. currentIndex = textContent.length - currentIndex;
  21236. }
  21237. var reg = new RegExp(str, 'g' + (opt.casesensitive ? '' : 'i')), match;
  21238. while (match = reg.exec(textContent)) {
  21239. if (match.index >= currentIndex) {
  21240. return opt.dir == -1 ? textContent.length - match.index - opt.searchStr.length : match.index;
  21241. }
  21242. }
  21243. return -1
  21244. }
  21245. function findTextBlockElm(node, currentIndex, opt) {
  21246. var textContent, index, methodName = opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode';
  21247. if (domUtils.isBody(node)) {
  21248. node = node.firstChild;
  21249. }
  21250. var first = 1;
  21251. while (node) {
  21252. textContent = node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent'];
  21253. index = findTextInString(textContent, opt, currentIndex);
  21254. first = 0;
  21255. if (index != -1) {
  21256. return {
  21257. 'node': node,
  21258. 'index': index
  21259. }
  21260. }
  21261. node = domUtils[methodName](node);
  21262. while (node && _blockElm[node.nodeName.toLowerCase()]) {
  21263. node = domUtils[methodName](node, true);
  21264. }
  21265. if (node) {
  21266. currentIndex = opt.dir == -1 ? (node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']).length : 0;
  21267. }
  21268. }
  21269. }
  21270. function findNTextInBlockElm(node, index, str) {
  21271. var currentIndex = 0,
  21272. currentNode = node.firstChild,
  21273. currentNodeLength = 0,
  21274. result;
  21275. while (currentNode) {
  21276. if (currentNode.nodeType == 3) {
  21277. currentNodeLength = currentNode.nodeValue.replace(/(^[\t\r\n]+)|([\t\r\n]+$)/, '').length;
  21278. currentIndex += currentNodeLength;
  21279. if (currentIndex >= index) {
  21280. return {
  21281. 'node': currentNode,
  21282. 'index': currentNodeLength - (currentIndex - index)
  21283. }
  21284. }
  21285. } else if (!dtd.$empty[currentNode.tagName]) {
  21286. currentNodeLength = currentNode[browser.ie ? 'innerText' : 'textContent'].replace(/(^[\t\r\n]+)|([\t\r\n]+$)/, '').length
  21287. currentIndex += currentNodeLength;
  21288. if (currentIndex >= index) {
  21289. result = findNTextInBlockElm(currentNode, currentNodeLength - (currentIndex - index), str);
  21290. if (result) {
  21291. return result;
  21292. }
  21293. }
  21294. }
  21295. currentNode = domUtils.getNextDomNode(currentNode);
  21296. }
  21297. }
  21298. function searchReplace(me, opt) {
  21299. var rng = me.selection.getRange(),
  21300. startBlockNode,
  21301. searchStr = opt.searchStr,
  21302. span = me.document.createElement('span');
  21303. span.innerHTML = '$$ueditor_searchreplace_key$$';
  21304. rng.shrinkBoundary(true);
  21305. //判断是不是第一次选中
  21306. if (!rng.collapsed) {
  21307. rng.select();
  21308. var rngText = me.selection.getText();
  21309. if (new RegExp('^' + opt.searchStr + '$', (opt.casesensitive ? '' : 'i')).test(rngText)) {
  21310. if (opt.replaceStr != undefined) {
  21311. replaceText(rng, opt.replaceStr);
  21312. rng.select();
  21313. return true;
  21314. } else {
  21315. rng.collapse(opt.dir == -1)
  21316. }
  21317. }
  21318. }
  21319. rng.insertNode(span);
  21320. rng.enlargeToBlockElm(true);
  21321. startBlockNode = rng.startContainer;
  21322. var currentIndex = startBlockNode[browser.ie ? 'innerText' : 'textContent'].indexOf('$$ueditor_searchreplace_key$$');
  21323. rng.setStartBefore(span);
  21324. domUtils.remove(span);
  21325. var result = findTextBlockElm(startBlockNode, currentIndex, opt);
  21326. if (result) {
  21327. var rngStart = findNTextInBlockElm(result.node, result.index, searchStr);
  21328. var rngEnd = findNTextInBlockElm(result.node, result.index + searchStr.length, searchStr);
  21329. rng.setStart(rngStart.node, rngStart.index).setEnd(rngEnd.node, rngEnd.index);
  21330. if (opt.replaceStr !== undefined) {
  21331. replaceText(rng, opt.replaceStr)
  21332. }
  21333. rng.select();
  21334. return true;
  21335. } else {
  21336. rng.setCursor()
  21337. }
  21338. }
  21339. function replaceText(rng, str) {
  21340. str = me.document.createTextNode(str);
  21341. rng.deleteContents().insertNode(str);
  21342. }
  21343. return {
  21344. commands: {
  21345. 'searchreplace': {
  21346. execCommand: function (cmdName, opt) {
  21347. utils.extend(opt, {
  21348. all: false,
  21349. casesensitive: false,
  21350. dir: 1
  21351. }, true);
  21352. var num = 0;
  21353. if (opt.all) {
  21354. var rng = me.selection.getRange(),
  21355. first = me.body.firstChild;
  21356. if (first && first.nodeType == 1) {
  21357. rng.setStart(first, 0);
  21358. rng.shrinkBoundary(true);
  21359. } else if (first.nodeType == 3) {
  21360. rng.setStartBefore(first)
  21361. }
  21362. rng.collapse(true).select(true);
  21363. if (opt.replaceStr !== undefined) {
  21364. me.fireEvent('saveScene');
  21365. }
  21366. while (searchReplace(this, opt)) {
  21367. num++;
  21368. }
  21369. if (num) {
  21370. me.fireEvent('saveScene');
  21371. }
  21372. } else {
  21373. if (opt.replaceStr !== undefined) {
  21374. me.fireEvent('saveScene');
  21375. }
  21376. if (searchReplace(this, opt)) {
  21377. num++
  21378. }
  21379. if (num) {
  21380. me.fireEvent('saveScene');
  21381. }
  21382. }
  21383. return num;
  21384. },
  21385. notNeedUndo: 1
  21386. }
  21387. }
  21388. }
  21389. });
  21390. // plugins/customstyle.js
  21391. /**
  21392. * 自定义样式
  21393. * @file
  21394. * @since 1.2.6.1
  21395. */
  21396. /**
  21397. * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。
  21398. * @command customstyle
  21399. * @method execCommand
  21400. * @param { String } cmd 命令字符串
  21401. * @example
  21402. * ```javascript
  21403. * editor.execCommand( 'customstyle' );
  21404. * ```
  21405. */
  21406. UE.plugins['customstyle'] = function () {
  21407. var me = this;
  21408. me.setOpt({
  21409. 'customstyle': [
  21410. {
  21411. tag: 'h1',
  21412. name: 'tc',
  21413. style: 'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'
  21414. },
  21415. {
  21416. tag: 'h1',
  21417. name: 'tl',
  21418. style: 'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'
  21419. },
  21420. {tag: 'span', name: 'im', style: 'font-size:16px;font-style:italic;font-weight:bold;line-height:18px;'},
  21421. {
  21422. tag: 'span',
  21423. name: 'hi',
  21424. style: 'font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'
  21425. }
  21426. ]
  21427. });
  21428. me.commands['customstyle'] = {
  21429. execCommand: function (cmdName, obj) {
  21430. var me = this,
  21431. tagName = obj.tag,
  21432. node = domUtils.findParent(me.selection.getStart(), function (node) {
  21433. return node.getAttribute('label');
  21434. }, true),
  21435. range, bk, tmpObj = {};
  21436. for (var p in obj) {
  21437. if (obj[p] !== undefined)
  21438. tmpObj[p] = obj[p];
  21439. }
  21440. delete tmpObj.tag;
  21441. if (node && node.getAttribute('label') == obj.label) {
  21442. range = this.selection.getRange();
  21443. bk = range.createBookmark();
  21444. if (range.collapsed) {
  21445. //trace:1732 删掉自定义标签,要有p来回填站位
  21446. if (dtd.$block[node.tagName]) {
  21447. var fillNode = me.document.createElement('p');
  21448. domUtils.moveChild(node, fillNode);
  21449. node.parentNode.insertBefore(fillNode, node);
  21450. domUtils.remove(node);
  21451. } else {
  21452. domUtils.remove(node, true);
  21453. }
  21454. } else {
  21455. var common = domUtils.getCommonAncestor(bk.start, bk.end),
  21456. nodes = domUtils.getElementsByTagName(common, tagName);
  21457. if (new RegExp(tagName, 'i').test(common.tagName)) {
  21458. nodes.push(common);
  21459. }
  21460. for (var i = 0, ni; ni = nodes[i++];) {
  21461. if (ni.getAttribute('label') == obj.label) {
  21462. var ps = domUtils.getPosition(ni, bk.start), pe = domUtils.getPosition(ni, bk.end);
  21463. if ((ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS)
  21464. &&
  21465. (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
  21466. )
  21467. if (dtd.$block[tagName]) {
  21468. var fillNode = me.document.createElement('p');
  21469. domUtils.moveChild(ni, fillNode);
  21470. ni.parentNode.insertBefore(fillNode, ni);
  21471. }
  21472. domUtils.remove(ni, true);
  21473. }
  21474. }
  21475. node = domUtils.findParent(common, function (node) {
  21476. return node.getAttribute('label') == obj.label;
  21477. }, true);
  21478. if (node) {
  21479. domUtils.remove(node, true);
  21480. }
  21481. }
  21482. range.moveToBookmark(bk).select();
  21483. } else {
  21484. if (dtd.$block[tagName]) {
  21485. this.execCommand('paragraph', tagName, tmpObj, 'customstyle');
  21486. range = me.selection.getRange();
  21487. if (!range.collapsed) {
  21488. range.collapse();
  21489. node = domUtils.findParent(me.selection.getStart(), function (node) {
  21490. return node.getAttribute('label') == obj.label;
  21491. }, true);
  21492. var pNode = me.document.createElement('p');
  21493. domUtils.insertAfter(node, pNode);
  21494. domUtils.fillNode(me.document, pNode);
  21495. range.setStart(pNode, 0).setCursor();
  21496. }
  21497. } else {
  21498. range = me.selection.getRange();
  21499. if (range.collapsed) {
  21500. node = me.document.createElement(tagName);
  21501. domUtils.setAttributes(node, tmpObj);
  21502. range.insertNode(node).setStart(node, 0).setCursor();
  21503. return;
  21504. }
  21505. bk = range.createBookmark();
  21506. range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select();
  21507. }
  21508. }
  21509. },
  21510. queryCommandValue: function () {
  21511. var parent = domUtils.filterNodeList(
  21512. this.selection.getStartElementPath(),
  21513. function (node) {
  21514. return node.getAttribute('label')
  21515. }
  21516. );
  21517. return parent ? parent.getAttribute('label') : '';
  21518. }
  21519. };
  21520. //当去掉customstyle是,如果是块元素,用p代替
  21521. me.addListener('keyup', function (type, evt) {
  21522. var keyCode = evt.keyCode || evt.which;
  21523. if (keyCode == 32 || keyCode == 13) {
  21524. var range = me.selection.getRange();
  21525. if (range.collapsed) {
  21526. var node = domUtils.findParent(me.selection.getStart(), function (node) {
  21527. return node.getAttribute('label');
  21528. }, true);
  21529. if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
  21530. var p = me.document.createElement('p');
  21531. domUtils.insertAfter(node, p);
  21532. domUtils.fillNode(me.document, p);
  21533. domUtils.remove(node);
  21534. range.setStart(p, 0).setCursor();
  21535. }
  21536. }
  21537. }
  21538. });
  21539. };
  21540. // plugins/catchremoteimage.js
  21541. ///import core
  21542. ///commands 远程图片抓取
  21543. ///commandsName catchRemoteImage,catchremoteimageenable
  21544. ///commandsTitle 远程图片抓取
  21545. /**
  21546. * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片
  21547. */
  21548. UE.plugins['catchremoteimage'] = function () {
  21549. var me = this,
  21550. ajax = UE.ajax;
  21551. /* 设置默认值 */
  21552. if (me.options.catchRemoteImageEnable === false) return;
  21553. me.setOpt({
  21554. catchRemoteImageEnable: false
  21555. });
  21556. me.addListener("afterpaste", function () {
  21557. me.fireEvent("catchRemoteImage");
  21558. });
  21559. me.addListener("catchRemoteImage", function () {
  21560. var catcherLocalDomain = me.getOpt('catcherLocalDomain'),
  21561. catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')),
  21562. catcherUrlPrefix = me.getOpt('catcherUrlPrefix'),
  21563. catcherFieldName = me.getOpt('catcherFieldName');
  21564. var remoteImages = [],
  21565. imgs = domUtils.getElementsByTagName(me.document, "img"),
  21566. test = function (src, urls) {
  21567. if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {
  21568. return true;
  21569. }
  21570. if (urls) {
  21571. for (var j = 0, url; url = urls[j++];) {
  21572. if (src.indexOf(url) !== -1) {
  21573. return true;
  21574. }
  21575. }
  21576. }
  21577. return false;
  21578. };
  21579. for (var i = 0, ci; ci = imgs[i++];) {
  21580. if (ci.getAttribute("word_img")) {
  21581. continue;
  21582. }
  21583. var src = ci.getAttribute("_src") || ci.src || "";
  21584. if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
  21585. remoteImages.push(src);
  21586. }
  21587. }
  21588. if (remoteImages.length) {
  21589. catchremoteimage(remoteImages, {
  21590. //成功抓取
  21591. success: function (r) {
  21592. try {
  21593. var info = r.state !== undefined ? r : eval("(" + r.responseText + ")");
  21594. } catch (e) {
  21595. return;
  21596. }
  21597. /* 获取源路径和新路径 */
  21598. var i, j, ci, cj, oldSrc, newSrc, list = info.list;
  21599. for (i = 0; ci = imgs[i++];) {
  21600. oldSrc = ci.getAttribute("_src") || ci.src || "";
  21601. for (j = 0; cj = list[j++];) {
  21602. if (oldSrc == cj.source && cj.state == "SUCCESS") { //抓取失败时不做替换处理
  21603. newSrc = catcherUrlPrefix + cj.url;
  21604. domUtils.setAttributes(ci, {
  21605. "src": newSrc,
  21606. "_src": newSrc
  21607. });
  21608. break;
  21609. }
  21610. }
  21611. }
  21612. me.fireEvent('catchremotesuccess')
  21613. },
  21614. //回调失败,本次请求超时
  21615. error: function () {
  21616. me.fireEvent("catchremoteerror");
  21617. }
  21618. });
  21619. }
  21620. function catchremoteimage(imgs, callbacks) {
  21621. var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
  21622. url = utils.formatUrl(catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?' : '&') + params),
  21623. isJsonp = utils.isCrossDomainUrl(url),
  21624. opt = {
  21625. 'method': 'POST',
  21626. 'dataType': isJsonp ? 'jsonp' : '',
  21627. 'timeout': 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值
  21628. 'onsuccess': callbacks["success"],
  21629. 'onerror': callbacks["error"]
  21630. };
  21631. opt[catcherFieldName] = imgs;
  21632. ajax.request(url, opt);
  21633. }
  21634. });
  21635. };
  21636. // plugins/snapscreen.js
  21637. /**
  21638. * 截屏插件,为UEditor提供插入支持
  21639. * @file
  21640. * @since 1.4.2
  21641. */
  21642. UE.plugin.register('snapscreen', function () {
  21643. var me = this;
  21644. var snapplugin;
  21645. function getLocation(url) {
  21646. var search,
  21647. a = document.createElement('a'),
  21648. params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
  21649. a.href = url;
  21650. if (browser.ie) {
  21651. a.href = a.href;
  21652. }
  21653. search = a.search;
  21654. if (params) {
  21655. search = search + (search.indexOf('?') == -1 ? '?' : '&') + params;
  21656. search = search.replace(/[&]+/ig, '&');
  21657. }
  21658. return {
  21659. 'port': a.port,
  21660. 'hostname': a.hostname,
  21661. 'path': a.pathname + search || +a.hash
  21662. }
  21663. }
  21664. return {
  21665. commands: {
  21666. /**
  21667. * 字体背景颜色
  21668. * @command snapscreen
  21669. * @method execCommand
  21670. * @param { String } cmd 命令字符串
  21671. * @example
  21672. * ```javascript
  21673. * editor.execCommand('snapscreen');
  21674. * ```
  21675. */
  21676. 'snapscreen': {
  21677. execCommand: function (cmd) {
  21678. var url, local, res;
  21679. var lang = me.getLang("snapScreen_plugin");
  21680. if (!snapplugin) {
  21681. var container = me.container;
  21682. var doc = me.container.ownerDocument || me.container.document;
  21683. snapplugin = doc.createElement("object");
  21684. try {
  21685. snapplugin.type = "application/x-pluginbaidusnap";
  21686. } catch (e) {
  21687. return;
  21688. }
  21689. snapplugin.style.cssText = "position:absolute;left:-9999px;width:0;height:0;";
  21690. snapplugin.setAttribute("width", "0");
  21691. snapplugin.setAttribute("height", "0");
  21692. container.appendChild(snapplugin);
  21693. }
  21694. function onSuccess(rs) {
  21695. try {
  21696. rs = eval("(" + rs + ")");
  21697. if (rs.state == 'SUCCESS') {
  21698. var opt = me.options;
  21699. me.execCommand('insertimage', {
  21700. src: opt.snapscreenUrlPrefix + rs.url,
  21701. _src: opt.snapscreenUrlPrefix + rs.url,
  21702. alt: rs.title || '',
  21703. floatStyle: opt.snapscreenImgAlign
  21704. });
  21705. } else {
  21706. alert(rs.state);
  21707. }
  21708. } catch (e) {
  21709. alert(lang.callBackErrorMsg);
  21710. }
  21711. }
  21712. url = me.getActionUrl(me.getOpt('snapscreenActionName'));
  21713. local = getLocation(url);
  21714. setTimeout(function () {
  21715. try {
  21716. res = snapplugin.saveSnapshot(local.hostname, local.path, local.port);
  21717. } catch (e) {
  21718. me.ui._dialogs['snapscreenDialog'].open();
  21719. return;
  21720. }
  21721. onSuccess(res);
  21722. }, 50);
  21723. },
  21724. queryCommandState: function () {
  21725. return (navigator.userAgent.indexOf("Windows", 0) != -1) ? 0 : -1;
  21726. }
  21727. }
  21728. }
  21729. }
  21730. });
  21731. // plugins/insertparagraph.js
  21732. /**
  21733. * 插入段落
  21734. * @file
  21735. * @since 1.2.6.1
  21736. */
  21737. /**
  21738. * 插入段落
  21739. * @command insertparagraph
  21740. * @method execCommand
  21741. * @param { String } cmd 命令字符串
  21742. * @example
  21743. * ```javascript
  21744. * //editor是编辑器实例
  21745. * editor.execCommand( 'insertparagraph' );
  21746. * ```
  21747. */
  21748. UE.commands['insertparagraph'] = {
  21749. execCommand: function (cmdName, front) {
  21750. var me = this,
  21751. range = me.selection.getRange(),
  21752. start = range.startContainer, tmpNode;
  21753. while (start) {
  21754. if (domUtils.isBody(start)) {
  21755. break;
  21756. }
  21757. tmpNode = start;
  21758. start = start.parentNode;
  21759. }
  21760. if (tmpNode) {
  21761. var p = me.document.createElement('p');
  21762. if (front) {
  21763. tmpNode.parentNode.insertBefore(p, tmpNode)
  21764. } else {
  21765. tmpNode.parentNode.insertBefore(p, tmpNode.nextSibling)
  21766. }
  21767. domUtils.fillNode(me.document, p);
  21768. range.setStart(p, 0).setCursor(false, true);
  21769. }
  21770. }
  21771. };
  21772. // plugins/webapp.js
  21773. /**
  21774. * 百度应用
  21775. * @file
  21776. * @since 1.2.6.1
  21777. */
  21778. /**
  21779. * 插入百度应用
  21780. * @command webapp
  21781. * @method execCommand
  21782. * @remind 需要百度APPKey
  21783. * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
  21784. * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
  21785. * height=>应用容器高度,logo=>应用logo,url=>应用地址
  21786. * @example
  21787. * ```javascript
  21788. * //editor是编辑器实例
  21789. * //在编辑器里插入一个“植物大战僵尸”的APP
  21790. * editor.execCommand( 'webapp' , {
  21791. * title: '植物大战僵尸',
  21792. * width: 560,
  21793. * height: 465,
  21794. * logo: '应用展示的图片',
  21795. * url: '百度应用的地址'
  21796. * } );
  21797. * ```
  21798. */
  21799. //UE.plugins['webapp'] = function () {
  21800. // var me = this;
  21801. // function createInsertStr( obj, toIframe, addParagraph ) {
  21802. // return !toIframe ?
  21803. // (addParagraph ? '<p>' : '') + '<img title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '"' +
  21804. // ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" style="background:url(' + obj.logo+') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' + obj.url + '" />' +
  21805. // (addParagraph ? '</p>' : '')
  21806. // :
  21807. // '<iframe class="edui-faked-webapp" title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '" scrolling="no" frameborder="0" src="' + obj.url + '" logo_url = '+obj.logo+'></iframe>';
  21808. // }
  21809. //
  21810. // function switchImgAndIframe( img2frame ) {
  21811. // var tmpdiv,
  21812. // nodes = domUtils.getElementsByTagName( me.document, !img2frame ? "iframe" : "img" );
  21813. // for ( var i = 0, node; node = nodes[i++]; ) {
  21814. // if ( node.className != "edui-faked-webapp" ){
  21815. // continue;
  21816. // }
  21817. // tmpdiv = me.document.createElement( "div" );
  21818. // tmpdiv.innerHTML = createInsertStr( img2frame ? {url:node.getAttribute( "_url" ), width:node.width, height:node.height,title:node.title,logo:node.style.backgroundImage.replace("url(","").replace(")","")} : {url:node.getAttribute( "src", 2 ),title:node.title, width:node.width, height:node.height,logo:node.getAttribute("logo_url")}, img2frame ? true : false,false );
  21819. // node.parentNode.replaceChild( tmpdiv.firstChild, node );
  21820. // }
  21821. // }
  21822. //
  21823. // me.addListener( "beforegetcontent", function () {
  21824. // switchImgAndIframe( true );
  21825. // } );
  21826. // me.addListener( 'aftersetcontent', function () {
  21827. // switchImgAndIframe( false );
  21828. // } );
  21829. // me.addListener( 'aftergetcontent', function ( cmdName ) {
  21830. // if ( cmdName == 'aftergetcontent' && me.queryCommandState( 'source' ) ){
  21831. // return;
  21832. // }
  21833. // switchImgAndIframe( false );
  21834. // } );
  21835. //
  21836. // me.commands['webapp'] = {
  21837. // execCommand:function ( cmd, obj ) {
  21838. // me.execCommand( "inserthtml", createInsertStr( obj, false,true ) );
  21839. // }
  21840. // };
  21841. //};
  21842. UE.plugin.register('webapp', function () {
  21843. var me = this;
  21844. function createInsertStr(obj, toEmbed) {
  21845. return !toEmbed ?
  21846. '<img title="' + obj.title + '" width="' + obj.width + '" height="' + obj.height + '"' +
  21847. ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" _logo_url="' + obj.logo + '" style="background:url(' + obj.logo
  21848. + ') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' + obj.url + '" ' +
  21849. (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
  21850. (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
  21851. '/>'
  21852. :
  21853. '<iframe class="edui-faked-webapp" title="' + obj.title + '" ' +
  21854. (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
  21855. (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
  21856. 'width="' + obj.width + '" height="' + obj.height + '" scrolling="no" frameborder="0" src="' + obj.url + '" logo_url = "' + obj.logo + '"></iframe>'
  21857. }
  21858. return {
  21859. outputRule: function (root) {
  21860. utils.each(root.getNodesByTagName('img'), function (node) {
  21861. var html;
  21862. if (node.getAttr('class') == 'edui-faked-webapp') {
  21863. html = createInsertStr({
  21864. title: node.getAttr('title'),
  21865. 'width': node.getAttr('width'),
  21866. 'height': node.getAttr('height'),
  21867. 'align': node.getAttr('align'),
  21868. 'cssfloat': node.getStyle('float'),
  21869. 'url': node.getAttr("_url"),
  21870. 'logo': node.getAttr('_logo_url')
  21871. }, true);
  21872. var embed = UE.uNode.createElement(html);
  21873. node.parentNode.replaceChild(embed, node);
  21874. }
  21875. })
  21876. },
  21877. inputRule: function (root) {
  21878. utils.each(root.getNodesByTagName('iframe'), function (node) {
  21879. if (node.getAttr('class') == 'edui-faked-webapp') {
  21880. var img = UE.uNode.createElement(createInsertStr({
  21881. title: node.getAttr('title'),
  21882. 'width': node.getAttr('width'),
  21883. 'height': node.getAttr('height'),
  21884. 'align': node.getAttr('align'),
  21885. 'cssfloat': node.getStyle('float'),
  21886. 'url': node.getAttr("src"),
  21887. 'logo': node.getAttr('logo_url')
  21888. }));
  21889. node.parentNode.replaceChild(img, node);
  21890. }
  21891. })
  21892. },
  21893. commands: {
  21894. /**
  21895. * 插入百度应用
  21896. * @command webapp
  21897. * @method execCommand
  21898. * @remind 需要百度APPKey
  21899. * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
  21900. * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
  21901. * height=>应用容器高度,logo=>应用logo,url=>应用地址
  21902. * @example
  21903. * ```javascript
  21904. * //editor是编辑器实例
  21905. * //在编辑器里插入一个“植物大战僵尸”的APP
  21906. * editor.execCommand( 'webapp' , {
  21907. * title: '植物大战僵尸',
  21908. * width: 560,
  21909. * height: 465,
  21910. * logo: '应用展示的图片',
  21911. * url: '百度应用的地址'
  21912. * } );
  21913. * ```
  21914. */
  21915. 'webapp': {
  21916. execCommand: function (cmd, obj) {
  21917. var me = this,
  21918. str = createInsertStr(utils.extend(obj, {
  21919. align: 'none'
  21920. }), false);
  21921. me.execCommand("inserthtml", str);
  21922. },
  21923. queryCommandState: function () {
  21924. var me = this,
  21925. img = me.selection.getRange().getClosedNode(),
  21926. flag = img && (img.className == "edui-faked-webapp");
  21927. return flag ? 1 : 0;
  21928. }
  21929. }
  21930. }
  21931. }
  21932. });
  21933. // plugins/template.js
  21934. ///import core
  21935. ///import plugins\inserthtml.js
  21936. ///import plugins\cleardoc.js
  21937. ///commands 模板
  21938. ///commandsName template
  21939. ///commandsTitle 模板
  21940. ///commandsDialog dialogs\template
  21941. UE.plugins['template'] = function () {
  21942. UE.commands['template'] = {
  21943. execCommand: function (cmd, obj) {
  21944. obj.html && this.execCommand("inserthtml", obj.html);
  21945. }
  21946. };
  21947. this.addListener("click", function (type, evt) {
  21948. var el = evt.target || evt.srcElement,
  21949. range = this.selection.getRange();
  21950. var tnode = domUtils.findParent(el, function (node) {
  21951. if (node.className && domUtils.hasClass(node, "ue_t")) {
  21952. return node;
  21953. }
  21954. }, true);
  21955. tnode && range.selectNode(tnode).shrinkBoundary().select();
  21956. });
  21957. this.addListener("keydown", function (type, evt) {
  21958. var range = this.selection.getRange();
  21959. if (!range.collapsed) {
  21960. if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  21961. var tnode = domUtils.findParent(range.startContainer, function (node) {
  21962. if (node.className && domUtils.hasClass(node, "ue_t")) {
  21963. return node;
  21964. }
  21965. }, true);
  21966. if (tnode) {
  21967. domUtils.removeClasses(tnode, ["ue_t"]);
  21968. }
  21969. }
  21970. }
  21971. });
  21972. };
  21973. // plugins/music.js
  21974. /**
  21975. * 插入音乐命令
  21976. * @file
  21977. */
  21978. UE.plugin.register('music', function () {
  21979. var me = this;
  21980. function creatInsertStr(url, width, height, align, cssfloat, toEmbed) {
  21981. return !toEmbed ?
  21982. '<img ' +
  21983. (align && !cssfloat ? 'align="' + align + '"' : '') +
  21984. (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
  21985. ' width="' + width + '" height="' + height + '" _url="' + url + '" class="edui-faked-music"' +
  21986. ' src="' + me.options.langPath + me.options.lang + '/images/music.png" />'
  21987. :
  21988. '<embed type="application/x-shockwave-flash" class="edui-faked-music" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
  21989. ' src="' + url + '" width="' + width + '" height="' + height + '" ' + (align && !cssfloat ? 'align="' + align + '"' : '') +
  21990. (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
  21991. ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
  21992. }
  21993. return {
  21994. outputRule: function (root) {
  21995. utils.each(root.getNodesByTagName('img'), function (node) {
  21996. var html;
  21997. if (node.getAttr('class') == 'edui-faked-music') {
  21998. var cssfloat = node.getStyle('float');
  21999. var align = node.getAttr('align');
  22000. html = creatInsertStr(node.getAttr("_url"), node.getAttr('width'), node.getAttr('height'), align, cssfloat, true);
  22001. var embed = UE.uNode.createElement(html);
  22002. node.parentNode.replaceChild(embed, node);
  22003. }
  22004. })
  22005. },
  22006. inputRule: function (root) {
  22007. utils.each(root.getNodesByTagName('embed'), function (node) {
  22008. if (node.getAttr('class') == 'edui-faked-music') {
  22009. var cssfloat = node.getStyle('float');
  22010. var align = node.getAttr('align');
  22011. html = creatInsertStr(node.getAttr("src"), node.getAttr('width'), node.getAttr('height'), align, cssfloat, false);
  22012. var img = UE.uNode.createElement(html);
  22013. node.parentNode.replaceChild(img, node);
  22014. }
  22015. })
  22016. },
  22017. commands: {
  22018. /**
  22019. * 插入音乐
  22020. * @command music
  22021. * @method execCommand
  22022. * @param { Object } musicOptions 插入音乐的参数项, 支持的key有: url=>音乐地址;
  22023. * width=>音乐容器宽度;height=>音乐容器高度;align=>音乐文件的对齐方式, 可选值有: left, center, right, none
  22024. * @example
  22025. * ```javascript
  22026. * //editor是编辑器实例
  22027. * //在编辑器里插入一个“植物大战僵尸”的APP
  22028. * editor.execCommand( 'music' , {
  22029. * width: 400,
  22030. * height: 95,
  22031. * align: "center",
  22032. * url: "音乐地址"
  22033. * } );
  22034. * ```
  22035. */
  22036. 'music': {
  22037. execCommand: function (cmd, musicObj) {
  22038. var me = this,
  22039. str = creatInsertStr(musicObj.url, musicObj.width || 400, musicObj.height || 95, "none", false);
  22040. me.execCommand("inserthtml", str);
  22041. },
  22042. queryCommandState: function () {
  22043. var me = this,
  22044. img = me.selection.getRange().getClosedNode(),
  22045. flag = img && (img.className == "edui-faked-music");
  22046. return flag ? 1 : 0;
  22047. }
  22048. }
  22049. }
  22050. }
  22051. });
  22052. // plugins/autoupload.js
  22053. /**
  22054. * @description
  22055. * 1.拖放文件到编辑区域,自动上传并插入到选区
  22056. * 2.插入粘贴板的图片,自动上传并插入到选区
  22057. * @author Jinqn
  22058. * @date 2013-10-14
  22059. */
  22060. UE.plugin.register('autoupload', function () {
  22061. function sendAndInsertFile(file, editor) {
  22062. var me = editor;
  22063. //模拟数据
  22064. var fieldName, urlPrefix, maxSize, allowFiles, actionUrl,
  22065. loadingHtml, errorHandler, successHandler,
  22066. filetype = /image\/\w+/i.test(file.type) ? 'image' : 'file',
  22067. loadingId = 'loading_' + (+new Date()).toString(36);
  22068. fieldName = me.getOpt(filetype + 'FieldName');
  22069. urlPrefix = me.getOpt(filetype + 'UrlPrefix');
  22070. maxSize = me.getOpt(filetype + 'MaxSize');
  22071. allowFiles = me.getOpt(filetype + 'AllowFiles');
  22072. actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName'));
  22073. errorHandler = function (title) {
  22074. var loader = me.document.getElementById(loadingId);
  22075. loader && domUtils.remove(loader);
  22076. me.fireEvent('showmessage', {
  22077. 'id': loadingId,
  22078. 'content': title,
  22079. 'type': 'error',
  22080. 'timeout': 4000
  22081. });
  22082. };
  22083. if (filetype == 'image') {
  22084. loadingHtml = '<img class="loadingclass" id="' + loadingId + '" src="' +
  22085. me.options.themePath + me.options.theme +
  22086. '/images/spacer.gif" title="' + (me.getLang('autoupload.loading') || '') + '" >';
  22087. successHandler = function (data) {
  22088. var link = urlPrefix + data.url,
  22089. loader = me.document.getElementById(loadingId);
  22090. if (loader) {
  22091. loader.setAttribute('src', ns.img(link));
  22092. loader.setAttribute('_src', ns.img(link));
  22093. loader.setAttribute('title', data.title || '');
  22094. loader.setAttribute('alt', data.original || '');
  22095. loader.removeAttribute('id');
  22096. domUtils.removeClasses(loader, 'loadingclass');
  22097. }
  22098. };
  22099. } else {
  22100. loadingHtml = '<p>' +
  22101. '<img class="loadingclass" id="' + loadingId + '" src="' +
  22102. me.options.themePath + me.options.theme +
  22103. '/images/spacer.gif" title="' + (me.getLang('autoupload.loading') || '') + '" >' +
  22104. '</p>';
  22105. successHandler = function (data) {
  22106. var link = urlPrefix + data.url,
  22107. loader = me.document.getElementById(loadingId);
  22108. var rng = me.selection.getRange(),
  22109. bk = rng.createBookmark();
  22110. rng.selectNode(loader).select();
  22111. me.execCommand('insertfile', {'url': link});
  22112. rng.moveToBookmark(bk).select();
  22113. };
  22114. }
  22115. /* 插入loading的占位符 */
  22116. me.execCommand('inserthtml', loadingHtml);
  22117. /* 判断后端配置是否没有加载成功 */
  22118. if (!me.getOpt(filetype + 'ActionName')) {
  22119. errorHandler(me.getLang('autoupload.errorLoadConfig'));
  22120. return;
  22121. }
  22122. /* 判断文件大小是否超出限制 */
  22123. if (file.size > maxSize) {
  22124. errorHandler(me.getLang('autoupload.exceedSizeError'));
  22125. return;
  22126. }
  22127. /* 判断文件格式是否超出允许 */
  22128. var fileext = file.name ? file.name.substr(file.name.lastIndexOf('.')) : '';
  22129. if ((fileext && filetype != 'image') || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
  22130. errorHandler(me.getLang('autoupload.exceedTypeError'));
  22131. return;
  22132. }
  22133. /* 创建Ajax并提交 */
  22134. var xhr = new XMLHttpRequest(),
  22135. fd = new FormData(),
  22136. params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
  22137. url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?' : '&') + params);
  22138. fd.append(fieldName, file, file.name || ('blob.' + file.type.substr('image/'.length)));
  22139. fd.append('type', 'ajax');
  22140. xhr.open("post", url, true);
  22141. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  22142. xhr.addEventListener('load', function (e) {
  22143. try {
  22144. var json = (new Function("return " + utils.trim(e.target.response)))();
  22145. if (json.state == 'SUCCESS' && json.url) {
  22146. successHandler(json);
  22147. } else {
  22148. errorHandler(json.state);
  22149. }
  22150. } catch (er) {
  22151. errorHandler(me.getLang('autoupload.loadError'));
  22152. }
  22153. });
  22154. xhr.send(fd);
  22155. }
  22156. function getPasteImage(e) {
  22157. return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
  22158. }
  22159. function getDropImage(e) {
  22160. return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
  22161. }
  22162. return {
  22163. outputRule: function (root) {
  22164. utils.each(root.getNodesByTagName('img'), function (n) {
  22165. if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
  22166. n.parentNode.removeChild(n);
  22167. }
  22168. });
  22169. utils.each(root.getNodesByTagName('p'), function (n) {
  22170. if (/\bloadpara\b/.test(n.getAttr('class'))) {
  22171. n.parentNode.removeChild(n);
  22172. }
  22173. });
  22174. },
  22175. bindEvents: {
  22176. //插入粘贴板的图片,拖放插入图片
  22177. 'ready': function (e) {
  22178. var me = this;
  22179. if (window.FormData && window.FileReader) {
  22180. domUtils.on(me.body, 'paste drop', function (e) {
  22181. var hasImg = false,
  22182. items;
  22183. //获取粘贴板文件列表或者拖放文件列表
  22184. items = e.type == 'paste' ? getPasteImage(e) : getDropImage(e);
  22185. if (items) {
  22186. var len = items.length,
  22187. file;
  22188. while (len--) {
  22189. file = items[len];
  22190. if (file.getAsFile) file = file.getAsFile();
  22191. if (file && file.size > 0) {
  22192. sendAndInsertFile(file, me);
  22193. hasImg = true;
  22194. }
  22195. }
  22196. hasImg && e.preventDefault();
  22197. }
  22198. });
  22199. //取消拖放图片时出现的文字光标位置提示
  22200. domUtils.on(me.body, 'dragover', function (e) {
  22201. if (e.dataTransfer.types[0] == 'Files') {
  22202. e.preventDefault();
  22203. }
  22204. });
  22205. //设置loading的样式
  22206. utils.cssRule('loading',
  22207. '.loadingclass{display:inline-block;cursor:default;background: url(\''
  22208. + this.options.themePath
  22209. + this.options.theme + '/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n' +
  22210. '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
  22211. + this.options.themePath
  22212. + this.options.theme + '/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
  22213. '}',
  22214. this.document);
  22215. }
  22216. }
  22217. }
  22218. }
  22219. });
  22220. // plugins/autosave.js
  22221. UE.plugin.register('autosave', function () {
  22222. var me = this,
  22223. //无限循环保护
  22224. lastSaveTime = new Date(),
  22225. //最小保存间隔时间
  22226. MIN_TIME = 20,
  22227. //auto save key
  22228. saveKey = null;
  22229. function save(editor) {
  22230. var saveData;
  22231. if (new Date() - lastSaveTime < MIN_TIME) {
  22232. return;
  22233. }
  22234. if (!editor.hasContents()) {
  22235. //这里不能调用命令来删除, 会造成事件死循环
  22236. saveKey && me.removePreferences(saveKey);
  22237. return;
  22238. }
  22239. lastSaveTime = new Date();
  22240. editor._saveFlag = null;
  22241. saveData = me.body.innerHTML;
  22242. if (editor.fireEvent("beforeautosave", {
  22243. content: saveData
  22244. }) === false) {
  22245. return;
  22246. }
  22247. me.setPreferences(saveKey, saveData);
  22248. editor.fireEvent("afterautosave", {
  22249. content: saveData
  22250. });
  22251. }
  22252. return {
  22253. defaultOptions: {
  22254. //默认间隔时间
  22255. saveInterval: 500
  22256. },
  22257. bindEvents: {
  22258. 'ready': function () {
  22259. var _suffix = "-drafts-data",
  22260. key = null;
  22261. if (me.key) {
  22262. key = me.key + _suffix;
  22263. } else {
  22264. key = (me.container.parentNode.id || 'ue-common') + _suffix;
  22265. }
  22266. //页面地址+编辑器ID 保持唯一
  22267. saveKey = (location.protocol + location.host + location.pathname).replace(/[.:\/]/g, '_') + key;
  22268. },
  22269. 'contentchange': function () {
  22270. if (!saveKey) {
  22271. return;
  22272. }
  22273. if (me._saveFlag) {
  22274. window.clearTimeout(me._saveFlag);
  22275. }
  22276. if (me.options.saveInterval > 0) {
  22277. me._saveFlag = window.setTimeout(function () {
  22278. save(me);
  22279. }, me.options.saveInterval);
  22280. } else {
  22281. save(me);
  22282. }
  22283. }
  22284. },
  22285. commands: {
  22286. 'clearlocaldata': {
  22287. execCommand: function (cmd, name) {
  22288. if (saveKey && me.getPreferences(saveKey)) {
  22289. me.removePreferences(saveKey)
  22290. }
  22291. },
  22292. notNeedUndo: true,
  22293. ignoreContentChange: true
  22294. },
  22295. 'getlocaldata': {
  22296. execCommand: function (cmd, name) {
  22297. return saveKey ? me.getPreferences(saveKey) || '' : '';
  22298. },
  22299. notNeedUndo: true,
  22300. ignoreContentChange: true
  22301. },
  22302. 'drafts': {
  22303. execCommand: function (cmd, name) {
  22304. if (saveKey) {
  22305. me.body.innerHTML = me.getPreferences(saveKey) || '<p>' + domUtils.fillHtml + '</p>';
  22306. me.focus(true);
  22307. }
  22308. },
  22309. queryCommandState: function () {
  22310. return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1;
  22311. },
  22312. notNeedUndo: true,
  22313. ignoreContentChange: true
  22314. }
  22315. }
  22316. }
  22317. });
  22318. // plugins/charts.js
  22319. UE.plugin.register('charts', function () {
  22320. var me = this;
  22321. return {
  22322. bindEvents: {
  22323. 'chartserror': function () {
  22324. }
  22325. },
  22326. commands: {
  22327. 'charts': {
  22328. execCommand: function (cmd, data) {
  22329. var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true),
  22330. flagText = [],
  22331. config = {};
  22332. if (!tableNode) {
  22333. return false;
  22334. }
  22335. if (!validData(tableNode)) {
  22336. me.fireEvent("chartserror");
  22337. return false;
  22338. }
  22339. config.title = data.title || '';
  22340. config.subTitle = data.subTitle || '';
  22341. config.xTitle = data.xTitle || '';
  22342. config.yTitle = data.yTitle || '';
  22343. config.suffix = data.suffix || '';
  22344. config.tip = data.tip || '';
  22345. //数据对齐方式
  22346. config.dataFormat = data.tableDataFormat || '';
  22347. //图表类型
  22348. config.chartType = data.chartType || 0;
  22349. for (var key in config) {
  22350. if (!config.hasOwnProperty(key)) {
  22351. continue;
  22352. }
  22353. flagText.push(key + ":" + config[key]);
  22354. }
  22355. tableNode.setAttribute("data-chart", flagText.join(";"));
  22356. domUtils.addClass(tableNode, "edui-charts-table");
  22357. },
  22358. queryCommandState: function (cmd, name) {
  22359. var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true);
  22360. return tableNode && validData(tableNode) ? 0 : -1;
  22361. }
  22362. }
  22363. },
  22364. inputRule: function (root) {
  22365. utils.each(root.getNodesByTagName('table'), function (tableNode) {
  22366. if (tableNode.getAttr("data-chart") !== undefined) {
  22367. tableNode.setAttr("style");
  22368. }
  22369. })
  22370. },
  22371. outputRule: function (root) {
  22372. utils.each(root.getNodesByTagName('table'), function (tableNode) {
  22373. if (tableNode.getAttr("data-chart") !== undefined) {
  22374. tableNode.setAttr("style", "display: none;");
  22375. }
  22376. })
  22377. }
  22378. }
  22379. function validData(table) {
  22380. var firstRows = null,
  22381. cellCount = 0;
  22382. //行数不够
  22383. if (table.rows.length < 2) {
  22384. return false;
  22385. }
  22386. //列数不够
  22387. if (table.rows[0].cells.length < 2) {
  22388. return false;
  22389. }
  22390. //第一行所有cell必须是th
  22391. firstRows = table.rows[0].cells;
  22392. cellCount = firstRows.length;
  22393. for (var i = 0, cell; cell = firstRows[i]; i++) {
  22394. if (cell.tagName.toLowerCase() !== 'th') {
  22395. return false;
  22396. }
  22397. }
  22398. for (var i = 1, row; row = table.rows[i]; i++) {
  22399. //每行单元格数不匹配, 返回false
  22400. if (row.cells.length != cellCount) {
  22401. return false;
  22402. }
  22403. //第一列不是th也返回false
  22404. if (row.cells[0].tagName.toLowerCase() !== 'th') {
  22405. return false;
  22406. }
  22407. for (var j = 1, cell; cell = row.cells[j]; j++) {
  22408. var value = utils.trim((cell.innerText || cell.textContent || ''));
  22409. value = value.replace(new RegExp(UE.dom.domUtils.fillChar, 'g'), '').replace(/^\s+|\s+$/g, '');
  22410. //必须是数字
  22411. if (!/^\d*\.?\d+$/.test(value)) {
  22412. return false;
  22413. }
  22414. }
  22415. }
  22416. return true;
  22417. }
  22418. });
  22419. // plugins/section.js
  22420. /**
  22421. * 目录大纲支持插件
  22422. * @file
  22423. * @since 1.3.0
  22424. */
  22425. UE.plugin.register('section', function () {
  22426. /* 目录节点对象 */
  22427. function Section(option) {
  22428. this.tag = '';
  22429. this.level = -1,
  22430. this.dom = null;
  22431. this.nextSection = null;
  22432. this.previousSection = null;
  22433. this.parentSection = null;
  22434. this.startAddress = [];
  22435. this.endAddress = [];
  22436. this.children = [];
  22437. }
  22438. function getSection(option) {
  22439. var section = new Section();
  22440. return utils.extend(section, option);
  22441. }
  22442. function getNodeFromAddress(startAddress, root) {
  22443. var current = root;
  22444. for (var i = 0; i < startAddress.length; i++) {
  22445. if (!current.childNodes) return null;
  22446. current = current.childNodes[startAddress[i]];
  22447. }
  22448. return current;
  22449. }
  22450. var me = this;
  22451. return {
  22452. bindMultiEvents: {
  22453. type: 'aftersetcontent afterscencerestore',
  22454. handler: function () {
  22455. me.fireEvent('updateSections');
  22456. }
  22457. },
  22458. bindEvents: {
  22459. /* 初始化、拖拽、粘贴、执行setcontent之后 */
  22460. 'ready': function () {
  22461. me.fireEvent('updateSections');
  22462. domUtils.on(me.body, 'drop paste', function () {
  22463. me.fireEvent('updateSections');
  22464. });
  22465. },
  22466. /* 执行paragraph命令之后 */
  22467. 'afterexeccommand': function (type, cmd) {
  22468. if (cmd == 'paragraph') {
  22469. me.fireEvent('updateSections');
  22470. }
  22471. },
  22472. /* 部分键盘操作,触发updateSections事件 */
  22473. 'keyup': function (type, e) {
  22474. var me = this,
  22475. range = me.selection.getRange();
  22476. if (range.collapsed != true) {
  22477. me.fireEvent('updateSections');
  22478. } else {
  22479. var keyCode = e.keyCode || e.which;
  22480. if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
  22481. me.fireEvent('updateSections');
  22482. }
  22483. }
  22484. }
  22485. },
  22486. commands: {
  22487. 'getsections': {
  22488. execCommand: function (cmd, levels) {
  22489. var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
  22490. for (var i = 0; i < levelFn.length; i++) {
  22491. if (typeof levelFn[i] == 'string') {
  22492. levelFn[i] = function (fn) {
  22493. return function (node) {
  22494. return node.tagName == fn.toUpperCase()
  22495. };
  22496. }(levelFn[i]);
  22497. } else if (typeof levelFn[i] != 'function') {
  22498. levelFn[i] = function (node) {
  22499. return null;
  22500. }
  22501. }
  22502. }
  22503. function getSectionLevel(node) {
  22504. for (var i = 0; i < levelFn.length; i++) {
  22505. if (levelFn[i](node)) return i;
  22506. }
  22507. return -1;
  22508. }
  22509. var me = this,
  22510. Directory = getSection({'level': -1, 'title': 'root'}),
  22511. previous = Directory;
  22512. function traversal(node, Directory) {
  22513. var level,
  22514. tmpSection = null,
  22515. parent,
  22516. child,
  22517. children = node.childNodes;
  22518. for (var i = 0, len = children.length; i < len; i++) {
  22519. child = children[i];
  22520. level = getSectionLevel(child);
  22521. if (level >= 0) {
  22522. var address = me.selection.getRange().selectNode(child).createAddress(true).startAddress,
  22523. current = getSection({
  22524. 'tag': child.tagName,
  22525. 'title': child.innerText || child.textContent || '',
  22526. 'level': level,
  22527. 'dom': child,
  22528. 'startAddress': utils.clone(address, []),
  22529. 'endAddress': utils.clone(address, []),
  22530. 'children': []
  22531. });
  22532. previous.nextSection = current;
  22533. current.previousSection = previous;
  22534. parent = previous;
  22535. while (level <= parent.level) {
  22536. parent = parent.parentSection;
  22537. }
  22538. current.parentSection = parent;
  22539. parent.children.push(current);
  22540. tmpSection = previous = current;
  22541. } else {
  22542. child.nodeType === 1 && traversal(child, Directory);
  22543. tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1]++;
  22544. }
  22545. }
  22546. }
  22547. traversal(me.body, Directory);
  22548. return Directory;
  22549. },
  22550. notNeedUndo: true
  22551. },
  22552. 'movesection': {
  22553. execCommand: function (cmd, sourceSection, targetSection, isAfter) {
  22554. var me = this,
  22555. targetAddress,
  22556. target;
  22557. if (!sourceSection || !targetSection || targetSection.level == -1) return;
  22558. targetAddress = isAfter ? targetSection.endAddress : targetSection.startAddress;
  22559. target = getNodeFromAddress(targetAddress, me.body);
  22560. /* 判断目标地址是否被源章节包含 */
  22561. if (!targetAddress || !target || isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)) return;
  22562. var startNode = getNodeFromAddress(sourceSection.startAddress, me.body),
  22563. endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
  22564. current,
  22565. nextNode;
  22566. if (isAfter) {
  22567. current = endNode;
  22568. while (current && !(domUtils.getPosition(startNode, current) & domUtils.POSITION_FOLLOWING)) {
  22569. nextNode = current.previousSibling;
  22570. domUtils.insertAfter(target, current);
  22571. if (current == startNode) break;
  22572. current = nextNode;
  22573. }
  22574. } else {
  22575. current = startNode;
  22576. while (current && !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)) {
  22577. nextNode = current.nextSibling;
  22578. target.parentNode.insertBefore(current, target);
  22579. if (current == endNode) break;
  22580. current = nextNode;
  22581. }
  22582. }
  22583. me.fireEvent('updateSections');
  22584. /* 获取地址的包含关系 */
  22585. function isContainsAddress(startAddress, endAddress, addressTarget) {
  22586. var isAfterStartAddress = false,
  22587. isBeforeEndAddress = false;
  22588. for (var i = 0; i < startAddress.length; i++) {
  22589. if (i >= addressTarget.length) break;
  22590. if (addressTarget[i] > startAddress[i]) {
  22591. isAfterStartAddress = true;
  22592. break;
  22593. } else if (addressTarget[i] < startAddress[i]) {
  22594. break;
  22595. }
  22596. }
  22597. for (var i = 0; i < endAddress.length; i++) {
  22598. if (i >= addressTarget.length) break;
  22599. if (addressTarget[i] < startAddress[i]) {
  22600. isBeforeEndAddress = true;
  22601. break;
  22602. } else if (addressTarget[i] > startAddress[i]) {
  22603. break;
  22604. }
  22605. }
  22606. return isAfterStartAddress && isBeforeEndAddress;
  22607. }
  22608. }
  22609. },
  22610. 'deletesection': {
  22611. execCommand: function (cmd, section, keepChildren) {
  22612. var me = this;
  22613. if (!section) return;
  22614. function getNodeFromAddress(startAddress) {
  22615. var current = me.body;
  22616. for (var i = 0; i < startAddress.length; i++) {
  22617. if (!current.childNodes) return null;
  22618. current = current.childNodes[startAddress[i]];
  22619. }
  22620. return current;
  22621. }
  22622. var startNode = getNodeFromAddress(section.startAddress),
  22623. endNode = getNodeFromAddress(section.endAddress),
  22624. current = startNode,
  22625. nextNode;
  22626. if (!keepChildren) {
  22627. while (current && domUtils.inDoc(endNode, me.document) && !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)) {
  22628. nextNode = current.nextSibling;
  22629. domUtils.remove(current);
  22630. current = nextNode;
  22631. }
  22632. } else {
  22633. domUtils.remove(current);
  22634. }
  22635. me.fireEvent('updateSections');
  22636. }
  22637. },
  22638. 'selectsection': {
  22639. execCommand: function (cmd, section) {
  22640. if (!section && !section.dom) return false;
  22641. var me = this,
  22642. range = me.selection.getRange(),
  22643. address = {
  22644. 'startAddress': utils.clone(section.startAddress, []),
  22645. 'endAddress': utils.clone(section.endAddress, [])
  22646. };
  22647. address.endAddress[address.endAddress.length - 1]++;
  22648. range.moveToAddress(address).select().scrollToView();
  22649. return true;
  22650. },
  22651. notNeedUndo: true
  22652. },
  22653. 'scrolltosection': {
  22654. execCommand: function (cmd, section) {
  22655. if (!section && !section.dom) return false;
  22656. var me = this,
  22657. range = me.selection.getRange(),
  22658. address = {
  22659. 'startAddress': section.startAddress,
  22660. 'endAddress': section.endAddress
  22661. };
  22662. address.endAddress[address.endAddress.length - 1]++;
  22663. range.moveToAddress(address).scrollToView();
  22664. return true;
  22665. },
  22666. notNeedUndo: true
  22667. }
  22668. }
  22669. }
  22670. });
  22671. // plugins/simpleupload.js
  22672. /**
  22673. * @description
  22674. * 简单上传:点击按钮,直接选择文件上传
  22675. * @author Jinqn
  22676. * @date 2014-03-31
  22677. */
  22678. UE.plugin.register('simpleupload', function () {
  22679. var me = this,
  22680. isLoaded = false,
  22681. containerBtn;
  22682. function initUploadBtn() {
  22683. var w = containerBtn.offsetWidth || 20,
  22684. h = containerBtn.offsetHeight || 20,
  22685. btnIframe = document.createElement('iframe'),
  22686. btnStyle = 'display:block;width:' + w + 'px;height:' + h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;';
  22687. domUtils.on(btnIframe, 'load', function () {
  22688. var timestrap = (+new Date()).toString(36),
  22689. wrapper,
  22690. btnIframeDoc,
  22691. btnIframeBody;
  22692. btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document);
  22693. btnIframeBody = btnIframeDoc.body;
  22694. wrapper = btnIframeDoc.createElement('div');
  22695. wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_' + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
  22696. 'style="' + btnStyle + '">' +
  22697. '<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' + me.options.imageFieldName + '" ' +
  22698. 'style="' + btnStyle + '">' +
  22699. '</form>' +
  22700. '<iframe id="edui_iframe_' + timestrap + '" name="edui_iframe_' + timestrap + '" style="display:none;width:0;height:0;border:0;margin:0;padding:0;position:absolute;"></iframe>';
  22701. wrapper.className = 'edui-' + me.options.theme;
  22702. wrapper.id = me.ui.id + '_iframeupload';
  22703. btnIframeBody.style.cssText = btnStyle;
  22704. btnIframeBody.style.width = w + 'px';
  22705. btnIframeBody.style.height = h + 'px';
  22706. btnIframeBody.appendChild(wrapper);
  22707. if (btnIframeBody.parentNode) {
  22708. btnIframeBody.parentNode.style.width = w + 'px';
  22709. btnIframeBody.parentNode.style.height = w + 'px';
  22710. }
  22711. var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
  22712. var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
  22713. var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);
  22714. domUtils.on(input, 'change', function () {
  22715. if (!input.value) return;
  22716. var loadingId = 'loading_' + (+new Date()).toString(36);
  22717. var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
  22718. var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
  22719. var allowFiles = me.getOpt('imageAllowFiles');
  22720. me.focus();
  22721. me.execCommand('inserthtml', '<img class="loadingclass" id="' + loadingId + '" src="' + me.options.themePath + me.options.theme + '/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >');
  22722. function callback() {
  22723. try {
  22724. var link, json, loader,
  22725. body = (iframe.contentDocument || iframe.contentWindow.document).body,
  22726. result = body.innerText || body.textContent || '';
  22727. json = (new Function("return " + result))();
  22728. link = me.options.imageUrlPrefix + json.url;
  22729. if (json.state == 'SUCCESS' && json.url) {
  22730. loader = me.document.getElementById(loadingId);
  22731. loader.setAttribute('src', ns.img(link));
  22732. loader.setAttribute('_src', ns.img(link));
  22733. loader.setAttribute('title', json.title || '');
  22734. loader.setAttribute('alt', json.original || '');
  22735. loader.removeAttribute('id');
  22736. domUtils.removeClasses(loader, 'loadingclass');
  22737. } else {
  22738. showErrorLoader && showErrorLoader(json.state);
  22739. }
  22740. me.fireEvent('contentchange');
  22741. } catch (er) {
  22742. showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
  22743. }
  22744. form.reset();
  22745. domUtils.un(iframe, 'load', callback);
  22746. }
  22747. function showErrorLoader(title) {
  22748. if (loadingId) {
  22749. var loader = me.document.getElementById(loadingId);
  22750. loader && domUtils.remove(loader);
  22751. me.fireEvent('showmessage', {
  22752. 'id': loadingId,
  22753. 'content': title,
  22754. 'type': 'error',
  22755. 'timeout': 4000
  22756. });
  22757. }
  22758. }
  22759. /* 判断后端配置是否没有加载成功 */
  22760. if (!me.getOpt('imageActionName')) {
  22761. errorHandler(me.getLang('autoupload.errorLoadConfig'));
  22762. return;
  22763. }
  22764. // 判断文件格式是否错误
  22765. var filename = input.value,
  22766. fileext = filename ? filename.substr(filename.lastIndexOf('.')) : '';
  22767. if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
  22768. showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
  22769. return;
  22770. }
  22771. domUtils.on(iframe, 'load', callback);
  22772. form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?' : '&') + params);
  22773. form.submit();
  22774. });
  22775. var stateTimer;
  22776. me.addListener('selectionchange', function () {
  22777. clearTimeout(stateTimer);
  22778. stateTimer = setTimeout(function () {
  22779. var state = me.queryCommandState('simpleupload');
  22780. if (state == -1) {
  22781. input.disabled = 'disabled';
  22782. } else {
  22783. input.disabled = false;
  22784. }
  22785. }, 400);
  22786. });
  22787. isLoaded = true;
  22788. });
  22789. btnIframe.style.cssText = btnStyle;
  22790. containerBtn.appendChild(btnIframe);
  22791. }
  22792. return {
  22793. bindEvents: {
  22794. 'ready': function () {
  22795. //设置loading的样式
  22796. utils.cssRule('loading',
  22797. '.loadingclass{display:inline-block;cursor:default;background: url(\''
  22798. + this.options.themePath
  22799. + this.options.theme + '/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
  22800. '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
  22801. + this.options.themePath
  22802. + this.options.theme + '/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
  22803. '}',
  22804. this.document);
  22805. },
  22806. /* 初始化简单上传按钮 */
  22807. 'simpleuploadbtnready': function (type, container) {
  22808. containerBtn = container;
  22809. me.afterConfigReady(initUploadBtn);
  22810. }
  22811. },
  22812. outputRule: function (root) {
  22813. utils.each(root.getNodesByTagName('img'), function (n) {
  22814. if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
  22815. n.parentNode.removeChild(n);
  22816. }
  22817. });
  22818. },
  22819. commands: {
  22820. 'simpleupload': {
  22821. queryCommandState: function () {
  22822. return isLoaded ? 0 : -1;
  22823. }
  22824. }
  22825. }
  22826. }
  22827. });
  22828. // plugins/serverparam.js
  22829. /**
  22830. * 服务器提交的额外参数列表设置插件
  22831. * @file
  22832. * @since 1.2.6.1
  22833. */
  22834. UE.plugin.register('serverparam', function () {
  22835. var me = this,
  22836. serverParam = {};
  22837. return {
  22838. commands: {
  22839. /**
  22840. * 修改服务器提交的额外参数列表,清除所有项
  22841. * @command serverparam
  22842. * @method execCommand
  22843. * @param { String } cmd 命令字符串
  22844. * @example
  22845. * ```javascript
  22846. * editor.execCommand('serverparam');
  22847. * editor.queryCommandValue('serverparam'); //返回空
  22848. * ```
  22849. */
  22850. /**
  22851. * 修改服务器提交的额外参数列表,删除指定项
  22852. * @command serverparam
  22853. * @method execCommand
  22854. * @param { String } cmd 命令字符串
  22855. * @param { String } key 要清除的属性
  22856. * @example
  22857. * ```javascript
  22858. * editor.execCommand('serverparam', 'name'); //删除属性name
  22859. * ```
  22860. */
  22861. /**
  22862. * 修改服务器提交的额外参数列表,使用键值添加项
  22863. * @command serverparam
  22864. * @method execCommand
  22865. * @param { String } cmd 命令字符串
  22866. * @param { String } key 要添加的属性
  22867. * @param { String } value 要添加属性的值
  22868. * @example
  22869. * ```javascript
  22870. * editor.execCommand('serverparam', 'name', 'hello');
  22871. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  22872. * ```
  22873. */
  22874. /**
  22875. * 修改服务器提交的额外参数列表,传入键值对对象添加多项
  22876. * @command serverparam
  22877. * @method execCommand
  22878. * @param { String } cmd 命令字符串
  22879. * @param { Object } key 传入的键值对对象
  22880. * @example
  22881. * ```javascript
  22882. * editor.execCommand('serverparam', {'name': 'hello'});
  22883. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  22884. * ```
  22885. */
  22886. /**
  22887. * 修改服务器提交的额外参数列表,使用自定义函数添加多项
  22888. * @command serverparam
  22889. * @method execCommand
  22890. * @param { String } cmd 命令字符串
  22891. * @param { Function } key 自定义获取参数的函数
  22892. * @example
  22893. * ```javascript
  22894. * editor.execCommand('serverparam', function(editor){
  22895. * return {'key': 'value'};
  22896. * });
  22897. * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'}
  22898. * ```
  22899. */
  22900. /**
  22901. * 获取服务器提交的额外参数列表
  22902. * @command serverparam
  22903. * @method queryCommandValue
  22904. * @param { String } cmd 命令字符串
  22905. * @example
  22906. * ```javascript
  22907. * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'}
  22908. * ```
  22909. */
  22910. 'serverparam': {
  22911. execCommand: function (cmd, key, value) {
  22912. if (key === undefined || key === null) { //不传参数,清空列表
  22913. serverParam = {};
  22914. } else if (utils.isString(key)) { //传入键值
  22915. if (value === undefined || value === null) {
  22916. delete serverParam[key];
  22917. } else {
  22918. serverParam[key] = value;
  22919. }
  22920. } else if (utils.isObject(key)) { //传入对象,覆盖列表项
  22921. utils.extend(serverParam, key, true);
  22922. } else if (utils.isFunction(key)) { //传入函数,添加列表项
  22923. utils.extend(serverParam, key(), true);
  22924. }
  22925. },
  22926. queryCommandValue: function () {
  22927. return serverParam || {};
  22928. }
  22929. }
  22930. }
  22931. }
  22932. });
  22933. // plugins/insertfile.js
  22934. /**
  22935. * 插入附件
  22936. */
  22937. UE.plugin.register('insertfile', function () {
  22938. var me = this;
  22939. function getFileIcon(url) {
  22940. var ext = url.substr(url.lastIndexOf('.') + 1).toLowerCase(),
  22941. maps = {
  22942. "rar": "icon_rar.gif",
  22943. "zip": "icon_rar.gif",
  22944. "tar": "icon_rar.gif",
  22945. "gz": "icon_rar.gif",
  22946. "bz2": "icon_rar.gif",
  22947. "doc": "icon_doc.gif",
  22948. "docx": "icon_doc.gif",
  22949. "pdf": "icon_pdf.gif",
  22950. "mp3": "icon_mp3.gif",
  22951. "xls": "icon_xls.gif",
  22952. "chm": "icon_chm.gif",
  22953. "ppt": "icon_ppt.gif",
  22954. "pptx": "icon_ppt.gif",
  22955. "avi": "icon_mv.gif",
  22956. "rmvb": "icon_mv.gif",
  22957. "wmv": "icon_mv.gif",
  22958. "flv": "icon_mv.gif",
  22959. "swf": "icon_mv.gif",
  22960. "rm": "icon_mv.gif",
  22961. "exe": "icon_exe.gif",
  22962. "psd": "icon_psd.gif",
  22963. "txt": "icon_txt.gif",
  22964. "jpg": "icon_jpg.gif",
  22965. "png": "icon_jpg.gif",
  22966. "jpeg": "icon_jpg.gif",
  22967. "gif": "icon_jpg.gif",
  22968. "ico": "icon_jpg.gif",
  22969. "bmp": "icon_jpg.gif"
  22970. };
  22971. return maps[ext] ? maps[ext] : maps['txt'];
  22972. }
  22973. return {
  22974. commands: {
  22975. 'insertfile': {
  22976. execCommand: function (command, filelist) {
  22977. filelist = utils.isArray(filelist) ? filelist : [filelist];
  22978. var i, item, icon, title,
  22979. html = '',
  22980. URL = me.getOpt('UEDITOR_HOME_URL'),
  22981. iconDir = URL + (URL.substr(URL.length - 1) == '/' ? '' : '/') + 'dialogs/attachment/fileTypeImages/';
  22982. for (i = 0; i < filelist.length; i++) {
  22983. item = filelist[i];
  22984. icon = iconDir + getFileIcon(item.url);
  22985. title = item.title || item.url.substr(item.url.lastIndexOf('/') + 1);
  22986. html += '<p style="line-height: 16px;">' +
  22987. '<img style="vertical-align: middle; margin-right: 2px;" src="' + icon + '" _src="' + icon + '" />' +
  22988. '<a style="font-size:12px; color:#0066cc;" href="' + item.url + '" title="' + title + '">' + title + '</a>' +
  22989. '</p>';
  22990. }
  22991. me.execCommand('insertHtml', html);
  22992. }
  22993. }
  22994. }
  22995. }
  22996. });
  22997. // plugins/xssFilter.js
  22998. /**
  22999. * @file xssFilter.js
  23000. * @desc xss过滤器
  23001. * @author robbenmu
  23002. */
  23003. UE.plugins.xssFilter = function () {
  23004. var config = UEDITOR_CONFIG;
  23005. var whitList = config.whitList;
  23006. function filter(node) {
  23007. var tagName = node.tagName;
  23008. var attrs = node.attrs;
  23009. if (!whitList.hasOwnProperty(tagName)) {
  23010. node.parentNode.removeChild(node);
  23011. return false;
  23012. }
  23013. UE.utils.each(attrs, function (val, key) {
  23014. if (whitList[tagName].indexOf(key) === -1) {
  23015. node.setAttr(key);
  23016. }
  23017. });
  23018. }
  23019. // 添加inserthtml\paste等操作用的过滤规则
  23020. if (whitList && config.xssFilterRules) {
  23021. this.options.filterRules = function () {
  23022. var result = {};
  23023. UE.utils.each(whitList, function (val, key) {
  23024. result[key] = function (node) {
  23025. return filter(node);
  23026. };
  23027. });
  23028. return result;
  23029. }();
  23030. }
  23031. var tagList = [];
  23032. UE.utils.each(whitList, function (val, key) {
  23033. tagList.push(key);
  23034. });
  23035. // 添加input过滤规则
  23036. //
  23037. if (whitList && config.inputXssFilter) {
  23038. this.addInputRule(function (root) {
  23039. root.traversal(function (node) {
  23040. if (node.type !== 'element') {
  23041. return false;
  23042. }
  23043. filter(node);
  23044. });
  23045. });
  23046. }
  23047. // 添加output过滤规则
  23048. //
  23049. if (whitList && config.outputXssFilter) {
  23050. this.addOutputRule(function (root) {
  23051. root.traversal(function (node) {
  23052. if (node.type !== 'element') {
  23053. return false;
  23054. }
  23055. filter(node);
  23056. });
  23057. });
  23058. }
  23059. };
  23060. // ui/ui.js
  23061. var baidu = baidu || {};
  23062. baidu.editor = baidu.editor || {};
  23063. UE.ui = baidu.editor.ui = {};
  23064. // ui/uiutils.js
  23065. (function () {
  23066. var browser = baidu.editor.browser,
  23067. domUtils = baidu.editor.dom.domUtils;
  23068. var magic = '$EDITORUI';
  23069. var root = window[magic] = {};
  23070. var uidMagic = 'ID' + magic;
  23071. var uidCount = 0;
  23072. var uiUtils = baidu.editor.ui.uiUtils = {
  23073. uid: function (obj) {
  23074. return (obj ? obj[uidMagic] || (obj[uidMagic] = ++uidCount) : ++uidCount);
  23075. },
  23076. hook: function (fn, callback) {
  23077. var dg;
  23078. if (fn && fn._callbacks) {
  23079. dg = fn;
  23080. } else {
  23081. dg = function () {
  23082. var q;
  23083. if (fn) {
  23084. q = fn.apply(this, arguments);
  23085. }
  23086. var callbacks = dg._callbacks;
  23087. var k = callbacks.length;
  23088. while (k--) {
  23089. var r = callbacks[k].apply(this, arguments);
  23090. if (q === undefined) {
  23091. q = r;
  23092. }
  23093. }
  23094. return q;
  23095. };
  23096. dg._callbacks = [];
  23097. }
  23098. dg._callbacks.push(callback);
  23099. return dg;
  23100. },
  23101. createElementByHtml: function (html) {
  23102. var el = document.createElement('div');
  23103. el.innerHTML = html;
  23104. el = el.firstChild;
  23105. el.parentNode.removeChild(el);
  23106. return el;
  23107. },
  23108. getViewportElement: function () {
  23109. return (browser.ie && browser.quirks) ?
  23110. document.body : document.documentElement;
  23111. },
  23112. getClientRect: function (element) {
  23113. var bcr;
  23114. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  23115. try {
  23116. bcr = element.getBoundingClientRect();
  23117. } catch (e) {
  23118. bcr = {left: 0, top: 0, height: 0, width: 0}
  23119. }
  23120. var rect = {
  23121. left: Math.round(bcr.left),
  23122. top: Math.round(bcr.top),
  23123. height: Math.round(bcr.bottom - bcr.top),
  23124. width: Math.round(bcr.right - bcr.left)
  23125. };
  23126. var doc;
  23127. while ((doc = element.ownerDocument) !== document &&
  23128. (element = domUtils.getWindow(doc).frameElement)) {
  23129. bcr = element.getBoundingClientRect();
  23130. rect.left += bcr.left;
  23131. rect.top += bcr.top;
  23132. }
  23133. rect.bottom = rect.top + rect.height;
  23134. rect.right = rect.left + rect.width;
  23135. return rect;
  23136. },
  23137. getViewportRect: function () {
  23138. var viewportEl = uiUtils.getViewportElement();
  23139. var width = (window.innerWidth || viewportEl.clientWidth) | 0;
  23140. var height = (window.innerHeight || viewportEl.clientHeight) | 0;
  23141. return {
  23142. left: 0,
  23143. top: 0,
  23144. height: height,
  23145. width: width,
  23146. bottom: height,
  23147. right: width
  23148. };
  23149. },
  23150. setViewportOffset: function (element, offset) {
  23151. var rect;
  23152. var fixedLayer = uiUtils.getFixedLayer();
  23153. if (element.parentNode === fixedLayer) {
  23154. element.style.left = offset.left + 'px';
  23155. element.style.top = offset.top + 'px';
  23156. } else {
  23157. domUtils.setViewportOffset(element, offset);
  23158. }
  23159. },
  23160. getEventOffset: function (evt) {
  23161. var el = evt.target || evt.srcElement;
  23162. var rect = uiUtils.getClientRect(el);
  23163. var offset = uiUtils.getViewportOffsetByEvent(evt);
  23164. return {
  23165. left: offset.left - rect.left,
  23166. top: offset.top - rect.top
  23167. };
  23168. },
  23169. getViewportOffsetByEvent: function (evt) {
  23170. var el = evt.target || evt.srcElement;
  23171. var frameEl = domUtils.getWindow(el).frameElement;
  23172. var offset = {
  23173. left: evt.clientX,
  23174. top: evt.clientY
  23175. };
  23176. if (frameEl && el.ownerDocument !== document) {
  23177. var rect = uiUtils.getClientRect(frameEl);
  23178. offset.left += rect.left;
  23179. offset.top += rect.top;
  23180. }
  23181. return offset;
  23182. },
  23183. setGlobal: function (id, obj) {
  23184. root[id] = obj;
  23185. return magic + '["' + id + '"]';
  23186. },
  23187. unsetGlobal: function (id) {
  23188. delete root[id];
  23189. },
  23190. copyAttributes: function (tgt, src) {
  23191. var attributes = src.attributes;
  23192. var k = attributes.length;
  23193. while (k--) {
  23194. var attrNode = attributes[k];
  23195. if (attrNode.nodeName != 'style' && attrNode.nodeName != 'class' && (!browser.ie || attrNode.specified)) {
  23196. tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue);
  23197. }
  23198. }
  23199. if (src.className) {
  23200. domUtils.addClass(tgt, src.className);
  23201. }
  23202. if (src.style.cssText) {
  23203. tgt.style.cssText += ';' + src.style.cssText;
  23204. }
  23205. },
  23206. removeStyle: function (el, styleName) {
  23207. if (el.style.removeProperty) {
  23208. el.style.removeProperty(styleName);
  23209. } else if (el.style.removeAttribute) {
  23210. el.style.removeAttribute(styleName);
  23211. } else throw '';
  23212. },
  23213. contains: function (elA, elB) {
  23214. return elA && elB && (elA === elB ? false : (
  23215. elA.contains ? elA.contains(elB) :
  23216. elA.compareDocumentPosition(elB) & 16
  23217. ));
  23218. },
  23219. startDrag: function (evt, callbacks, doc) {
  23220. var doc = doc || document;
  23221. var startX = evt.clientX;
  23222. var startY = evt.clientY;
  23223. function handleMouseMove(evt) {
  23224. var x = evt.clientX - startX;
  23225. var y = evt.clientY - startY;
  23226. callbacks.ondragmove(x, y, evt);
  23227. if (evt.stopPropagation) {
  23228. evt.stopPropagation();
  23229. } else {
  23230. evt.cancelBubble = true;
  23231. }
  23232. }
  23233. if (doc.addEventListener) {
  23234. function handleMouseUp(evt) {
  23235. doc.removeEventListener('mousemove', handleMouseMove, true);
  23236. doc.removeEventListener('mouseup', handleMouseUp, true);
  23237. window.removeEventListener('mouseup', handleMouseUp, true);
  23238. callbacks.ondragstop();
  23239. }
  23240. doc.addEventListener('mousemove', handleMouseMove, true);
  23241. doc.addEventListener('mouseup', handleMouseUp, true);
  23242. window.addEventListener('mouseup', handleMouseUp, true);
  23243. evt.preventDefault();
  23244. } else {
  23245. var elm = evt.srcElement;
  23246. elm.setCapture();
  23247. function releaseCaptrue() {
  23248. elm.releaseCapture();
  23249. elm.detachEvent('onmousemove', handleMouseMove);
  23250. elm.detachEvent('onmouseup', releaseCaptrue);
  23251. elm.detachEvent('onlosecaptrue', releaseCaptrue);
  23252. callbacks.ondragstop();
  23253. }
  23254. elm.attachEvent('onmousemove', handleMouseMove);
  23255. elm.attachEvent('onmouseup', releaseCaptrue);
  23256. elm.attachEvent('onlosecaptrue', releaseCaptrue);
  23257. evt.returnValue = false;
  23258. }
  23259. callbacks.ondragstart();
  23260. },
  23261. getFixedLayer: function () {
  23262. var layer = document.getElementById('edui_fixedlayer');
  23263. if (layer == null) {
  23264. layer = document.createElement('div');
  23265. layer.id = 'edui_fixedlayer';
  23266. document.body.appendChild(layer);
  23267. if (browser.ie && browser.version <= 8) {
  23268. layer.style.position = 'absolute';
  23269. bindFixedLayer();
  23270. setTimeout(updateFixedOffset);
  23271. } else {
  23272. layer.style.position = 'fixed';
  23273. }
  23274. layer.style.left = '0';
  23275. layer.style.top = '0';
  23276. layer.style.width = '0';
  23277. layer.style.height = '0';
  23278. }
  23279. return layer;
  23280. },
  23281. makeUnselectable: function (element) {
  23282. if (browser.opera || (browser.ie && browser.version < 9)) {
  23283. element.unselectable = 'on';
  23284. if (element.hasChildNodes()) {
  23285. for (var i = 0; i < element.childNodes.length; i++) {
  23286. if (element.childNodes[i].nodeType == 1) {
  23287. uiUtils.makeUnselectable(element.childNodes[i]);
  23288. }
  23289. }
  23290. }
  23291. } else {
  23292. if (element.style.MozUserSelect !== undefined) {
  23293. element.style.MozUserSelect = 'none';
  23294. } else if (element.style.WebkitUserSelect !== undefined) {
  23295. element.style.WebkitUserSelect = 'none';
  23296. } else if (element.style.KhtmlUserSelect !== undefined) {
  23297. element.style.KhtmlUserSelect = 'none';
  23298. }
  23299. }
  23300. }
  23301. };
  23302. function updateFixedOffset() {
  23303. var layer = document.getElementById('edui_fixedlayer');
  23304. uiUtils.setViewportOffset(layer, {
  23305. left: 0,
  23306. top: 0
  23307. });
  23308. // layer.style.display = 'none';
  23309. // layer.style.display = 'block';
  23310. //#trace: 1354
  23311. // setTimeout(updateFixedOffset);
  23312. }
  23313. function bindFixedLayer(adjOffset) {
  23314. domUtils.on(window, 'scroll', updateFixedOffset);
  23315. domUtils.on(window, 'resize', baidu.editor.utils.defer(updateFixedOffset, 0, true));
  23316. }
  23317. })();
  23318. // ui/uibase.js
  23319. (function () {
  23320. var utils = baidu.editor.utils,
  23321. uiUtils = baidu.editor.ui.uiUtils,
  23322. EventBase = baidu.editor.EventBase,
  23323. UIBase = baidu.editor.ui.UIBase = function () {
  23324. };
  23325. UIBase.prototype = {
  23326. className: '',
  23327. uiName: '',
  23328. initOptions: function (options) {
  23329. var me = this;
  23330. for (var k in options) {
  23331. me[k] = options[k];
  23332. }
  23333. this.id = this.id || 'edui' + uiUtils.uid();
  23334. },
  23335. initUIBase: function () {
  23336. this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this));
  23337. },
  23338. render: function (holder) {
  23339. var html = this.renderHtml();
  23340. var el = uiUtils.createElementByHtml(html);
  23341. //by xuheng 给每个node添加class
  23342. var list = domUtils.getElementsByTagName(el, "*");
  23343. var theme = "edui-" + (this.theme || this.editor.options.theme);
  23344. var layer = document.getElementById('edui_fixedlayer');
  23345. for (var i = 0, node; node = list[i++];) {
  23346. domUtils.addClass(node, theme);
  23347. }
  23348. domUtils.addClass(el, theme);
  23349. if (layer) {
  23350. layer.className = "";
  23351. domUtils.addClass(layer, theme);
  23352. }
  23353. var seatEl = this.getDom();
  23354. if (seatEl != null) {
  23355. seatEl.parentNode.replaceChild(el, seatEl);
  23356. uiUtils.copyAttributes(el, seatEl);
  23357. } else {
  23358. if (typeof holder == 'string') {
  23359. holder = document.getElementById(holder);
  23360. }
  23361. holder = holder || uiUtils.getFixedLayer();
  23362. domUtils.addClass(holder, theme);
  23363. holder.appendChild(el);
  23364. }
  23365. this.postRender();
  23366. },
  23367. getDom: function (name) {
  23368. if (!name) {
  23369. return document.getElementById(this.id);
  23370. } else {
  23371. return document.getElementById(this.id + '_' + name);
  23372. }
  23373. },
  23374. postRender: function () {
  23375. this.fireEvent('postrender');
  23376. },
  23377. getHtmlTpl: function () {
  23378. return '';
  23379. },
  23380. formatHtml: function (tpl) {
  23381. var prefix = 'edui-' + this.uiName;
  23382. return (tpl
  23383. .replace(/##/g, this.id)
  23384. .replace(/%%-/g, this.uiName ? prefix + '-' : '')
  23385. .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
  23386. .replace(/\$\$/g, this._globalKey));
  23387. },
  23388. renderHtml: function () {
  23389. return this.formatHtml(this.getHtmlTpl());
  23390. },
  23391. dispose: function () {
  23392. var box = this.getDom();
  23393. if (box) baidu.editor.dom.domUtils.remove(box);
  23394. uiUtils.unsetGlobal(this.id);
  23395. }
  23396. };
  23397. utils.inherits(UIBase, EventBase);
  23398. })();
  23399. // ui/separator.js
  23400. (function () {
  23401. var utils = baidu.editor.utils,
  23402. UIBase = baidu.editor.ui.UIBase,
  23403. Separator = baidu.editor.ui.Separator = function (options) {
  23404. this.initOptions(options);
  23405. this.initSeparator();
  23406. };
  23407. Separator.prototype = {
  23408. uiName: 'separator',
  23409. initSeparator: function () {
  23410. this.initUIBase();
  23411. },
  23412. getHtmlTpl: function () {
  23413. return '<div id="##" class="edui-box %%"></div>';
  23414. }
  23415. };
  23416. utils.inherits(Separator, UIBase);
  23417. })();
  23418. // ui/mask.js
  23419. ///import core
  23420. ///import uicore
  23421. (function () {
  23422. var utils = baidu.editor.utils,
  23423. domUtils = baidu.editor.dom.domUtils,
  23424. UIBase = baidu.editor.ui.UIBase,
  23425. uiUtils = baidu.editor.ui.uiUtils;
  23426. var Mask = baidu.editor.ui.Mask = function (options) {
  23427. this.initOptions(options);
  23428. this.initUIBase();
  23429. };
  23430. Mask.prototype = {
  23431. getHtmlTpl: function () {
  23432. return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>';
  23433. },
  23434. postRender: function () {
  23435. var me = this;
  23436. domUtils.on(window, 'resize', function () {
  23437. setTimeout(function () {
  23438. if (!me.isHidden()) {
  23439. me._fill();
  23440. }
  23441. });
  23442. });
  23443. },
  23444. show: function (zIndex) {
  23445. this._fill();
  23446. this.getDom().style.display = '';
  23447. this.getDom().style.zIndex = zIndex;
  23448. },
  23449. hide: function () {
  23450. this.getDom().style.display = 'none';
  23451. this.getDom().style.zIndex = '';
  23452. },
  23453. isHidden: function () {
  23454. return this.getDom().style.display == 'none';
  23455. },
  23456. _onMouseDown: function () {
  23457. return false;
  23458. },
  23459. _onClick: function (e, target) {
  23460. this.fireEvent('click', e, target);
  23461. },
  23462. _fill: function () {
  23463. var el = this.getDom();
  23464. var vpRect = uiUtils.getViewportRect();
  23465. el.style.width = vpRect.width + 'px';
  23466. el.style.height = vpRect.height + 'px';
  23467. }
  23468. };
  23469. utils.inherits(Mask, UIBase);
  23470. })();
  23471. // ui/popup.js
  23472. ///import core
  23473. ///import uicore
  23474. (function () {
  23475. var utils = baidu.editor.utils,
  23476. uiUtils = baidu.editor.ui.uiUtils,
  23477. domUtils = baidu.editor.dom.domUtils,
  23478. UIBase = baidu.editor.ui.UIBase,
  23479. Popup = baidu.editor.ui.Popup = function (options) {
  23480. this.initOptions(options);
  23481. this.initPopup();
  23482. };
  23483. var allPopups = [];
  23484. function closeAllPopup(evt, el) {
  23485. for (var i = 0; i < allPopups.length; i++) {
  23486. var pop = allPopups[i];
  23487. if (!pop.isHidden()) {
  23488. if (pop.queryAutoHide(el) !== false) {
  23489. if (evt && /scroll/ig.test(evt.type) && pop.className == "edui-wordpastepop") return;
  23490. pop.hide();
  23491. }
  23492. }
  23493. }
  23494. if (allPopups.length)
  23495. pop.editor.fireEvent("afterhidepop");
  23496. }
  23497. Popup.postHide = closeAllPopup;
  23498. var ANCHOR_CLASSES = ['edui-anchor-topleft', 'edui-anchor-topright',
  23499. 'edui-anchor-bottomleft', 'edui-anchor-bottomright'];
  23500. Popup.prototype = {
  23501. SHADOW_RADIUS: 5,
  23502. content: null,
  23503. _hidden: false,
  23504. autoRender: true,
  23505. canSideLeft: true,
  23506. canSideUp: true,
  23507. initPopup: function () {
  23508. this.initUIBase();
  23509. allPopups.push(this);
  23510. },
  23511. getHtmlTpl: function () {
  23512. return '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
  23513. ' <div id="##_body" class="edui-popup-body">' +
  23514. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  23515. ' <div class="edui-shadow"></div>' +
  23516. ' <div id="##_content" class="edui-popup-content">' +
  23517. this.getContentHtmlTpl() +
  23518. ' </div>' +
  23519. ' </div>' +
  23520. '</div>';
  23521. },
  23522. getContentHtmlTpl: function () {
  23523. if (this.content) {
  23524. if (typeof this.content == 'string') {
  23525. return this.content;
  23526. }
  23527. return this.content.renderHtml();
  23528. } else {
  23529. return ''
  23530. }
  23531. },
  23532. _UIBase_postRender: UIBase.prototype.postRender,
  23533. postRender: function () {
  23534. if (this.content instanceof UIBase) {
  23535. this.content.postRender();
  23536. }
  23537. //捕获鼠标滚轮
  23538. if (this.captureWheel && !this.captured) {
  23539. this.captured = true;
  23540. var winHeight = (document.documentElement.clientHeight || document.body.clientHeight) - 80,
  23541. _height = this.getDom().offsetHeight,
  23542. _top = uiUtils.getClientRect(this.combox.getDom()).top,
  23543. content = this.getDom('content'),
  23544. ifr = this.getDom('body').getElementsByTagName('iframe'),
  23545. me = this;
  23546. ifr.length && (ifr = ifr[0]);
  23547. while (_top + _height > winHeight) {
  23548. _height -= 30;
  23549. }
  23550. content.style.height = _height + 'px';
  23551. //同步更改iframe高度
  23552. ifr && (ifr.style.height = _height + 'px');
  23553. //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
  23554. if (window.XMLHttpRequest) {
  23555. domUtils.on(content, ('onmousewheel' in document.body) ? 'mousewheel' : 'DOMMouseScroll', function (e) {
  23556. if (e.preventDefault) {
  23557. e.preventDefault();
  23558. } else {
  23559. e.returnValue = false;
  23560. }
  23561. if (e.wheelDelta) {
  23562. content.scrollTop -= (e.wheelDelta / 120) * 60;
  23563. } else {
  23564. content.scrollTop -= (e.detail / -3) * 60;
  23565. }
  23566. });
  23567. } else {
  23568. //ie6
  23569. domUtils.on(this.getDom(), 'mousewheel', function (e) {
  23570. e.returnValue = false;
  23571. me.getDom('content').scrollTop -= (e.wheelDelta / 120) * 60;
  23572. });
  23573. }
  23574. }
  23575. this.fireEvent('postRenderAfter');
  23576. this.hide(true);
  23577. this._UIBase_postRender();
  23578. },
  23579. _doAutoRender: function () {
  23580. if (!this.getDom() && this.autoRender) {
  23581. this.render();
  23582. }
  23583. },
  23584. mesureSize: function () {
  23585. var box = this.getDom('content');
  23586. return uiUtils.getClientRect(box);
  23587. },
  23588. fitSize: function () {
  23589. if (this.captureWheel && this.sized) {
  23590. return this.__size;
  23591. }
  23592. this.sized = true;
  23593. var popBodyEl = this.getDom('body');
  23594. popBodyEl.style.width = '';
  23595. popBodyEl.style.height = '';
  23596. var size = this.mesureSize();
  23597. if (this.captureWheel) {
  23598. popBodyEl.style.width = -(-20 - size.width) + 'px';
  23599. var height = parseInt(this.getDom('content').style.height, 10);
  23600. !window.isNaN(height) && (size.height = height);
  23601. } else {
  23602. popBodyEl.style.width = size.width + 'px';
  23603. }
  23604. popBodyEl.style.height = size.height + 'px';
  23605. this.__size = size;
  23606. this.captureWheel && (this.getDom('content').style.overflow = 'auto');
  23607. return size;
  23608. },
  23609. showAnchor: function (element, hoz) {
  23610. this.showAnchorRect(uiUtils.getClientRect(element), hoz);
  23611. },
  23612. showAnchorRect: function (rect, hoz, adj) {
  23613. this._doAutoRender();
  23614. var vpRect = uiUtils.getViewportRect();
  23615. this.getDom().style.visibility = 'hidden';
  23616. this._show();
  23617. var popSize = this.fitSize();
  23618. var sideLeft, sideUp, left, top;
  23619. if (hoz) {
  23620. sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
  23621. sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
  23622. left = (sideLeft ? rect.left - popSize.width : rect.right);
  23623. top = (sideUp ? rect.bottom - popSize.height : rect.top);
  23624. } else {
  23625. sideLeft = this.canSideLeft && (rect.right + popSize.width > vpRect.right && rect.left > popSize.width);
  23626. sideUp = this.canSideUp && (rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height);
  23627. left = (sideLeft ? rect.right - popSize.width : rect.left);
  23628. top = (sideUp ? rect.top - popSize.height : rect.bottom);
  23629. }
  23630. var popEl = this.getDom();
  23631. uiUtils.setViewportOffset(popEl, {
  23632. left: left,
  23633. top: top
  23634. });
  23635. domUtils.removeClasses(popEl, ANCHOR_CLASSES);
  23636. popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)];
  23637. if (this.editor) {
  23638. popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10;
  23639. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1;
  23640. }
  23641. this.getDom().style.visibility = 'visible';
  23642. },
  23643. showAt: function (offset) {
  23644. var left = offset.left;
  23645. var top = offset.top;
  23646. var rect = {
  23647. left: left,
  23648. top: top,
  23649. right: left,
  23650. bottom: top,
  23651. height: 0,
  23652. width: 0
  23653. };
  23654. this.showAnchorRect(rect, false, true);
  23655. },
  23656. _show: function () {
  23657. if (this._hidden) {
  23658. var box = this.getDom();
  23659. box.style.display = '';
  23660. this._hidden = false;
  23661. // if (box.setActive) {
  23662. // box.setActive();
  23663. // }
  23664. this.fireEvent('show');
  23665. }
  23666. },
  23667. isHidden: function () {
  23668. return this._hidden;
  23669. },
  23670. show: function () {
  23671. this._doAutoRender();
  23672. this._show();
  23673. },
  23674. hide: function (notNofity) {
  23675. if (!this._hidden && this.getDom()) {
  23676. this.getDom().style.display = 'none';
  23677. this._hidden = true;
  23678. if (!notNofity) {
  23679. this.fireEvent('hide');
  23680. }
  23681. }
  23682. },
  23683. queryAutoHide: function (el) {
  23684. return !el || !uiUtils.contains(this.getDom(), el);
  23685. }
  23686. };
  23687. utils.inherits(Popup, UIBase);
  23688. domUtils.on(document, 'mousedown', function (evt) {
  23689. var el = evt.target || evt.srcElement;
  23690. closeAllPopup(evt, el);
  23691. });
  23692. domUtils.on(window, 'scroll', function (evt, el) {
  23693. closeAllPopup(evt, el);
  23694. });
  23695. })();
  23696. // ui/colorpicker.js
  23697. ///import core
  23698. ///import uicore
  23699. (function () {
  23700. var utils = baidu.editor.utils,
  23701. UIBase = baidu.editor.ui.UIBase,
  23702. ColorPicker = baidu.editor.ui.ColorPicker = function (options) {
  23703. this.initOptions(options);
  23704. this.noColorText = this.noColorText || this.editor.getLang("clearColor");
  23705. this.initUIBase();
  23706. };
  23707. ColorPicker.prototype = {
  23708. getHtmlTpl: function () {
  23709. return genColorPicker(this.noColorText, this.editor);
  23710. },
  23711. _onTableClick: function (evt) {
  23712. var tgt = evt.target || evt.srcElement;
  23713. var color = tgt.getAttribute('data-color');
  23714. if (color) {
  23715. this.fireEvent('pickcolor', color);
  23716. }
  23717. },
  23718. _onTableOver: function (evt) {
  23719. var tgt = evt.target || evt.srcElement;
  23720. var color = tgt.getAttribute('data-color');
  23721. if (color) {
  23722. this.getDom('preview').style.backgroundColor = color;
  23723. }
  23724. },
  23725. _onTableOut: function () {
  23726. this.getDom('preview').style.backgroundColor = '';
  23727. },
  23728. _onPickNoColor: function () {
  23729. this.fireEvent('picknocolor');
  23730. }
  23731. };
  23732. utils.inherits(ColorPicker, UIBase);
  23733. var COLORS = (
  23734. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  23735. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  23736. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  23737. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  23738. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  23739. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  23740. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  23741. function genColorPicker(noColorText, editor) {
  23742. var html = '<div id="##" class="edui-colorpicker %%">' +
  23743. '<div class="edui-colorpicker-topbar edui-clearfix">' +
  23744. '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
  23745. '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">' + noColorText + '</div>' +
  23746. '</div>' +
  23747. '<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">' +
  23748. '<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>' +
  23749. '<tr class="edui-colorpicker-tablefirstrow" >';
  23750. for (var i = 0; i < COLORS.length; i++) {
  23751. if (i && i % 10 === 0) {
  23752. 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"' : '') + '>';
  23753. }
  23754. 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"' +
  23755. ' data-color="#' + COLORS[i] + '"' +
  23756. ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
  23757. (i < 10 || i >= 60 ? 'border-width:1px;' :
  23758. i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
  23759. 'border-width:0 1px 0 1px;') +
  23760. '"' +
  23761. '></a></td>' : '';
  23762. }
  23763. html += '</tr></table></div>';
  23764. return html;
  23765. }
  23766. })();
  23767. // ui/tablepicker.js
  23768. ///import core
  23769. ///import uicore
  23770. (function () {
  23771. var utils = baidu.editor.utils,
  23772. uiUtils = baidu.editor.ui.uiUtils,
  23773. UIBase = baidu.editor.ui.UIBase;
  23774. var TablePicker = baidu.editor.ui.TablePicker = function (options) {
  23775. this.initOptions(options);
  23776. this.initTablePicker();
  23777. };
  23778. TablePicker.prototype = {
  23779. defaultNumRows: 10,
  23780. defaultNumCols: 10,
  23781. maxNumRows: 20,
  23782. maxNumCols: 20,
  23783. numRows: 10,
  23784. numCols: 10,
  23785. lengthOfCellSide: 22,
  23786. initTablePicker: function () {
  23787. this.initUIBase();
  23788. },
  23789. getHtmlTpl: function () {
  23790. var me = this;
  23791. return '<div id="##" class="edui-tablepicker %%">' +
  23792. '<div class="edui-tablepicker-body">' +
  23793. '<div class="edui-infoarea">' +
  23794. '<span id="##_label" class="edui-label"></span>' +
  23795. '</div>' +
  23796. '<div class="edui-pickarea"' +
  23797. ' onmousemove="$$._onMouseMove(event, this);"' +
  23798. ' onmouseover="$$._onMouseOver(event, this);"' +
  23799. ' onmouseout="$$._onMouseOut(event, this);"' +
  23800. ' onclick="$$._onClick(event, this);"' +
  23801. '>' +
  23802. '<div id="##_overlay" class="edui-overlay"></div>' +
  23803. '</div>' +
  23804. '</div>' +
  23805. '</div>';
  23806. },
  23807. _UIBase_render: UIBase.prototype.render,
  23808. render: function (holder) {
  23809. this._UIBase_render(holder);
  23810. this.getDom('label').innerHTML = '0' + this.editor.getLang("t_row") + ' x 0' + this.editor.getLang("t_col");
  23811. },
  23812. _track: function (numCols, numRows) {
  23813. var style = this.getDom('overlay').style;
  23814. var sideLen = this.lengthOfCellSide;
  23815. style.width = numCols * sideLen + 'px';
  23816. style.height = numRows * sideLen + 'px';
  23817. var label = this.getDom('label');
  23818. label.innerHTML = numCols + this.editor.getLang("t_col") + ' x ' + numRows + this.editor.getLang("t_row");
  23819. this.numCols = numCols;
  23820. this.numRows = numRows;
  23821. },
  23822. _onMouseOver: function (evt, el) {
  23823. var rel = evt.relatedTarget || evt.fromElement;
  23824. if (!uiUtils.contains(el, rel) && el !== rel) {
  23825. this.getDom('label').innerHTML = '0' + this.editor.getLang("t_col") + ' x 0' + this.editor.getLang("t_row");
  23826. this.getDom('overlay').style.visibility = '';
  23827. }
  23828. },
  23829. _onMouseOut: function (evt, el) {
  23830. var rel = evt.relatedTarget || evt.toElement;
  23831. if (!uiUtils.contains(el, rel) && el !== rel) {
  23832. this.getDom('label').innerHTML = '0' + this.editor.getLang("t_col") + ' x 0' + this.editor.getLang("t_row");
  23833. this.getDom('overlay').style.visibility = 'hidden';
  23834. }
  23835. },
  23836. _onMouseMove: function (evt, el) {
  23837. var style = this.getDom('overlay').style;
  23838. var offset = uiUtils.getEventOffset(evt);
  23839. var sideLen = this.lengthOfCellSide;
  23840. var numCols = Math.ceil(offset.left / sideLen);
  23841. var numRows = Math.ceil(offset.top / sideLen);
  23842. this._track(numCols, numRows);
  23843. },
  23844. _onClick: function () {
  23845. this.fireEvent('picktable', this.numCols, this.numRows);
  23846. }
  23847. };
  23848. utils.inherits(TablePicker, UIBase);
  23849. })();
  23850. // ui/stateful.js
  23851. (function () {
  23852. var browser = baidu.editor.browser,
  23853. domUtils = baidu.editor.dom.domUtils,
  23854. uiUtils = baidu.editor.ui.uiUtils;
  23855. var TPL_STATEFUL = 'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
  23856. ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
  23857. (browser.ie ? (
  23858. ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
  23859. ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"')
  23860. : (
  23861. ' onmouseover="$$.Stateful_onMouseOver(event, this);"' +
  23862. ' onmouseout="$$.Stateful_onMouseOut(event, this);"'));
  23863. baidu.editor.ui.Stateful = {
  23864. alwalysHoverable: false,
  23865. target: null,//目标元素和this指向dom不一样
  23866. Stateful_init: function () {
  23867. this._Stateful_dGetHtmlTpl = this.getHtmlTpl;
  23868. this.getHtmlTpl = this.Stateful_getHtmlTpl;
  23869. },
  23870. Stateful_getHtmlTpl: function () {
  23871. var tpl = this._Stateful_dGetHtmlTpl();
  23872. // 使用function避免$转义
  23873. return tpl.replace(/stateful/g, function () {
  23874. return TPL_STATEFUL;
  23875. });
  23876. },
  23877. Stateful_onMouseEnter: function (evt, el) {
  23878. this.target = el;
  23879. if (!this.isDisabled() || this.alwalysHoverable) {
  23880. this.addState('hover');
  23881. this.fireEvent('over');
  23882. }
  23883. },
  23884. Stateful_onMouseLeave: function (evt, el) {
  23885. if (!this.isDisabled() || this.alwalysHoverable) {
  23886. this.removeState('hover');
  23887. this.removeState('active');
  23888. this.fireEvent('out');
  23889. }
  23890. },
  23891. Stateful_onMouseOver: function (evt, el) {
  23892. var rel = evt.relatedTarget;
  23893. if (!uiUtils.contains(el, rel) && el !== rel) {
  23894. this.Stateful_onMouseEnter(evt, el);
  23895. }
  23896. },
  23897. Stateful_onMouseOut: function (evt, el) {
  23898. var rel = evt.relatedTarget;
  23899. if (!uiUtils.contains(el, rel) && el !== rel) {
  23900. this.Stateful_onMouseLeave(evt, el);
  23901. }
  23902. },
  23903. Stateful_onMouseDown: function (evt, el) {
  23904. if (!this.isDisabled()) {
  23905. this.addState('active');
  23906. }
  23907. },
  23908. Stateful_onMouseUp: function (evt, el) {
  23909. if (!this.isDisabled()) {
  23910. this.removeState('active');
  23911. }
  23912. },
  23913. Stateful_postRender: function () {
  23914. if (this.disabled && !this.hasState('disabled')) {
  23915. this.addState('disabled');
  23916. }
  23917. },
  23918. hasState: function (state) {
  23919. return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state);
  23920. },
  23921. addState: function (state) {
  23922. if (!this.hasState(state)) {
  23923. this.getStateDom().className += ' edui-state-' + state;
  23924. }
  23925. },
  23926. removeState: function (state) {
  23927. if (this.hasState(state)) {
  23928. domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state]);
  23929. }
  23930. },
  23931. getStateDom: function () {
  23932. return this.getDom('state');
  23933. },
  23934. isChecked: function () {
  23935. return this.hasState('checked');
  23936. },
  23937. setChecked: function (checked) {
  23938. if (!this.isDisabled() && checked) {
  23939. this.addState('checked');
  23940. } else {
  23941. this.removeState('checked');
  23942. }
  23943. },
  23944. isDisabled: function () {
  23945. return this.hasState('disabled');
  23946. },
  23947. setDisabled: function (disabled) {
  23948. if (disabled) {
  23949. this.removeState('hover');
  23950. this.removeState('checked');
  23951. this.removeState('active');
  23952. this.addState('disabled');
  23953. } else {
  23954. this.removeState('disabled');
  23955. }
  23956. }
  23957. };
  23958. })();
  23959. // ui/button.js
  23960. ///import core
  23961. ///import uicore
  23962. ///import ui/stateful.js
  23963. (function () {
  23964. var utils = baidu.editor.utils,
  23965. UIBase = baidu.editor.ui.UIBase,
  23966. Stateful = baidu.editor.ui.Stateful,
  23967. Button = baidu.editor.ui.Button = function (options) {
  23968. if (options.name) {
  23969. var btnName = options.name;
  23970. var cssRules = options.cssRules;
  23971. if (!options.className) {
  23972. options.className = 'edui-for-' + btnName;
  23973. }
  23974. options.cssRules = '.edui-default .edui-for-' + btnName + ' .edui-icon {' + cssRules + '}'
  23975. }
  23976. this.initOptions(options);
  23977. this.initButton();
  23978. };
  23979. Button.prototype = {
  23980. uiName: 'button',
  23981. label: '',
  23982. title: '',
  23983. showIcon: true,
  23984. showText: true,
  23985. cssRules: '',
  23986. initButton: function () {
  23987. this.initUIBase();
  23988. this.Stateful_init();
  23989. if (this.cssRules) {
  23990. utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules);
  23991. }
  23992. },
  23993. getHtmlTpl: function () {
  23994. return '<div id="##" class="edui-box %%">' +
  23995. '<div id="##_state" stateful>' +
  23996. '<div class="%%-wrap"><div id="##_body" unselectable="on" ' + (this.title ? 'title="' + this.title + '"' : '') +
  23997. ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
  23998. (this.showIcon ? '<div class="edui-box edui-icon"></div>' : '') +
  23999. (this.showText ? '<div class="edui-box edui-label">' + this.label + '</div>' : '') +
  24000. '</div>' +
  24001. '</div>' +
  24002. '</div></div>';
  24003. },
  24004. postRender: function () {
  24005. this.Stateful_postRender();
  24006. this.setDisabled(this.disabled)
  24007. },
  24008. _onMouseDown: function (e) {
  24009. var target = e.target || e.srcElement,
  24010. tagName = target && target.tagName && target.tagName.toLowerCase();
  24011. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  24012. return false;
  24013. }
  24014. },
  24015. _onClick: function () {
  24016. if (!this.isDisabled()) {
  24017. this.fireEvent('click');
  24018. }
  24019. },
  24020. setTitle: function (text) {
  24021. var label = this.getDom('label');
  24022. label.innerHTML = text;
  24023. }
  24024. };
  24025. utils.inherits(Button, UIBase);
  24026. utils.extend(Button.prototype, Stateful);
  24027. })();
  24028. // ui/splitbutton.js
  24029. ///import core
  24030. ///import uicore
  24031. ///import ui/stateful.js
  24032. (function () {
  24033. var utils = baidu.editor.utils,
  24034. uiUtils = baidu.editor.ui.uiUtils,
  24035. domUtils = baidu.editor.dom.domUtils,
  24036. UIBase = baidu.editor.ui.UIBase,
  24037. Stateful = baidu.editor.ui.Stateful,
  24038. SplitButton = baidu.editor.ui.SplitButton = function (options) {
  24039. this.initOptions(options);
  24040. this.initSplitButton();
  24041. };
  24042. SplitButton.prototype = {
  24043. popup: null,
  24044. uiName: 'splitbutton',
  24045. title: '',
  24046. initSplitButton: function () {
  24047. this.initUIBase();
  24048. this.Stateful_init();
  24049. var me = this;
  24050. if (this.popup != null) {
  24051. var popup = this.popup;
  24052. this.popup = null;
  24053. this.setPopup(popup);
  24054. }
  24055. },
  24056. _UIBase_postRender: UIBase.prototype.postRender,
  24057. postRender: function () {
  24058. this.Stateful_postRender();
  24059. this._UIBase_postRender();
  24060. },
  24061. setPopup: function (popup) {
  24062. if (this.popup === popup) return;
  24063. if (this.popup != null) {
  24064. this.popup.dispose();
  24065. }
  24066. popup.addListener('show', utils.bind(this._onPopupShow, this));
  24067. popup.addListener('hide', utils.bind(this._onPopupHide, this));
  24068. popup.addListener('postrender', utils.bind(function () {
  24069. popup.getDom('body').appendChild(
  24070. uiUtils.createElementByHtml('<div id="' +
  24071. this.popup.id + '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
  24072. (uiUtils.getClientRect(this.getDom()).width + 20) + 'px"></div>')
  24073. );
  24074. popup.getDom().className += ' ' + this.className;
  24075. }, this));
  24076. this.popup = popup;
  24077. },
  24078. _onPopupShow: function () {
  24079. this.addState('opened');
  24080. },
  24081. _onPopupHide: function () {
  24082. this.removeState('opened');
  24083. },
  24084. getHtmlTpl: function () {
  24085. return '<div id="##" class="edui-box %%">' +
  24086. '<div ' + (this.title ? 'title="' + this.title + '"' : '') + ' id="##_state" stateful><div class="%%-body">' +
  24087. '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
  24088. '<div class="edui-box edui-icon"></div>' +
  24089. '</div>' +
  24090. '<div class="edui-box edui-splitborder"></div>' +
  24091. '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
  24092. '</div></div></div>';
  24093. },
  24094. showPopup: function () {
  24095. // 当popup往上弹出的时候,做特殊处理
  24096. var rect = uiUtils.getClientRect(this.getDom());
  24097. rect.top -= this.popup.SHADOW_RADIUS;
  24098. rect.height += this.popup.SHADOW_RADIUS;
  24099. this.popup.showAnchorRect(rect);
  24100. },
  24101. _onArrowClick: function (event, el) {
  24102. if (!this.isDisabled()) {
  24103. this.showPopup();
  24104. }
  24105. },
  24106. _onButtonClick: function () {
  24107. if (!this.isDisabled()) {
  24108. this.fireEvent('buttonclick');
  24109. }
  24110. }
  24111. };
  24112. utils.inherits(SplitButton, UIBase);
  24113. utils.extend(SplitButton.prototype, Stateful, true);
  24114. })();
  24115. // ui/colorbutton.js
  24116. ///import core
  24117. ///import uicore
  24118. ///import ui/colorpicker.js
  24119. ///import ui/popup.js
  24120. ///import ui/splitbutton.js
  24121. (function () {
  24122. var utils = baidu.editor.utils,
  24123. uiUtils = baidu.editor.ui.uiUtils,
  24124. ColorPicker = baidu.editor.ui.ColorPicker,
  24125. Popup = baidu.editor.ui.Popup,
  24126. SplitButton = baidu.editor.ui.SplitButton,
  24127. ColorButton = baidu.editor.ui.ColorButton = function (options) {
  24128. this.initOptions(options);
  24129. this.initColorButton();
  24130. };
  24131. ColorButton.prototype = {
  24132. initColorButton: function () {
  24133. var me = this;
  24134. this.popup = new Popup({
  24135. content: new ColorPicker({
  24136. noColorText: me.editor.getLang("clearColor"),
  24137. editor: me.editor,
  24138. onpickcolor: function (t, color) {
  24139. me._onPickColor(color);
  24140. },
  24141. onpicknocolor: function (t, color) {
  24142. me._onPickNoColor(color);
  24143. }
  24144. }),
  24145. editor: me.editor
  24146. });
  24147. this.initSplitButton();
  24148. },
  24149. _SplitButton_postRender: SplitButton.prototype.postRender,
  24150. postRender: function () {
  24151. this._SplitButton_postRender();
  24152. this.getDom('button_body').appendChild(
  24153. uiUtils.createElementByHtml('<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>')
  24154. );
  24155. this.getDom().className += ' edui-colorbutton';
  24156. },
  24157. setColor: function (color) {
  24158. this.getDom('colorlump').style.backgroundColor = color;
  24159. this.color = color;
  24160. },
  24161. _onPickColor: function (color) {
  24162. if (this.fireEvent('pickcolor', color) !== false) {
  24163. this.setColor(color);
  24164. this.popup.hide();
  24165. }
  24166. },
  24167. _onPickNoColor: function (color) {
  24168. if (this.fireEvent('picknocolor') !== false) {
  24169. this.popup.hide();
  24170. }
  24171. }
  24172. };
  24173. utils.inherits(ColorButton, SplitButton);
  24174. })();
  24175. // ui/tablebutton.js
  24176. ///import core
  24177. ///import uicore
  24178. ///import ui/popup.js
  24179. ///import ui/tablepicker.js
  24180. ///import ui/splitbutton.js
  24181. (function () {
  24182. var utils = baidu.editor.utils,
  24183. Popup = baidu.editor.ui.Popup,
  24184. TablePicker = baidu.editor.ui.TablePicker,
  24185. SplitButton = baidu.editor.ui.SplitButton,
  24186. TableButton = baidu.editor.ui.TableButton = function (options) {
  24187. this.initOptions(options);
  24188. this.initTableButton();
  24189. };
  24190. TableButton.prototype = {
  24191. initTableButton: function () {
  24192. var me = this;
  24193. this.popup = new Popup({
  24194. content: new TablePicker({
  24195. editor: me.editor,
  24196. onpicktable: function (t, numCols, numRows) {
  24197. me._onPickTable(numCols, numRows);
  24198. }
  24199. }),
  24200. 'editor': me.editor
  24201. });
  24202. this.initSplitButton();
  24203. },
  24204. _onPickTable: function (numCols, numRows) {
  24205. if (this.fireEvent('picktable', numCols, numRows) !== false) {
  24206. this.popup.hide();
  24207. }
  24208. }
  24209. };
  24210. utils.inherits(TableButton, SplitButton);
  24211. })();
  24212. // ui/autotypesetpicker.js
  24213. ///import core
  24214. ///import uicore
  24215. (function () {
  24216. var utils = baidu.editor.utils,
  24217. UIBase = baidu.editor.ui.UIBase;
  24218. var AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker = function (options) {
  24219. this.initOptions(options);
  24220. this.initAutoTypeSetPicker();
  24221. };
  24222. AutoTypeSetPicker.prototype = {
  24223. initAutoTypeSetPicker: function () {
  24224. this.initUIBase();
  24225. },
  24226. getHtmlTpl: function () {
  24227. var me = this.editor,
  24228. opt = me.options.autotypeset,
  24229. lang = me.getLang("autoTypeSet");
  24230. var textAlignInputName = 'textAlignValue' + me.uid,
  24231. imageBlockInputName = 'imageBlockLineValue' + me.uid,
  24232. symbolConverInputName = 'symbolConverValue' + me.uid;
  24233. return '<div id="##" class="edui-autotypesetpicker %%">' +
  24234. '<div class="edui-autotypesetpicker-body">' +
  24235. '<table >' +
  24236. '<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>' +
  24237. '<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>' +
  24238. '<tr>' +
  24239. '<td nowrap><input type="checkbox" name="textAlign" ' + (opt["textAlign"] ? "checked" : "") + '>' + lang.alignment + '</td>' +
  24240. '<td colspan="2" id="' + textAlignInputName + '">' +
  24241. '<input type="radio" name="' + textAlignInputName + '" value="left" ' + ((opt["textAlign"] && opt["textAlign"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
  24242. '<input type="radio" name="' + textAlignInputName + '" value="center" ' + ((opt["textAlign"] && opt["textAlign"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
  24243. '<input type="radio" name="' + textAlignInputName + '" value="right" ' + ((opt["textAlign"] && opt["textAlign"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
  24244. '</td>' +
  24245. '</tr>' +
  24246. '<tr>' +
  24247. '<td nowrap><input type="checkbox" name="imageBlockLine" ' + (opt["imageBlockLine"] ? "checked" : "") + '>' + lang.imageFloat + '</td>' +
  24248. '<td nowrap id="' + imageBlockInputName + '">' +
  24249. '<input type="radio" name="' + imageBlockInputName + '" value="none" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "none") ? "checked" : "") + '>' + me.getLang("default") +
  24250. '<input type="radio" name="' + imageBlockInputName + '" value="left" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "left") ? "checked" : "") + '>' + me.getLang("justifyleft") +
  24251. '<input type="radio" name="' + imageBlockInputName + '" value="center" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "center") ? "checked" : "") + '>' + me.getLang("justifycenter") +
  24252. '<input type="radio" name="' + imageBlockInputName + '" value="right" ' + ((opt["imageBlockLine"] && opt["imageBlockLine"] == "right") ? "checked" : "") + '>' + me.getLang("justifyright") +
  24253. '</td>' +
  24254. '</tr>' +
  24255. '<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>' +
  24256. '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' + (opt["removeEmptyNode"] ? "checked" : "") + '>' + lang.removeHtml + '</td></tr>' +
  24257. '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' + (opt["pasteFilter"] ? "checked" : "") + '>' + lang.pasteFilter + '</td></tr>' +
  24258. '<tr>' +
  24259. '<td nowrap><input type="checkbox" name="symbolConver" ' + (opt["bdc2sb"] || opt["tobdc"] ? "checked" : "") + '>' + lang.symbol + '</td>' +
  24260. '<td id="' + symbolConverInputName + '">' +
  24261. '<input type="radio" name="bdc" value="bdc2sb" ' + (opt["bdc2sb"] ? "checked" : "") + '>' + lang.bdc2sb +
  24262. '<input type="radio" name="bdc" value="tobdc" ' + (opt["tobdc"] ? "checked" : "") + '>' + lang.tobdc + '' +
  24263. '</td>' +
  24264. '<td nowrap align="right"><button >' + lang.run + '</button></td>' +
  24265. '</tr>' +
  24266. '</table>' +
  24267. '</div>' +
  24268. '</div>';
  24269. },
  24270. _UIBase_render: UIBase.prototype.render
  24271. };
  24272. utils.inherits(AutoTypeSetPicker, UIBase);
  24273. })();
  24274. // ui/autotypesetbutton.js
  24275. ///import core
  24276. ///import uicore
  24277. ///import ui/popup.js
  24278. ///import ui/autotypesetpicker.js
  24279. ///import ui/splitbutton.js
  24280. (function () {
  24281. var utils = baidu.editor.utils,
  24282. Popup = baidu.editor.ui.Popup,
  24283. AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
  24284. SplitButton = baidu.editor.ui.SplitButton,
  24285. AutoTypeSetButton = baidu.editor.ui.AutoTypeSetButton = function (options) {
  24286. this.initOptions(options);
  24287. this.initAutoTypeSetButton();
  24288. };
  24289. function getPara(me) {
  24290. var opt = {},
  24291. cont = me.getDom(),
  24292. editorId = me.editor.uid,
  24293. inputType = null,
  24294. attrName = null,
  24295. ipts = domUtils.getElementsByTagName(cont, "input");
  24296. for (var i = ipts.length - 1, ipt; ipt = ipts[i--];) {
  24297. inputType = ipt.getAttribute("type");
  24298. if (inputType == "checkbox") {
  24299. attrName = ipt.getAttribute("name");
  24300. opt[attrName] && delete opt[attrName];
  24301. if (ipt.checked) {
  24302. var attrValue = document.getElementById(attrName + "Value" + editorId);
  24303. if (attrValue) {
  24304. if (/input/ig.test(attrValue.tagName)) {
  24305. opt[attrName] = attrValue.value;
  24306. } else {
  24307. var iptChilds = attrValue.getElementsByTagName("input");
  24308. for (var j = iptChilds.length - 1, iptchild; iptchild = iptChilds[j--];) {
  24309. if (iptchild.checked) {
  24310. opt[attrName] = iptchild.value;
  24311. break;
  24312. }
  24313. }
  24314. }
  24315. } else {
  24316. opt[attrName] = true;
  24317. }
  24318. } else {
  24319. opt[attrName] = false;
  24320. }
  24321. } else {
  24322. opt[ipt.getAttribute("value")] = ipt.checked;
  24323. }
  24324. }
  24325. var selects = domUtils.getElementsByTagName(cont, "select");
  24326. for (var i = 0, si; si = selects[i++];) {
  24327. var attr = si.getAttribute('name');
  24328. opt[attr] = opt[attr] ? si.value : '';
  24329. }
  24330. utils.extend(me.editor.options.autotypeset, opt);
  24331. me.editor.setPreferences('autotypeset', opt);
  24332. }
  24333. AutoTypeSetButton.prototype = {
  24334. initAutoTypeSetButton: function () {
  24335. var me = this;
  24336. this.popup = new Popup({
  24337. //传入配置参数
  24338. content: new AutoTypeSetPicker({editor: me.editor}),
  24339. 'editor': me.editor,
  24340. hide: function () {
  24341. if (!this._hidden && this.getDom()) {
  24342. getPara(this);
  24343. this.getDom().style.display = 'none';
  24344. this._hidden = true;
  24345. this.fireEvent('hide');
  24346. }
  24347. }
  24348. });
  24349. var flag = 0;
  24350. this.popup.addListener('postRenderAfter', function () {
  24351. var popupUI = this;
  24352. if (flag) return;
  24353. var cont = this.getDom(),
  24354. btn = cont.getElementsByTagName('button')[0];
  24355. btn.onclick = function () {
  24356. getPara(popupUI);
  24357. me.editor.execCommand('autotypeset');
  24358. popupUI.hide()
  24359. };
  24360. domUtils.on(cont, 'click', function (e) {
  24361. var target = e.target || e.srcElement,
  24362. editorId = me.editor.uid;
  24363. if (target && target.tagName == 'INPUT') {
  24364. // 点击图片浮动的checkbox,去除对应的radio
  24365. if (target.name == 'imageBlockLine' || target.name == 'textAlign' || target.name == 'symbolConver') {
  24366. var checked = target.checked,
  24367. radioTd = document.getElementById(target.name + 'Value' + editorId),
  24368. radios = radioTd.getElementsByTagName('input'),
  24369. defalutSelect = {
  24370. 'imageBlockLine': 'none',
  24371. 'textAlign': 'left',
  24372. 'symbolConver': 'tobdc'
  24373. };
  24374. for (var i = 0; i < radios.length; i++) {
  24375. if (checked) {
  24376. if (radios[i].value == defalutSelect[target.name]) {
  24377. radios[i].checked = 'checked';
  24378. }
  24379. } else {
  24380. radios[i].checked = false;
  24381. }
  24382. }
  24383. }
  24384. // 点击radio,选中对应的checkbox
  24385. if (target.name == ('imageBlockLineValue' + editorId) || target.name == ('textAlignValue' + editorId) || target.name == 'bdc') {
  24386. var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input');
  24387. checkboxs && (checkboxs[0].checked = true);
  24388. }
  24389. getPara(popupUI);
  24390. }
  24391. });
  24392. flag = 1;
  24393. });
  24394. this.initSplitButton();
  24395. }
  24396. };
  24397. utils.inherits(AutoTypeSetButton, SplitButton);
  24398. })();
  24399. // ui/cellalignpicker.js
  24400. ///import core
  24401. ///import uicore
  24402. (function () {
  24403. var utils = baidu.editor.utils,
  24404. Popup = baidu.editor.ui.Popup,
  24405. Stateful = baidu.editor.ui.Stateful,
  24406. UIBase = baidu.editor.ui.UIBase;
  24407. /**
  24408. * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
  24409. * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
  24410. * @update 2013/4/2 hancong03@baidu.com
  24411. */
  24412. var CellAlignPicker = baidu.editor.ui.CellAlignPicker = function (options) {
  24413. this.initOptions(options);
  24414. this.initSelected();
  24415. this.initCellAlignPicker();
  24416. };
  24417. CellAlignPicker.prototype = {
  24418. //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
  24419. initSelected: function () {
  24420. var status = {
  24421. valign: {
  24422. top: 0,
  24423. middle: 1,
  24424. bottom: 2
  24425. },
  24426. align: {
  24427. left: 0,
  24428. center: 1,
  24429. right: 2
  24430. },
  24431. count: 3
  24432. },
  24433. result = -1;
  24434. if (this.selected) {
  24435. this.selectedIndex = status.valign[this.selected.valign] * status.count + status.align[this.selected.align];
  24436. }
  24437. },
  24438. initCellAlignPicker: function () {
  24439. this.initUIBase();
  24440. this.Stateful_init();
  24441. },
  24442. getHtmlTpl: function () {
  24443. var alignType = ['left', 'center', 'right'],
  24444. COUNT = 9,
  24445. tempClassName = null,
  24446. tempIndex = -1,
  24447. tmpl = [];
  24448. for (var i = 0; i < COUNT; i++) {
  24449. tempClassName = this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : '';
  24450. tempIndex = i % 3;
  24451. tempIndex === 0 && tmpl.push('<tr>');
  24452. tmpl.push('<td index="' + i + '" ' + tempClassName + ' stateful><div class="edui-icon edui-' + alignType[tempIndex] + '"></div></td>');
  24453. tempIndex === 2 && tmpl.push('</tr>');
  24454. }
  24455. return '<div id="##" class="edui-cellalignpicker %%">' +
  24456. '<div class="edui-cellalignpicker-body">' +
  24457. '<table onclick="$$._onClick(event);">' +
  24458. tmpl.join('') +
  24459. '</table>' +
  24460. '</div>' +
  24461. '</div>';
  24462. },
  24463. getStateDom: function () {
  24464. return this.target;
  24465. },
  24466. _onClick: function (evt) {
  24467. var target = evt.target || evt.srcElement;
  24468. if (/icon/.test(target.className)) {
  24469. this.items[target.parentNode.getAttribute("index")].onclick();
  24470. Popup.postHide(evt);
  24471. }
  24472. },
  24473. _UIBase_render: UIBase.prototype.render
  24474. };
  24475. utils.inherits(CellAlignPicker, UIBase);
  24476. utils.extend(CellAlignPicker.prototype, Stateful, true);
  24477. })();
  24478. // ui/pastepicker.js
  24479. ///import core
  24480. ///import uicore
  24481. (function () {
  24482. var utils = baidu.editor.utils,
  24483. Stateful = baidu.editor.ui.Stateful,
  24484. uiUtils = baidu.editor.ui.uiUtils,
  24485. UIBase = baidu.editor.ui.UIBase;
  24486. var PastePicker = baidu.editor.ui.PastePicker = function (options) {
  24487. this.initOptions(options);
  24488. this.initPastePicker();
  24489. };
  24490. PastePicker.prototype = {
  24491. initPastePicker: function () {
  24492. this.initUIBase();
  24493. this.Stateful_init();
  24494. },
  24495. getHtmlTpl: function () {
  24496. return '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
  24497. '<div class="edui-pastecontainer">' +
  24498. '<div class="edui-title">' + this.editor.getLang("pasteOpt") + '</div>' +
  24499. '<div class="edui-button">' +
  24500. '<div title="' + this.editor.getLang("pasteSourceFormat") + '" onclick="$$.format(false)" stateful>' +
  24501. '<div class="edui-richtxticon"></div></div>' +
  24502. '<div title="' + this.editor.getLang("tagFormat") + '" onclick="$$.format(2)" stateful>' +
  24503. '<div class="edui-tagicon"></div></div>' +
  24504. '<div title="' + this.editor.getLang("pasteTextFormat") + '" onclick="$$.format(true)" stateful>' +
  24505. '<div class="edui-plaintxticon"></div></div>' +
  24506. '</div>' +
  24507. '</div>' +
  24508. '</div>'
  24509. },
  24510. getStateDom: function () {
  24511. return this.target;
  24512. },
  24513. format: function (param) {
  24514. this.editor.ui._isTransfer = true;
  24515. this.editor.fireEvent('pasteTransfer', param);
  24516. },
  24517. _onClick: function (cur) {
  24518. var node = domUtils.getNextDomNode(cur),
  24519. screenHt = uiUtils.getViewportRect().height,
  24520. subPop = uiUtils.getClientRect(node);
  24521. if ((subPop.top + subPop.height) > screenHt)
  24522. node.style.top = (-subPop.height - cur.offsetHeight) + "px";
  24523. else
  24524. node.style.top = "";
  24525. if (/hidden/ig.test(domUtils.getComputedStyle(node, "visibility"))) {
  24526. node.style.visibility = "visible";
  24527. domUtils.addClass(cur, "edui-state-opened");
  24528. } else {
  24529. node.style.visibility = "hidden";
  24530. domUtils.removeClasses(cur, "edui-state-opened")
  24531. }
  24532. },
  24533. _UIBase_render: UIBase.prototype.render
  24534. };
  24535. utils.inherits(PastePicker, UIBase);
  24536. utils.extend(PastePicker.prototype, Stateful, true);
  24537. })();
  24538. // ui/toolbar.js
  24539. (function () {
  24540. var utils = baidu.editor.utils,
  24541. uiUtils = baidu.editor.ui.uiUtils,
  24542. UIBase = baidu.editor.ui.UIBase,
  24543. Toolbar = baidu.editor.ui.Toolbar = function (options) {
  24544. this.initOptions(options);
  24545. this.initToolbar();
  24546. };
  24547. Toolbar.prototype = {
  24548. items: null,
  24549. initToolbar: function () {
  24550. this.items = this.items || [];
  24551. this.initUIBase();
  24552. },
  24553. add: function (item, index) {
  24554. if (index === undefined) {
  24555. this.items.push(item);
  24556. } else {
  24557. this.items.splice(index, 0, item)
  24558. }
  24559. },
  24560. getHtmlTpl: function () {
  24561. var buff = [];
  24562. for (var i = 0; i < this.items.length; i++) {
  24563. buff[i] = this.items[i].renderHtml();
  24564. }
  24565. return '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
  24566. buff.join('') +
  24567. '</div>'
  24568. },
  24569. postRender: function () {
  24570. var box = this.getDom();
  24571. for (var i = 0; i < this.items.length; i++) {
  24572. this.items[i].postRender();
  24573. }
  24574. uiUtils.makeUnselectable(box);
  24575. },
  24576. _onMouseDown: function (e) {
  24577. var target = e.target || e.srcElement,
  24578. tagName = target && target.tagName && target.tagName.toLowerCase();
  24579. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  24580. return false;
  24581. }
  24582. }
  24583. };
  24584. utils.inherits(Toolbar, UIBase);
  24585. })();
  24586. // ui/menu.js
  24587. ///import core
  24588. ///import uicore
  24589. ///import ui\popup.js
  24590. ///import ui\stateful.js
  24591. (function () {
  24592. var utils = baidu.editor.utils,
  24593. domUtils = baidu.editor.dom.domUtils,
  24594. uiUtils = baidu.editor.ui.uiUtils,
  24595. UIBase = baidu.editor.ui.UIBase,
  24596. Popup = baidu.editor.ui.Popup,
  24597. Stateful = baidu.editor.ui.Stateful,
  24598. CellAlignPicker = baidu.editor.ui.CellAlignPicker,
  24599. Menu = baidu.editor.ui.Menu = function (options) {
  24600. this.initOptions(options);
  24601. this.initMenu();
  24602. };
  24603. var menuSeparator = {
  24604. renderHtml: function () {
  24605. return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>';
  24606. },
  24607. postRender: function () {
  24608. },
  24609. queryAutoHide: function () {
  24610. return true;
  24611. }
  24612. };
  24613. Menu.prototype = {
  24614. items: null,
  24615. uiName: 'menu',
  24616. initMenu: function () {
  24617. this.items = this.items || [];
  24618. this.initPopup();
  24619. this.initItems();
  24620. },
  24621. initItems: function () {
  24622. for (var i = 0; i < this.items.length; i++) {
  24623. var item = this.items[i];
  24624. if (item == '-') {
  24625. this.items[i] = this.getSeparator();
  24626. } else if (!(item instanceof MenuItem)) {
  24627. item.editor = this.editor;
  24628. item.theme = this.editor.options.theme;
  24629. this.items[i] = this.createItem(item);
  24630. }
  24631. }
  24632. },
  24633. getSeparator: function () {
  24634. return menuSeparator;
  24635. },
  24636. createItem: function (item) {
  24637. //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  24638. item.menu = this;
  24639. return new MenuItem(item);
  24640. },
  24641. _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
  24642. getContentHtmlTpl: function () {
  24643. if (this.items.length == 0) {
  24644. return this._Popup_getContentHtmlTpl();
  24645. }
  24646. var buff = [];
  24647. for (var i = 0; i < this.items.length; i++) {
  24648. var item = this.items[i];
  24649. buff[i] = item.renderHtml();
  24650. }
  24651. return ('<div class="%%-body">' + buff.join('') + '</div>');
  24652. },
  24653. _Popup_postRender: Popup.prototype.postRender,
  24654. postRender: function () {
  24655. var me = this;
  24656. for (var i = 0; i < this.items.length; i++) {
  24657. var item = this.items[i];
  24658. item.ownerMenu = this;
  24659. item.postRender();
  24660. }
  24661. domUtils.on(this.getDom(), 'mouseover', function (evt) {
  24662. evt = evt || event;
  24663. var rel = evt.relatedTarget || evt.fromElement;
  24664. var el = me.getDom();
  24665. if (!uiUtils.contains(el, rel) && el !== rel) {
  24666. me.fireEvent('over');
  24667. }
  24668. });
  24669. this._Popup_postRender();
  24670. },
  24671. queryAutoHide: function (el) {
  24672. if (el) {
  24673. if (uiUtils.contains(this.getDom(), el)) {
  24674. return false;
  24675. }
  24676. for (var i = 0; i < this.items.length; i++) {
  24677. var item = this.items[i];
  24678. if (item.queryAutoHide(el) === false) {
  24679. return false;
  24680. }
  24681. }
  24682. }
  24683. },
  24684. clearItems: function () {
  24685. for (var i = 0; i < this.items.length; i++) {
  24686. var item = this.items[i];
  24687. clearTimeout(item._showingTimer);
  24688. clearTimeout(item._closingTimer);
  24689. if (item.subMenu) {
  24690. item.subMenu.destroy();
  24691. }
  24692. }
  24693. this.items = [];
  24694. },
  24695. destroy: function () {
  24696. if (this.getDom()) {
  24697. domUtils.remove(this.getDom());
  24698. }
  24699. this.clearItems();
  24700. },
  24701. dispose: function () {
  24702. this.destroy();
  24703. }
  24704. };
  24705. utils.inherits(Menu, Popup);
  24706. /**
  24707. * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  24708. * @type {Function}
  24709. */
  24710. var MenuItem = baidu.editor.ui.MenuItem = function (options) {
  24711. this.initOptions(options);
  24712. this.initUIBase();
  24713. this.Stateful_init();
  24714. if (this.subMenu && !(this.subMenu instanceof Menu)) {
  24715. if (options.className && options.className.indexOf("aligntd") != -1) {
  24716. var me = this;
  24717. //获取单元格对齐初始状态
  24718. this.subMenu.selected = this.editor.queryCommandValue('cellalignment');
  24719. this.subMenu = new Popup({
  24720. content: new CellAlignPicker(this.subMenu),
  24721. parentMenu: me,
  24722. editor: me.editor,
  24723. destroy: function () {
  24724. if (this.getDom()) {
  24725. domUtils.remove(this.getDom());
  24726. }
  24727. }
  24728. });
  24729. this.subMenu.addListener("postRenderAfter", function () {
  24730. domUtils.on(this.getDom(), "mouseover", function () {
  24731. me.addState('opened');
  24732. });
  24733. });
  24734. } else {
  24735. this.subMenu = new Menu(this.subMenu);
  24736. }
  24737. }
  24738. };
  24739. MenuItem.prototype = {
  24740. label: '',
  24741. subMenu: null,
  24742. ownerMenu: null,
  24743. uiName: 'menuitem',
  24744. alwalysHoverable: true,
  24745. getHtmlTpl: function () {
  24746. return '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
  24747. '<div class="%%-body">' +
  24748. this.renderLabelHtml() +
  24749. '</div>' +
  24750. '</div>';
  24751. },
  24752. postRender: function () {
  24753. var me = this;
  24754. this.addListener('over', function () {
  24755. me.ownerMenu.fireEvent('submenuover', me);
  24756. if (me.subMenu) {
  24757. me.delayShowSubMenu();
  24758. }
  24759. });
  24760. if (this.subMenu) {
  24761. this.getDom().className += ' edui-hassubmenu';
  24762. this.subMenu.render();
  24763. this.addListener('out', function () {
  24764. me.delayHideSubMenu();
  24765. });
  24766. this.subMenu.addListener('over', function () {
  24767. clearTimeout(me._closingTimer);
  24768. me._closingTimer = null;
  24769. me.addState('opened');
  24770. });
  24771. this.ownerMenu.addListener('hide', function () {
  24772. me.hideSubMenu();
  24773. });
  24774. this.ownerMenu.addListener('submenuover', function (t, subMenu) {
  24775. if (subMenu !== me) {
  24776. me.delayHideSubMenu();
  24777. }
  24778. });
  24779. this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide;
  24780. this.subMenu.queryAutoHide = function (el) {
  24781. if (el && uiUtils.contains(me.getDom(), el)) {
  24782. return false;
  24783. }
  24784. return this._bakQueryAutoHide(el);
  24785. };
  24786. }
  24787. this.getDom().style.tabIndex = '-1';
  24788. uiUtils.makeUnselectable(this.getDom());
  24789. this.Stateful_postRender();
  24790. },
  24791. delayShowSubMenu: function () {
  24792. var me = this;
  24793. if (!me.isDisabled()) {
  24794. me.addState('opened');
  24795. clearTimeout(me._showingTimer);
  24796. clearTimeout(me._closingTimer);
  24797. me._closingTimer = null;
  24798. me._showingTimer = setTimeout(function () {
  24799. me.showSubMenu();
  24800. }, 250);
  24801. }
  24802. },
  24803. delayHideSubMenu: function () {
  24804. var me = this;
  24805. if (!me.isDisabled()) {
  24806. me.removeState('opened');
  24807. clearTimeout(me._showingTimer);
  24808. if (!me._closingTimer) {
  24809. me._closingTimer = setTimeout(function () {
  24810. if (!me.hasState('opened')) {
  24811. me.hideSubMenu();
  24812. }
  24813. me._closingTimer = null;
  24814. }, 400);
  24815. }
  24816. }
  24817. },
  24818. renderLabelHtml: function () {
  24819. return '<div class="edui-arrow"></div>' +
  24820. '<div class="edui-box edui-icon"></div>' +
  24821. '<div class="edui-box edui-label %%-label">' + (this.label || '') + '</div>';
  24822. },
  24823. getStateDom: function () {
  24824. return this.getDom();
  24825. },
  24826. queryAutoHide: function (el) {
  24827. if (this.subMenu && this.hasState('opened')) {
  24828. return this.subMenu.queryAutoHide(el);
  24829. }
  24830. },
  24831. _onClick: function (event, this_) {
  24832. if (this.hasState('disabled')) return;
  24833. if (this.fireEvent('click', event, this_) !== false) {
  24834. if (this.subMenu) {
  24835. this.showSubMenu();
  24836. } else {
  24837. Popup.postHide(event);
  24838. }
  24839. }
  24840. },
  24841. showSubMenu: function () {
  24842. var rect = uiUtils.getClientRect(this.getDom());
  24843. rect.right -= 5;
  24844. rect.left += 2;
  24845. rect.width -= 7;
  24846. rect.top -= 4;
  24847. rect.bottom += 4;
  24848. rect.height += 8;
  24849. this.subMenu.showAnchorRect(rect, true, true);
  24850. },
  24851. hideSubMenu: function () {
  24852. this.subMenu.hide();
  24853. }
  24854. };
  24855. utils.inherits(MenuItem, UIBase);
  24856. utils.extend(MenuItem.prototype, Stateful, true);
  24857. })();
  24858. // ui/combox.js
  24859. ///import core
  24860. ///import uicore
  24861. ///import ui/menu.js
  24862. ///import ui/splitbutton.js
  24863. (function () {
  24864. // todo: menu和item提成通用list
  24865. var utils = baidu.editor.utils,
  24866. uiUtils = baidu.editor.ui.uiUtils,
  24867. Menu = baidu.editor.ui.Menu,
  24868. SplitButton = baidu.editor.ui.SplitButton,
  24869. Combox = baidu.editor.ui.Combox = function (options) {
  24870. this.initOptions(options);
  24871. this.initCombox();
  24872. };
  24873. Combox.prototype = {
  24874. uiName: 'combox',
  24875. onbuttonclick: function () {
  24876. this.showPopup();
  24877. },
  24878. initCombox: function () {
  24879. var me = this;
  24880. this.items = this.items || [];
  24881. for (var i = 0; i < this.items.length; i++) {
  24882. var item = this.items[i];
  24883. item.uiName = 'listitem';
  24884. item.index = i;
  24885. item.onclick = function () {
  24886. me.selectByIndex(this.index);
  24887. };
  24888. }
  24889. this.popup = new Menu({
  24890. items: this.items,
  24891. uiName: 'list',
  24892. editor: this.editor,
  24893. captureWheel: true,
  24894. combox: this
  24895. });
  24896. this.initSplitButton();
  24897. },
  24898. _SplitButton_postRender: SplitButton.prototype.postRender,
  24899. postRender: function () {
  24900. this._SplitButton_postRender();
  24901. this.setLabel(this.label || '');
  24902. this.setValue(this.initValue || '');
  24903. },
  24904. showPopup: function () {
  24905. var rect = uiUtils.getClientRect(this.getDom());
  24906. rect.top += 1;
  24907. rect.bottom -= 1;
  24908. rect.height -= 2;
  24909. this.popup.showAnchorRect(rect);
  24910. },
  24911. getValue: function () {
  24912. return this.value;
  24913. },
  24914. setValue: function (value) {
  24915. var index = this.indexByValue(value);
  24916. if (index != -1) {
  24917. this.selectedIndex = index;
  24918. this.setLabel(this.items[index].label);
  24919. this.value = this.items[index].value;
  24920. } else {
  24921. this.selectedIndex = -1;
  24922. this.setLabel(this.getLabelForUnknowValue(value));
  24923. this.value = value;
  24924. }
  24925. },
  24926. setLabel: function (label) {
  24927. this.getDom('button_body').innerHTML = label;
  24928. this.label = label;
  24929. },
  24930. getLabelForUnknowValue: function (value) {
  24931. return value;
  24932. },
  24933. indexByValue: function (value) {
  24934. for (var i = 0; i < this.items.length; i++) {
  24935. if (value == this.items[i].value) {
  24936. return i;
  24937. }
  24938. }
  24939. return -1;
  24940. },
  24941. getItem: function (index) {
  24942. return this.items[index];
  24943. },
  24944. selectByIndex: function (index) {
  24945. if (index < this.items.length && this.fireEvent('select', index) !== false) {
  24946. this.selectedIndex = index;
  24947. this.value = this.items[index].value;
  24948. this.setLabel(this.items[index].label);
  24949. }
  24950. }
  24951. };
  24952. utils.inherits(Combox, SplitButton);
  24953. })();
  24954. // ui/dialog.js
  24955. ///import core
  24956. ///import uicore
  24957. ///import ui/mask.js
  24958. ///import ui/button.js
  24959. (function () {
  24960. var utils = baidu.editor.utils,
  24961. domUtils = baidu.editor.dom.domUtils,
  24962. uiUtils = baidu.editor.ui.uiUtils,
  24963. Mask = baidu.editor.ui.Mask,
  24964. UIBase = baidu.editor.ui.UIBase,
  24965. Button = baidu.editor.ui.Button,
  24966. Dialog = baidu.editor.ui.Dialog = function (options) {
  24967. if (options.name) {
  24968. var name = options.name;
  24969. var cssRules = options.cssRules;
  24970. if (!options.className) {
  24971. options.className = 'edui-for-' + name;
  24972. }
  24973. if (cssRules) {
  24974. options.cssRules = '.edui-default .edui-for-' + name + ' .edui-dialog-content {' + cssRules + '}'
  24975. }
  24976. }
  24977. this.initOptions(utils.extend({
  24978. autoReset: true,
  24979. draggable: true,
  24980. onok: function () {
  24981. },
  24982. oncancel: function () {
  24983. },
  24984. onclose: function (t, ok) {
  24985. return ok ? this.onok() : this.oncancel();
  24986. },
  24987. //是否控制dialog中的scroll事件, 默认为不阻止
  24988. holdScroll: false
  24989. }, options));
  24990. this.initDialog();
  24991. };
  24992. var modalMask;
  24993. var dragMask;
  24994. var activeDialog;
  24995. Dialog.prototype = {
  24996. draggable: false,
  24997. uiName: 'dialog',
  24998. initDialog: function () {
  24999. var me = this,
  25000. theme = this.editor.options.theme;
  25001. if (this.cssRules) {
  25002. utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules);
  25003. }
  25004. this.initUIBase();
  25005. this.modalMask = (modalMask || (modalMask = new Mask({
  25006. className: 'edui-dialog-modalmask',
  25007. theme: theme,
  25008. onclick: function () {
  25009. activeDialog && activeDialog.close(false);
  25010. }
  25011. })));
  25012. this.dragMask = (dragMask || (dragMask = new Mask({
  25013. className: 'edui-dialog-dragmask',
  25014. theme: theme
  25015. })));
  25016. this.closeButton = new Button({
  25017. className: 'edui-dialog-closebutton',
  25018. title: me.closeDialog,
  25019. theme: theme,
  25020. onclick: function () {
  25021. me.close(false);
  25022. }
  25023. });
  25024. this.fullscreen && this.initResizeEvent();
  25025. if (this.buttons) {
  25026. for (var i = 0; i < this.buttons.length; i++) {
  25027. if (!(this.buttons[i] instanceof Button)) {
  25028. this.buttons[i] = new Button(utils.extend(this.buttons[i], {
  25029. editor: this.editor
  25030. }, true));
  25031. }
  25032. }
  25033. }
  25034. },
  25035. initResizeEvent: function () {
  25036. var me = this;
  25037. domUtils.on(window, "resize", function () {
  25038. if (me._hidden || me._hidden === undefined) {
  25039. return;
  25040. }
  25041. if (me.__resizeTimer) {
  25042. window.clearTimeout(me.__resizeTimer);
  25043. }
  25044. me.__resizeTimer = window.setTimeout(function () {
  25045. me.__resizeTimer = null;
  25046. var dialogWrapNode = me.getDom(),
  25047. contentNode = me.getDom('content'),
  25048. wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
  25049. contentRect = UE.ui.uiUtils.getClientRect(contentNode),
  25050. vpRect = uiUtils.getViewportRect();
  25051. contentNode.style.width = (vpRect.width - wrapRect.width + contentRect.width) + "px";
  25052. contentNode.style.height = (vpRect.height - wrapRect.height + contentRect.height) + "px";
  25053. dialogWrapNode.style.width = vpRect.width + "px";
  25054. dialogWrapNode.style.height = vpRect.height + "px";
  25055. me.fireEvent("resize");
  25056. }, 100);
  25057. });
  25058. },
  25059. fitSize: function () {
  25060. var popBodyEl = this.getDom('body');
  25061. // if (!(baidu.editor.browser.ie && baidu.editor.browser.version == 7)) {
  25062. // uiUtils.removeStyle(popBodyEl, 'width');
  25063. // uiUtils.removeStyle(popBodyEl, 'height');
  25064. // }
  25065. var size = this.mesureSize();
  25066. popBodyEl.style.width = size.width + 'px';
  25067. popBodyEl.style.height = size.height + 'px';
  25068. return size;
  25069. },
  25070. safeSetOffset: function (offset) {
  25071. var me = this;
  25072. var el = me.getDom();
  25073. var vpRect = uiUtils.getViewportRect();
  25074. var rect = uiUtils.getClientRect(el);
  25075. var left = offset.left;
  25076. if (left + rect.width > vpRect.right) {
  25077. left = vpRect.right - rect.width;
  25078. }
  25079. var top = offset.top;
  25080. if (top + rect.height > vpRect.bottom) {
  25081. top = vpRect.bottom - rect.height;
  25082. }
  25083. el.style.left = Math.max(left, 0) + 'px';
  25084. el.style.top = Math.max(top, 0) + 'px';
  25085. },
  25086. showAtCenter: function () {
  25087. var vpRect = uiUtils.getViewportRect();
  25088. if (!this.fullscreen) {
  25089. this.getDom().style.display = '';
  25090. var popSize = this.fitSize();
  25091. var titleHeight = this.getDom('titlebar').offsetHeight | 0;
  25092. var left = vpRect.width / 2 - popSize.width / 2;
  25093. var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight;
  25094. var popEl = this.getDom();
  25095. this.safeSetOffset({
  25096. left: Math.max(left | 0, 0),
  25097. top: Math.max(top | 0, 0)
  25098. });
  25099. if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
  25100. popEl.className += ' edui-state-centered';
  25101. }
  25102. } else {
  25103. var dialogWrapNode = this.getDom(),
  25104. contentNode = this.getDom('content');
  25105. dialogWrapNode.style.display = "block";
  25106. var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
  25107. contentRect = UE.ui.uiUtils.getClientRect(contentNode);
  25108. dialogWrapNode.style.left = "-100000px";
  25109. contentNode.style.width = (vpRect.width - wrapRect.width + contentRect.width) + "px";
  25110. contentNode.style.height = (vpRect.height - wrapRect.height + contentRect.height) + "px";
  25111. dialogWrapNode.style.width = vpRect.width + "px";
  25112. dialogWrapNode.style.height = vpRect.height + "px";
  25113. dialogWrapNode.style.left = 0;
  25114. //保存环境的overflow值
  25115. this._originalContext = {
  25116. html: {
  25117. overflowX: document.documentElement.style.overflowX,
  25118. overflowY: document.documentElement.style.overflowY
  25119. },
  25120. body: {
  25121. overflowX: document.body.style.overflowX,
  25122. overflowY: document.body.style.overflowY
  25123. }
  25124. };
  25125. document.documentElement.style.overflowX = 'hidden';
  25126. document.documentElement.style.overflowY = 'hidden';
  25127. document.body.style.overflowX = 'hidden';
  25128. document.body.style.overflowY = 'hidden';
  25129. }
  25130. this._show();
  25131. },
  25132. getContentHtml: function () {
  25133. var contentHtml = '';
  25134. if (typeof this.content == 'string') {
  25135. contentHtml = this.content;
  25136. } else if (this.iframeUrl) {
  25137. contentHtml = '<span id="' + this.id + '_contmask" class="dialogcontmask"></span><iframe id="' + this.id +
  25138. '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="' + this.iframeUrl + '"></iframe>';
  25139. }
  25140. return contentHtml;
  25141. },
  25142. getHtmlTpl: function () {
  25143. var footHtml = '';
  25144. if (this.buttons) {
  25145. var buff = [];
  25146. for (var i = 0; i < this.buttons.length; i++) {
  25147. buff[i] = this.buttons[i].renderHtml();
  25148. }
  25149. footHtml = '<div class="%%-foot">' +
  25150. '<div id="##_buttons" class="%%-buttons">' + buff.join('') + '</div>' +
  25151. '</div>';
  25152. }
  25153. return '<div id="##" class="%%"><div ' + (!this.fullscreen ? 'class="%%"' : 'class="%%-wrap edui-dialog-fullscreen-flag"') + '><div id="##_body" class="%%-body">' +
  25154. '<div class="%%-shadow"></div>' +
  25155. '<div id="##_titlebar" class="%%-titlebar">' +
  25156. '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
  25157. '<span class="%%-caption">' + (this.title || '') + '</span>' +
  25158. '</div>' +
  25159. this.closeButton.renderHtml() +
  25160. '</div>' +
  25161. '<div id="##_content" class="%%-content">' + (this.autoReset ? '' : this.getContentHtml()) + '</div>' +
  25162. footHtml +
  25163. '</div></div></div>';
  25164. },
  25165. postRender: function () {
  25166. // todo: 保持居中/记住上次关闭位置选项
  25167. if (!this.modalMask.getDom()) {
  25168. this.modalMask.render();
  25169. this.modalMask.hide();
  25170. }
  25171. if (!this.dragMask.getDom()) {
  25172. this.dragMask.render();
  25173. this.dragMask.hide();
  25174. }
  25175. var me = this;
  25176. this.addListener('show', function () {
  25177. me.modalMask.show(this.getDom().style.zIndex - 2);
  25178. });
  25179. this.addListener('hide', function () {
  25180. me.modalMask.hide();
  25181. });
  25182. if (this.buttons) {
  25183. for (var i = 0; i < this.buttons.length; i++) {
  25184. this.buttons[i].postRender();
  25185. }
  25186. }
  25187. domUtils.on(window, 'resize', function () {
  25188. setTimeout(function () {
  25189. if (!me.isHidden()) {
  25190. me.safeSetOffset(uiUtils.getClientRect(me.getDom()));
  25191. }
  25192. });
  25193. });
  25194. //hold住scroll事件,防止dialog的滚动影响页面
  25195. // if( this.holdScroll ) {
  25196. //
  25197. // if( !me.iframeUrl ) {
  25198. // domUtils.on( document.getElementById( me.id + "_iframe"), !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  25199. // domUtils.preventDefault(e);
  25200. // } );
  25201. // } else {
  25202. // me.addListener('dialogafterreset', function(){
  25203. // window.setTimeout(function(){
  25204. // var iframeWindow = document.getElementById( me.id + "_iframe").contentWindow;
  25205. //
  25206. // if( browser.ie ) {
  25207. //
  25208. // var timer = window.setInterval(function(){
  25209. //
  25210. // if( iframeWindow.document && iframeWindow.document.body ) {
  25211. // window.clearInterval( timer );
  25212. // timer = null;
  25213. // domUtils.on( iframeWindow.document.body, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  25214. // domUtils.preventDefault(e);
  25215. // } );
  25216. // }
  25217. //
  25218. // }, 100);
  25219. //
  25220. // } else {
  25221. // domUtils.on( iframeWindow, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  25222. // domUtils.preventDefault(e);
  25223. // } );
  25224. // }
  25225. //
  25226. // }, 1);
  25227. // });
  25228. // }
  25229. //
  25230. // }
  25231. this._hide();
  25232. },
  25233. mesureSize: function () {
  25234. var body = this.getDom('body');
  25235. var width = uiUtils.getClientRect(this.getDom('content')).width;
  25236. var dialogBodyStyle = body.style;
  25237. dialogBodyStyle.width = width;
  25238. return uiUtils.getClientRect(body);
  25239. },
  25240. _onTitlebarMouseDown: function (evt, el) {
  25241. if (this.draggable) {
  25242. var rect;
  25243. var vpRect = uiUtils.getViewportRect();
  25244. var me = this;
  25245. uiUtils.startDrag(evt, {
  25246. ondragstart: function () {
  25247. rect = uiUtils.getClientRect(me.getDom());
  25248. me.getDom('contmask').style.visibility = 'visible';
  25249. me.dragMask.show(me.getDom().style.zIndex - 1);
  25250. },
  25251. ondragmove: function (x, y) {
  25252. var left = rect.left + x;
  25253. var top = rect.top + y;
  25254. me.safeSetOffset({
  25255. left: left,
  25256. top: top
  25257. });
  25258. },
  25259. ondragstop: function () {
  25260. me.getDom('contmask').style.visibility = 'hidden';
  25261. domUtils.removeClasses(me.getDom(), ['edui-state-centered']);
  25262. me.dragMask.hide();
  25263. }
  25264. });
  25265. }
  25266. },
  25267. reset: function () {
  25268. this.getDom('content').innerHTML = this.getContentHtml();
  25269. this.fireEvent('dialogafterreset');
  25270. },
  25271. _show: function () {
  25272. if (this._hidden) {
  25273. this.getDom().style.display = '';
  25274. //要高过编辑器的zindxe
  25275. this.editor.container.style.zIndex && (this.getDom().style.zIndex = this.editor.container.style.zIndex * 1 + 10);
  25276. this._hidden = false;
  25277. this.fireEvent('show');
  25278. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = this.getDom().style.zIndex - 4;
  25279. }
  25280. },
  25281. isHidden: function () {
  25282. return this._hidden;
  25283. },
  25284. _hide: function () {
  25285. if (!this._hidden) {
  25286. var wrapNode = this.getDom();
  25287. wrapNode.style.display = 'none';
  25288. wrapNode.style.zIndex = '';
  25289. wrapNode.style.width = '';
  25290. wrapNode.style.height = '';
  25291. this._hidden = true;
  25292. this.fireEvent('hide');
  25293. }
  25294. },
  25295. open: function () {
  25296. if (this.autoReset) {
  25297. //有可能还没有渲染
  25298. try {
  25299. this.reset();
  25300. } catch (e) {
  25301. this.render();
  25302. this.open()
  25303. }
  25304. }
  25305. this.showAtCenter();
  25306. if (this.iframeUrl) {
  25307. try {
  25308. this.getDom('iframe').focus();
  25309. } catch (ex) {
  25310. }
  25311. }
  25312. activeDialog = this;
  25313. },
  25314. _onCloseButtonClick: function (evt, el) {
  25315. this.close(false);
  25316. },
  25317. close: function (ok) {
  25318. if (this.fireEvent('close', ok) !== false) {
  25319. //还原环境
  25320. if (this.fullscreen) {
  25321. document.documentElement.style.overflowX = this._originalContext.html.overflowX;
  25322. document.documentElement.style.overflowY = this._originalContext.html.overflowY;
  25323. document.body.style.overflowX = this._originalContext.body.overflowX;
  25324. document.body.style.overflowY = this._originalContext.body.overflowY;
  25325. delete this._originalContext;
  25326. }
  25327. this._hide();
  25328. //销毁content
  25329. var content = this.getDom('content');
  25330. var iframe = this.getDom('iframe');
  25331. if (content && iframe) {
  25332. var doc = iframe.contentDocument || iframe.contentWindow.document;
  25333. doc && (doc.body.innerHTML = '');
  25334. domUtils.remove(content);
  25335. }
  25336. }
  25337. }
  25338. };
  25339. utils.inherits(Dialog, UIBase);
  25340. })();
  25341. // ui/menubutton.js
  25342. ///import core
  25343. ///import uicore
  25344. ///import ui/menu.js
  25345. ///import ui/splitbutton.js
  25346. (function () {
  25347. var utils = baidu.editor.utils,
  25348. Menu = baidu.editor.ui.Menu,
  25349. SplitButton = baidu.editor.ui.SplitButton,
  25350. MenuButton = baidu.editor.ui.MenuButton = function (options) {
  25351. this.initOptions(options);
  25352. this.initMenuButton();
  25353. };
  25354. MenuButton.prototype = {
  25355. initMenuButton: function () {
  25356. var me = this;
  25357. this.uiName = "menubutton";
  25358. this.popup = new Menu({
  25359. items: me.items,
  25360. className: me.className,
  25361. editor: me.editor
  25362. });
  25363. this.popup.addListener('show', function () {
  25364. var list = this;
  25365. for (var i = 0; i < list.items.length; i++) {
  25366. list.items[i].removeState('checked');
  25367. if (list.items[i].value == me._value) {
  25368. list.items[i].addState('checked');
  25369. this.value = me._value;
  25370. }
  25371. }
  25372. });
  25373. this.initSplitButton();
  25374. },
  25375. setValue: function (value) {
  25376. this._value = value;
  25377. }
  25378. };
  25379. utils.inherits(MenuButton, SplitButton);
  25380. })();
  25381. // ui/multiMenu.js
  25382. ///import core
  25383. ///import uicore
  25384. ///commands 表情
  25385. (function () {
  25386. var utils = baidu.editor.utils,
  25387. Popup = baidu.editor.ui.Popup,
  25388. SplitButton = baidu.editor.ui.SplitButton,
  25389. MultiMenuPop = baidu.editor.ui.MultiMenuPop = function (options) {
  25390. this.initOptions(options);
  25391. this.initMultiMenu();
  25392. };
  25393. MultiMenuPop.prototype = {
  25394. initMultiMenu: function () {
  25395. var me = this;
  25396. this.popup = new Popup({
  25397. content: '',
  25398. editor: me.editor,
  25399. iframe_rendered: false,
  25400. onshow: function () {
  25401. if (!this.iframe_rendered) {
  25402. this.iframe_rendered = true;
  25403. this.getDom('content').innerHTML = '<iframe id="' + me.id + '_iframe" src="' + me.iframeUrl + '" frameborder="0"></iframe>';
  25404. me.editor.container.style.zIndex && (this.getDom().style.zIndex = me.editor.container.style.zIndex * 1 + 1);
  25405. }
  25406. }
  25407. // canSideUp:false,
  25408. // canSideLeft:false
  25409. });
  25410. this.onbuttonclick = function () {
  25411. this.showPopup();
  25412. };
  25413. this.initSplitButton();
  25414. }
  25415. };
  25416. utils.inherits(MultiMenuPop, SplitButton);
  25417. })();
  25418. // ui/shortcutmenu.js
  25419. (function () {
  25420. var UI = baidu.editor.ui,
  25421. UIBase = UI.UIBase,
  25422. uiUtils = UI.uiUtils,
  25423. utils = baidu.editor.utils,
  25424. domUtils = baidu.editor.dom.domUtils;
  25425. var allMenus = [],//存储所有快捷菜单
  25426. timeID,
  25427. isSubMenuShow = false;//是否有子pop显示
  25428. var ShortCutMenu = UI.ShortCutMenu = function (options) {
  25429. this.initOptions(options);
  25430. this.initShortCutMenu();
  25431. };
  25432. ShortCutMenu.postHide = hideAllMenu;
  25433. ShortCutMenu.prototype = {
  25434. isHidden: true,
  25435. SPACE: 5,
  25436. initShortCutMenu: function () {
  25437. this.items = this.items || [];
  25438. this.initUIBase();
  25439. this.initItems();
  25440. this.initEvent();
  25441. allMenus.push(this);
  25442. },
  25443. initEvent: function () {
  25444. var me = this,
  25445. doc = me.editor.document;
  25446. domUtils.on(doc, "mousemove", function (e) {
  25447. if (me.isHidden === false) {
  25448. //有pop显示就不隐藏快捷菜单
  25449. if (me.getSubMenuMark() || me.eventType == "contextmenu") return;
  25450. var flag = true,
  25451. el = me.getDom(),
  25452. wt = el.offsetWidth,
  25453. ht = el.offsetHeight,
  25454. distanceX = wt / 2 + me.SPACE,//距离中心X标准
  25455. distanceY = ht / 2,//距离中心Y标准
  25456. x = Math.abs(e.screenX - me.left),//离中心距离横坐标
  25457. y = Math.abs(e.screenY - me.top);//离中心距离纵坐标
  25458. clearTimeout(timeID);
  25459. timeID = setTimeout(function () {
  25460. if (y > 0 && y < distanceY) {
  25461. me.setOpacity(el, "1");
  25462. } else if (y > distanceY && y < distanceY + 70) {
  25463. me.setOpacity(el, "0.5");
  25464. flag = false;
  25465. } else if (y > distanceY + 70 && y < distanceY + 140) {
  25466. me.hide();
  25467. }
  25468. if (flag && x > 0 && x < distanceX) {
  25469. me.setOpacity(el, "1")
  25470. } else if (x > distanceX && x < distanceX + 70) {
  25471. me.setOpacity(el, "0.5")
  25472. } else if (x > distanceX + 70 && x < distanceX + 140) {
  25473. me.hide();
  25474. }
  25475. });
  25476. }
  25477. });
  25478. //ie\ff下 mouseout不准
  25479. if (browser.chrome) {
  25480. domUtils.on(doc, "mouseout", function (e) {
  25481. var relatedTgt = e.relatedTarget || e.toElement;
  25482. if (relatedTgt == null || relatedTgt.tagName == "HTML") {
  25483. me.hide();
  25484. }
  25485. });
  25486. }
  25487. me.editor.addListener("afterhidepop", function () {
  25488. if (!me.isHidden) {
  25489. isSubMenuShow = true;
  25490. }
  25491. });
  25492. },
  25493. initItems: function () {
  25494. if (utils.isArray(this.items)) {
  25495. for (var i = 0, len = this.items.length; i < len; i++) {
  25496. var item = this.items[i].toLowerCase();
  25497. if (UI[item]) {
  25498. this.items[i] = new UI[item](this.editor);
  25499. this.items[i].className += " edui-shortcutsubmenu ";
  25500. }
  25501. }
  25502. }
  25503. },
  25504. setOpacity: function (el, value) {
  25505. if (browser.ie && browser.version < 9) {
  25506. el.style.filter = "alpha(opacity = " + parseFloat(value) * 100 + ");"
  25507. } else {
  25508. el.style.opacity = value;
  25509. }
  25510. },
  25511. getSubMenuMark: function () {
  25512. isSubMenuShow = false;
  25513. var layerEle = uiUtils.getFixedLayer();
  25514. var list = domUtils.getElementsByTagName(layerEle, "div", function (node) {
  25515. return domUtils.hasClass(node, "edui-shortcutsubmenu edui-popup")
  25516. });
  25517. for (var i = 0, node; node = list[i++];) {
  25518. if (node.style.display != "none") {
  25519. isSubMenuShow = true;
  25520. }
  25521. }
  25522. return isSubMenuShow;
  25523. },
  25524. show: function (e, hasContextmenu) {
  25525. var me = this,
  25526. offset = {},
  25527. el = this.getDom(),
  25528. fixedlayer = uiUtils.getFixedLayer();
  25529. function setPos(offset) {
  25530. if (offset.left < 0) {
  25531. offset.left = 0;
  25532. }
  25533. if (offset.top < 0) {
  25534. offset.top = 0;
  25535. }
  25536. el.style.cssText = "position:absolute;left:" + offset.left + "px;top:" + offset.top + "px;";
  25537. }
  25538. function setPosByCxtMenu(menu) {
  25539. if (!menu.tagName) {
  25540. menu = menu.getDom();
  25541. }
  25542. offset.left = parseInt(menu.style.left);
  25543. offset.top = parseInt(menu.style.top);
  25544. offset.top -= el.offsetHeight + 15;
  25545. setPos(offset);
  25546. }
  25547. me.eventType = e.type;
  25548. el.style.cssText = "display:block;left:-9999px";
  25549. if (e.type == "contextmenu" && hasContextmenu) {
  25550. var menu = domUtils.getElementsByTagName(fixedlayer, "div", "edui-contextmenu")[0];
  25551. if (menu) {
  25552. setPosByCxtMenu(menu)
  25553. } else {
  25554. me.editor.addListener("aftershowcontextmenu", function (type, menu) {
  25555. setPosByCxtMenu(menu);
  25556. });
  25557. }
  25558. } else {
  25559. offset = uiUtils.getViewportOffsetByEvent(e);
  25560. offset.top -= el.offsetHeight + me.SPACE;
  25561. offset.left += me.SPACE + 20;
  25562. setPos(offset);
  25563. me.setOpacity(el, 0.2);
  25564. }
  25565. me.isHidden = false;
  25566. me.left = e.screenX + el.offsetWidth / 2 - me.SPACE;
  25567. me.top = e.screenY - (el.offsetHeight / 2) - me.SPACE;
  25568. if (me.editor) {
  25569. el.style.zIndex = me.editor.container.style.zIndex * 1 + 10;
  25570. fixedlayer.style.zIndex = el.style.zIndex - 1;
  25571. }
  25572. },
  25573. hide: function () {
  25574. if (this.getDom()) {
  25575. this.getDom().style.display = "none";
  25576. }
  25577. this.isHidden = true;
  25578. },
  25579. postRender: function () {
  25580. if (utils.isArray(this.items)) {
  25581. for (var i = 0, item; item = this.items[i++];) {
  25582. item.postRender();
  25583. }
  25584. }
  25585. },
  25586. getHtmlTpl: function () {
  25587. var buff;
  25588. if (utils.isArray(this.items)) {
  25589. buff = [];
  25590. for (var i = 0; i < this.items.length; i++) {
  25591. buff[i] = this.items[i].renderHtml();
  25592. }
  25593. buff = buff.join("");
  25594. } else {
  25595. buff = this.items;
  25596. }
  25597. return '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
  25598. buff +
  25599. '</div>';
  25600. }
  25601. };
  25602. utils.inherits(ShortCutMenu, UIBase);
  25603. function hideAllMenu(e) {
  25604. var tgt = e.target || e.srcElement,
  25605. cur = domUtils.findParent(tgt, function (node) {
  25606. return domUtils.hasClass(node, "edui-shortcutmenu") || domUtils.hasClass(node, "edui-popup");
  25607. }, true);
  25608. if (!cur) {
  25609. for (var i = 0, menu; menu = allMenus[i++];) {
  25610. menu.hide()
  25611. }
  25612. }
  25613. }
  25614. domUtils.on(document, 'mousedown', function (e) {
  25615. hideAllMenu(e);
  25616. });
  25617. domUtils.on(window, 'scroll', function (e) {
  25618. hideAllMenu(e);
  25619. });
  25620. })();
  25621. // ui/breakline.js
  25622. (function () {
  25623. var utils = baidu.editor.utils,
  25624. UIBase = baidu.editor.ui.UIBase,
  25625. Breakline = baidu.editor.ui.Breakline = function (options) {
  25626. this.initOptions(options);
  25627. this.initSeparator();
  25628. };
  25629. Breakline.prototype = {
  25630. uiName: 'Breakline',
  25631. initSeparator: function () {
  25632. this.initUIBase();
  25633. },
  25634. getHtmlTpl: function () {
  25635. return '<br/>';
  25636. }
  25637. };
  25638. utils.inherits(Breakline, UIBase);
  25639. })();
  25640. // ui/message.js
  25641. ///import core
  25642. ///import uicore
  25643. (function () {
  25644. var utils = baidu.editor.utils,
  25645. domUtils = baidu.editor.dom.domUtils,
  25646. UIBase = baidu.editor.ui.UIBase,
  25647. Message = baidu.editor.ui.Message = function (options) {
  25648. this.initOptions(options);
  25649. this.initMessage();
  25650. };
  25651. Message.prototype = {
  25652. initMessage: function () {
  25653. this.initUIBase();
  25654. },
  25655. getHtmlTpl: function () {
  25656. return '<div id="##" class="edui-message %%">' +
  25657. ' <div id="##_closer" class="edui-message-closer">×</div>' +
  25658. ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
  25659. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  25660. ' <div class="edui-shadow"></div>' +
  25661. ' <div id="##_content" class="edui-message-content">' +
  25662. ' </div>' +
  25663. ' </div>' +
  25664. '</div>';
  25665. },
  25666. reset: function (opt) {
  25667. var me = this;
  25668. if (!opt.keepshow) {
  25669. clearTimeout(this.timer);
  25670. me.timer = setTimeout(function () {
  25671. me.hide();
  25672. }, opt.timeout || 4000);
  25673. }
  25674. opt.content !== undefined && me.setContent(opt.content);
  25675. opt.type !== undefined && me.setType(opt.type);
  25676. me.show();
  25677. },
  25678. postRender: function () {
  25679. var me = this,
  25680. closer = this.getDom('closer');
  25681. closer && domUtils.on(closer, 'click', function () {
  25682. me.hide();
  25683. });
  25684. },
  25685. setContent: function (content) {
  25686. this.getDom('content').innerHTML = content;
  25687. },
  25688. setType: function (type) {
  25689. type = type || 'info';
  25690. var body = this.getDom('body');
  25691. body.className = body.className.replace(/edui-message-type-[\w-]+/, 'edui-message-type-' + type);
  25692. },
  25693. getContent: function () {
  25694. return this.getDom('content').innerHTML;
  25695. },
  25696. getType: function () {
  25697. var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/);
  25698. return arr ? arr[1] : '';
  25699. },
  25700. show: function () {
  25701. this.getDom().style.display = 'block';
  25702. },
  25703. hide: function () {
  25704. var dom = this.getDom();
  25705. if (dom) {
  25706. dom.style.display = 'none';
  25707. dom.parentNode && dom.parentNode.removeChild(dom);
  25708. }
  25709. }
  25710. };
  25711. utils.inherits(Message, UIBase);
  25712. })();
  25713. // adapter/editorui.js
  25714. //ui跟编辑器的适配層
  25715. //那个按钮弹出是dialog,是下拉筐等都是在这个js中配置
  25716. //自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化
  25717. (function () {
  25718. var utils = baidu.editor.utils;
  25719. var editorui = baidu.editor.ui;
  25720. var _Dialog = editorui.Dialog;
  25721. editorui.buttons = {};
  25722. editorui.Dialog = function (options) {
  25723. var dialog = new _Dialog(options);
  25724. dialog.addListener('hide', function () {
  25725. if (dialog.editor) {
  25726. var editor = dialog.editor;
  25727. try {
  25728. if (browser.gecko) {
  25729. var y = editor.window.scrollY,
  25730. x = editor.window.scrollX;
  25731. editor.body.focus();
  25732. editor.window.scrollTo(x, y);
  25733. } else {
  25734. editor.focus();
  25735. }
  25736. } catch (ex) {
  25737. }
  25738. }
  25739. });
  25740. return dialog;
  25741. };
  25742. var iframeUrlMap = {
  25743. 'anchor': '~/dialogs/anchor/anchor.html',
  25744. 'insertimage': '~/dialogs/image/image.html',
  25745. 'link': '~/dialogs/link/link.html',
  25746. 'spechars': '~/dialogs/spechars/spechars.html',
  25747. 'searchreplace': '~/dialogs/searchreplace/searchreplace.html',
  25748. 'map': '~/dialogs/map/map.html',
  25749. 'gmap': '~/dialogs/gmap/gmap.html',
  25750. 'insertvideo': '~/dialogs/video/video.html',
  25751. 'help': '~/dialogs/help/help.html',
  25752. 'preview': '~/dialogs/preview/preview.html',
  25753. 'emotion': '~/dialogs/emotion/emotion.html',
  25754. 'wordimage': '~/dialogs/wordimage/wordimage.html',
  25755. 'attachment': '~/dialogs/attachment/attachment.html',
  25756. 'insertframe': '~/dialogs/insertframe/insertframe.html',
  25757. 'edittip': '~/dialogs/table/edittip.html',
  25758. 'edittable': '~/dialogs/table/edittable.html',
  25759. 'edittd': '~/dialogs/table/edittd.html',
  25760. 'webapp': '~/dialogs/webapp/webapp.html',
  25761. 'snapscreen': '~/dialogs/snapscreen/snapscreen.html',
  25762. 'scrawl': '~/dialogs/scrawl/scrawl.html',
  25763. 'music': '~/dialogs/music/music.html',
  25764. 'template': '~/dialogs/template/template.html',
  25765. 'background': '~/dialogs/background/background.html',
  25766. 'charts': '~/dialogs/charts/charts.html'
  25767. };
  25768. //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起
  25769. var btnCmds = ['undo', 'redo', 'formatmatch',
  25770. 'bold', 'italic', 'underline', 'fontborder', 'touppercase', 'tolowercase',
  25771. 'strikethrough', 'subscript', 'superscript', 'source', 'indent', 'outdent',
  25772. 'blockquote', 'pasteplain', 'pagebreak',
  25773. 'selectall', 'print', 'horizontal', 'removeformat', 'time', 'date', 'unlink',
  25774. 'insertparagraphbeforetable', 'insertrow', 'insertcol', 'mergeright', 'mergedown', 'deleterow',
  25775. 'deletecol', 'splittorows', 'splittocols', 'splittocells', 'mergecells', 'deletetable', 'drafts'];
  25776. for (var i = 0, ci; ci = btnCmds[i++];) {
  25777. ci = ci.toLowerCase();
  25778. editorui[ci] = function (cmd) {
  25779. return function (editor) {
  25780. var ui = new editorui.Button({
  25781. className: 'edui-for-' + cmd,
  25782. title: editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
  25783. onclick: function () {
  25784. editor.execCommand(cmd);
  25785. },
  25786. theme: editor.options.theme,
  25787. showText: false
  25788. });
  25789. editorui.buttons[cmd] = ui;
  25790. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  25791. var state = editor.queryCommandState(cmd);
  25792. if (state == -1) {
  25793. ui.setDisabled(true);
  25794. ui.setChecked(false);
  25795. } else {
  25796. if (!uiReady) {
  25797. ui.setDisabled(false);
  25798. ui.setChecked(state);
  25799. }
  25800. }
  25801. });
  25802. return ui;
  25803. };
  25804. }(ci);
  25805. }
  25806. //清除文档
  25807. editorui.cleardoc = function (editor) {
  25808. var ui = new editorui.Button({
  25809. className: 'edui-for-cleardoc',
  25810. title: editor.options.labelMap.cleardoc || editor.getLang("labelMap.cleardoc") || '',
  25811. theme: editor.options.theme,
  25812. onclick: function () {
  25813. if (confirm(editor.getLang("confirmClear"))) {
  25814. editor.execCommand('cleardoc');
  25815. }
  25816. }
  25817. });
  25818. editorui.buttons["cleardoc"] = ui;
  25819. editor.addListener('selectionchange', function () {
  25820. ui.setDisabled(editor.queryCommandState('cleardoc') == -1);
  25821. });
  25822. return ui;
  25823. };
  25824. //排版,图片排版,文字方向
  25825. var typeset = {
  25826. 'justify': ['left', 'right', 'center', 'justify'],
  25827. 'imagefloat': ['none', 'left', 'center', 'right'],
  25828. 'directionality': ['ltr', 'rtl']
  25829. };
  25830. for (var p in typeset) {
  25831. (function (cmd, val) {
  25832. for (var i = 0, ci; ci = val[i++];) {
  25833. (function (cmd2) {
  25834. editorui[cmd.replace('float', '') + cmd2] = function (editor) {
  25835. var ui = new editorui.Button({
  25836. className: 'edui-for-' + cmd.replace('float', '') + cmd2,
  25837. title: editor.options.labelMap[cmd.replace('float', '') + cmd2] || editor.getLang("labelMap." + cmd.replace('float', '') + cmd2) || '',
  25838. theme: editor.options.theme,
  25839. onclick: function () {
  25840. editor.execCommand(cmd, cmd2);
  25841. }
  25842. });
  25843. editorui.buttons[cmd] = ui;
  25844. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  25845. ui.setDisabled(editor.queryCommandState(cmd) == -1);
  25846. ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady);
  25847. });
  25848. return ui;
  25849. };
  25850. })(ci)
  25851. }
  25852. })(p, typeset[p])
  25853. }
  25854. //字体颜色和背景颜色
  25855. for (var i = 0, ci; ci = ['backcolor', 'forecolor'][i++];) {
  25856. editorui[ci] = function (cmd) {
  25857. return function (editor) {
  25858. var ui = new editorui.ColorButton({
  25859. className: 'edui-for-' + cmd,
  25860. color: 'default',
  25861. title: editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '',
  25862. editor: editor,
  25863. onpickcolor: function (t, color) {
  25864. editor.execCommand(cmd, color);
  25865. },
  25866. onpicknocolor: function () {
  25867. editor.execCommand(cmd, 'default');
  25868. this.setColor('transparent');
  25869. this.color = 'default';
  25870. },
  25871. onbuttonclick: function () {
  25872. editor.execCommand(cmd, this.color);
  25873. }
  25874. });
  25875. editorui.buttons[cmd] = ui;
  25876. editor.addListener('selectionchange', function () {
  25877. ui.setDisabled(editor.queryCommandState(cmd) == -1);
  25878. });
  25879. return ui;
  25880. };
  25881. }(ci);
  25882. }
  25883. var dialogBtns = {
  25884. noOk: ['searchreplace', 'help', 'spechars', 'webapp', 'preview'],
  25885. ok: ['attachment', 'anchor', 'link', 'insertimage', 'map', 'gmap', 'insertframe', 'wordimage',
  25886. 'insertvideo', 'insertframe', 'edittip', 'edittable', 'edittd', 'scrawl', 'template', 'music', 'background', 'charts']
  25887. };
  25888. for (var p in dialogBtns) {
  25889. (function (type, vals) {
  25890. for (var i = 0, ci; ci = vals[i++];) {
  25891. //todo opera下存在问题
  25892. if (browser.opera && ci === "searchreplace") {
  25893. continue;
  25894. }
  25895. (function (cmd) {
  25896. editorui[cmd] = function (editor, iframeUrl, title) {
  25897. iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd];
  25898. title = editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd) || '';
  25899. var dialog;
  25900. //没有iframeUrl不创建dialog
  25901. if (iframeUrl) {
  25902. dialog = new editorui.Dialog(utils.extend({
  25903. iframeUrl: editor.ui.mapUrl(iframeUrl),
  25904. editor: editor,
  25905. className: 'edui-for-' + cmd,
  25906. title: title,
  25907. holdScroll: cmd === 'insertimage',
  25908. fullscreen: /charts|preview/.test(cmd),
  25909. closeDialog: editor.getLang("closeDialog")
  25910. }, type == 'ok' ? {
  25911. buttons: [
  25912. {
  25913. className: 'edui-okbutton',
  25914. label: editor.getLang("ok"),
  25915. editor: editor,
  25916. onclick: function () {
  25917. dialog.close(true);
  25918. }
  25919. },
  25920. {
  25921. className: 'edui-cancelbutton',
  25922. label: editor.getLang("cancel"),
  25923. editor: editor,
  25924. onclick: function () {
  25925. dialog.close(false);
  25926. }
  25927. }
  25928. ]
  25929. } : {}));
  25930. editor.ui._dialogs[cmd + "Dialog"] = dialog;
  25931. }
  25932. var ui = new editorui.Button({
  25933. className: 'edui-for-' + cmd,
  25934. title: title,
  25935. onclick: function () {
  25936. if (dialog) {
  25937. switch (cmd) {
  25938. case "wordimage":
  25939. var images = editor.execCommand("wordimage");
  25940. if (images && images.length) {
  25941. dialog.render();
  25942. dialog.open();
  25943. }
  25944. break;
  25945. case "scrawl":
  25946. if (editor.queryCommandState("scrawl") != -1) {
  25947. dialog.render();
  25948. dialog.open();
  25949. }
  25950. break;
  25951. default:
  25952. dialog.render();
  25953. dialog.open();
  25954. }
  25955. }
  25956. },
  25957. theme: editor.options.theme,
  25958. disabled: (cmd == 'scrawl' && editor.queryCommandState("scrawl") == -1) || (cmd == 'charts')
  25959. });
  25960. editorui.buttons[cmd] = ui;
  25961. editor.addListener('selectionchange', function () {
  25962. //只存在于右键菜单而无工具栏按钮的ui不需要检测状态
  25963. var unNeedCheckState = {'edittable': 1};
  25964. if (cmd in unNeedCheckState) return;
  25965. var state = editor.queryCommandState(cmd);
  25966. if (ui.getDom()) {
  25967. ui.setDisabled(state == -1);
  25968. ui.setChecked(state);
  25969. }
  25970. });
  25971. return ui;
  25972. };
  25973. })(ci.toLowerCase())
  25974. }
  25975. })(p, dialogBtns[p]);
  25976. }
  25977. editorui.snapscreen = function (editor, iframeUrl, title) {
  25978. title = editor.options.labelMap['snapscreen'] || editor.getLang("labelMap.snapscreen") || '';
  25979. var ui = new editorui.Button({
  25980. className: 'edui-for-snapscreen',
  25981. title: title,
  25982. onclick: function () {
  25983. editor.execCommand("snapscreen");
  25984. },
  25985. theme: editor.options.theme
  25986. });
  25987. editorui.buttons['snapscreen'] = ui;
  25988. iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})["snapscreen"] || iframeUrlMap["snapscreen"];
  25989. if (iframeUrl) {
  25990. var dialog = new editorui.Dialog({
  25991. iframeUrl: editor.ui.mapUrl(iframeUrl),
  25992. editor: editor,
  25993. className: 'edui-for-snapscreen',
  25994. title: title,
  25995. buttons: [
  25996. {
  25997. className: 'edui-okbutton',
  25998. label: editor.getLang("ok"),
  25999. editor: editor,
  26000. onclick: function () {
  26001. dialog.close(true);
  26002. }
  26003. },
  26004. {
  26005. className: 'edui-cancelbutton',
  26006. label: editor.getLang("cancel"),
  26007. editor: editor,
  26008. onclick: function () {
  26009. dialog.close(false);
  26010. }
  26011. }
  26012. ]
  26013. });
  26014. dialog.render();
  26015. editor.ui._dialogs["snapscreenDialog"] = dialog;
  26016. }
  26017. editor.addListener('selectionchange', function () {
  26018. ui.setDisabled(editor.queryCommandState('snapscreen') == -1);
  26019. });
  26020. return ui;
  26021. };
  26022. editorui.insertcode = function (editor, list, title) {
  26023. list = editor.options['insertcode'] || [];
  26024. title = editor.options.labelMap['insertcode'] || editor.getLang("labelMap.insertcode") || '';
  26025. // if (!list.length) return;
  26026. var items = [];
  26027. utils.each(list, function (key, val) {
  26028. items.push({
  26029. label: key,
  26030. value: val,
  26031. theme: editor.options.theme,
  26032. renderLabelHtml: function () {
  26033. return '<div class="edui-label %%-label" >' + (this.label || '') + '</div>';
  26034. }
  26035. });
  26036. });
  26037. var ui = new editorui.Combox({
  26038. editor: editor,
  26039. items: items,
  26040. onselect: function (t, index) {
  26041. editor.execCommand('insertcode', this.items[index].value);
  26042. },
  26043. onbuttonclick: function () {
  26044. this.showPopup();
  26045. },
  26046. title: title,
  26047. initValue: title,
  26048. className: 'edui-for-insertcode',
  26049. indexByValue: function (value) {
  26050. if (value) {
  26051. for (var i = 0, ci; ci = this.items[i]; i++) {
  26052. if (ci.value.indexOf(value) != -1)
  26053. return i;
  26054. }
  26055. }
  26056. return -1;
  26057. }
  26058. });
  26059. editorui.buttons['insertcode'] = ui;
  26060. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26061. if (!uiReady) {
  26062. var state = editor.queryCommandState('insertcode');
  26063. if (state == -1) {
  26064. ui.setDisabled(true);
  26065. } else {
  26066. ui.setDisabled(false);
  26067. var value = editor.queryCommandValue('insertcode');
  26068. if (!value) {
  26069. ui.setValue(title);
  26070. return;
  26071. }
  26072. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  26073. value && (value = value.replace(/['"]/g, '').split(',')[0]);
  26074. ui.setValue(value);
  26075. }
  26076. }
  26077. });
  26078. return ui;
  26079. };
  26080. editorui.fontfamily = function (editor, list, title) {
  26081. list = editor.options['fontfamily'] || [];
  26082. title = editor.options.labelMap['fontfamily'] || editor.getLang("labelMap.fontfamily") || '';
  26083. if (!list.length) return;
  26084. for (var i = 0, ci, items = []; ci = list[i]; i++) {
  26085. var langLabel = editor.getLang('fontfamily')[ci.name] || "";
  26086. (function (key, val) {
  26087. items.push({
  26088. label: key,
  26089. value: val,
  26090. theme: editor.options.theme,
  26091. renderLabelHtml: function () {
  26092. return '<div class="edui-label %%-label" style="font-family:' +
  26093. utils.unhtml(this.value) + '">' + (this.label || '') + '</div>';
  26094. }
  26095. });
  26096. })(ci.label || langLabel, ci.val)
  26097. }
  26098. var ui = new editorui.Combox({
  26099. editor: editor,
  26100. items: items,
  26101. onselect: function (t, index) {
  26102. editor.execCommand('FontFamily', this.items[index].value);
  26103. },
  26104. onbuttonclick: function () {
  26105. this.showPopup();
  26106. },
  26107. title: title,
  26108. initValue: title,
  26109. className: 'edui-for-fontfamily',
  26110. indexByValue: function (value) {
  26111. if (value) {
  26112. for (var i = 0, ci; ci = this.items[i]; i++) {
  26113. if (ci.value.indexOf(value) != -1)
  26114. return i;
  26115. }
  26116. }
  26117. return -1;
  26118. }
  26119. });
  26120. editorui.buttons['fontfamily'] = ui;
  26121. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26122. if (!uiReady) {
  26123. var state = editor.queryCommandState('FontFamily');
  26124. if (state == -1) {
  26125. ui.setDisabled(true);
  26126. } else {
  26127. ui.setDisabled(false);
  26128. var value = editor.queryCommandValue('FontFamily');
  26129. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  26130. value && (value = value.replace(/['"]/g, '').split(',')[0]);
  26131. ui.setValue(value);
  26132. }
  26133. }
  26134. });
  26135. return ui;
  26136. };
  26137. editorui.fontsize = function (editor, list, title) {
  26138. title = editor.options.labelMap['fontsize'] || editor.getLang("labelMap.fontsize") || '';
  26139. list = list || editor.options['fontsize'] || [];
  26140. if (!list.length) return;
  26141. var items = [];
  26142. for (var i = 0; i < list.length; i++) {
  26143. var size = list[i] + 'px';
  26144. items.push({
  26145. label: size,
  26146. value: size,
  26147. theme: editor.options.theme,
  26148. renderLabelHtml: function () {
  26149. return '<div class="edui-label %%-label" style="line-height:1;font-size:' +
  26150. this.value + '">' + (this.label || '') + '</div>';
  26151. }
  26152. });
  26153. }
  26154. var ui = new editorui.Combox({
  26155. editor: editor,
  26156. items: items,
  26157. title: title,
  26158. initValue: title,
  26159. onselect: function (t, index) {
  26160. editor.execCommand('FontSize', this.items[index].value);
  26161. },
  26162. onbuttonclick: function () {
  26163. this.showPopup();
  26164. },
  26165. className: 'edui-for-fontsize'
  26166. });
  26167. editorui.buttons['fontsize'] = ui;
  26168. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26169. if (!uiReady) {
  26170. var state = editor.queryCommandState('FontSize');
  26171. if (state == -1) {
  26172. ui.setDisabled(true);
  26173. } else {
  26174. ui.setDisabled(false);
  26175. ui.setValue(editor.queryCommandValue('FontSize'));
  26176. }
  26177. }
  26178. });
  26179. return ui;
  26180. };
  26181. editorui.paragraph = function (editor, list, title) {
  26182. title = editor.options.labelMap['paragraph'] || editor.getLang("labelMap.paragraph") || '';
  26183. list = editor.options['paragraph'] || [];
  26184. if (utils.isEmptyObject(list)) return;
  26185. var items = [];
  26186. for (var i in list) {
  26187. items.push({
  26188. value: i,
  26189. label: list[i] || editor.getLang("paragraph")[i],
  26190. theme: editor.options.theme,
  26191. renderLabelHtml: function () {
  26192. return '<div class="edui-label %%-label"><span class="edui-for-' + this.value + '">' + (this.label || '') + '</span></div>';
  26193. }
  26194. })
  26195. }
  26196. var ui = new editorui.Combox({
  26197. editor: editor,
  26198. items: items,
  26199. title: title,
  26200. initValue: title,
  26201. className: 'edui-for-paragraph',
  26202. onselect: function (t, index) {
  26203. editor.execCommand('Paragraph', this.items[index].value);
  26204. },
  26205. onbuttonclick: function () {
  26206. this.showPopup();
  26207. }
  26208. });
  26209. editorui.buttons['paragraph'] = ui;
  26210. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26211. if (!uiReady) {
  26212. var state = editor.queryCommandState('Paragraph');
  26213. if (state == -1) {
  26214. ui.setDisabled(true);
  26215. } else {
  26216. ui.setDisabled(false);
  26217. var value = editor.queryCommandValue('Paragraph');
  26218. var index = ui.indexByValue(value);
  26219. if (index != -1) {
  26220. ui.setValue(value);
  26221. } else {
  26222. ui.setValue(ui.initValue);
  26223. }
  26224. }
  26225. }
  26226. });
  26227. return ui;
  26228. };
  26229. //自定义标题
  26230. editorui.customstyle = function (editor) {
  26231. var list = editor.options['customstyle'] || [],
  26232. title = editor.options.labelMap['customstyle'] || editor.getLang("labelMap.customstyle") || '';
  26233. if (!list.length) return;
  26234. var langCs = editor.getLang('customstyle');
  26235. for (var i = 0, items = [], t; t = list[i++];) {
  26236. (function (t) {
  26237. var ck = {};
  26238. ck.label = t.label ? t.label : langCs[t.name];
  26239. ck.style = t.style;
  26240. ck.className = t.className;
  26241. ck.tag = t.tag;
  26242. items.push({
  26243. label: ck.label,
  26244. value: ck,
  26245. theme: editor.options.theme,
  26246. renderLabelHtml: function () {
  26247. return '<div class="edui-label %%-label">' + '<' + ck.tag + ' ' + (ck.className ? ' class="' + ck.className + '"' : "")
  26248. + (ck.style ? ' style="' + ck.style + '"' : "") + '>' + ck.label + "<\/" + ck.tag + ">"
  26249. + '</div>';
  26250. }
  26251. });
  26252. })(t);
  26253. }
  26254. var ui = new editorui.Combox({
  26255. editor: editor,
  26256. items: items,
  26257. title: title,
  26258. initValue: title,
  26259. className: 'edui-for-customstyle',
  26260. onselect: function (t, index) {
  26261. editor.execCommand('customstyle', this.items[index].value);
  26262. },
  26263. onbuttonclick: function () {
  26264. this.showPopup();
  26265. },
  26266. indexByValue: function (value) {
  26267. for (var i = 0, ti; ti = this.items[i++];) {
  26268. if (ti.label == value) {
  26269. return i - 1
  26270. }
  26271. }
  26272. return -1;
  26273. }
  26274. });
  26275. editorui.buttons['customstyle'] = ui;
  26276. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26277. if (!uiReady) {
  26278. var state = editor.queryCommandState('customstyle');
  26279. if (state == -1) {
  26280. ui.setDisabled(true);
  26281. } else {
  26282. ui.setDisabled(false);
  26283. var value = editor.queryCommandValue('customstyle');
  26284. var index = ui.indexByValue(value);
  26285. if (index != -1) {
  26286. ui.setValue(value);
  26287. } else {
  26288. ui.setValue(ui.initValue);
  26289. }
  26290. }
  26291. }
  26292. });
  26293. return ui;
  26294. };
  26295. editorui.inserttable = function (editor, iframeUrl, title) {
  26296. title = editor.options.labelMap['inserttable'] || editor.getLang("labelMap.inserttable") || '';
  26297. var ui = new editorui.TableButton({
  26298. editor: editor,
  26299. title: title,
  26300. className: 'edui-for-inserttable',
  26301. onpicktable: function (t, numCols, numRows) {
  26302. editor.execCommand('InsertTable', {numRows: numRows, numCols: numCols, border: 1});
  26303. },
  26304. onbuttonclick: function () {
  26305. this.showPopup();
  26306. }
  26307. });
  26308. editorui.buttons['inserttable'] = ui;
  26309. editor.addListener('selectionchange', function () {
  26310. ui.setDisabled(editor.queryCommandState('inserttable') == -1);
  26311. });
  26312. return ui;
  26313. };
  26314. editorui.lineheight = function (editor) {
  26315. var val = editor.options.lineheight || [];
  26316. if (!val.length) return;
  26317. for (var i = 0, ci, items = []; ci = val[i++];) {
  26318. items.push({
  26319. //todo:写死了
  26320. label: ci,
  26321. value: ci,
  26322. theme: editor.options.theme,
  26323. onclick: function () {
  26324. editor.execCommand("lineheight", this.value);
  26325. }
  26326. })
  26327. }
  26328. var ui = new editorui.MenuButton({
  26329. editor: editor,
  26330. className: 'edui-for-lineheight',
  26331. title: editor.options.labelMap['lineheight'] || editor.getLang("labelMap.lineheight") || '',
  26332. items: items,
  26333. onbuttonclick: function () {
  26334. var value = editor.queryCommandValue('LineHeight') || this.value;
  26335. editor.execCommand("LineHeight", value);
  26336. }
  26337. });
  26338. editorui.buttons['lineheight'] = ui;
  26339. editor.addListener('selectionchange', function () {
  26340. var state = editor.queryCommandState('LineHeight');
  26341. if (state == -1) {
  26342. ui.setDisabled(true);
  26343. } else {
  26344. ui.setDisabled(false);
  26345. var value = editor.queryCommandValue('LineHeight');
  26346. value && ui.setValue((value + '').replace(/cm/, ''));
  26347. ui.setChecked(state)
  26348. }
  26349. });
  26350. return ui;
  26351. };
  26352. var rowspacings = ['top', 'bottom'];
  26353. for (var r = 0, ri; ri = rowspacings[r++];) {
  26354. (function (cmd) {
  26355. editorui['rowspacing' + cmd] = function (editor) {
  26356. var val = editor.options['rowspacing' + cmd] || [];
  26357. if (!val.length) return null;
  26358. for (var i = 0, ci, items = []; ci = val[i++];) {
  26359. items.push({
  26360. label: ci,
  26361. value: ci,
  26362. theme: editor.options.theme,
  26363. onclick: function () {
  26364. editor.execCommand("rowspacing", this.value, cmd);
  26365. }
  26366. })
  26367. }
  26368. var ui = new editorui.MenuButton({
  26369. editor: editor,
  26370. className: 'edui-for-rowspacing' + cmd,
  26371. title: editor.options.labelMap['rowspacing' + cmd] || editor.getLang("labelMap.rowspacing" + cmd) || '',
  26372. items: items,
  26373. onbuttonclick: function () {
  26374. var value = editor.queryCommandValue('rowspacing', cmd) || this.value;
  26375. editor.execCommand("rowspacing", value, cmd);
  26376. }
  26377. });
  26378. editorui.buttons[cmd] = ui;
  26379. editor.addListener('selectionchange', function () {
  26380. var state = editor.queryCommandState('rowspacing', cmd);
  26381. if (state == -1) {
  26382. ui.setDisabled(true);
  26383. } else {
  26384. ui.setDisabled(false);
  26385. var value = editor.queryCommandValue('rowspacing', cmd);
  26386. value && ui.setValue((value + '').replace(/%/, ''));
  26387. ui.setChecked(state)
  26388. }
  26389. });
  26390. return ui;
  26391. }
  26392. })(ri)
  26393. }
  26394. //有序,无序列表
  26395. var lists = ['insertorderedlist', 'insertunorderedlist'];
  26396. for (var l = 0, cl; cl = lists[l++];) {
  26397. (function (cmd) {
  26398. editorui[cmd] = function (editor) {
  26399. var vals = editor.options[cmd],
  26400. _onMenuClick = function () {
  26401. editor.execCommand(cmd, this.value);
  26402. }, items = [];
  26403. for (var i in vals) {
  26404. items.push({
  26405. label: vals[i] || editor.getLang()[cmd][i] || "",
  26406. value: i,
  26407. theme: editor.options.theme,
  26408. onclick: _onMenuClick
  26409. })
  26410. }
  26411. var ui = new editorui.MenuButton({
  26412. editor: editor,
  26413. className: 'edui-for-' + cmd,
  26414. title: editor.getLang("labelMap." + cmd) || '',
  26415. 'items': items,
  26416. onbuttonclick: function () {
  26417. var value = editor.queryCommandValue(cmd) || this.value;
  26418. editor.execCommand(cmd, value);
  26419. }
  26420. });
  26421. editorui.buttons[cmd] = ui;
  26422. editor.addListener('selectionchange', function () {
  26423. var state = editor.queryCommandState(cmd);
  26424. if (state == -1) {
  26425. ui.setDisabled(true);
  26426. } else {
  26427. ui.setDisabled(false);
  26428. var value = editor.queryCommandValue(cmd);
  26429. ui.setValue(value);
  26430. ui.setChecked(state)
  26431. }
  26432. });
  26433. return ui;
  26434. };
  26435. })(cl)
  26436. }
  26437. editorui.fullscreen = function (editor, title) {
  26438. title = editor.options.labelMap['fullscreen'] || editor.getLang("labelMap.fullscreen") || '';
  26439. var ui = new editorui.Button({
  26440. className: 'edui-for-fullscreen',
  26441. title: title,
  26442. theme: editor.options.theme,
  26443. onclick: function () {
  26444. if (editor.ui) {
  26445. editor.ui.setFullScreen(!editor.ui.isFullScreen());
  26446. }
  26447. this.setChecked(editor.ui.isFullScreen());
  26448. }
  26449. });
  26450. editorui.buttons['fullscreen'] = ui;
  26451. editor.addListener('selectionchange', function () {
  26452. var state = editor.queryCommandState('fullscreen');
  26453. ui.setDisabled(state == -1);
  26454. ui.setChecked(editor.ui.isFullScreen());
  26455. });
  26456. return ui;
  26457. };
  26458. // 表情
  26459. editorui["emotion"] = function (editor, iframeUrl) {
  26460. var cmd = "emotion";
  26461. var ui = new editorui.MultiMenuPop({
  26462. title: editor.options.labelMap[cmd] || editor.getLang("labelMap." + cmd + "") || '',
  26463. editor: editor,
  26464. className: 'edui-for-' + cmd,
  26465. iframeUrl: editor.ui.mapUrl(iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd])
  26466. });
  26467. editorui.buttons[cmd] = ui;
  26468. editor.addListener('selectionchange', function () {
  26469. ui.setDisabled(editor.queryCommandState(cmd) == -1)
  26470. });
  26471. return ui;
  26472. };
  26473. editorui.autotypeset = function (editor) {
  26474. var ui = new editorui.AutoTypeSetButton({
  26475. editor: editor,
  26476. title: editor.options.labelMap['autotypeset'] || editor.getLang("labelMap.autotypeset") || '',
  26477. className: 'edui-for-autotypeset',
  26478. onbuttonclick: function () {
  26479. editor.execCommand('autotypeset')
  26480. }
  26481. });
  26482. editorui.buttons['autotypeset'] = ui;
  26483. editor.addListener('selectionchange', function () {
  26484. ui.setDisabled(editor.queryCommandState('autotypeset') == -1);
  26485. });
  26486. return ui;
  26487. };
  26488. /* 简单上传插件 */
  26489. editorui["simpleupload"] = function (editor) {
  26490. var name = 'simpleupload',
  26491. ui = new editorui.Button({
  26492. className: 'edui-for-' + name,
  26493. title: editor.options.labelMap[name] || editor.getLang("labelMap." + name) || '',
  26494. onclick: function () {
  26495. },
  26496. theme: editor.options.theme,
  26497. showText: false
  26498. });
  26499. editorui.buttons[name] = ui;
  26500. editor.addListener('ready', function () {
  26501. var b = ui.getDom('body'),
  26502. iconSpan = b.children[0];
  26503. editor.fireEvent('simpleuploadbtnready', iconSpan);
  26504. });
  26505. editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
  26506. var state = editor.queryCommandState(name);
  26507. if (state == -1) {
  26508. ui.setDisabled(true);
  26509. ui.setChecked(false);
  26510. } else {
  26511. if (!uiReady) {
  26512. ui.setDisabled(false);
  26513. ui.setChecked(state);
  26514. }
  26515. }
  26516. });
  26517. return ui;
  26518. };
  26519. })();
  26520. // adapter/editor.js
  26521. ///import core
  26522. ///commands 全屏
  26523. ///commandsName FullScreen
  26524. ///commandsTitle 全屏
  26525. (function () {
  26526. var utils = baidu.editor.utils,
  26527. uiUtils = baidu.editor.ui.uiUtils,
  26528. UIBase = baidu.editor.ui.UIBase,
  26529. domUtils = baidu.editor.dom.domUtils;
  26530. var nodeStack = [];
  26531. function EditorUI(options) {
  26532. this.initOptions(options);
  26533. this.initEditorUI();
  26534. }
  26535. EditorUI.prototype = {
  26536. uiName: 'editor',
  26537. initEditorUI: function () {
  26538. this.editor.ui = this;
  26539. this._dialogs = {};
  26540. this.initUIBase();
  26541. this._initToolbars();
  26542. var editor = this.editor,
  26543. me = this;
  26544. editor.addListener('ready', function () {
  26545. //提供getDialog方法
  26546. editor.getDialog = function (name) {
  26547. return editor.ui._dialogs[name + "Dialog"];
  26548. };
  26549. domUtils.on(editor.window, 'scroll', function (evt) {
  26550. baidu.editor.ui.Popup.postHide(evt);
  26551. });
  26552. //提供编辑器实时宽高(全屏时宽高不变化)
  26553. editor.ui._actualFrameWidth = editor.options.initialFrameWidth;
  26554. UE.browser.ie && UE.browser.version === 6 && editor.container.ownerDocument.execCommand("BackgroundImageCache", false, true);
  26555. //display bottom-bar label based on config
  26556. if (editor.options.elementPathEnabled) {
  26557. editor.ui.getDom('elementpath').innerHTML = '<div class="edui-editor-breadcrumb">' + editor.getLang("elementPathTip") + ':</div>';
  26558. }
  26559. if (editor.options.wordCount) {
  26560. function countFn() {
  26561. setCount(editor, me);
  26562. domUtils.un(editor.document, "click", arguments.callee);
  26563. }
  26564. domUtils.on(editor.document, "click", countFn);
  26565. editor.ui.getDom('wordcount').innerHTML = editor.getLang("wordCountTip");
  26566. }
  26567. editor.ui._scale();
  26568. if (editor.options.scaleEnabled) {
  26569. if (editor.autoHeightEnabled) {
  26570. editor.disableAutoHeight();
  26571. }
  26572. me.enableScale();
  26573. } else {
  26574. me.disableScale();
  26575. }
  26576. if (!editor.options.elementPathEnabled && !editor.options.wordCount && !editor.options.scaleEnabled) {
  26577. editor.ui.getDom('elementpath').style.display = "none";
  26578. editor.ui.getDom('wordcount').style.display = "none";
  26579. editor.ui.getDom('scale').style.display = "none";
  26580. }
  26581. if (!editor.selection.isFocus()) return;
  26582. editor.fireEvent('selectionchange', false, true);
  26583. });
  26584. editor.addListener('mousedown', function (t, evt) {
  26585. var el = evt.target || evt.srcElement;
  26586. baidu.editor.ui.Popup.postHide(evt, el);
  26587. baidu.editor.ui.ShortCutMenu.postHide(evt);
  26588. });
  26589. editor.addListener("delcells", function () {
  26590. if (UE.ui['edittip']) {
  26591. new UE.ui['edittip'](editor);
  26592. }
  26593. editor.getDialog('edittip').open();
  26594. });
  26595. var pastePop, isPaste = false, timer;
  26596. editor.addListener("afterpaste", function () {
  26597. if (editor.queryCommandState('pasteplain'))
  26598. return;
  26599. if (baidu.editor.ui.PastePicker) {
  26600. pastePop = new baidu.editor.ui.Popup({
  26601. content: new baidu.editor.ui.PastePicker({editor: editor}),
  26602. editor: editor,
  26603. className: 'edui-wordpastepop'
  26604. });
  26605. pastePop.render();
  26606. }
  26607. isPaste = true;
  26608. });
  26609. editor.addListener("afterinserthtml", function () {
  26610. clearTimeout(timer);
  26611. timer = setTimeout(function () {
  26612. if (pastePop && (isPaste || editor.ui._isTransfer)) {
  26613. if (pastePop.isHidden()) {
  26614. var span = domUtils.createElement(editor.document, 'span', {
  26615. 'style': "line-height:0px;",
  26616. 'innerHTML': '\ufeff'
  26617. }),
  26618. range = editor.selection.getRange();
  26619. range.insertNode(span);
  26620. var tmp = getDomNode(span, 'firstChild', 'previousSibling');
  26621. tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp);
  26622. domUtils.remove(span);
  26623. } else {
  26624. pastePop.show();
  26625. }
  26626. delete editor.ui._isTransfer;
  26627. isPaste = false;
  26628. }
  26629. }, 200)
  26630. });
  26631. editor.addListener('contextmenu', function (t, evt) {
  26632. baidu.editor.ui.Popup.postHide(evt);
  26633. });
  26634. editor.addListener('keydown', function (t, evt) {
  26635. if (pastePop) pastePop.dispose(evt);
  26636. var keyCode = evt.keyCode || evt.which;
  26637. if (evt.altKey && keyCode == 90) {
  26638. UE.ui.buttons['fullscreen'].onclick();
  26639. }
  26640. });
  26641. editor.addListener('wordcount', function (type) {
  26642. setCount(this, me);
  26643. });
  26644. function setCount(editor, ui) {
  26645. editor.setOpt({
  26646. wordCount: true,
  26647. maximumWords: 1e4,
  26648. wordCountMsg: editor.options.wordCountMsg || editor.getLang("wordCountMsg"),
  26649. wordOverFlowMsg: editor.options.wordOverFlowMsg || editor.getLang("wordOverFlowMsg")
  26650. });
  26651. var opt = editor.options,
  26652. max = opt.maximumWords,
  26653. msg = opt.wordCountMsg,
  26654. errMsg = opt.wordOverFlowMsg,
  26655. countDom = ui.getDom('wordcount');
  26656. if (!opt.wordCount) {
  26657. return;
  26658. }
  26659. var count = editor.getContentLength(true);
  26660. if (count > max) {
  26661. countDom.innerHTML = errMsg;
  26662. editor.fireEvent("wordcountoverflow");
  26663. } else {
  26664. countDom.innerHTML = msg.replace("{#leave}", max - count).replace("{#count}", count);
  26665. }
  26666. }
  26667. editor.addListener('selectionchange', function () {
  26668. if (editor.options.elementPathEnabled) {
  26669. me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']()
  26670. }
  26671. if (editor.options.scaleEnabled) {
  26672. me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale']();
  26673. }
  26674. });
  26675. var popup = new baidu.editor.ui.Popup({
  26676. editor: editor,
  26677. content: '',
  26678. className: 'edui-bubble',
  26679. _onEditButtonClick: function () {
  26680. this.hide();
  26681. editor.ui._dialogs.linkDialog.open();
  26682. },
  26683. _onImgEditButtonClick: function (name) {
  26684. this.hide();
  26685. editor.ui._dialogs[name] && editor.ui._dialogs[name].open();
  26686. },
  26687. _onImgSetFloat: function (value) {
  26688. this.hide();
  26689. editor.execCommand("imagefloat", value);
  26690. },
  26691. _setIframeAlign: function (value) {
  26692. var frame = popup.anchorEl;
  26693. var newFrame = frame.cloneNode(true);
  26694. switch (value) {
  26695. case -2:
  26696. newFrame.setAttribute("align", "");
  26697. break;
  26698. case -1:
  26699. newFrame.setAttribute("align", "left");
  26700. break;
  26701. case 1:
  26702. newFrame.setAttribute("align", "right");
  26703. break;
  26704. }
  26705. frame.parentNode.insertBefore(newFrame, frame);
  26706. domUtils.remove(frame);
  26707. popup.anchorEl = newFrame;
  26708. popup.showAnchor(popup.anchorEl);
  26709. },
  26710. _updateIframe: function () {
  26711. var frame = editor._iframe = popup.anchorEl;
  26712. if (domUtils.hasClass(frame, 'ueditor_baidumap')) {
  26713. editor.selection.getRange().selectNode(frame).select();
  26714. editor.ui._dialogs.mapDialog.open();
  26715. popup.hide();
  26716. } else {
  26717. editor.ui._dialogs.insertframeDialog.open();
  26718. popup.hide();
  26719. }
  26720. },
  26721. _onRemoveButtonClick: function (cmdName) {
  26722. editor.execCommand(cmdName);
  26723. this.hide();
  26724. },
  26725. queryAutoHide: function (el) {
  26726. if (el && el.ownerDocument == editor.document) {
  26727. if (el.tagName.toLowerCase() == 'img' || domUtils.findParentByTagName(el, 'a', true)) {
  26728. return el !== popup.anchorEl;
  26729. }
  26730. }
  26731. return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el);
  26732. }
  26733. });
  26734. popup.render();
  26735. if (editor.options.imagePopup) {
  26736. editor.addListener('mouseover', function (t, evt) {
  26737. evt = evt || window.event;
  26738. var el = evt.target || evt.srcElement;
  26739. if (editor.ui._dialogs.insertframeDialog && /iframe/ig.test(el.tagName)) {
  26740. var html = popup.formatHtml(
  26741. '<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;' +
  26742. ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>');
  26743. if (html) {
  26744. popup.getDom('content').innerHTML = html;
  26745. popup.anchorEl = el;
  26746. popup.showAnchor(popup.anchorEl);
  26747. } else {
  26748. popup.hide();
  26749. }
  26750. }
  26751. });
  26752. editor.addListener('selectionchange', function (t, causeByUi) {
  26753. if (!causeByUi) return;
  26754. var html = '', str = "",
  26755. img = editor.selection.getRange().getClosedNode(),
  26756. dialogs = editor.ui._dialogs;
  26757. if (img && img.tagName == 'IMG') {
  26758. var dialogName = 'insertimageDialog';
  26759. if (img.className.indexOf("edui-faked-video") != -1 || img.className.indexOf("edui-upload-video") != -1) {
  26760. dialogName = "insertvideoDialog"
  26761. }
  26762. if (img.className.indexOf("edui-faked-webapp") != -1) {
  26763. dialogName = "webappDialog"
  26764. }
  26765. if (img.src.indexOf("http://api.map.baidu.com") != -1) {
  26766. dialogName = "mapDialog"
  26767. }
  26768. if (img.className.indexOf("edui-faked-music") != -1) {
  26769. dialogName = "musicDialog"
  26770. }
  26771. if (img.src.indexOf("http://maps.google.com/maps/api/staticmap") != -1) {
  26772. dialogName = "gmapDialog"
  26773. }
  26774. if (img.getAttribute("anchorname")) {
  26775. dialogName = "anchorDialog";
  26776. html = popup.formatHtml(
  26777. '<nobr>' + editor.getLang("property") + ': <span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' + editor.getLang("modify") + '</span>&nbsp;&nbsp;' +
  26778. '<span onclick=$$._onRemoveButtonClick(\'anchor\') class="edui-clickable">' + editor.getLang("delete") + '</span></nobr>');
  26779. }
  26780. if (img.getAttribute("word_img")) {
  26781. //todo 放到dialog去做查询
  26782. editor.word_img = [img.getAttribute("word_img")];
  26783. dialogName = "wordimageDialog"
  26784. }
  26785. if (domUtils.hasClass(img, 'loadingclass') || domUtils.hasClass(img, 'loaderrorclass')) {
  26786. dialogName = "";
  26787. }
  26788. if (!dialogs[dialogName]) {
  26789. return;
  26790. }
  26791. str = '<nobr>' + editor.getLang("property") + ': ' +
  26792. '<span onclick=$$._onImgSetFloat("none") class="edui-clickable">' + editor.getLang("default") + '</span>&nbsp;&nbsp;' +
  26793. '<span onclick=$$._onImgSetFloat("left") class="edui-clickable">' + editor.getLang("justifyleft") + '</span>&nbsp;&nbsp;' +
  26794. '<span onclick=$$._onImgSetFloat("right") class="edui-clickable">' + editor.getLang("justifyright") + '</span>&nbsp;&nbsp;' +
  26795. '<span onclick=$$._onImgSetFloat("center") class="edui-clickable">' + editor.getLang("justifycenter") + '</span>&nbsp;&nbsp;' +
  26796. '<span onclick="$$._onImgEditButtonClick(\'' + dialogName + '\');" class="edui-clickable">' + editor.getLang("modify") + '</span></nobr>';
  26797. !html && (html = popup.formatHtml(str))
  26798. }
  26799. if (editor.ui._dialogs.linkDialog) {
  26800. var link = editor.queryCommandValue('link');
  26801. var url;
  26802. if (link && (url = (link.getAttribute('_href') || link.getAttribute('href', 2)))) {
  26803. var txt = url;
  26804. if (url.length > 30) {
  26805. txt = url.substring(0, 20) + "...";
  26806. }
  26807. if (html) {
  26808. html += '<div style="height:5px;"></div>'
  26809. }
  26810. html += popup.formatHtml(
  26811. '<nobr>' + editor.getLang("anthorMsg") + ': <a target="_blank" href="' + url + '" title="' + url + '" >' + txt + '</a>' +
  26812. ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' + editor.getLang("modify") + '</span>' +
  26813. ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' + editor.getLang("clear") + '</span></nobr>');
  26814. popup.showAnchor(link);
  26815. }
  26816. }
  26817. if (html) {
  26818. popup.getDom('content').innerHTML = html;
  26819. popup.anchorEl = img || link;
  26820. popup.showAnchor(popup.anchorEl);
  26821. } else {
  26822. popup.hide();
  26823. }
  26824. });
  26825. }
  26826. },
  26827. _initToolbars: function () {
  26828. var editor = this.editor;
  26829. var toolbars = this.toolbars || [];
  26830. var toolbarUis = [];
  26831. for (var i = 0; i < toolbars.length; i++) {
  26832. var toolbar = toolbars[i];
  26833. var toolbarUi = new baidu.editor.ui.Toolbar({theme: editor.options.theme});
  26834. for (var j = 0; j < toolbar.length; j++) {
  26835. var toolbarItem = toolbar[j];
  26836. var toolbarItemUi = null;
  26837. if (typeof toolbarItem == 'string') {
  26838. toolbarItem = toolbarItem.toLowerCase();
  26839. if (toolbarItem == '|') {
  26840. toolbarItem = 'Separator';
  26841. }
  26842. if (toolbarItem == '||') {
  26843. toolbarItem = 'Breakline';
  26844. }
  26845. if (baidu.editor.ui[toolbarItem]) {
  26846. toolbarItemUi = new baidu.editor.ui[toolbarItem](editor);
  26847. }
  26848. //fullscreen这里单独处理一下,放到首行去
  26849. if (toolbarItem == 'fullscreen') {
  26850. if (toolbarUis && toolbarUis[0]) {
  26851. toolbarUis[0].items.splice(0, 0, toolbarItemUi);
  26852. } else {
  26853. toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi);
  26854. }
  26855. continue;
  26856. }
  26857. } else {
  26858. toolbarItemUi = toolbarItem;
  26859. }
  26860. if (toolbarItemUi && toolbarItemUi.id) {
  26861. toolbarUi.add(toolbarItemUi);
  26862. }
  26863. }
  26864. toolbarUis[i] = toolbarUi;
  26865. }
  26866. //接受外部定制的UI
  26867. utils.each(UE._customizeUI, function (obj, key) {
  26868. var itemUI, index;
  26869. if (obj.id && obj.id != editor.key) {
  26870. return false;
  26871. }
  26872. itemUI = obj.execFn.call(editor, editor, key);
  26873. if (itemUI) {
  26874. index = obj.index;
  26875. if (index === undefined) {
  26876. index = toolbarUi.items.length;
  26877. }
  26878. toolbarUi.add(itemUI, index)
  26879. }
  26880. });
  26881. this.toolbars = toolbarUis;
  26882. },
  26883. getHtmlTpl: function () {
  26884. return '<div id="##" class="%%">' +
  26885. '<div id="##_toolbarbox" class="%%-toolbarbox">' +
  26886. (this.toolbars.length ?
  26887. '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
  26888. this.renderToolbarBoxHtml() +
  26889. '</div></div>' : '') +
  26890. '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
  26891. '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' + this.editor.getLang("clickToUpload") + '</div>' +
  26892. '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
  26893. '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
  26894. '<div style="height:0;overflow:hidden;clear:both;"></div>' +
  26895. '</div>' +
  26896. '<div id="##_message_holder" class="%%-messageholder"></div>' +
  26897. '</div>' +
  26898. '<div id="##_iframeholder" class="%%-iframeholder">' +
  26899. '</div>' +
  26900. //modify wdcount by matao
  26901. '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
  26902. '<td id="##_elementpath" class="%%-bottombar"></td>' +
  26903. '<td id="##_wordcount" class="%%-wordcount"></td>' +
  26904. '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
  26905. '</tr></table></div>' +
  26906. '<div id="##_scalelayer"></div>' +
  26907. '</div>';
  26908. },
  26909. showWordImageDialog: function () {
  26910. this._dialogs['wordimageDialog'].open();
  26911. },
  26912. renderToolbarBoxHtml: function () {
  26913. var buff = [];
  26914. for (var i = 0; i < this.toolbars.length; i++) {
  26915. buff.push(this.toolbars[i].renderHtml());
  26916. }
  26917. return buff.join('');
  26918. },
  26919. setFullScreen: function (fullscreen) {
  26920. var editor = this.editor,
  26921. container = editor.container.parentNode.parentNode;
  26922. if (this._fullscreen != fullscreen) {
  26923. this._fullscreen = fullscreen;
  26924. this.editor.fireEvent('beforefullscreenchange', fullscreen);
  26925. if (baidu.editor.browser.gecko) {
  26926. var bk = editor.selection.getRange().createBookmark();
  26927. }
  26928. if (fullscreen) {
  26929. while (container.tagName != "BODY") {
  26930. var position = baidu.editor.dom.domUtils.getComputedStyle(container, "position");
  26931. nodeStack.push(position);
  26932. container.style.position = "static";
  26933. container = container.parentNode;
  26934. }
  26935. this._bakHtmlOverflow = document.documentElement.style.overflow;
  26936. this._bakBodyOverflow = document.body.style.overflow;
  26937. this._bakAutoHeight = this.editor.autoHeightEnabled;
  26938. this._bakScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
  26939. this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth;
  26940. if (this._bakAutoHeight) {
  26941. //当全屏时不能执行自动长高
  26942. editor.autoHeightEnabled = false;
  26943. this.editor.disableAutoHeight();
  26944. }
  26945. document.documentElement.style.overflow = 'hidden';
  26946. //修复,滚动条不收起的问题
  26947. window.scrollTo(0, window.scrollY);
  26948. this._bakCssText = this.getDom().style.cssText;
  26949. this._bakCssText1 = this.getDom('iframeholder').style.cssText;
  26950. editor.iframe.parentNode.style.width = '';
  26951. this._updateFullScreen();
  26952. } else {
  26953. while (container.tagName != "BODY") {
  26954. container.style.position = nodeStack.shift();
  26955. container = container.parentNode;
  26956. }
  26957. this.getDom().style.cssText = this._bakCssText;
  26958. this.getDom('iframeholder').style.cssText = this._bakCssText1;
  26959. if (this._bakAutoHeight) {
  26960. editor.autoHeightEnabled = true;
  26961. this.editor.enableAutoHeight();
  26962. }
  26963. document.documentElement.style.overflow = this._bakHtmlOverflow;
  26964. document.body.style.overflow = this._bakBodyOverflow;
  26965. editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + 'px';
  26966. window.scrollTo(0, this._bakScrollTop);
  26967. }
  26968. if (browser.gecko && editor.body.contentEditable === 'true') {
  26969. var input = document.createElement('input');
  26970. document.body.appendChild(input);
  26971. editor.body.contentEditable = false;
  26972. setTimeout(function () {
  26973. input.focus();
  26974. setTimeout(function () {
  26975. editor.body.contentEditable = true;
  26976. editor.fireEvent('fullscreenchanged', fullscreen);
  26977. editor.selection.getRange().moveToBookmark(bk).select(true);
  26978. baidu.editor.dom.domUtils.remove(input);
  26979. fullscreen && window.scroll(0, 0);
  26980. }, 0)
  26981. }, 0)
  26982. }
  26983. if (editor.body.contentEditable === 'true') {
  26984. this.editor.fireEvent('fullscreenchanged', fullscreen);
  26985. this.triggerLayout();
  26986. }
  26987. }
  26988. },
  26989. _updateFullScreen: function () {
  26990. if (this._fullscreen) {
  26991. var vpRect = uiUtils.getViewportRect();
  26992. 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);
  26993. uiUtils.setViewportOffset(this.getDom(), {left: 0, top: this.editor.options.topOffset || 0});
  26994. this.editor.setHeight(vpRect.height - this.getDom('toolbarbox').offsetHeight - this.getDom('bottombar').offsetHeight - (this.editor.options.topOffset || 0), true);
  26995. //不手动调一下,会导致全屏失效
  26996. if (browser.gecko) {
  26997. try {
  26998. window.onresize();
  26999. } catch (e) {
  27000. }
  27001. }
  27002. }
  27003. },
  27004. _updateElementPath: function () {
  27005. var bottom = this.getDom('elementpath'), list;
  27006. if (this.elementPathEnabled && (list = this.editor.queryCommandValue('elementpath'))) {
  27007. var buff = [];
  27008. for (var i = 0, ci; ci = list[i]; i++) {
  27009. buff[i] = this.formatHtml('<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' + i + '&quot;);">' + ci + '</span>');
  27010. }
  27011. bottom.innerHTML = '<div class="edui-editor-breadcrumb" onmousedown="return false;">' + this.editor.getLang("elementPathTip") + ': ' + buff.join(' &gt; ') + '</div>';
  27012. } else {
  27013. bottom.style.display = 'none'
  27014. }
  27015. },
  27016. disableElementPath: function () {
  27017. var bottom = this.getDom('elementpath');
  27018. bottom.innerHTML = '';
  27019. bottom.style.display = 'none';
  27020. this.elementPathEnabled = false;
  27021. },
  27022. enableElementPath: function () {
  27023. var bottom = this.getDom('elementpath');
  27024. bottom.style.display = '';
  27025. this.elementPathEnabled = true;
  27026. this._updateElementPath();
  27027. },
  27028. _scale: function () {
  27029. var doc = document,
  27030. editor = this.editor,
  27031. editorHolder = editor.container,
  27032. editorDocument = editor.document,
  27033. toolbarBox = this.getDom("toolbarbox"),
  27034. bottombar = this.getDom("bottombar"),
  27035. scale = this.getDom("scale"),
  27036. scalelayer = this.getDom("scalelayer");
  27037. var isMouseMove = false,
  27038. position = null,
  27039. minEditorHeight = 0,
  27040. minEditorWidth = editor.options.minFrameWidth,
  27041. pageX = 0,
  27042. pageY = 0,
  27043. scaleWidth = 0,
  27044. scaleHeight = 0;
  27045. function down() {
  27046. position = domUtils.getXY(editorHolder);
  27047. if (!minEditorHeight) {
  27048. minEditorHeight = editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight;
  27049. }
  27050. 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:"
  27051. + editorHolder.offsetHeight + "px;z-index:" + (editor.options.zIndex + 1);
  27052. domUtils.on(doc, "mousemove", move);
  27053. domUtils.on(editorDocument, "mouseup", up);
  27054. domUtils.on(doc, "mouseup", up);
  27055. }
  27056. var me = this;
  27057. //by xuheng 全屏时关掉缩放
  27058. this.editor.addListener('fullscreenchanged', function (e, fullScreen) {
  27059. if (fullScreen) {
  27060. me.disableScale();
  27061. } else {
  27062. if (me.editor.options.scaleEnabled) {
  27063. me.enableScale();
  27064. var tmpNode = me.editor.document.createElement('span');
  27065. me.editor.body.appendChild(tmpNode);
  27066. me.editor.body.style.height = Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px';
  27067. domUtils.remove(tmpNode)
  27068. }
  27069. }
  27070. });
  27071. function move(event) {
  27072. clearSelection();
  27073. var e = event || window.event;
  27074. pageX = e.pageX || (doc.documentElement.scrollLeft + e.clientX);
  27075. pageY = e.pageY || (doc.documentElement.scrollTop + e.clientY);
  27076. scaleWidth = pageX - position.x;
  27077. scaleHeight = pageY - position.y;
  27078. if (scaleWidth >= minEditorWidth) {
  27079. isMouseMove = true;
  27080. scalelayer.style.width = scaleWidth + 'px';
  27081. }
  27082. if (scaleHeight >= minEditorHeight) {
  27083. isMouseMove = true;
  27084. scalelayer.style.height = scaleHeight + "px";
  27085. }
  27086. }
  27087. function up() {
  27088. if (isMouseMove) {
  27089. isMouseMove = false;
  27090. editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2;
  27091. editorHolder.style.width = editor.ui._actualFrameWidth + 'px';
  27092. editor.setHeight(scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2, true);
  27093. }
  27094. if (scalelayer) {
  27095. scalelayer.style.display = "none";
  27096. }
  27097. clearSelection();
  27098. domUtils.un(doc, "mousemove", move);
  27099. domUtils.un(editorDocument, "mouseup", up);
  27100. domUtils.un(doc, "mouseup", up);
  27101. }
  27102. function clearSelection() {
  27103. if (browser.ie)
  27104. doc.selection.clear();
  27105. else
  27106. window.getSelection().removeAllRanges();
  27107. }
  27108. this.enableScale = function () {
  27109. //trace:2868
  27110. if (editor.queryCommandState("source") == 1) return;
  27111. scale.style.display = "";
  27112. this.scaleEnabled = true;
  27113. domUtils.on(scale, "mousedown", down);
  27114. };
  27115. this.disableScale = function () {
  27116. scale.style.display = "none";
  27117. this.scaleEnabled = false;
  27118. domUtils.un(scale, "mousedown", down);
  27119. };
  27120. },
  27121. isFullScreen: function () {
  27122. return this._fullscreen;
  27123. },
  27124. postRender: function () {
  27125. UIBase.prototype.postRender.call(this);
  27126. for (var i = 0; i < this.toolbars.length; i++) {
  27127. this.toolbars[i].postRender();
  27128. }
  27129. var me = this;
  27130. var timerId,
  27131. domUtils = baidu.editor.dom.domUtils,
  27132. updateFullScreenTime = function () {
  27133. clearTimeout(timerId);
  27134. timerId = setTimeout(function () {
  27135. me._updateFullScreen();
  27136. });
  27137. };
  27138. domUtils.on(window, 'resize', updateFullScreenTime);
  27139. me.addListener('destroy', function () {
  27140. domUtils.un(window, 'resize', updateFullScreenTime);
  27141. clearTimeout(timerId);
  27142. })
  27143. },
  27144. showToolbarMsg: function (msg, flag) {
  27145. this.getDom('toolbarmsg_label').innerHTML = msg;
  27146. this.getDom('toolbarmsg').style.display = '';
  27147. //
  27148. if (!flag) {
  27149. var w = this.getDom('upload_dialog');
  27150. w.style.display = 'none';
  27151. }
  27152. },
  27153. hideToolbarMsg: function () {
  27154. this.getDom('toolbarmsg').style.display = 'none';
  27155. },
  27156. mapUrl: function (url) {
  27157. return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : ''
  27158. },
  27159. triggerLayout: function () {
  27160. var dom = this.getDom();
  27161. if (dom.style.zoom == '1') {
  27162. dom.style.zoom = '100%';
  27163. } else {
  27164. dom.style.zoom = '1';
  27165. }
  27166. }
  27167. };
  27168. utils.inherits(EditorUI, baidu.editor.ui.UIBase);
  27169. var instances = {};
  27170. UE.ui.Editor = function (options) {
  27171. var editor = new UE.Editor(options);
  27172. editor.options.editor = editor;
  27173. utils.loadFile(document, {
  27174. href: editor.options.themePath + editor.options.theme + "/css/ueditor.css",
  27175. tag: "link",
  27176. type: "text/css",
  27177. rel: "stylesheet"
  27178. });
  27179. var oldRender = editor.render;
  27180. editor.render = function (holder) {
  27181. if (holder.constructor === String) {
  27182. editor.key = holder;
  27183. instances[holder] = editor;
  27184. }
  27185. utils.domReady(function () {
  27186. editor.langIsReady ? renderUI() : editor.addListener("langReady", renderUI);
  27187. function renderUI() {
  27188. editor.setOpt({
  27189. labelMap: editor.options.labelMap || editor.getLang('labelMap')
  27190. });
  27191. new EditorUI(editor.options);
  27192. if (holder) {
  27193. if (holder.constructor === String) {
  27194. holder = document.getElementById(holder);
  27195. }
  27196. holder && holder.getAttribute('name') && (editor.options.textarea = holder.getAttribute('name'));
  27197. if (holder && /script|textarea/ig.test(holder.tagName)) {
  27198. var newDiv = document.createElement('div');
  27199. holder.parentNode.insertBefore(newDiv, holder);
  27200. var cont = holder.value || holder.innerHTML;
  27201. editor.options.initialContent = /^[\t\r\n ]*$/.test(cont) ? editor.options.initialContent :
  27202. cont.replace(/>[\n\r\t]+([ ]{4})+/g, '>')
  27203. .replace(/[\n\r\t]+([ ]{4})+</g, '<')
  27204. .replace(/>[\n\r\t]+</g, '><');
  27205. holder.className && (newDiv.className = holder.className);
  27206. holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  27207. if (/textarea/i.test(holder.tagName)) {
  27208. editor.textarea = holder;
  27209. editor.textarea.style.display = 'none';
  27210. } else {
  27211. holder.parentNode.removeChild(holder);
  27212. }
  27213. if (holder.id) {
  27214. newDiv.id = holder.id;
  27215. domUtils.removeAttributes(holder, 'id');
  27216. }
  27217. holder = newDiv;
  27218. holder.innerHTML = '';
  27219. }
  27220. }
  27221. domUtils.addClass(holder, "edui-" + editor.options.theme);
  27222. editor.ui.render(holder);
  27223. var opt = editor.options;
  27224. //给实例添加一个编辑器的容器引用
  27225. editor.container = editor.ui.getDom();
  27226. var parents = domUtils.findParents(holder, true);
  27227. var displays = [];
  27228. for (var i = 0, ci; ci = parents[i]; i++) {
  27229. displays[i] = ci.style.display;
  27230. ci.style.display = 'block'
  27231. }
  27232. if (opt.initialFrameWidth) {
  27233. opt.minFrameWidth = opt.initialFrameWidth;
  27234. } else {
  27235. opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth;
  27236. var styleWidth = holder.style.width;
  27237. if (/%$/.test(styleWidth)) {
  27238. opt.initialFrameWidth = styleWidth;
  27239. }
  27240. }
  27241. if (opt.initialFrameHeight) {
  27242. opt.minFrameHeight = opt.initialFrameHeight;
  27243. } else {
  27244. opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight;
  27245. }
  27246. for (var i = 0, ci; ci = parents[i]; i++) {
  27247. ci.style.display = displays[i]
  27248. }
  27249. //编辑器最外容器设置了高度,会导致,编辑器不占位
  27250. //todo 先去掉,没有找到原因
  27251. if (holder.style.height) {
  27252. holder.style.height = ''
  27253. }
  27254. editor.container.style.width = opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px');
  27255. editor.container.style.zIndex = opt.zIndex;
  27256. oldRender.call(editor, editor.ui.getDom('iframeholder'));
  27257. editor.fireEvent("afteruiready");
  27258. }
  27259. })
  27260. };
  27261. return editor;
  27262. };
  27263. /**
  27264. * @file
  27265. * @name UE
  27266. * @short UE
  27267. * @desc UEditor的顶部命名空间
  27268. */
  27269. /**
  27270. * @name getEditor
  27271. * @since 1.2.4+
  27272. * @grammar UE.getEditor(id,[opt]) => Editor实例
  27273. * @desc 提供一个全局的方法得到编辑器实例
  27274. *
  27275. * * ''id'' 放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
  27276. * * ''opt'' 编辑器的可选参数
  27277. * @example
  27278. * UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
  27279. * this.setContent('hello')
  27280. * }});
  27281. * UE.getEditor('containerId'); //返回刚创建的实例
  27282. *
  27283. */
  27284. UE.getEditor = function (id, opt) {
  27285. var editor = instances[id];
  27286. if (!editor) {
  27287. editor = instances[id] = new UE.ui.Editor(opt);
  27288. editor.render(id);
  27289. }
  27290. return editor;
  27291. };
  27292. UE.delEditor = function (id) {
  27293. var editor;
  27294. if (editor = instances[id]) {
  27295. editor.key && editor.destroy();
  27296. delete instances[id]
  27297. }
  27298. };
  27299. UE.registerUI = function (uiName, fn, index, editorId) {
  27300. utils.each(uiName.split(/\s+/), function (name) {
  27301. UE._customizeUI[name] = {
  27302. id: editorId,
  27303. execFn: fn,
  27304. index: index
  27305. };
  27306. })
  27307. }
  27308. })();
  27309. // adapter/message.js
  27310. UE.registerUI('message', function (editor) {
  27311. var editorui = baidu.editor.ui;
  27312. var Message = editorui.Message;
  27313. var holder;
  27314. var _messageItems = [];
  27315. var me = editor;
  27316. me.addListener('ready', function () {
  27317. holder = document.getElementById(me.ui.id + '_message_holder');
  27318. updateHolderPos();
  27319. setTimeout(function () {
  27320. updateHolderPos();
  27321. }, 500);
  27322. });
  27323. me.addListener('showmessage', function (type, opt) {
  27324. opt = utils.isString(opt) ? {
  27325. 'content': opt
  27326. } : opt;
  27327. var message = new Message({
  27328. 'timeout': opt.timeout,
  27329. 'type': opt.type,
  27330. 'content': opt.content,
  27331. 'keepshow': opt.keepshow,
  27332. 'editor': me
  27333. }),
  27334. mid = opt.id || ('msg_' + (+new Date()).toString(36));
  27335. message.render(holder);
  27336. _messageItems[mid] = message;
  27337. message.reset(opt);
  27338. updateHolderPos();
  27339. return mid;
  27340. });
  27341. me.addListener('updatemessage', function (type, id, opt) {
  27342. opt = utils.isString(opt) ? {
  27343. 'content': opt
  27344. } : opt;
  27345. var message = _messageItems[id];
  27346. message.render(holder);
  27347. message && message.reset(opt);
  27348. });
  27349. me.addListener('hidemessage', function (type, id) {
  27350. var message = _messageItems[id];
  27351. message && message.hide();
  27352. });
  27353. function updateHolderPos() {
  27354. var toolbarbox = me.ui.getDom('toolbarbox');
  27355. if (toolbarbox) {
  27356. holder.style.top = toolbarbox.offsetHeight + 3 + 'px';
  27357. }
  27358. holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1;
  27359. }
  27360. });
  27361. // adapter/autosave.js
  27362. UE.registerUI('autosave', function (editor) {
  27363. var timer = null, uid = null;
  27364. editor.on('afterautosave', function () {
  27365. clearTimeout(timer);
  27366. timer = setTimeout(function () {
  27367. if (uid) {
  27368. editor.trigger('hidemessage', uid);
  27369. }
  27370. uid = editor.trigger('showmessage', {
  27371. content: editor.getLang('autosave.success'),
  27372. timeout: 2000
  27373. });
  27374. }, 2000)
  27375. })
  27376. });
  27377. UE.registerUI('albumimage', function (editor, uiName) {
  27378. // 注册按钮执行时的command命令,使用命令默认就会带有回退操作
  27379. editor.registerCommand(uiName, {
  27380. execCommand: function () {
  27381. var me = this;
  27382. openAlbum(function (data) {
  27383. var html = '';
  27384. for (var i = 0; i < data.length; i++) {
  27385. html += "<img src='" + ns.img(data[i].pic_path) + "' />";
  27386. }
  27387. me.execCommand('insertHtml', html);
  27388. });
  27389. }
  27390. });
  27391. // 创建一个button
  27392. var btn = new UE.ui.Button({
  27393. // 按钮的名字
  27394. name: uiName,
  27395. // 提示
  27396. title: uiName,
  27397. // 添加额外样式,指定icon图标,这里默认使用一个重复的icon
  27398. cssRules: 'background-position: -380px 0;',
  27399. // 点击时执行的命令
  27400. onclick: function () {
  27401. // 这里可以不用执行命令,做你自己的操作也可
  27402. editor.execCommand(uiName);
  27403. }
  27404. });
  27405. // 当点到编辑内容上时,按钮要做的状态反射
  27406. // editor.addListener('selectionchange', function() {
  27407. // var state = editor.queryCommandState(uiName);
  27408. // if (state == -1) {
  27409. // btn.setDisabled(true);
  27410. // btn.setChecked(false);
  27411. // } else {
  27412. // btn.setDisabled(false);
  27413. // btn.setChecked(state);
  27414. // }
  27415. // });
  27416. // 因为你是添加button,所以需要返回这个button
  27417. return btn;
  27418. });
  27419. })();