umeditor.js 422 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119
  1. /*!
  2. * UEditor Mini版本
  3. * version: 1.2.2
  4. * build: Thu Dec 22 2016 16:33:28 GMT+0800 (CST)
  5. */
  6. (function ($) {
  7. UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};
  8. window.UM = {
  9. list: {},
  10. plugins: {},
  11. commands: {},
  12. I18N: {},
  13. version: "1.2.2"
  14. };
  15. var dom = UM.dom = {};
  16. /**
  17. * 浏览器判断模块
  18. * @file
  19. * @module UE.browser
  20. * @since 1.2.6.1
  21. */
  22. /**
  23. * 提供浏览器检测的模块
  24. * @unfile
  25. * @module UE.browser
  26. */
  27. var browser = UM.browser = function () {
  28. var agent = navigator.userAgent.toLowerCase(),
  29. opera = window.opera,
  30. browser = {
  31. /**
  32. * @property {boolean} ie 检测当前浏览器是否为IE
  33. * @example
  34. * ```javascript
  35. * if ( UE.browser.ie ) {
  36. * console.log( '当前浏览器是IE' );
  37. * }
  38. * ```
  39. */
  40. ie: /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  41. /**
  42. * @property {boolean} opera 检测当前浏览器是否为Opera
  43. * @example
  44. * ```javascript
  45. * if ( UE.browser.opera ) {
  46. * console.log( '当前浏览器是Opera' );
  47. * }
  48. * ```
  49. */
  50. opera: (!!opera && opera.version),
  51. /**
  52. * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
  53. * @example
  54. * ```javascript
  55. * if ( UE.browser.webkit ) {
  56. * console.log( '当前浏览器是webkit内核浏览器' );
  57. * }
  58. * ```
  59. */
  60. webkit: (agent.indexOf(' applewebkit/') > -1),
  61. /**
  62. * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
  63. * @example
  64. * ```javascript
  65. * if ( UE.browser.mac ) {
  66. * console.log( '当前浏览器运行在mac平台下' );
  67. * }
  68. * ```
  69. */
  70. mac: (agent.indexOf('macintosh') > -1),
  71. /**
  72. * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
  73. * @example
  74. * ```javascript
  75. * if ( UE.browser.quirks ) {
  76. * console.log( '当前浏览器运行处于“怪异模式”' );
  77. * }
  78. * ```
  79. */
  80. quirks: (document.compatMode == 'BackCompat')
  81. };
  82. /**
  83. * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
  84. * @example
  85. * ```javascript
  86. * if ( UE.browser.gecko ) {
  87. * console.log( '当前浏览器内核是gecko内核' );
  88. * }
  89. * ```
  90. */
  91. browser.gecko = (navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
  92. var version = 0;
  93. // Internet Explorer 6.0+
  94. if (browser.ie) {
  95. var v1 = agent.match(/(?:msie\s([\w.]+))/);
  96. var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
  97. if (v1 && v2 && v1[1] && v2[1]) {
  98. version = Math.max(v1[1] * 1, v2[1] * 1);
  99. } else if (v1 && v1[1]) {
  100. version = v1[1] * 1;
  101. } else if (v2 && v2[1]) {
  102. version = v2[1] * 1;
  103. } else {
  104. version = 0;
  105. }
  106. browser.ie11Compat = document.documentMode == 11;
  107. /**
  108. * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
  109. * @warning 如果浏览器不是IE, 则该值为undefined
  110. * @example
  111. * ```javascript
  112. * if ( UE.browser.ie9Compat ) {
  113. * console.log( '当前浏览器运行在IE9兼容模式下' );
  114. * }
  115. * ```
  116. */
  117. browser.ie9Compat = document.documentMode == 9;
  118. /**
  119. * @property { boolean } ie8 检测浏览器是否是IE8浏览器
  120. * @warning 如果浏览器不是IE, 则该值为undefined
  121. * @example
  122. * ```javascript
  123. * if ( UE.browser.ie8 ) {
  124. * console.log( '当前浏览器是IE8浏览器' );
  125. * }
  126. * ```
  127. */
  128. browser.ie8 = !!document.documentMode;
  129. /**
  130. * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
  131. * @warning 如果浏览器不是IE, 则该值为undefined
  132. * @example
  133. * ```javascript
  134. * if ( UE.browser.ie8Compat ) {
  135. * console.log( '当前浏览器运行在IE8兼容模式下' );
  136. * }
  137. * ```
  138. */
  139. browser.ie8Compat = document.documentMode == 8;
  140. /**
  141. * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
  142. * @warning 如果浏览器不是IE, 则该值为undefined
  143. * @example
  144. * ```javascript
  145. * if ( UE.browser.ie7Compat ) {
  146. * console.log( '当前浏览器运行在IE7兼容模式下' );
  147. * }
  148. * ```
  149. */
  150. browser.ie7Compat = ((version == 7 && !document.documentMode)
  151. || document.documentMode == 7);
  152. /**
  153. * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
  154. * @warning 如果浏览器不是IE, 则该值为undefined
  155. * @example
  156. * ```javascript
  157. * if ( UE.browser.ie6Compat ) {
  158. * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
  159. * }
  160. * ```
  161. */
  162. browser.ie6Compat = (version < 7 || browser.quirks);
  163. browser.ie9above = version > 8;
  164. browser.ie9below = version < 9;
  165. }
  166. // Gecko.
  167. if (browser.gecko) {
  168. var geckoRelease = agent.match(/rv:([\d\.]+)/);
  169. if (geckoRelease) {
  170. geckoRelease = geckoRelease[1].split('.');
  171. version = geckoRelease[0] * 10000 + (geckoRelease[1] || 0) * 100 + (geckoRelease[2] || 0) * 1;
  172. }
  173. }
  174. /**
  175. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  176. * @warning 如果浏览器不是chrome, 则该值为undefined
  177. * @example
  178. * ```javascript
  179. * if ( UE.browser.chrome ) {
  180. * console.log( '当前浏览器是Chrome' );
  181. * }
  182. * ```
  183. */
  184. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  185. browser.chrome = +RegExp['\x241'];
  186. }
  187. /**
  188. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  189. * @warning 如果浏览器不是safari, 则该值为undefined
  190. * @example
  191. * ```javascript
  192. * if ( UE.browser.safari ) {
  193. * console.log( '当前浏览器是Safari' );
  194. * }
  195. * ```
  196. */
  197. if (/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)) {
  198. browser.safari = +(RegExp['\x241'] || RegExp['\x242']);
  199. }
  200. // Opera 9.50+
  201. if (browser.opera)
  202. version = parseFloat(opera.version());
  203. // WebKit 522+ (Safari 3+)
  204. if (browser.webkit)
  205. version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]);
  206. /**
  207. * @property { Number } version 检测当前浏览器版本号
  208. * @remind
  209. * <ul>
  210. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  211. * <li>gecko系列会返回10900,158900等</li>
  212. * <li>webkit系列会返回其build号 (如 522等)</li>
  213. * </ul>
  214. * @example
  215. * ```javascript
  216. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  217. * ```
  218. */
  219. browser.version = version;
  220. /**
  221. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  222. * @example
  223. * ```javascript
  224. * if ( UE.browser.isCompatible ) {
  225. * console.log( '浏览器与UEditor能够良好兼容' );
  226. * }
  227. * ```
  228. */
  229. browser.isCompatible =
  230. !browser.mobile && (
  231. (browser.ie && version >= 6) ||
  232. (browser.gecko && version >= 10801) ||
  233. (browser.opera && version >= 9.5) ||
  234. (browser.air && version >= 1) ||
  235. (browser.webkit && version >= 522) ||
  236. false);
  237. return browser;
  238. }();
  239. //快捷方式
  240. var ie = browser.ie,
  241. webkit = browser.webkit,
  242. gecko = browser.gecko,
  243. opera = browser.opera;
  244. /**
  245. * @file
  246. * @name UM.Utils
  247. * @short Utils
  248. * @desc UEditor封装使用的静态工具函数
  249. * @import editor.js
  250. */
  251. var utils = UM.utils = {
  252. /**
  253. * 遍历数组,对象,nodeList
  254. * @name each
  255. * @grammar UM.utils.each(obj,iterator,[context])
  256. * @since 1.2.4+
  257. * @desc
  258. * * obj 要遍历的对象
  259. * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
  260. * * context iterator的上下文
  261. * @example
  262. * UM.utils.each([1,2],function(v,i){
  263. * console.log(v)//值
  264. * console.log(i)//索引
  265. * })
  266. * UM.utils.each(document.getElementsByTagName('*'),function(n){
  267. * console.log(n.tagName)
  268. * })
  269. */
  270. each: function (obj, iterator, context) {
  271. if (obj == null) return;
  272. if (obj.length === +obj.length) {
  273. for (var i = 0, l = obj.length; i < l; i++) {
  274. if (iterator.call(context, obj[i], i, obj) === false)
  275. return false;
  276. }
  277. } else {
  278. for (var key in obj) {
  279. if (obj.hasOwnProperty(key)) {
  280. if (iterator.call(context, obj[key], key, obj) === false)
  281. return false;
  282. }
  283. }
  284. }
  285. },
  286. makeInstance: function (obj) {
  287. var noop = new Function();
  288. noop.prototype = obj;
  289. obj = new noop;
  290. noop.prototype = null;
  291. return obj;
  292. },
  293. /**
  294. * 将source对象中的属性扩展到target对象上
  295. * @name extend
  296. * @grammar UM.utils.extend(target,source) => Object //覆盖扩展
  297. * @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展
  298. */
  299. extend: function (t, s, b) {
  300. if (s) {
  301. for (var k in s) {
  302. if (!b || !t.hasOwnProperty(k)) {
  303. t[k] = s[k];
  304. }
  305. }
  306. }
  307. return t;
  308. },
  309. extend2: function (t) {
  310. var a = arguments;
  311. for (var i = 1; i < a.length; i++) {
  312. var x = a[i];
  313. for (var k in x) {
  314. if (!t.hasOwnProperty(k)) {
  315. t[k] = x[k];
  316. }
  317. }
  318. }
  319. return t;
  320. },
  321. /**
  322. * 模拟继承机制,subClass继承superClass
  323. * @name inherits
  324. * @grammar UM.utils.inherits(subClass,superClass) => subClass
  325. * @example
  326. * function SuperClass(){
  327. * this.name = "小李";
  328. * }
  329. * SuperClass.prototype = {
  330. * hello:function(str){
  331. * console.log(this.name + str);
  332. * }
  333. * }
  334. * function SubClass(){
  335. * this.name = "小张";
  336. * }
  337. * UM.utils.inherits(SubClass,SuperClass);
  338. * var sub = new SubClass();
  339. * sub.hello("早上好!"); ==> "小张早上好!"
  340. */
  341. inherits: function (subClass, superClass) {
  342. var oldP = subClass.prototype,
  343. newP = utils.makeInstance(superClass.prototype);
  344. utils.extend(newP, oldP, true);
  345. subClass.prototype = newP;
  346. return (newP.constructor = subClass);
  347. },
  348. /**
  349. * 用指定的context作为fn上下文,也就是this
  350. * @name bind
  351. * @grammar UM.utils.bind(fn,context) => fn
  352. */
  353. bind: function (fn, context) {
  354. return function () {
  355. return fn.apply(context, arguments);
  356. };
  357. },
  358. /**
  359. * 创建延迟delay执行的函数fn
  360. * @name defer
  361. * @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn
  362. * @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
  363. * @example
  364. * function test(){
  365. * console.log("延迟输出!");
  366. * }
  367. * //非互斥延迟执行
  368. * var testDefer = UM.utils.defer(test,1000);
  369. * testDefer(); => "延迟输出!";
  370. * testDefer(); => "延迟输出!";
  371. * //互斥延迟执行
  372. * var testDefer1 = UM.utils.defer(test,1000,true);
  373. * testDefer1(); => //本次不执行
  374. * testDefer1(); => "延迟输出!";
  375. */
  376. defer: function (fn, delay, exclusion) {
  377. var timerID;
  378. return function () {
  379. if (exclusion) {
  380. clearTimeout(timerID);
  381. }
  382. timerID = setTimeout(fn, delay);
  383. };
  384. },
  385. /**
  386. * 查找元素item在数组array中的索引, 若找不到返回-1
  387. * @name indexOf
  388. * @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索
  389. * @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置
  390. */
  391. indexOf: function (array, item, start) {
  392. var index = -1;
  393. start = this.isNumber(start) ? start : 0;
  394. this.each(array, function (v, i) {
  395. if (i >= start && v === item) {
  396. index = i;
  397. return false;
  398. }
  399. });
  400. return index;
  401. },
  402. /**
  403. * 移除数组array中的元素item
  404. * @name removeItem
  405. * @grammar UM.utils.removeItem(array,item)
  406. */
  407. removeItem: function (array, item) {
  408. for (var i = 0, l = array.length; i < l; i++) {
  409. if (array[i] === item) {
  410. array.splice(i, 1);
  411. i--;
  412. }
  413. }
  414. },
  415. /**
  416. * 删除字符串str的首尾空格
  417. * @name trim
  418. * @grammar UM.utils.trim(str) => String
  419. */
  420. trim: function (str) {
  421. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
  422. },
  423. /**
  424. * 将字符串list(以','分隔)或者数组list转成哈希对象
  425. * @name listToMap
  426. * @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1}
  427. */
  428. listToMap: function (list) {
  429. if (!list) return {};
  430. list = utils.isArray(list) ? list : list.split(',');
  431. for (var i = 0, ci, obj = {}; ci = list[i++];) {
  432. obj[ci.toUpperCase()] = obj[ci] = 1;
  433. }
  434. return obj;
  435. },
  436. /**
  437. * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符
  438. * @name unhtml
  439. * @grammar UM.utils.unhtml(str); => String
  440. * @grammar UM.utils.unhtml(str,reg) => String
  441. * @example
  442. * var html = '<body>You say:"你好!Baidu & UEditor!"</body>';
  443. * UM.utils.unhtml(html); ==> &lt;body&gt;You say:&quot;你好!Baidu &amp; UEditor!&quot;&lt;/body&gt;
  444. * UM.utils.unhtml(html,/[<>]/g) ==> &lt;body&gt;You say:"你好!Baidu & UEditor!"&lt;/body&gt;
  445. */
  446. unhtml: function (str, reg) {
  447. return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
  448. if (b) {
  449. return a;
  450. } else {
  451. return {
  452. '<': '&lt;',
  453. '&': '&amp;',
  454. '"': '&quot;',
  455. '>': '&gt;',
  456. "'": '&#39;'
  457. }[a]
  458. }
  459. }) : '';
  460. },
  461. /**
  462. * 将str中的转义字符还原成html字符
  463. * @name html
  464. * @grammar UM.utils.html(str) => String //详细参见<code><a href = '#unhtml'>unhtml</a></code>
  465. */
  466. html: function (str) {
  467. return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
  468. return {
  469. '&lt;': '<',
  470. '&amp;': '&',
  471. '&quot;': '"',
  472. '&gt;': '>',
  473. '&#39;': "'"
  474. }[m]
  475. }) : '';
  476. },
  477. /**
  478. * 将css样式转换为驼峰的形式。如font-size => fontSize
  479. * @name cssStyleToDomStyle
  480. * @grammar UM.utils.cssStyleToDomStyle(cssName) => String
  481. */
  482. cssStyleToDomStyle: function () {
  483. var test = document.createElement('div').style,
  484. cache = {
  485. 'float': test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
  486. };
  487. return function (cssName) {
  488. return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
  489. return match.charAt(1).toUpperCase();
  490. }));
  491. };
  492. }(),
  493. /**
  494. * 动态加载文件到doc中,并依据obj来设置属性,加载成功后执行回调函数fn
  495. * @name loadFile
  496. * @grammar UM.utils.loadFile(doc,obj)
  497. * @grammar UM.utils.loadFile(doc,obj,fn)
  498. * @example
  499. * //指定加载到当前document中一个script文件,加载成功后执行function
  500. * utils.loadFile( document, {
  501. * src:"test.js",
  502. * tag:"script",
  503. * type:"text/javascript",
  504. * defer:"defer"
  505. * }, function () {
  506. * console.log('加载成功!')
  507. * });
  508. */
  509. loadFile: function () {
  510. var tmpList = [];
  511. function getItem(doc, obj) {
  512. try {
  513. for (var i = 0, ci; ci = tmpList[i++];) {
  514. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  515. return ci;
  516. }
  517. }
  518. } catch (e) {
  519. return null;
  520. }
  521. }
  522. return function (doc, obj, fn) {
  523. var item = getItem(doc, obj);
  524. if (item) {
  525. if (item.ready) {
  526. fn && fn();
  527. } else {
  528. item.funs.push(fn)
  529. }
  530. return;
  531. }
  532. tmpList.push({
  533. doc: doc,
  534. url: obj.src || obj.href,
  535. funs: [fn]
  536. });
  537. if (!doc.body) {
  538. var html = [];
  539. for (var p in obj) {
  540. if (p == 'tag') continue;
  541. html.push(p + '="' + obj[p] + '"')
  542. }
  543. doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
  544. return;
  545. }
  546. if (obj.id && doc.getElementById(obj.id)) {
  547. return;
  548. }
  549. var element = doc.createElement(obj.tag);
  550. delete obj.tag;
  551. for (var p in obj) {
  552. element.setAttribute(p, obj[p]);
  553. }
  554. element.onload = element.onreadystatechange = function () {
  555. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  556. item = getItem(doc, obj);
  557. if (item.funs.length > 0) {
  558. item.ready = 1;
  559. for (var fi; fi = item.funs.pop();) {
  560. fi();
  561. }
  562. }
  563. element.onload = element.onreadystatechange = null;
  564. }
  565. };
  566. element.onerror = function () {
  567. throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file umeditor.config.js ')
  568. };
  569. doc.getElementsByTagName("head")[0].appendChild(element);
  570. }
  571. }(),
  572. /**
  573. * 判断obj对象是否为空
  574. * @name isEmptyObject
  575. * @grammar UM.utils.isEmptyObject(obj) => true|false
  576. * @example
  577. * UM.utils.isEmptyObject({}) ==>true
  578. * UM.utils.isEmptyObject([]) ==>true
  579. * UM.utils.isEmptyObject("") ==>true
  580. */
  581. isEmptyObject: function (obj) {
  582. if (obj == null) return true;
  583. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
  584. for (var key in obj) if (obj.hasOwnProperty(key)) return false;
  585. return true;
  586. },
  587. /**
  588. * 统一将颜色值使用16进制形式表示
  589. * @name fixColor
  590. * @grammar UM.utils.fixColor(name,value) => value
  591. * @example
  592. * rgb(255,255,255) => "#ffffff"
  593. */
  594. fixColor: function (name, value) {
  595. if (/color/i.test(name) && /rgba?/.test(value)) {
  596. var array = value.split(",");
  597. if (array.length > 3)
  598. return "";
  599. value = "#";
  600. for (var i = 0, color; color = array[i++];) {
  601. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
  602. value += color.length == 1 ? "0" + color : color;
  603. }
  604. value = value.toUpperCase();
  605. }
  606. return value;
  607. },
  608. /**
  609. * 深度克隆对象,从source到target
  610. * @name clone
  611. * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
  612. * @grammar UM.utils.clone(source,target) => target包含了source的所有内容,重名会覆盖
  613. */
  614. clone: function (source, target) {
  615. var tmp;
  616. target = target || {};
  617. for (var i in source) {
  618. if (source.hasOwnProperty(i)) {
  619. tmp = source[i];
  620. if (typeof tmp == 'object') {
  621. target[i] = utils.isArray(tmp) ? [] : {};
  622. utils.clone(source[i], target[i])
  623. } else {
  624. target[i] = tmp;
  625. }
  626. }
  627. }
  628. return target;
  629. },
  630. /**
  631. * 转换cm/pt到px
  632. * @name transUnitToPx
  633. * @grammar UM.utils.transUnitToPx('20pt') => '27px'
  634. * @grammar UM.utils.transUnitToPx('0pt') => '0'
  635. */
  636. transUnitToPx: function (val) {
  637. if (!/(pt|cm)/.test(val)) {
  638. return val
  639. }
  640. var unit;
  641. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  642. val = v;
  643. unit = u;
  644. });
  645. switch (unit) {
  646. case 'cm':
  647. val = parseFloat(val) * 25;
  648. break;
  649. case 'pt':
  650. val = Math.round(parseFloat(val) * 96 / 72);
  651. }
  652. return val + (val ? 'px' : '');
  653. },
  654. /**
  655. * 动态添加css样式
  656. * @name cssRule
  657. * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  658. * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  659. * @grammar UM.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  660. * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  661. */
  662. cssRule: browser.ie && browser.version != 11 ? function (key, style, doc) {
  663. var indexList, index;
  664. doc = doc || document;
  665. if (doc.indexList) {
  666. indexList = doc.indexList;
  667. } else {
  668. indexList = doc.indexList = {};
  669. }
  670. var sheetStyle;
  671. if (!indexList[key]) {
  672. if (style === undefined) {
  673. return ''
  674. }
  675. sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
  676. indexList[key] = index;
  677. } else {
  678. sheetStyle = doc.styleSheets[indexList[key]];
  679. }
  680. if (style === undefined) {
  681. return sheetStyle.cssText
  682. }
  683. sheetStyle.cssText = style || ''
  684. } : function (key, style, doc) {
  685. doc = doc || document;
  686. var head = doc.getElementsByTagName('head')[0], node;
  687. if (!(node = doc.getElementById(key))) {
  688. if (style === undefined) {
  689. return ''
  690. }
  691. node = doc.createElement('style');
  692. node.id = key;
  693. head.appendChild(node)
  694. }
  695. if (style === undefined) {
  696. return node.innerHTML
  697. }
  698. if (style !== '') {
  699. node.innerHTML = style;
  700. } else {
  701. head.removeChild(node)
  702. }
  703. }
  704. };
  705. /**
  706. * 判断str是否为字符串
  707. * @name isString
  708. * @grammar UM.utils.isString(str) => true|false
  709. */
  710. /**
  711. * 判断array是否为数组
  712. * @name isArray
  713. * @grammar UM.utils.isArray(obj) => true|false
  714. */
  715. /**
  716. * 判断obj对象是否为方法
  717. * @name isFunction
  718. * @grammar UM.utils.isFunction(obj) => true|false
  719. */
  720. /**
  721. * 判断obj对象是否为数字
  722. * @name isNumber
  723. * @grammar UM.utils.isNumber(obj) => true|false
  724. */
  725. utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
  726. UM.utils['is' + v] = function (obj) {
  727. return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
  728. }
  729. });
  730. /**
  731. * @file
  732. * @name UM.EventBase
  733. * @short EventBase
  734. * @import editor.js,core/utils.js
  735. * @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  736. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  737. */
  738. var EventBase = UM.EventBase = function () {
  739. };
  740. EventBase.prototype = {
  741. /**
  742. * 注册事件监听器
  743. * @name addListener
  744. * @grammar editor.addListener(types,fn) //types为事件名称,多个可用空格分隔
  745. * @example
  746. * editor.addListener('selectionchange',function(){
  747. * console.log("选区已经变化!");
  748. * })
  749. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  750. * if(type == 'beforegetcontent'){
  751. * //do something
  752. * }else{
  753. * //do something
  754. * }
  755. * console.log(this.getContent) // this是注册的事件的编辑器实例
  756. * })
  757. */
  758. addListener: function (types, listener) {
  759. types = utils.trim(types).split(' ');
  760. for (var i = 0, ti; ti = types[i++];) {
  761. getListener(this, ti, true).push(listener);
  762. }
  763. },
  764. /**
  765. * 移除事件监听器
  766. * @name removeListener
  767. * @grammar editor.removeListener(types,fn) //types为事件名称,多个可用空格分隔
  768. * @example
  769. * //changeCallback为方法体
  770. * editor.removeListener("selectionchange",changeCallback);
  771. */
  772. removeListener: function (types, listener) {
  773. types = utils.trim(types).split(' ');
  774. for (var i = 0, ti; ti = types[i++];) {
  775. utils.removeItem(getListener(this, ti) || [], listener);
  776. }
  777. },
  778. /**
  779. * 触发事件
  780. * @name fireEvent
  781. * @grammar editor.fireEvent(types) //types为事件名称,多个可用空格分隔
  782. * @example
  783. * editor.fireEvent("selectionchange");
  784. */
  785. fireEvent: function () {
  786. var types = arguments[0];
  787. types = utils.trim(types).split(' ');
  788. for (var i = 0, ti; ti = types[i++];) {
  789. var listeners = getListener(this, ti),
  790. r, t, k;
  791. if (listeners) {
  792. k = listeners.length;
  793. while (k--) {
  794. if (!listeners[k]) continue;
  795. t = listeners[k].apply(this, arguments);
  796. if (t === true) {
  797. return t;
  798. }
  799. if (t !== undefined) {
  800. r = t;
  801. }
  802. }
  803. }
  804. if (t = this['on' + ti.toLowerCase()]) {
  805. r = t.apply(this, arguments);
  806. }
  807. }
  808. return r;
  809. }
  810. };
  811. /**
  812. * 获得对象所拥有监听类型的所有监听器
  813. * @public
  814. * @function
  815. * @param {Object} obj 查询监听器的对象
  816. * @param {String} type 事件类型
  817. * @param {Boolean} force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  818. * @returns {Array} 监听器数组
  819. */
  820. function getListener(obj, type, force) {
  821. var allListeners;
  822. type = type.toLowerCase();
  823. return ((allListeners = (obj.__allListeners || force && (obj.__allListeners = {})))
  824. && (allListeners[type] || force && (allListeners[type] = [])));
  825. }
  826. ///import editor.js
  827. ///import core/dom/dom.js
  828. ///import core/utils.js
  829. /**
  830. * dtd html语义化的体现类
  831. * @constructor
  832. * @namespace dtd
  833. */
  834. var dtd = dom.dtd = (function () {
  835. function _(s) {
  836. for (var k in s) {
  837. s[k.toUpperCase()] = s[k];
  838. }
  839. return s;
  840. }
  841. var X = utils.extend2;
  842. var A = _({isindex: 1, fieldset: 1}),
  843. B = _({input: 1, button: 1, select: 1, textarea: 1, label: 1}),
  844. C = X(_({a: 1}), B),
  845. D = X({iframe: 1}, C),
  846. E = _({hr: 1, ul: 1, menu: 1, div: 1, blockquote: 1, noscript: 1, table: 1, center: 1, address: 1, dir: 1, pre: 1, h5: 1, dl: 1, h4: 1, noframes: 1, h6: 1, ol: 1, h1: 1, h3: 1, h2: 1}),
  847. F = _({ins: 1, del: 1, script: 1, style: 1}),
  848. G = X(_({b: 1, acronym: 1, bdo: 1, 'var': 1, '#': 1, abbr: 1, code: 1, br: 1, i: 1, cite: 1, kbd: 1, u: 1, strike: 1, s: 1, tt: 1, strong: 1, q: 1, samp: 1, em: 1, dfn: 1, span: 1}), F),
  849. H = X(_({sub: 1, img: 1, embed: 1, object: 1, sup: 1, basefont: 1, map: 1, applet: 1, font: 1, big: 1, small: 1}), G),
  850. I = X(_({p: 1}), H),
  851. J = X(_({iframe: 1}), H, B),
  852. K = _({
  853. img: 1,
  854. embed: 1,
  855. noscript: 1,
  856. br: 1,
  857. kbd: 1,
  858. center: 1,
  859. button: 1,
  860. basefont: 1,
  861. h5: 1,
  862. h4: 1,
  863. samp: 1,
  864. h6: 1,
  865. ol: 1,
  866. h1: 1,
  867. h3: 1,
  868. h2: 1,
  869. form: 1,
  870. font: 1,
  871. '#': 1,
  872. select: 1,
  873. menu: 1,
  874. ins: 1,
  875. abbr: 1,
  876. label: 1,
  877. code: 1,
  878. table: 1,
  879. script: 1,
  880. cite: 1,
  881. input: 1,
  882. iframe: 1,
  883. strong: 1,
  884. textarea: 1,
  885. noframes: 1,
  886. big: 1,
  887. small: 1,
  888. span: 1,
  889. hr: 1,
  890. sub: 1,
  891. bdo: 1,
  892. 'var': 1,
  893. div: 1,
  894. object: 1,
  895. sup: 1,
  896. strike: 1,
  897. dir: 1,
  898. map: 1,
  899. dl: 1,
  900. applet: 1,
  901. del: 1,
  902. isindex: 1,
  903. fieldset: 1,
  904. ul: 1,
  905. b: 1,
  906. acronym: 1,
  907. a: 1,
  908. blockquote: 1,
  909. i: 1,
  910. u: 1,
  911. s: 1,
  912. tt: 1,
  913. address: 1,
  914. q: 1,
  915. pre: 1,
  916. p: 1,
  917. em: 1,
  918. dfn: 1
  919. }),
  920. L = X(_({a: 0}), J),//a不能被切开,所以把他
  921. M = _({tr: 1}),
  922. N = _({'#': 1}),
  923. O = X(_({param: 1}), K),
  924. P = X(_({form: 1}), A, D, E, I),
  925. Q = _({li: 1, ol: 1, ul: 1}),
  926. R = _({style: 1, script: 1}),
  927. S = _({base: 1, link: 1, meta: 1, title: 1}),
  928. T = X(S, R),
  929. U = _({head: 1, body: 1}),
  930. V = _({html: 1});
  931. var block = _({address: 1, blockquote: 1, center: 1, dir: 1, div: 1, dl: 1, fieldset: 1, form: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, hr: 1, isindex: 1, menu: 1, noframes: 1, ol: 1, p: 1, pre: 1, table: 1, ul: 1}),
  932. empty = _({area: 1, base: 1, basefont: 1, br: 1, col: 1, command: 1, dialog: 1, embed: 1, hr: 1, img: 1, input: 1, isindex: 1, keygen: 1, link: 1, meta: 1, param: 1, source: 1, track: 1, wbr: 1});
  933. return _({
  934. // $ 表示自定的属性
  935. // body外的元素列表.
  936. $nonBodyContent: X(V, U, S),
  937. //块结构元素列表
  938. $block: block,
  939. //内联元素列表
  940. $inline: L,
  941. $inlineWithA: X(_({a: 1}), L),
  942. $body: X(_({script: 1, style: 1}), block),
  943. $cdata: _({script: 1, style: 1}),
  944. //自闭和元素
  945. $empty: empty,
  946. //不是自闭合,但不能让range选中里边
  947. $nonChild: _({iframe: 1, textarea: 1}),
  948. //列表元素列表
  949. $listItem: _({dd: 1, dt: 1, li: 1}),
  950. //列表根元素列表
  951. $list: _({ul: 1, ol: 1, dl: 1}),
  952. //不能认为是空的元素
  953. $isNotEmpty: _({table: 1, ul: 1, ol: 1, dl: 1, iframe: 1, area: 1, base: 1, col: 1, hr: 1, img: 1, embed: 1, input: 1, link: 1, meta: 1, param: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1}),
  954. //如果没有子节点就可以删除的元素列表,像span,a
  955. $removeEmpty: _({a: 1, abbr: 1, acronym: 1, address: 1, b: 1, bdo: 1, big: 1, cite: 1, code: 1, del: 1, dfn: 1, em: 1, font: 1, i: 1, ins: 1, label: 1, kbd: 1, q: 1, s: 1, samp: 1, small: 1, span: 1, strike: 1, strong: 1, sub: 1, sup: 1, tt: 1, u: 1, 'var': 1}),
  956. $removeEmptyBlock: _({'p': 1, 'div': 1}),
  957. //在table元素里的元素列表
  958. $tableContent: _({caption: 1, col: 1, colgroup: 1, tbody: 1, td: 1, tfoot: 1, th: 1, thead: 1, tr: 1, table: 1}),
  959. //不转换的标签
  960. $notTransContent: _({pre: 1, script: 1, style: 1, textarea: 1}),
  961. html: U,
  962. head: T,
  963. style: N,
  964. script: N,
  965. body: P,
  966. base: {},
  967. link: {},
  968. meta: {},
  969. title: N,
  970. col: {},
  971. tr: _({td: 1, th: 1}),
  972. img: {},
  973. embed: {},
  974. colgroup: _({thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1}),
  975. noscript: P,
  976. td: P,
  977. br: {},
  978. th: P,
  979. center: P,
  980. kbd: L,
  981. button: X(I, E),
  982. basefont: {},
  983. h5: L,
  984. h4: L,
  985. samp: L,
  986. h6: L,
  987. ol: Q,
  988. h1: L,
  989. h3: L,
  990. option: N,
  991. h2: L,
  992. form: X(A, D, E, I),
  993. select: _({optgroup: 1, option: 1}),
  994. font: L,
  995. ins: L,
  996. menu: Q,
  997. abbr: L,
  998. label: L,
  999. table: _({thead: 1, col: 1, tbody: 1, tr: 1, colgroup: 1, caption: 1, tfoot: 1}),
  1000. code: L,
  1001. tfoot: M,
  1002. cite: L,
  1003. li: P,
  1004. input: {},
  1005. iframe: P,
  1006. strong: L,
  1007. textarea: N,
  1008. noframes: P,
  1009. big: L,
  1010. small: L,
  1011. //trace:
  1012. span: _({'#': 1, br: 1, b: 1, strong: 1, u: 1, i: 1, em: 1, sub: 1, sup: 1, strike: 1, span: 1}),
  1013. hr: L,
  1014. dt: L,
  1015. sub: L,
  1016. optgroup: _({option: 1}),
  1017. param: {},
  1018. bdo: L,
  1019. 'var': L,
  1020. div: P,
  1021. object: O,
  1022. sup: L,
  1023. dd: P,
  1024. strike: L,
  1025. area: {},
  1026. dir: Q,
  1027. map: X(_({area: 1, form: 1, p: 1}), A, F, E),
  1028. applet: O,
  1029. dl: _({dt: 1, dd: 1}),
  1030. del: L,
  1031. isindex: {},
  1032. fieldset: X(_({legend: 1}), K),
  1033. thead: M,
  1034. ul: Q,
  1035. acronym: L,
  1036. b: L,
  1037. a: X(_({a: 1}), J),
  1038. blockquote: X(_({td: 1, tr: 1, tbody: 1, li: 1}), P),
  1039. caption: L,
  1040. i: L,
  1041. u: L,
  1042. tbody: M,
  1043. s: L,
  1044. address: X(D, I),
  1045. tt: L,
  1046. legend: L,
  1047. q: L,
  1048. pre: X(G, C),
  1049. p: X(_({'a': 1}), L),
  1050. em: L,
  1051. dfn: L
  1052. });
  1053. })();
  1054. /**
  1055. * @file
  1056. * @name UM.dom.domUtils
  1057. * @short DomUtils
  1058. * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js
  1059. * @desc UEditor封装的底层dom操作库
  1060. */
  1061. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  1062. var tmpNode = startFromChild && node[start],
  1063. parent;
  1064. !tmpNode && (tmpNode = node[ltr]);
  1065. while (!tmpNode && (parent = (parent || node).parentNode)) {
  1066. if (parent.tagName == 'BODY' || guard && !guard(parent)) {
  1067. return null;
  1068. }
  1069. tmpNode = parent[ltr];
  1070. }
  1071. if (tmpNode && fn && !fn(tmpNode)) {
  1072. return getDomNode(tmpNode, start, ltr, false, fn);
  1073. }
  1074. return tmpNode;
  1075. }
  1076. var attrFix = ie && browser.version < 9 ? {
  1077. tabindex: "tabIndex",
  1078. readonly: "readOnly",
  1079. "for": "htmlFor",
  1080. "class": "className",
  1081. maxlength: "maxLength",
  1082. cellspacing: "cellSpacing",
  1083. cellpadding: "cellPadding",
  1084. rowspan: "rowSpan",
  1085. colspan: "colSpan",
  1086. usemap: "useMap",
  1087. frameborder: "frameBorder"
  1088. } : {
  1089. tabindex: "tabIndex",
  1090. readonly: "readOnly"
  1091. },
  1092. styleBlock = utils.listToMap([
  1093. '-webkit-box', '-moz-box', 'block',
  1094. 'list-item', 'table', 'table-row-group',
  1095. 'table-header-group', 'table-footer-group',
  1096. 'table-row', 'table-column-group', 'table-column',
  1097. 'table-cell', 'table-caption'
  1098. ]);
  1099. var domUtils = dom.domUtils = {
  1100. //节点常量
  1101. NODE_ELEMENT: 1,
  1102. NODE_DOCUMENT: 9,
  1103. NODE_TEXT: 3,
  1104. NODE_COMMENT: 8,
  1105. NODE_DOCUMENT_FRAGMENT: 11,
  1106. //位置关系
  1107. POSITION_IDENTICAL: 0,
  1108. POSITION_DISCONNECTED: 1,
  1109. POSITION_FOLLOWING: 2,
  1110. POSITION_PRECEDING: 4,
  1111. POSITION_IS_CONTAINED: 8,
  1112. POSITION_CONTAINS: 16,
  1113. //ie6使用其他的会有一段空白出现
  1114. fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
  1115. //-------------------------Node部分--------------------------------
  1116. keys: {
  1117. /*Backspace*/ 8: 1, /*Delete*/ 46: 1,
  1118. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  1119. 37: 1, 38: 1, 39: 1, 40: 1,
  1120. 13: 1 /*enter*/
  1121. },
  1122. breakParent: function (node, parent) {
  1123. var tmpNode,
  1124. parentClone = node,
  1125. clone = node,
  1126. leftNodes,
  1127. rightNodes;
  1128. do {
  1129. parentClone = parentClone.parentNode;
  1130. if (leftNodes) {
  1131. tmpNode = parentClone.cloneNode(false);
  1132. tmpNode.appendChild(leftNodes);
  1133. leftNodes = tmpNode;
  1134. tmpNode = parentClone.cloneNode(false);
  1135. tmpNode.appendChild(rightNodes);
  1136. rightNodes = tmpNode;
  1137. } else {
  1138. leftNodes = parentClone.cloneNode(false);
  1139. rightNodes = leftNodes.cloneNode(false);
  1140. }
  1141. while (tmpNode = clone.previousSibling) {
  1142. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  1143. }
  1144. while (tmpNode = clone.nextSibling) {
  1145. rightNodes.appendChild(tmpNode);
  1146. }
  1147. clone = parentClone;
  1148. } while (parent !== parentClone);
  1149. tmpNode = parent.parentNode;
  1150. tmpNode.insertBefore(leftNodes, parent);
  1151. tmpNode.insertBefore(rightNodes, parent);
  1152. tmpNode.insertBefore(node, rightNodes);
  1153. domUtils.remove(parent);
  1154. return node;
  1155. },
  1156. trimWhiteTextNode: function (node) {
  1157. function remove(dir) {
  1158. var child;
  1159. while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
  1160. node.removeChild(child);
  1161. }
  1162. }
  1163. remove('firstChild');
  1164. remove('lastChild');
  1165. },
  1166. /**
  1167. * 获取节点A相对于节点B的位置关系
  1168. * @name getPosition
  1169. * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB) => Number
  1170. * @example
  1171. * switch (returnValue) {
  1172. * case 0: //相等,同一节点
  1173. * case 1: //无关,节点不相连
  1174. * case 2: //跟随,即节点A头部位于节点B头部的后面
  1175. * case 4: //前置,即节点A头部位于节点B头部的前面
  1176. * case 8: //被包含,即节点A被节点B包含
  1177. * case 10://组合类型,即节点A满足跟随节点B且被节点B包含。实际上,如果被包含,必定跟随,所以returnValue事实上不会存在8的情况。
  1178. * case 16://包含,即节点A包含节点B
  1179. * case 20://组合类型,即节点A满足前置节点A且包含节点B。同样,如果包含,必定前置,所以returnValue事实上也不会存在16的情况
  1180. * }
  1181. */
  1182. getPosition: function (nodeA, nodeB) {
  1183. // 如果两个节点是同一个节点
  1184. if (nodeA === nodeB) {
  1185. // domUtils.POSITION_IDENTICAL
  1186. return 0;
  1187. }
  1188. var node,
  1189. parentsA = [nodeA],
  1190. parentsB = [nodeB];
  1191. node = nodeA;
  1192. while (node = node.parentNode) {
  1193. // 如果nodeB是nodeA的祖先节点
  1194. if (node === nodeB) {
  1195. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  1196. return 10;
  1197. }
  1198. parentsA.push(node);
  1199. }
  1200. node = nodeB;
  1201. while (node = node.parentNode) {
  1202. // 如果nodeA是nodeB的祖先节点
  1203. if (node === nodeA) {
  1204. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  1205. return 20;
  1206. }
  1207. parentsB.push(node);
  1208. }
  1209. parentsA.reverse();
  1210. parentsB.reverse();
  1211. if (parentsA[0] !== parentsB[0]) {
  1212. // domUtils.POSITION_DISCONNECTED
  1213. return 1;
  1214. }
  1215. var i = -1;
  1216. while (i++, parentsA[i] === parentsB[i]) {
  1217. }
  1218. nodeA = parentsA[i];
  1219. nodeB = parentsB[i];
  1220. while (nodeA = nodeA.nextSibling) {
  1221. if (nodeA === nodeB) {
  1222. // domUtils.POSITION_PRECEDING
  1223. return 4
  1224. }
  1225. }
  1226. // domUtils.POSITION_FOLLOWING
  1227. return 2;
  1228. },
  1229. /**
  1230. * 返回节点node在父节点中的索引位置
  1231. * @name getNodeIndex
  1232. * @grammar UM.dom.domUtils.getNodeIndex(node) => Number //索引值从0开始
  1233. */
  1234. getNodeIndex: function (node, ignoreTextNode) {
  1235. var preNode = node,
  1236. i = 0;
  1237. while (preNode = preNode.previousSibling) {
  1238. if (ignoreTextNode && preNode.nodeType == 3) {
  1239. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  1240. i++;
  1241. }
  1242. continue;
  1243. }
  1244. i++;
  1245. }
  1246. return i;
  1247. },
  1248. /**
  1249. * 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含
  1250. * @name inDoc
  1251. * @grammar UM.dom.domUtils.inDoc(node,doc) => true|false
  1252. */
  1253. inDoc: function (node, doc) {
  1254. return domUtils.getPosition(node, doc) == 10;
  1255. },
  1256. /**
  1257. * 查找node节点的祖先节点
  1258. * @name findParent
  1259. * @grammar UM.dom.domUtils.findParent(node) => Element // 直接返回node节点的父节点
  1260. * @grammar UM.dom.domUtils.findParent(node,filterFn) => Element //filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回
  1261. * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf) => Element //includeSelf指定是否包含自身
  1262. */
  1263. findParent: function (node, filterFn, includeSelf) {
  1264. if (node && !domUtils.isBody(node)) {
  1265. node = includeSelf ? node : node.parentNode;
  1266. while (node) {
  1267. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  1268. return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
  1269. }
  1270. node = node.parentNode;
  1271. }
  1272. }
  1273. return null;
  1274. },
  1275. /**
  1276. * 通过tagName查找node节点的祖先节点
  1277. * @name findParentByTagName
  1278. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames) => Element //tagNames支持数组,区分大小写
  1279. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf) => Element //includeSelf指定是否包含自身
  1280. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn) => Element //excludeFn指定例外过滤条件,返回true时忽略该节点
  1281. */
  1282. findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
  1283. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  1284. return domUtils.findParent(node, function (node) {
  1285. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  1286. }, includeSelf);
  1287. },
  1288. /**
  1289. * 查找节点node的祖先节点集合
  1290. * @name findParents
  1291. * @grammar UM.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  1292. * @grammar UM.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  1293. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  1294. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  1295. */
  1296. findParents: function (node, includeSelf, filterFn, closerFirst) {
  1297. var parents = includeSelf && (filterFn && filterFn(node) || !filterFn) ? [node] : [];
  1298. while (node = domUtils.findParent(node, filterFn)) {
  1299. parents.push(node);
  1300. }
  1301. return closerFirst ? parents : parents.reverse();
  1302. },
  1303. /**
  1304. * 在节点node后面插入新节点newNode
  1305. * @name insertAfter
  1306. * @grammar UM.dom.domUtils.insertAfter(node,newNode) => newNode
  1307. */
  1308. insertAfter: function (node, newNode) {
  1309. return node.parentNode.insertBefore(newNode, node.nextSibling);
  1310. },
  1311. /**
  1312. * 删除节点node,并根据keepChildren指定是否保留子节点
  1313. * @name remove
  1314. * @grammar UM.dom.domUtils.remove(node) => node
  1315. * @grammar UM.dom.domUtils.remove(node,keepChildren) => node
  1316. */
  1317. remove: function (node, keepChildren) {
  1318. var parent = node.parentNode,
  1319. child;
  1320. if (parent) {
  1321. if (keepChildren && node.hasChildNodes()) {
  1322. while (child = node.firstChild) {
  1323. parent.insertBefore(child, node);
  1324. }
  1325. }
  1326. parent.removeChild(node);
  1327. }
  1328. return node;
  1329. },
  1330. /**
  1331. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  1332. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  1333. * @method getNextDomNode
  1334. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1335. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1336. * @example
  1337. * ```html
  1338. * <body>
  1339. * <div id="test">
  1340. * <span></span>
  1341. * </div>
  1342. * <i>xxx</i>
  1343. * </body>
  1344. * <script>
  1345. *
  1346. * //output: i节点
  1347. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1348. *
  1349. * </script>
  1350. * ```
  1351. * @example
  1352. * ```html
  1353. * <body>
  1354. * <div>
  1355. * <span></span>
  1356. * <i id="test">xxx</i>
  1357. * </div>
  1358. * <b>xxx</b>
  1359. * </body>
  1360. * <script>
  1361. *
  1362. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  1363. * //output: b节点
  1364. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1365. *
  1366. * </script>
  1367. * ```
  1368. */
  1369. /**
  1370. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  1371. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  1372. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  1373. * @method getNextDomNode
  1374. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1375. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  1376. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1377. * @see UE.dom.domUtils.getNextDomNode(Node)
  1378. */
  1379. getNextDomNode: function (node, startFromChild, filterFn, guard) {
  1380. return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
  1381. },
  1382. getPreDomNode: function (node, startFromChild, filterFn, guard) {
  1383. return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
  1384. },
  1385. /**
  1386. * 检测节点node是否属于bookmark节点
  1387. * @name isBookmarkNode
  1388. * @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false
  1389. */
  1390. isBookmarkNode: function (node) {
  1391. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  1392. },
  1393. /**
  1394. * 获取节点node所在的window对象
  1395. * @name getWindow
  1396. * @grammar UM.dom.domUtils.getWindow(node) => window对象
  1397. */
  1398. getWindow: function (node) {
  1399. var doc = node.ownerDocument || node;
  1400. return doc.defaultView || doc.parentWindow;
  1401. },
  1402. /**
  1403. * 获取离nodeA与nodeB最近的公共的祖先节点
  1404. * @method getCommonAncestor
  1405. * @param { Node } nodeA 第一个节点
  1406. * @param { Node } nodeB 第二个节点
  1407. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  1408. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  1409. * @example
  1410. * ```javascript
  1411. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  1412. * //output: true
  1413. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  1414. * ```
  1415. */
  1416. getCommonAncestor: function (nodeA, nodeB) {
  1417. if (nodeA === nodeB)
  1418. return nodeA;
  1419. var parentsA = [nodeA], parentsB = [nodeB], parent = nodeA, i = -1;
  1420. while (parent = parent.parentNode) {
  1421. if (parent === nodeB) {
  1422. return parent;
  1423. }
  1424. parentsA.push(parent);
  1425. }
  1426. parent = nodeB;
  1427. while (parent = parent.parentNode) {
  1428. if (parent === nodeA)
  1429. return parent;
  1430. parentsB.push(parent);
  1431. }
  1432. parentsA.reverse();
  1433. parentsB.reverse();
  1434. while (i++, parentsA[i] === parentsB[i]) {
  1435. }
  1436. return i == 0 ? null : parentsA[i - 1];
  1437. },
  1438. /**
  1439. * 清除node节点左右连续为空的兄弟inline节点
  1440. * @method clearEmptySibling
  1441. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1442. * 则这些兄弟节点将被删除
  1443. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  1444. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  1445. * @example
  1446. * ```html
  1447. * <body>
  1448. * <div></div>
  1449. * <span id="test"></span>
  1450. * <i></i>
  1451. * <b></b>
  1452. * <em>xxx</em>
  1453. * <span></span>
  1454. * </body>
  1455. * <script>
  1456. *
  1457. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  1458. *
  1459. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  1460. * console.log( document.body.innerHTML );
  1461. *
  1462. * </script>
  1463. * ```
  1464. */
  1465. /**
  1466. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1467. * 则忽略对右边兄弟节点的操作。
  1468. * @method clearEmptySibling
  1469. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1470. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1471. * 则这些兄弟节点将被删除
  1472. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1473. */
  1474. /**
  1475. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1476. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  1477. * @method clearEmptySibling
  1478. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1479. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1480. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  1481. * 则这些兄弟节点将被删除
  1482. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1483. */
  1484. clearEmptySibling: function (node, ignoreNext, ignorePre) {
  1485. function clear(next, dir) {
  1486. var tmpNode;
  1487. while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
  1488. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  1489. || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue))) {
  1490. tmpNode = next[dir];
  1491. domUtils.remove(next);
  1492. next = tmpNode;
  1493. }
  1494. }
  1495. !ignoreNext && clear(node.nextSibling, 'nextSibling');
  1496. !ignorePre && clear(node.previousSibling, 'previousSibling');
  1497. },
  1498. /**
  1499. * 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
  1500. * @name split
  1501. * @grammar UM.dom.domUtils.split(node,offset) => TextNode //返回从切分位置开始的后一个文本节点
  1502. */
  1503. split: function (node, offset) {
  1504. var doc = node.ownerDocument;
  1505. if (browser.ie && offset == node.nodeValue.length) {
  1506. var next = doc.createTextNode('');
  1507. return domUtils.insertAfter(node, next);
  1508. }
  1509. var retval = node.splitText(offset);
  1510. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  1511. if (browser.ie8) {
  1512. var tmpNode = doc.createTextNode('');
  1513. domUtils.insertAfter(retval, tmpNode);
  1514. domUtils.remove(tmpNode);
  1515. }
  1516. return retval;
  1517. },
  1518. /**
  1519. * 检测节点node是否为空节点(包括空格、换行、占位符等字符)
  1520. * @name isWhitespace
  1521. * @grammar UM.dom.domUtils.isWhitespace(node) => true|false
  1522. */
  1523. isWhitespace: function (node) {
  1524. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
  1525. },
  1526. /**
  1527. * 获取元素element相对于viewport的位置坐标
  1528. * @name getXY
  1529. * @grammar UM.dom.domUtils.getXY(element) => Object //返回坐标对象{x:left,y:top}
  1530. */
  1531. getXY: function (element) {
  1532. var x = 0, y = 0;
  1533. while (element.offsetParent) {
  1534. y += element.offsetTop;
  1535. x += element.offsetLeft;
  1536. element = element.offsetParent;
  1537. }
  1538. return {'x': x, 'y': y};
  1539. },
  1540. /**
  1541. * 检查节点node是否是空inline节点
  1542. * @name isEmptyInlineElement
  1543. * @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0
  1544. * @example
  1545. * <b><i></i></b> => 1
  1546. * <b><i></i><u></u></b> => 1
  1547. * <b></b> => 1
  1548. * <b>xx<i></i></b> => 0
  1549. */
  1550. isEmptyInlineElement: function (node) {
  1551. if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
  1552. return 0;
  1553. }
  1554. node = node.firstChild;
  1555. while (node) {
  1556. //如果是创建的bookmark就跳过
  1557. if (domUtils.isBookmarkNode(node)) {
  1558. return 0;
  1559. }
  1560. if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
  1561. node.nodeType == 3 && !domUtils.isWhitespace(node)
  1562. ) {
  1563. return 0;
  1564. }
  1565. node = node.nextSibling;
  1566. }
  1567. return 1;
  1568. },
  1569. /**
  1570. * 检查节点node是否为块元素
  1571. * @name isBlockElm
  1572. * @grammar UM.dom.domUtils.isBlockElm(node) => true|false
  1573. */
  1574. isBlockElm: function (node) {
  1575. return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
  1576. },
  1577. /**
  1578. * 原生方法getElementsByTagName的封装
  1579. * @name getElementsByTagName
  1580. * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组
  1581. */
  1582. getElementsByTagName: function (node, name, filter) {
  1583. if (filter && utils.isString(filter)) {
  1584. var className = filter;
  1585. filter = function (node) {
  1586. var result = false;
  1587. $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
  1588. if ($(node).hasClass(v)) {
  1589. result = true;
  1590. return false;
  1591. }
  1592. })
  1593. return result;
  1594. }
  1595. }
  1596. name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
  1597. var arr = [];
  1598. for (var n = 0, ni; ni = name[n++];) {
  1599. var list = node.getElementsByTagName(ni);
  1600. for (var i = 0, ci; ci = list[i++];) {
  1601. if (!filter || filter(ci))
  1602. arr.push(ci);
  1603. }
  1604. }
  1605. return arr;
  1606. },
  1607. /**
  1608. * 设置节点node及其子节点不会被选中
  1609. * @name unSelectable
  1610. * @grammar UM.dom.domUtils.unSelectable(node)
  1611. */
  1612. unSelectable: ie && browser.ie9below || browser.opera ? function (node) {
  1613. //for ie9
  1614. node.onselectstart = function () {
  1615. return false;
  1616. };
  1617. node.onclick = node.onkeyup = node.onkeydown = function () {
  1618. return false;
  1619. };
  1620. node.unselectable = 'on';
  1621. node.setAttribute("unselectable", "on");
  1622. for (var i = 0, ci; ci = node.all[i++];) {
  1623. switch (ci.tagName.toLowerCase()) {
  1624. case 'iframe' :
  1625. case 'textarea' :
  1626. case 'input' :
  1627. case 'select' :
  1628. break;
  1629. default :
  1630. ci.unselectable = 'on';
  1631. node.setAttribute("unselectable", "on");
  1632. }
  1633. }
  1634. } : function (node) {
  1635. node.style.MozUserSelect =
  1636. node.style.webkitUserSelect =
  1637. node.style.msUserSelect =
  1638. node.style.KhtmlUserSelect = 'none';
  1639. },
  1640. /**
  1641. * 删除节点node上的属性attrNames,attrNames为属性名称数组
  1642. * @name removeAttributes
  1643. * @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
  1644. * @example
  1645. * //Before remove
  1646. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  1647. * //Remove
  1648. * UM.dom.domUtils.removeAttributes(node,["id","name"]);
  1649. * //After remove
  1650. * <span style="font-size:14px;">xxxxx</span>
  1651. */
  1652. removeAttributes: function (node, attrNames) {
  1653. attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
  1654. for (var i = 0, ci; ci = attrNames[i++];) {
  1655. ci = attrFix[ci] || ci;
  1656. switch (ci) {
  1657. case 'className':
  1658. node[ci] = '';
  1659. break;
  1660. case 'style':
  1661. node.style.cssText = '';
  1662. !browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))
  1663. }
  1664. node.removeAttribute(ci);
  1665. }
  1666. },
  1667. /**
  1668. * 在doc下创建一个标签名为tag,属性为attrs的元素
  1669. * @name createElement
  1670. * @grammar UM.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点
  1671. */
  1672. createElement: function (doc, tag, attrs) {
  1673. return domUtils.setAttributes(doc.createElement(tag), attrs)
  1674. },
  1675. /**
  1676. * 为节点node添加属性attrs,attrs为属性键值对
  1677. * @name setAttributes
  1678. * @grammar UM.dom.domUtils.setAttributes(node,attrs) => node
  1679. */
  1680. setAttributes: function (node, attrs) {
  1681. for (var attr in attrs) {
  1682. if (attrs.hasOwnProperty(attr)) {
  1683. var value = attrs[attr];
  1684. switch (attr) {
  1685. case 'class':
  1686. //ie下要这样赋值,setAttribute不起作用
  1687. node.className = value;
  1688. break;
  1689. case 'style' :
  1690. node.style.cssText = node.style.cssText + ";" + value;
  1691. break;
  1692. case 'innerHTML':
  1693. node[attr] = value;
  1694. break;
  1695. case 'value':
  1696. node.value = value;
  1697. break;
  1698. default:
  1699. node.setAttribute(attrFix[attr] || attr, value);
  1700. }
  1701. }
  1702. }
  1703. return node;
  1704. },
  1705. /**
  1706. * 获取元素element的计算样式
  1707. * @name getComputedStyle
  1708. * @grammar UM.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值
  1709. * @example
  1710. * getComputedStyle(document.body,"font-size") => "15px"
  1711. * getComputedStyle(form,"color") => "#ffccdd"
  1712. */
  1713. getComputedStyle: function (element, styleName) {
  1714. return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));
  1715. },
  1716. /**
  1717. * 阻止事件默认行为
  1718. * @param {Event} evt 需要组织的事件对象
  1719. */
  1720. preventDefault: function (evt) {
  1721. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  1722. },
  1723. /**
  1724. * 删除元素element指定的样式
  1725. * @method removeStyle
  1726. * @param { Element } element 需要删除样式的元素
  1727. * @param { String } styleName 需要删除的样式名
  1728. * @example
  1729. * ```html
  1730. * <span id="test" style="color: red; background: blue;"></span>
  1731. *
  1732. * <script>
  1733. *
  1734. * var testNode = document.getElementById("test");
  1735. *
  1736. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  1737. *
  1738. * //output: background: blue;
  1739. * console.log( testNode.style.cssText );
  1740. *
  1741. * </script>
  1742. * ```
  1743. */
  1744. removeStyle: function (element, name) {
  1745. if (browser.ie) {
  1746. //针对color先单独处理一下
  1747. if (name == 'color') {
  1748. name = '(^|;)' + name;
  1749. }
  1750. element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?', 'ig'), '')
  1751. } else {
  1752. if (element.style.removeProperty) {
  1753. element.style.removeProperty(name);
  1754. } else {
  1755. element.style.removeAttribute(utils.cssStyleToDomStyle(name));
  1756. }
  1757. }
  1758. if (!element.style.cssText) {
  1759. domUtils.removeAttributes(element, ['style']);
  1760. }
  1761. },
  1762. /**
  1763. * 获取元素element的某个样式值
  1764. * @name getStyle
  1765. * @grammar UM.dom.domUtils.getStyle(element,name) => String
  1766. */
  1767. getStyle: function (element, name) {
  1768. var value = element.style[utils.cssStyleToDomStyle(name)];
  1769. return utils.fixColor(name, value);
  1770. },
  1771. /**
  1772. * 为元素element设置样式属性值
  1773. * @name setStyle
  1774. * @grammar UM.dom.domUtils.setStyle(element,name,value)
  1775. */
  1776. setStyle: function (element, name, value) {
  1777. element.style[utils.cssStyleToDomStyle(name)] = value;
  1778. if (!utils.trim(element.style.cssText)) {
  1779. this.removeAttributes(element, 'style')
  1780. }
  1781. },
  1782. /**
  1783. * 删除_moz_dirty属性
  1784. * @function
  1785. */
  1786. removeDirtyAttr: function (node) {
  1787. for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
  1788. ci.removeAttribute('_moz_dirty');
  1789. }
  1790. node.removeAttribute('_moz_dirty');
  1791. },
  1792. /**
  1793. * 返回子节点的数量
  1794. * @function
  1795. * @param {Node} node 父节点
  1796. * @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量
  1797. * @return {Number} 符合条件子节点的数量
  1798. */
  1799. getChildCount: function (node, fn) {
  1800. var count = 0, first = node.firstChild;
  1801. fn = fn || function () {
  1802. return 1;
  1803. };
  1804. while (first) {
  1805. if (fn(first)) {
  1806. count++;
  1807. }
  1808. first = first.nextSibling;
  1809. }
  1810. return count;
  1811. },
  1812. /**
  1813. * 判断是否为空节点
  1814. * @function
  1815. * @param {Node} node 节点
  1816. * @return {Boolean} 是否为空节点
  1817. */
  1818. isEmptyNode: function (node) {
  1819. return !node.firstChild || domUtils.getChildCount(node, function (node) {
  1820. return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
  1821. }) == 0
  1822. },
  1823. /**
  1824. * 判断节点是否为br
  1825. * @function
  1826. * @param {Node} node 节点
  1827. */
  1828. isBr: function (node) {
  1829. return node.nodeType == 1 && node.tagName == 'BR';
  1830. },
  1831. isEmptyBlock: function (node, reg) {
  1832. if (node.nodeType != 1)
  1833. return 0;
  1834. reg = reg || new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g');
  1835. if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
  1836. return 0;
  1837. }
  1838. for (var n in dtd.$isNotEmpty) {
  1839. if (node.getElementsByTagName(n).length) {
  1840. return 0;
  1841. }
  1842. }
  1843. return 1;
  1844. },
  1845. //判断是否是编辑器自定义的参数
  1846. isCustomeNode: function (node) {
  1847. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
  1848. },
  1849. fillNode: function (doc, node) {
  1850. var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
  1851. node.innerHTML = '';
  1852. node.appendChild(tmpNode);
  1853. },
  1854. isBoundaryNode: function (node, dir) {
  1855. var tmp;
  1856. while (!domUtils.isBody(node)) {
  1857. tmp = node;
  1858. node = node.parentNode;
  1859. if (tmp !== node[dir]) {
  1860. return false;
  1861. }
  1862. }
  1863. return true;
  1864. },
  1865. isFillChar: function (node, isInStart) {
  1866. return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '') + domUtils.fillChar), '').length
  1867. },
  1868. isBody: function (node) {
  1869. return $(node).hasClass('edui-body-container');
  1870. }
  1871. };
  1872. var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  1873. ///import editor.js
  1874. ///import core/utils.js
  1875. ///import core/browser.js
  1876. ///import core/dom/dom.js
  1877. ///import core/dom/dtd.js
  1878. ///import core/dom/domUtils.js
  1879. /**
  1880. * @file
  1881. * @name UM.dom.Range
  1882. * @anthor zhanyi
  1883. * @short Range
  1884. * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
  1885. * @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性
  1886. */
  1887. (function () {
  1888. var guid = 0,
  1889. fillChar = domUtils.fillChar,
  1890. fillData;
  1891. /**
  1892. * 更新range的collapse状态
  1893. * @param {Range} range range对象
  1894. */
  1895. function updateCollapse(range) {
  1896. range.collapsed =
  1897. range.startContainer && range.endContainer &&
  1898. range.startContainer === range.endContainer &&
  1899. range.startOffset == range.endOffset;
  1900. }
  1901. function selectOneNode(rng) {
  1902. return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
  1903. }
  1904. function setEndPoint(toStart, node, offset, range) {
  1905. //如果node是自闭合标签要处理
  1906. if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
  1907. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
  1908. node = node.parentNode;
  1909. }
  1910. if (toStart) {
  1911. range.startContainer = node;
  1912. range.startOffset = offset;
  1913. if (!range.endContainer) {
  1914. range.collapse(true);
  1915. }
  1916. } else {
  1917. range.endContainer = node;
  1918. range.endOffset = offset;
  1919. if (!range.startContainer) {
  1920. range.collapse(false);
  1921. }
  1922. }
  1923. updateCollapse(range);
  1924. return range;
  1925. }
  1926. /**
  1927. * @name Range
  1928. * @grammar new UM.dom.Range(document) => Range 实例
  1929. * @desc 创建一个跟document绑定的空的Range实例
  1930. * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
  1931. * - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1932. * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
  1933. * - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1934. * - ***document*** 跟range关联的document对象
  1935. * - ***collapsed*** 是否是闭合状态
  1936. */
  1937. var Range = dom.Range = function (document, body) {
  1938. var me = this;
  1939. me.startContainer =
  1940. me.startOffset =
  1941. me.endContainer =
  1942. me.endOffset = null;
  1943. me.document = document;
  1944. me.collapsed = true;
  1945. me.body = body;
  1946. };
  1947. /**
  1948. * 删除fillData
  1949. * @param doc
  1950. * @param excludeNode
  1951. */
  1952. function removeFillData(doc, excludeNode) {
  1953. try {
  1954. if (fillData && domUtils.inDoc(fillData, doc)) {
  1955. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  1956. var tmpNode = fillData.parentNode;
  1957. domUtils.remove(fillData);
  1958. while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
  1959. //safari的contains有bug
  1960. (browser.safari ? !(domUtils.getPosition(tmpNode, excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
  1961. ) {
  1962. fillData = tmpNode.parentNode;
  1963. domUtils.remove(tmpNode);
  1964. tmpNode = fillData;
  1965. }
  1966. } else {
  1967. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
  1968. }
  1969. }
  1970. } catch (e) {
  1971. }
  1972. }
  1973. /**
  1974. *
  1975. * @param node
  1976. * @param dir
  1977. */
  1978. function mergeSibling(node, dir) {
  1979. var tmpNode;
  1980. node = node[dir];
  1981. while (node && domUtils.isFillChar(node)) {
  1982. tmpNode = node[dir];
  1983. domUtils.remove(node);
  1984. node = tmpNode;
  1985. }
  1986. }
  1987. function execContentsAction(range, action) {
  1988. //调整边界
  1989. //range.includeBookmark();
  1990. var start = range.startContainer,
  1991. end = range.endContainer,
  1992. startOffset = range.startOffset,
  1993. endOffset = range.endOffset,
  1994. doc = range.document,
  1995. frag = doc.createDocumentFragment(),
  1996. tmpStart, tmpEnd;
  1997. if (start.nodeType == 1) {
  1998. start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
  1999. }
  2000. if (end.nodeType == 1) {
  2001. end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
  2002. }
  2003. if (start === end && start.nodeType == 3) {
  2004. frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
  2005. //is not clone
  2006. if (action) {
  2007. start.deleteData(startOffset, endOffset - startOffset);
  2008. range.collapse(true);
  2009. }
  2010. return frag;
  2011. }
  2012. var current, currentLevel, clone = frag,
  2013. startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
  2014. for (var i = 0; startParents[i] == endParents[i];) {
  2015. i++;
  2016. }
  2017. for (var j = i, si; si = startParents[j]; j++) {
  2018. current = si.nextSibling;
  2019. if (si == start) {
  2020. if (!tmpStart) {
  2021. if (range.startContainer.nodeType == 3) {
  2022. clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
  2023. //is not clone
  2024. if (action) {
  2025. start.deleteData(startOffset, start.nodeValue.length - startOffset);
  2026. }
  2027. } else {
  2028. clone.appendChild(!action ? start.cloneNode(true) : start);
  2029. }
  2030. }
  2031. } else {
  2032. currentLevel = si.cloneNode(false);
  2033. clone.appendChild(currentLevel);
  2034. }
  2035. while (current) {
  2036. if (current === end || current === endParents[j]) {
  2037. break;
  2038. }
  2039. si = current.nextSibling;
  2040. clone.appendChild(!action ? current.cloneNode(true) : current);
  2041. current = si;
  2042. }
  2043. clone = currentLevel;
  2044. }
  2045. clone = frag;
  2046. if (!startParents[i]) {
  2047. clone.appendChild(startParents[i - 1].cloneNode(false));
  2048. clone = clone.firstChild;
  2049. }
  2050. for (var j = i, ei; ei = endParents[j]; j++) {
  2051. current = ei.previousSibling;
  2052. if (ei == end) {
  2053. if (!tmpEnd && range.endContainer.nodeType == 3) {
  2054. clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
  2055. //is not clone
  2056. if (action) {
  2057. end.deleteData(0, endOffset);
  2058. }
  2059. }
  2060. } else {
  2061. currentLevel = ei.cloneNode(false);
  2062. clone.appendChild(currentLevel);
  2063. }
  2064. //如果两端同级,右边第一次已经被开始做了
  2065. if (j != i || !startParents[i]) {
  2066. while (current) {
  2067. if (current === start) {
  2068. break;
  2069. }
  2070. ei = current.previousSibling;
  2071. clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
  2072. current = ei;
  2073. }
  2074. }
  2075. clone = currentLevel;
  2076. }
  2077. if (action) {
  2078. range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
  2079. }
  2080. tmpStart && domUtils.remove(tmpStart);
  2081. tmpEnd && domUtils.remove(tmpEnd);
  2082. return frag;
  2083. }
  2084. Range.prototype = {
  2085. /**
  2086. * @name deleteContents
  2087. * @grammar range.deleteContents() => Range
  2088. * @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态
  2089. * @example
  2090. * DOM Element :
  2091. * <b>x<i>x[x<i>xx]x</b>
  2092. * //执行方法后
  2093. * <b>x<i>x<i>|x</b>
  2094. * 注意range改变了
  2095. * range.startContainer => b
  2096. * range.startOffset => 2
  2097. * range.endContainer => b
  2098. * range.endOffset => 2
  2099. * range.collapsed => true
  2100. */
  2101. deleteContents: function () {
  2102. var txt;
  2103. if (!this.collapsed) {
  2104. execContentsAction(this, 1);
  2105. }
  2106. if (browser.webkit) {
  2107. txt = this.startContainer;
  2108. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  2109. this.setStartBefore(txt).collapse(true);
  2110. domUtils.remove(txt);
  2111. }
  2112. }
  2113. return this;
  2114. },
  2115. inFillChar: function () {
  2116. var start = this.startContainer;
  2117. if (this.collapsed && start.nodeType == 3
  2118. && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '').length + 1 == start.nodeValue.length
  2119. ) {
  2120. return true;
  2121. }
  2122. return false;
  2123. },
  2124. /**
  2125. * @name setStart
  2126. * @grammar range.setStart(node,offset) => Range
  2127. * @desc 设置range的开始位置位于node节点内,偏移量为offset
  2128. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2129. */
  2130. setStart: function (node, offset) {
  2131. return setEndPoint(true, node, offset, this);
  2132. },
  2133. /**
  2134. * 设置range的结束位置位于node节点,偏移量为offset
  2135. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2136. * @name setEnd
  2137. * @grammar range.setEnd(node,offset) => Range
  2138. */
  2139. setEnd: function (node, offset) {
  2140. return setEndPoint(false, node, offset, this);
  2141. },
  2142. /**
  2143. * 将Range开始位置设置到node节点之后
  2144. * @name setStartAfter
  2145. * @grammar range.setStartAfter(node) => Range
  2146. * @example
  2147. * <b>xx<i>x|x</i>x</b>
  2148. * 执行setStartAfter(i)后
  2149. * range.startContainer =>b
  2150. * range.startOffset =>2
  2151. */
  2152. setStartAfter: function (node) {
  2153. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2154. },
  2155. /**
  2156. * 将Range开始位置设置到node节点之前
  2157. * @name setStartBefore
  2158. * @grammar range.setStartBefore(node) => Range
  2159. * @example
  2160. * <b>xx<i>x|x</i>x</b>
  2161. * 执行setStartBefore(i)后
  2162. * range.startContainer =>b
  2163. * range.startOffset =>1
  2164. */
  2165. setStartBefore: function (node) {
  2166. return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
  2167. },
  2168. /**
  2169. * 将Range结束位置设置到node节点之后
  2170. * @name setEndAfter
  2171. * @grammar range.setEndAfter(node) => Range
  2172. * @example
  2173. * <b>xx<i>x|x</i>x</b>
  2174. * setEndAfter(i)后
  2175. * range.endContainer =>b
  2176. * range.endtOffset =>2
  2177. */
  2178. setEndAfter: function (node) {
  2179. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2180. },
  2181. /**
  2182. * 将Range结束位置设置到node节点之前
  2183. * @name setEndBefore
  2184. * @grammar range.setEndBefore(node) => Range
  2185. * @example
  2186. * <b>xx<i>x|x</i>x</b>
  2187. * 执行setEndBefore(i)后
  2188. * range.endContainer =>b
  2189. * range.endtOffset =>1
  2190. */
  2191. setEndBefore: function (node) {
  2192. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
  2193. },
  2194. /**
  2195. * 将Range开始位置设置到node节点内的开始位置
  2196. * @name setStartAtFirst
  2197. * @grammar range.setStartAtFirst(node) => Range
  2198. */
  2199. setStartAtFirst: function (node) {
  2200. return this.setStart(node, 0);
  2201. },
  2202. /**
  2203. * 将Range开始位置设置到node节点内的结束位置
  2204. * @name setStartAtLast
  2205. * @grammar range.setStartAtLast(node) => Range
  2206. */
  2207. setStartAtLast: function (node) {
  2208. return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2209. },
  2210. /**
  2211. * 将Range结束位置设置到node节点内的开始位置
  2212. * @name setEndAtFirst
  2213. * @grammar range.setEndAtFirst(node) => Range
  2214. */
  2215. setEndAtFirst: function (node) {
  2216. return this.setEnd(node, 0);
  2217. },
  2218. /**
  2219. * 将Range结束位置设置到node节点内的结束位置
  2220. * @name setEndAtLast
  2221. * @grammar range.setEndAtLast(node) => Range
  2222. */
  2223. setEndAtLast: function (node) {
  2224. return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2225. },
  2226. /**
  2227. * 选中完整的指定节点,并返回包含该节点的range
  2228. * @name selectNode
  2229. * @grammar range.selectNode(node) => Range
  2230. */
  2231. selectNode: function (node) {
  2232. return this.setStartBefore(node).setEndAfter(node);
  2233. },
  2234. /**
  2235. * 选中node内部的所有节点,并返回对应的range
  2236. * @name selectNodeContents
  2237. * @grammar range.selectNodeContents(node) => Range
  2238. * @example
  2239. * <b>xx[x<i>xxx</i>]xxx</b>
  2240. * 执行后
  2241. * <b>[xxx<i>xxx</i>xxx]</b>
  2242. * range.startContainer =>b
  2243. * range.startOffset =>0
  2244. * range.endContainer =>b
  2245. * range.endOffset =>3
  2246. */
  2247. selectNodeContents: function (node) {
  2248. return this.setStart(node, 0).setEndAtLast(node);
  2249. },
  2250. /**
  2251. * 克隆一个新的range对象
  2252. * @name cloneRange
  2253. * @grammar range.cloneRange() => Range
  2254. */
  2255. cloneRange: function () {
  2256. var me = this;
  2257. return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  2258. },
  2259. /**
  2260. * 让选区闭合到尾部,若toStart为真,则闭合到头部
  2261. * @name collapse
  2262. * @grammar range.collapse() => Range
  2263. * @grammar range.collapse(true) => Range //闭合选区到头部
  2264. */
  2265. collapse: function (toStart) {
  2266. var me = this;
  2267. if (toStart) {
  2268. me.endContainer = me.startContainer;
  2269. me.endOffset = me.startOffset;
  2270. } else {
  2271. me.startContainer = me.endContainer;
  2272. me.startOffset = me.endOffset;
  2273. }
  2274. me.collapsed = true;
  2275. return me;
  2276. },
  2277. /**
  2278. * 调整range的边界,使其"收缩"到最小的位置
  2279. * @name shrinkBoundary
  2280. * @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>
  2281. * @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
  2282. * @example
  2283. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2284. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>
  2285. * [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>
  2286. */
  2287. shrinkBoundary: function (ignoreEnd) {
  2288. var me = this, child,
  2289. collapsed = me.collapsed;
  2290. function check(node) {
  2291. return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
  2292. }
  2293. while (me.startContainer.nodeType == 1 //是element
  2294. && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
  2295. && check(child)) {
  2296. me.setStart(child, 0);
  2297. }
  2298. if (collapsed) {
  2299. return me.collapse(true);
  2300. }
  2301. if (!ignoreEnd) {
  2302. while (me.endContainer.nodeType == 1//是element
  2303. && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  2304. && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
  2305. && check(child)) {
  2306. me.setEnd(child, child.childNodes.length);
  2307. }
  2308. }
  2309. return me;
  2310. },
  2311. /**
  2312. * 调整边界容器,如果是textNode,就调整到elementNode上
  2313. * @name trimBoundary
  2314. * @grammar range.trimBoundary([ignoreEnd]) => Range //true忽略结束边界
  2315. * @example
  2316. * DOM Element :
  2317. * <b>|xxx</b>
  2318. * startContainer = xxx; startOffset = 0
  2319. * //执行后本方法后
  2320. * startContainer = <b>; startOffset = 0
  2321. * @example
  2322. * Dom Element :
  2323. * <b>xx|x</b>
  2324. * startContainer = xxx; startOffset = 2
  2325. * //执行本方法后,xxx被实实在在地切分成两个TextNode
  2326. * startContainer = <b>; startOffset = 1
  2327. */
  2328. trimBoundary: function (ignoreEnd) {
  2329. this.txtToElmBoundary();
  2330. var start = this.startContainer,
  2331. offset = this.startOffset,
  2332. collapsed = this.collapsed,
  2333. end = this.endContainer;
  2334. if (start.nodeType == 3) {
  2335. if (offset == 0) {
  2336. this.setStartBefore(start);
  2337. } else {
  2338. if (offset >= start.nodeValue.length) {
  2339. this.setStartAfter(start);
  2340. } else {
  2341. var textNode = domUtils.split(start, offset);
  2342. //跟新结束边界
  2343. if (start === end) {
  2344. this.setEnd(textNode, this.endOffset - offset);
  2345. } else if (start.parentNode === end) {
  2346. this.endOffset += 1;
  2347. }
  2348. this.setStartBefore(textNode);
  2349. }
  2350. }
  2351. if (collapsed) {
  2352. return this.collapse(true);
  2353. }
  2354. }
  2355. if (!ignoreEnd) {
  2356. offset = this.endOffset;
  2357. end = this.endContainer;
  2358. if (end.nodeType == 3) {
  2359. if (offset == 0) {
  2360. this.setEndBefore(end);
  2361. } else {
  2362. offset < end.nodeValue.length && domUtils.split(end, offset);
  2363. this.setEndAfter(end);
  2364. }
  2365. }
  2366. }
  2367. return this;
  2368. },
  2369. /**
  2370. * 如果选区在文本的边界上,就扩展选区到文本的父节点上
  2371. * @name txtToElmBoundary
  2372. * @example
  2373. * Dom Element :
  2374. * <b> |xxx</b>
  2375. * startContainer = xxx; startOffset = 0
  2376. * //本方法执行后
  2377. * startContainer = <b>; startOffset = 0
  2378. * @example
  2379. * Dom Element :
  2380. * <b> xxx| </b>
  2381. * startContainer = xxx; startOffset = 3
  2382. * //本方法执行后
  2383. * startContainer = <b>; startOffset = 1
  2384. */
  2385. txtToElmBoundary: function (ignoreCollapsed) {
  2386. function adjust(r, c) {
  2387. var container = r[c + 'Container'],
  2388. offset = r[c + 'Offset'];
  2389. if (container.nodeType == 3) {
  2390. if (!offset) {
  2391. r['set' + c.replace(/(\w)/, function (a) {
  2392. return a.toUpperCase();
  2393. }) + 'Before'](container);
  2394. } else if (offset >= container.nodeValue.length) {
  2395. r['set' + c.replace(/(\w)/, function (a) {
  2396. return a.toUpperCase();
  2397. }) + 'After'](container);
  2398. }
  2399. }
  2400. }
  2401. if (ignoreCollapsed || !this.collapsed) {
  2402. adjust(this, 'start');
  2403. adjust(this, 'end');
  2404. }
  2405. return this;
  2406. },
  2407. /**
  2408. * 在当前选区的开始位置前插入一个节点或者fragment,range的开始位置会在插入节点的前边
  2409. * @name insertNode
  2410. * @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment
  2411. * @example
  2412. * Range :
  2413. * xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2414. * 待插入Node :
  2415. * <p>ssss</p>
  2416. * 执行本方法后的Range :
  2417. * xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2418. */
  2419. insertNode: function (node) {
  2420. var first = node, length = 1;
  2421. if (node.nodeType == 11) {
  2422. first = node.firstChild;
  2423. length = node.childNodes.length;
  2424. }
  2425. this.trimBoundary(true);
  2426. var start = this.startContainer,
  2427. offset = this.startOffset;
  2428. var nextNode = start.childNodes[offset];
  2429. if (nextNode) {
  2430. start.insertBefore(node, nextNode);
  2431. } else {
  2432. start.appendChild(node);
  2433. }
  2434. if (first.parentNode === this.endContainer) {
  2435. this.endOffset = this.endOffset + length;
  2436. }
  2437. return this.setStartBefore(first);
  2438. },
  2439. /**
  2440. * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾
  2441. * @name setCursor
  2442. * @grammar range.setCursor([toEnd]) => Range //toEnd为true时,光标闭合到选区的末尾
  2443. */
  2444. setCursor: function (toEnd, noFillData) {
  2445. return this.collapse(!toEnd).select(noFillData);
  2446. },
  2447. /**
  2448. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  2449. * @name createBookmark
  2450. * @grammar range.createBookmark([serialize]) => Object //{start:开始标记,end:结束标记,id:serialize} serialize为真时,开始结束标记是插入节点的id,否则是插入节点的引用
  2451. */
  2452. createBookmark: function (serialize, same) {
  2453. var endNode,
  2454. startNode = this.document.createElement('span');
  2455. startNode.style.cssText = 'display:none;line-height:0px;';
  2456. startNode.appendChild(this.document.createTextNode('\u200D'));
  2457. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  2458. if (!this.collapsed) {
  2459. endNode = startNode.cloneNode(true);
  2460. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
  2461. }
  2462. this.insertNode(startNode);
  2463. if (endNode) {
  2464. this.collapse().insertNode(endNode).setEndBefore(endNode);
  2465. }
  2466. this.setStartAfter(startNode);
  2467. return {
  2468. start: serialize ? startNode.id : startNode,
  2469. end: endNode ? serialize ? endNode.id : endNode : null,
  2470. id: serialize
  2471. }
  2472. },
  2473. /**
  2474. * 移动边界到书签位置,并删除插入的书签节点
  2475. * @name moveToBookmark
  2476. * @grammar range.moveToBookmark(bookmark) => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的
  2477. */
  2478. moveToBookmark: function (bookmark) {
  2479. var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
  2480. end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
  2481. this.setStartBefore(start);
  2482. domUtils.remove(start);
  2483. if (end) {
  2484. this.setEndBefore(end);
  2485. domUtils.remove(end);
  2486. } else {
  2487. this.collapse(true);
  2488. }
  2489. return this;
  2490. },
  2491. /**
  2492. * 调整Range的边界,使其"缩小"到最合适的位置
  2493. * @name adjustmentBoundary
  2494. * @grammar range.adjustmentBoundary() => Range //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>
  2495. * @example
  2496. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2497. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>
  2498. */
  2499. adjustmentBoundary: function () {
  2500. if (!this.collapsed) {
  2501. while (!domUtils.isBody(this.startContainer) &&
  2502. this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
  2503. this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2504. ) {
  2505. this.setStartAfter(this.startContainer);
  2506. }
  2507. while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
  2508. this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2509. ) {
  2510. this.setEndBefore(this.endContainer);
  2511. }
  2512. }
  2513. return this;
  2514. },
  2515. /**
  2516. * 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点
  2517. * @name getClosedNode
  2518. * @grammar range.getClosedNode() => node|null
  2519. * @example
  2520. * <b>xxxx[<img />]xxx</b>
  2521. */
  2522. getClosedNode: function () {
  2523. var node;
  2524. if (!this.collapsed) {
  2525. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
  2526. if (selectOneNode(range)) {
  2527. var child = range.startContainer.childNodes[range.startOffset];
  2528. if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
  2529. node = child;
  2530. }
  2531. }
  2532. }
  2533. return node;
  2534. },
  2535. /**
  2536. * 根据当前range选中内容节点(在页面上表现为反白显示)
  2537. * @name select
  2538. * @grammar range.select(); => Range
  2539. */
  2540. select: browser.ie ? function (noFillData, textRange) {
  2541. var nativeRange;
  2542. if (!this.collapsed)
  2543. this.shrinkBoundary();
  2544. var node = this.getClosedNode();
  2545. if (node && !textRange) {
  2546. try {
  2547. nativeRange = this.document.body.createControlRange();
  2548. nativeRange.addElement(node);
  2549. nativeRange.select();
  2550. } catch (e) {
  2551. }
  2552. return this;
  2553. }
  2554. var bookmark = this.createBookmark(),
  2555. start = bookmark.start,
  2556. end;
  2557. nativeRange = this.document.body.createTextRange();
  2558. nativeRange.moveToElementText(start);
  2559. nativeRange.moveStart('character', 1);
  2560. if (!this.collapsed) {
  2561. var nativeRangeEnd = this.document.body.createTextRange();
  2562. end = bookmark.end;
  2563. nativeRangeEnd.moveToElementText(end);
  2564. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
  2565. } else {
  2566. if (!noFillData && this.startContainer.nodeType != 3) {
  2567. //使用<span>|x<span>固定住光标
  2568. var tmpText = this.document.createTextNode(fillChar),
  2569. tmp = this.document.createElement('span');
  2570. tmp.appendChild(this.document.createTextNode(fillChar));
  2571. start.parentNode.insertBefore(tmp, start);
  2572. start.parentNode.insertBefore(tmpText, start);
  2573. //当点b,i,u时,不能清除i上边的b
  2574. removeFillData(this.document, tmpText);
  2575. fillData = tmpText;
  2576. mergeSibling(tmp, 'previousSibling');
  2577. mergeSibling(start, 'nextSibling');
  2578. nativeRange.moveStart('character', -1);
  2579. nativeRange.collapse(true);
  2580. }
  2581. }
  2582. this.moveToBookmark(bookmark);
  2583. tmp && domUtils.remove(tmp);
  2584. //IE在隐藏状态下不支持range操作,catch一下
  2585. try {
  2586. nativeRange.select();
  2587. } catch (e) {
  2588. }
  2589. return this;
  2590. } : function (notInsertFillData) {
  2591. function checkOffset(rng) {
  2592. function check(node, offset, dir) {
  2593. if (node.nodeType == 3 && node.nodeValue.length < offset) {
  2594. rng[dir + 'Offset'] = node.nodeValue.length
  2595. }
  2596. }
  2597. check(rng.startContainer, rng.startOffset, 'start');
  2598. check(rng.endContainer, rng.endOffset, 'end');
  2599. }
  2600. var win = domUtils.getWindow(this.document),
  2601. sel = win.getSelection(),
  2602. txtNode;
  2603. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  2604. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  2605. browser.gecko ? this.body.focus() : win.focus();
  2606. if (sel) {
  2607. sel.removeAllRanges();
  2608. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  2609. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  2610. if (this.collapsed && !notInsertFillData) {
  2611. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  2612. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  2613. // var tmp = this.document.createTextNode('');
  2614. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  2615. // }
  2616. //
  2617. //处理光标落在文本节点的情况
  2618. //处理以下的情况
  2619. //<b>|xxxx</b>
  2620. //<b>xxxx</b>|xxxx
  2621. //xxxx<b>|</b>
  2622. var start = this.startContainer, child = start;
  2623. if (start.nodeType == 1) {
  2624. child = start.childNodes[this.startOffset];
  2625. }
  2626. if (!(start.nodeType == 3 && this.startOffset) &&
  2627. (child ?
  2628. (!child.previousSibling || child.previousSibling.nodeType != 3)
  2629. :
  2630. (!start.lastChild || start.lastChild.nodeType != 3)
  2631. )
  2632. ) {
  2633. txtNode = this.document.createTextNode(fillChar);
  2634. //跟着前边走
  2635. this.insertNode(txtNode);
  2636. removeFillData(this.document, txtNode);
  2637. mergeSibling(txtNode, 'previousSibling');
  2638. mergeSibling(txtNode, 'nextSibling');
  2639. fillData = txtNode;
  2640. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
  2641. }
  2642. }
  2643. var nativeRange = this.document.createRange();
  2644. if (this.collapsed && browser.opera && this.startContainer.nodeType == 1) {
  2645. var child = this.startContainer.childNodes[this.startOffset];
  2646. if (!child) {
  2647. //往前靠拢
  2648. child = this.startContainer.lastChild;
  2649. if (child && domUtils.isBr(child)) {
  2650. this.setStartBefore(child).collapse(true);
  2651. }
  2652. } else {
  2653. //向后靠拢
  2654. while (child && domUtils.isBlockElm(child)) {
  2655. if (child.nodeType == 1 && child.childNodes[0]) {
  2656. child = child.childNodes[0]
  2657. } else {
  2658. break;
  2659. }
  2660. }
  2661. child && this.setStartBefore(child).collapse(true)
  2662. }
  2663. }
  2664. //是createAddress最后一位算的不准,现在这里进行微调
  2665. checkOffset(this);
  2666. nativeRange.setStart(this.startContainer, this.startOffset);
  2667. nativeRange.setEnd(this.endContainer, this.endOffset);
  2668. sel.addRange(nativeRange);
  2669. }
  2670. return this;
  2671. },
  2672. createAddress: function (ignoreEnd, ignoreTxt) {
  2673. var addr = {}, me = this;
  2674. function getAddress(isStart) {
  2675. var node = isStart ? me.startContainer : me.endContainer;
  2676. var parents = domUtils.findParents(node, true, function (node) {
  2677. return !domUtils.isBody(node)
  2678. }),
  2679. addrs = [];
  2680. for (var i = 0, ci; ci = parents[i++];) {
  2681. addrs.push(domUtils.getNodeIndex(ci, ignoreTxt));
  2682. }
  2683. var firstIndex = 0;
  2684. if (ignoreTxt) {
  2685. if (node.nodeType == 3) {
  2686. var tmpNode = node.previousSibling;
  2687. while (tmpNode && tmpNode.nodeType == 3) {
  2688. firstIndex += tmpNode.nodeValue.replace(fillCharReg, '').length;
  2689. tmpNode = tmpNode.previousSibling;
  2690. }
  2691. firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  2692. } else {
  2693. node = node.childNodes[isStart ? me.startOffset : me.endOffset];
  2694. if (node) {
  2695. firstIndex = domUtils.getNodeIndex(node, ignoreTxt);
  2696. } else {
  2697. node = isStart ? me.startContainer : me.endContainer;
  2698. var first = node.firstChild;
  2699. while (first) {
  2700. if (domUtils.isFillChar(first)) {
  2701. first = first.nextSibling;
  2702. continue;
  2703. }
  2704. firstIndex++;
  2705. if (first.nodeType == 3) {
  2706. while (first && first.nodeType == 3) {
  2707. first = first.nextSibling;
  2708. }
  2709. } else {
  2710. first = first.nextSibling;
  2711. }
  2712. }
  2713. }
  2714. }
  2715. } else {
  2716. firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
  2717. }
  2718. if (firstIndex < 0) {
  2719. firstIndex = 0;
  2720. }
  2721. addrs.push(firstIndex);
  2722. return addrs;
  2723. }
  2724. addr.startAddress = getAddress(true);
  2725. if (!ignoreEnd) {
  2726. addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
  2727. }
  2728. return addr;
  2729. },
  2730. moveToAddress: function (addr, ignoreEnd) {
  2731. var me = this;
  2732. function getNode(address, isStart) {
  2733. var tmpNode = me.body,
  2734. parentNode, offset;
  2735. for (var i = 0, ci, l = address.length; i < l; i++) {
  2736. ci = address[i];
  2737. parentNode = tmpNode;
  2738. tmpNode = tmpNode.childNodes[ci];
  2739. if (!tmpNode) {
  2740. offset = ci;
  2741. break;
  2742. }
  2743. }
  2744. if (isStart) {
  2745. if (tmpNode) {
  2746. me.setStartBefore(tmpNode)
  2747. } else {
  2748. me.setStart(parentNode, offset)
  2749. }
  2750. } else {
  2751. if (tmpNode) {
  2752. me.setEndBefore(tmpNode)
  2753. } else {
  2754. me.setEnd(parentNode, offset)
  2755. }
  2756. }
  2757. }
  2758. getNode(addr.startAddress, true);
  2759. !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
  2760. return me;
  2761. },
  2762. equals: function (rng) {
  2763. for (var p in this) {
  2764. if (this.hasOwnProperty(p)) {
  2765. if (this[p] !== rng[p])
  2766. return false
  2767. }
  2768. }
  2769. return true;
  2770. },
  2771. scrollIntoView: function () {
  2772. var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');
  2773. this.cloneRange().insertNode($span.get(0));
  2774. var winScrollTop = $(window).scrollTop(),
  2775. winHeight = $(window).height(),
  2776. spanTop = $span.offset().top;
  2777. if (spanTop < winScrollTop - winHeight || spanTop > winScrollTop + winHeight) {
  2778. if (spanTop > winScrollTop + winHeight) {
  2779. window.scrollTo(0, spanTop - winHeight + $span.height())
  2780. } else {
  2781. window.scrollTo(0, winScrollTop - spanTop)
  2782. }
  2783. }
  2784. $span.remove();
  2785. },
  2786. getOffset: function () {
  2787. var bk = this.createBookmark();
  2788. var offset = $(bk.start).css('display', 'inline-block').offset();
  2789. this.moveToBookmark(bk);
  2790. return offset
  2791. }
  2792. };
  2793. })();
  2794. ///import editor.js
  2795. ///import core/browser.js
  2796. ///import core/dom/dom.js
  2797. ///import core/dom/dtd.js
  2798. ///import core/dom/domUtils.js
  2799. ///import core/dom/Range.js
  2800. /**
  2801. * @class UM.dom.Selection Selection类
  2802. */
  2803. (function () {
  2804. function getBoundaryInformation(range, start) {
  2805. var getIndex = domUtils.getNodeIndex;
  2806. range = range.duplicate();
  2807. range.collapse(start);
  2808. var parent = range.parentElement();
  2809. //如果节点里没有子节点,直接退出
  2810. if (!parent.hasChildNodes()) {
  2811. return {container: parent, offset: 0};
  2812. }
  2813. var siblings = parent.children,
  2814. child,
  2815. testRange = range.duplicate(),
  2816. startIndex = 0, endIndex = siblings.length - 1, index = -1,
  2817. distance;
  2818. while (startIndex <= endIndex) {
  2819. index = Math.floor((startIndex + endIndex) / 2);
  2820. child = siblings[index];
  2821. testRange.moveToElementText(child);
  2822. var position = testRange.compareEndPoints('StartToStart', range);
  2823. if (position > 0) {
  2824. endIndex = index - 1;
  2825. } else if (position < 0) {
  2826. startIndex = index + 1;
  2827. } else {
  2828. //trace:1043
  2829. return {container: parent, offset: getIndex(child)};
  2830. }
  2831. }
  2832. if (index == -1) {
  2833. testRange.moveToElementText(parent);
  2834. testRange.setEndPoint('StartToStart', range);
  2835. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length;
  2836. siblings = parent.childNodes;
  2837. if (!distance) {
  2838. child = siblings[siblings.length - 1];
  2839. return {container: child, offset: child.nodeValue.length};
  2840. }
  2841. var i = siblings.length;
  2842. while (distance > 0) {
  2843. distance -= siblings[--i].nodeValue.length;
  2844. }
  2845. return {container: siblings[i], offset: -distance};
  2846. }
  2847. testRange.collapse(position > 0);
  2848. testRange.setEndPoint(position > 0 ? 'StartToStart' : 'EndToStart', range);
  2849. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length;
  2850. if (!distance) {
  2851. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
  2852. {container: parent, offset: getIndex(child) + (position > 0 ? 0 : 1)} :
  2853. {container: child, offset: position > 0 ? 0 : child.childNodes.length}
  2854. }
  2855. while (distance > 0) {
  2856. try {
  2857. var pre = child;
  2858. child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
  2859. distance -= child.nodeValue.length;
  2860. } catch (e) {
  2861. return {container: parent, offset: getIndex(pre)};
  2862. }
  2863. }
  2864. return {container: child, offset: position > 0 ? -distance : child.nodeValue.length + distance}
  2865. }
  2866. /**
  2867. * 将ieRange转换为Range对象
  2868. * @param {Range} ieRange ieRange对象
  2869. * @param {Range} range Range对象
  2870. * @return {Range} range 返回转换后的Range对象
  2871. */
  2872. function transformIERangeToRange(ieRange, range) {
  2873. if (ieRange.item) {
  2874. range.selectNode(ieRange.item(0));
  2875. } else {
  2876. var bi = getBoundaryInformation(ieRange, true);
  2877. range.setStart(bi.container, bi.offset);
  2878. if (ieRange.compareEndPoints('StartToEnd', ieRange) != 0) {
  2879. bi = getBoundaryInformation(ieRange, false);
  2880. range.setEnd(bi.container, bi.offset);
  2881. }
  2882. }
  2883. return range;
  2884. }
  2885. /**
  2886. * 获得ieRange
  2887. * @param {Selection} sel Selection对象
  2888. * @return {ieRange} 得到ieRange
  2889. */
  2890. function _getIERange(sel, txtRange) {
  2891. var ieRange;
  2892. //ie下有可能报错
  2893. try {
  2894. ieRange = sel.getNative(txtRange).createRange();
  2895. } catch (e) {
  2896. return null;
  2897. }
  2898. var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
  2899. if ((el.ownerDocument || el) === sel.document) {
  2900. return ieRange;
  2901. }
  2902. return null;
  2903. }
  2904. var Selection = dom.Selection = function (doc, body) {
  2905. var me = this;
  2906. me.document = doc;
  2907. me.body = body;
  2908. if (browser.ie9below) {
  2909. $(body).on('beforedeactivate', function () {
  2910. me._bakIERange = me.getIERange();
  2911. }).on('activate', function () {
  2912. try {
  2913. var ieNativRng = _getIERange(me);
  2914. if ((!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange) {
  2915. me._bakIERange.select();
  2916. }
  2917. } catch (ex) {
  2918. }
  2919. me._bakIERange = null;
  2920. });
  2921. }
  2922. };
  2923. Selection.prototype = {
  2924. hasNativeRange: function () {
  2925. var rng;
  2926. if (!browser.ie || browser.ie9above) {
  2927. var nativeSel = this.getNative();
  2928. if (!nativeSel.rangeCount) {
  2929. return false;
  2930. }
  2931. rng = nativeSel.getRangeAt(0);
  2932. } else {
  2933. rng = _getIERange(this);
  2934. }
  2935. return this.rangeInBody(rng);
  2936. },
  2937. /**
  2938. * 获取原生seleciton对象
  2939. * @public
  2940. * @function
  2941. * @name UM.dom.Selection.getNative
  2942. * @return {Selection} 获得selection对象
  2943. */
  2944. getNative: function (txtRange) {
  2945. var doc = this.document;
  2946. try {
  2947. return !doc ? null : browser.ie9below || txtRange ? doc.selection : domUtils.getWindow(doc).getSelection();
  2948. } catch (e) {
  2949. return null;
  2950. }
  2951. },
  2952. /**
  2953. * 获得ieRange
  2954. * @public
  2955. * @function
  2956. * @name UM.dom.Selection.getIERange
  2957. * @return {ieRange} 返回ie原生的Range
  2958. */
  2959. getIERange: function (txtRange) {
  2960. var ieRange = _getIERange(this, txtRange);
  2961. if (!ieRange || !this.rangeInBody(ieRange, txtRange)) {
  2962. if (this._bakIERange) {
  2963. return this._bakIERange;
  2964. }
  2965. }
  2966. return ieRange;
  2967. },
  2968. rangeInBody: function (rng, txtRange) {
  2969. var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  2970. return node === this.body || domUtils.inDoc(node, this.body);
  2971. },
  2972. /**
  2973. * 缓存当前选区的range和选区的开始节点
  2974. * @public
  2975. * @function
  2976. * @name UM.dom.Selection.cache
  2977. */
  2978. cache: function () {
  2979. this.clear();
  2980. this._cachedRange = this.getRange();
  2981. this._cachedStartElement = this.getStart();
  2982. this._cachedStartElementPath = this.getStartElementPath();
  2983. },
  2984. getStartElementPath: function () {
  2985. if (this._cachedStartElementPath) {
  2986. return this._cachedStartElementPath;
  2987. }
  2988. var start = this.getStart();
  2989. if (start) {
  2990. return domUtils.findParents(start, true, null, true)
  2991. }
  2992. return [];
  2993. },
  2994. /**
  2995. * 清空缓存
  2996. * @public
  2997. * @function
  2998. * @name UM.dom.Selection.clear
  2999. */
  3000. clear: function () {
  3001. this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
  3002. },
  3003. /**
  3004. * 编辑器是否得到了选区
  3005. */
  3006. isFocus: function () {
  3007. return this.hasNativeRange()
  3008. },
  3009. /**
  3010. * 获取选区对应的Range
  3011. * @public
  3012. * @function
  3013. * @name UM.dom.Selection.getRange
  3014. * @returns {UM.dom.Range} 得到Range对象
  3015. */
  3016. getRange: function () {
  3017. var me = this;
  3018. function optimze(range) {
  3019. var child = me.body.firstChild,
  3020. collapsed = range.collapsed;
  3021. while (child && child.firstChild) {
  3022. range.setStart(child, 0);
  3023. child = child.firstChild;
  3024. }
  3025. if (!range.startContainer) {
  3026. range.setStart(me.body, 0)
  3027. }
  3028. if (collapsed) {
  3029. range.collapse(true);
  3030. }
  3031. }
  3032. if (me._cachedRange != null) {
  3033. return this._cachedRange;
  3034. }
  3035. var range = new dom.Range(me.document, me.body);
  3036. if (browser.ie9below) {
  3037. var nativeRange = me.getIERange();
  3038. if (nativeRange && this.rangeInBody(nativeRange)) {
  3039. try {
  3040. transformIERangeToRange(nativeRange, range);
  3041. } catch (e) {
  3042. optimze(range);
  3043. }
  3044. } else {
  3045. optimze(range);
  3046. }
  3047. } else {
  3048. var sel = me.getNative();
  3049. if (sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt(0))) {
  3050. var firstRange = sel.getRangeAt(0);
  3051. var lastRange = sel.getRangeAt(sel.rangeCount - 1);
  3052. range.setStart(firstRange.startContainer, firstRange.startOffset).setEnd(lastRange.endContainer, lastRange.endOffset);
  3053. if (range.collapsed && domUtils.isBody(range.startContainer) && !range.startOffset) {
  3054. optimze(range);
  3055. }
  3056. } else {
  3057. //trace:1734 有可能已经不在dom树上了,标识的节点
  3058. if (this._bakRange && (this._bakRange.startContainer === this.body || domUtils.inDoc(this._bakRange.startContainer, this.body))) {
  3059. return this._bakRange;
  3060. }
  3061. optimze(range);
  3062. }
  3063. }
  3064. return this._bakRange = range;
  3065. },
  3066. /**
  3067. * 获取开始元素,用于状态反射
  3068. * @public
  3069. * @function
  3070. * @name UM.dom.Selection.getStart
  3071. * @return {Element} 获得开始元素
  3072. */
  3073. getStart: function () {
  3074. if (this._cachedStartElement) {
  3075. return this._cachedStartElement;
  3076. }
  3077. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  3078. tmpRange,
  3079. start, tmp, parent;
  3080. if (browser.ie9below) {
  3081. if (!range) {
  3082. //todo 给第一个值可能会有问题
  3083. return this.document.body.firstChild;
  3084. }
  3085. //control元素
  3086. if (range.item) {
  3087. return range.item(0);
  3088. }
  3089. tmpRange = range.duplicate();
  3090. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  3091. tmpRange.text.length > 0 && tmpRange.moveStart('character', 1);
  3092. tmpRange.collapse(1);
  3093. start = tmpRange.parentElement();
  3094. parent = tmp = range.parentElement();
  3095. while (tmp = tmp.parentNode) {
  3096. if (tmp == start) {
  3097. start = parent;
  3098. break;
  3099. }
  3100. }
  3101. } else {
  3102. start = range.startContainer;
  3103. if (start.nodeType == 1 && start.hasChildNodes()) {
  3104. start = start.childNodes[Math.min(start.childNodes.length - 1, range.startOffset)];
  3105. }
  3106. if (start.nodeType == 3) {
  3107. return start.parentNode;
  3108. }
  3109. }
  3110. return start;
  3111. },
  3112. /**
  3113. * 得到选区中的文本
  3114. * @public
  3115. * @function
  3116. * @name UM.dom.Selection.getText
  3117. * @return {String} 选区中包含的文本
  3118. */
  3119. getText: function () {
  3120. var nativeSel, nativeRange;
  3121. if (this.isFocus() && (nativeSel = this.getNative())) {
  3122. nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt(0);
  3123. return browser.ie9below ? nativeRange.text : nativeRange.toString();
  3124. }
  3125. return '';
  3126. }
  3127. };
  3128. })();
  3129. /**
  3130. * @file
  3131. * @name UM.Editor
  3132. * @short Editor
  3133. * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js
  3134. * @desc 编辑器主类,包含编辑器提供的大部分公用接口
  3135. */
  3136. (function () {
  3137. var uid = 0, _selectionChangeTimer;
  3138. /**
  3139. * @private
  3140. * @ignore
  3141. * @param form 编辑器所在的form元素
  3142. * @param editor 编辑器实例对象
  3143. */
  3144. function setValue(form, editor) {
  3145. var textarea;
  3146. if (editor.textarea) {
  3147. if (utils.isString(editor.textarea)) {
  3148. for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
  3149. if (ti.id == 'umeditor_textarea_' + editor.options.textarea) {
  3150. textarea = ti;
  3151. break;
  3152. }
  3153. }
  3154. } else {
  3155. textarea = editor.textarea;
  3156. }
  3157. }
  3158. if (!textarea) {
  3159. form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
  3160. 'name': editor.options.textarea,
  3161. 'id': 'umeditor_textarea_' + editor.options.textarea,
  3162. 'style': "display:none"
  3163. }));
  3164. //不要产生多个textarea
  3165. editor.textarea = textarea;
  3166. }
  3167. textarea.value = editor.hasContents() ?
  3168. (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
  3169. ''
  3170. }
  3171. function loadPlugins(me) {
  3172. //初始化插件
  3173. for (var pi in UM.plugins) {
  3174. if (me.options.excludePlugins.indexOf(pi) == -1) {
  3175. UM.plugins[pi].call(me);
  3176. me.plugins[pi] = 1;
  3177. }
  3178. }
  3179. me.langIsReady = true;
  3180. me.fireEvent("langReady");
  3181. }
  3182. function checkCurLang(I18N) {
  3183. for (var lang in I18N) {
  3184. return lang
  3185. }
  3186. }
  3187. /**
  3188. * UEditor编辑器类
  3189. * @name Editor
  3190. * @desc 创建一个跟编辑器实例
  3191. * - ***container*** 编辑器容器对象
  3192. * - ***iframe*** 编辑区域所在的iframe对象
  3193. * - ***window*** 编辑区域所在的window
  3194. * - ***document*** 编辑区域所在的document对象
  3195. * - ***body*** 编辑区域所在的body对象
  3196. * - ***selection*** 编辑区域的选区对象
  3197. */
  3198. var Editor = UM.Editor = function (options) {
  3199. var me = this;
  3200. me.uid = uid++;
  3201. EventBase.call(me);
  3202. me.commands = {};
  3203. me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);
  3204. me.shortcutkeys = {};
  3205. me.inputRules = [];
  3206. me.outputRules = [];
  3207. //设置默认的常用属性
  3208. me.setOpt({
  3209. isShow: true,
  3210. initialContent: '',
  3211. initialStyle: '',
  3212. autoClearinitialContent: false,
  3213. textarea: 'editorValue',
  3214. focus: false,
  3215. focusInEnd: true,
  3216. autoClearEmptyNode: true,
  3217. fullscreen: false,
  3218. readonly: false,
  3219. zIndex: 999,
  3220. enterTag: 'p',
  3221. lang: 'zh-cn',
  3222. langPath: me.options.UMEDITOR_HOME_URL + 'lang/',
  3223. theme: 'default',
  3224. themePath: me.options.UMEDITOR_HOME_URL + 'themes/',
  3225. allHtmlEnabled: false,
  3226. autoSyncData: true,
  3227. autoHeightEnabled: true,
  3228. excludePlugins: ''
  3229. });
  3230. me.plugins = {};
  3231. if (!utils.isEmptyObject(UM.I18N)) {
  3232. //修改默认的语言类型
  3233. me.options.lang = checkCurLang(UM.I18N);
  3234. loadPlugins(me)
  3235. } else {
  3236. utils.loadFile(document, {
  3237. src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
  3238. tag: "script",
  3239. type: "text/javascript",
  3240. defer: "defer"
  3241. }, function () {
  3242. loadPlugins(me)
  3243. });
  3244. }
  3245. };
  3246. Editor.prototype = {
  3247. /**
  3248. * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready,就马上执行fn,fn的中的this是编辑器实例。
  3249. * 大部分的实例接口都需要放在该方法内部执行,否则在IE下可能会报错。
  3250. * @name ready
  3251. * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function
  3252. * @example
  3253. * var editor = new UM.ui.Editor();
  3254. * editor.render("myEditor");
  3255. * editor.ready(function(){
  3256. * editor.setContent("欢迎使用UEditor!");
  3257. * })
  3258. */
  3259. ready: function (fn) {
  3260. var me = this;
  3261. if (fn) {
  3262. me.isReady ? fn.apply(me) : me.addListener('ready', fn);
  3263. }
  3264. },
  3265. /**
  3266. * 为编辑器设置默认参数值。若用户配置为空,则以默认配置为准
  3267. * @grammar editor.setOpt(key,value); //传入一个键、值对
  3268. * @grammar editor.setOpt({ key:value}); //传入一个json对象
  3269. */
  3270. setOpt: function (key, val) {
  3271. var obj = {};
  3272. if (utils.isString(key)) {
  3273. obj[key] = val
  3274. } else {
  3275. obj = key;
  3276. }
  3277. utils.extend(this.options, obj, true);
  3278. },
  3279. getOpt: function (key) {
  3280. return this.options[key] || ''
  3281. },
  3282. /**
  3283. * 销毁编辑器实例对象
  3284. * @name destroy
  3285. * @grammar editor.destroy();
  3286. */
  3287. destroy: function () {
  3288. var me = this;
  3289. me.fireEvent('destroy');
  3290. var container = me.container.parentNode;
  3291. if (container === document.body) {
  3292. container = me.container;
  3293. }
  3294. var textarea = me.textarea;
  3295. if (!textarea) {
  3296. textarea = document.createElement('textarea');
  3297. container.parentNode.insertBefore(textarea, container);
  3298. } else {
  3299. textarea.style.display = ''
  3300. }
  3301. textarea.style.width = me.body.offsetWidth + 'px';
  3302. textarea.style.height = me.body.offsetHeight + 'px';
  3303. textarea.value = me.getContent();
  3304. textarea.id = me.key;
  3305. if (container.contains(textarea)) {
  3306. $(textarea).insertBefore(container);
  3307. }
  3308. container.innerHTML = '';
  3309. domUtils.remove(container);
  3310. UM.clearCache(me.id);
  3311. //trace:2004
  3312. for (var p in me) {
  3313. if (me.hasOwnProperty(p)) {
  3314. delete this[p];
  3315. }
  3316. }
  3317. },
  3318. initialCont: function (holder) {
  3319. if (holder) {
  3320. holder.getAttribute('name') && (this.options.textarea = holder.getAttribute('name'));
  3321. if (holder && /script|textarea/ig.test(holder.tagName)) {
  3322. var newDiv = document.createElement('div');
  3323. holder.parentNode.insertBefore(newDiv, holder);
  3324. this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML || this.options.initialContent).toHtml();
  3325. holder.className && (newDiv.className = holder.className);
  3326. holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  3327. if (/textarea/i.test(holder.tagName)) {
  3328. this.textarea = holder;
  3329. this.textarea.style.display = 'none';
  3330. } else {
  3331. holder.parentNode.removeChild(holder);
  3332. holder.id && (newDiv.id = holder.id);
  3333. }
  3334. holder = newDiv;
  3335. holder.innerHTML = '';
  3336. }
  3337. return holder;
  3338. } else {
  3339. return null;
  3340. }
  3341. },
  3342. /**
  3343. * 渲染编辑器的DOM到指定容器,必须且只能调用一次
  3344. * @name render
  3345. * @grammar editor.render(containerId); //可以指定一个容器ID
  3346. * @grammar editor.render(containerDom); //也可以直接指定容器对象
  3347. */
  3348. render: function (container) {
  3349. var me = this,
  3350. options = me.options,
  3351. getStyleValue = function (attr) {
  3352. return parseInt($(container).css(attr));
  3353. };
  3354. if (utils.isString(container)) {
  3355. container = document.getElementById(container);
  3356. }
  3357. if (container) {
  3358. this.id = container.getAttribute('id');
  3359. UM.setEditor(this);
  3360. utils.cssRule('edui-style-body', me.options.initialStyle, document);
  3361. container = this.initialCont(container);
  3362. container.className += ' edui-body-container';
  3363. if (options.initialFrameWidth) {
  3364. options.minFrameWidth = options.initialFrameWidth
  3365. } else {
  3366. //都没给值,先写死了
  3367. options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;
  3368. }
  3369. if (options.initialFrameHeight) {
  3370. options.minFrameHeight = options.initialFrameHeight
  3371. } else {
  3372. options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;
  3373. }
  3374. container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth -
  3375. getStyleValue("padding-left") -
  3376. getStyleValue("padding-right") + 'px';
  3377. var height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - getStyleValue("padding-top") - getStyleValue("padding-bottom"));
  3378. if (this.options.autoHeightEnabled) {
  3379. container.style.minHeight = height + 'px';
  3380. container.style.height = '';
  3381. if (browser.ie && browser.version <= 6) {
  3382. container.style.height = height;
  3383. container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3384. }
  3385. } else {
  3386. $(container).height(height)
  3387. }
  3388. container.style.zIndex = options.zIndex;
  3389. this._setup(container);
  3390. }
  3391. },
  3392. /**
  3393. * 编辑器初始化
  3394. * @private
  3395. * @ignore
  3396. * @param {Element} doc 编辑器Iframe中的文档对象
  3397. */
  3398. _setup: function (cont) {
  3399. var me = this,
  3400. options = me.options;
  3401. cont.contentEditable = true;
  3402. document.body.spellcheck = false;
  3403. me.document = document;
  3404. me.window = document.defaultView || document.parentWindow;
  3405. me.body = cont;
  3406. me.$body = $(cont);
  3407. me.selection = new dom.Selection(document, me.body);
  3408. me._isEnabled = false;
  3409. //gecko初始化就能得到range,无法判断isFocus了
  3410. var geckoSel;
  3411. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3412. geckoSel.removeAllRanges();
  3413. }
  3414. this._initEvents();
  3415. //为form提交提供一个隐藏的textarea
  3416. for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {
  3417. if (form.tagName == 'FORM') {
  3418. me.form = form;
  3419. if (me.options.autoSyncData) {
  3420. $(cont).on('blur', function () {
  3421. setValue(form, me);
  3422. })
  3423. } else {
  3424. $(form).on('submit', function () {
  3425. setValue(this, me);
  3426. })
  3427. }
  3428. break;
  3429. }
  3430. }
  3431. if (options.initialContent) {
  3432. if (options.autoClearinitialContent) {
  3433. var oldExecCommand = me.execCommand;
  3434. me.execCommand = function () {
  3435. me.fireEvent('firstBeforeExecCommand');
  3436. return oldExecCommand.apply(me, arguments);
  3437. };
  3438. this._setDefaultContent(options.initialContent);
  3439. } else
  3440. this.setContent(options.initialContent, false, true);
  3441. }
  3442. //编辑器不能为空内容
  3443. if (domUtils.isEmptyNode(me.body)) {
  3444. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  3445. }
  3446. //如果要求focus, 就把光标定位到内容开始
  3447. if (options.focus) {
  3448. setTimeout(function () {
  3449. me.focus(me.options.focusInEnd);
  3450. //如果自动清除开着,就不需要做selectionchange;
  3451. !me.options.autoClearinitialContent && me._selectionChange();
  3452. }, 0);
  3453. }
  3454. if (!me.container) {
  3455. me.container = cont.parentNode;
  3456. }
  3457. me._bindshortcutKeys();
  3458. me.isReady = 1;
  3459. me.fireEvent('ready');
  3460. options.onready && options.onready.call(me);
  3461. if (!browser.ie || browser.ie9above) {
  3462. $(me.body).on('blur focus', function (e) {
  3463. var nSel = me.selection.getNative();
  3464. //chrome下会出现alt+tab切换时,导致选区位置不对
  3465. if (e.type == 'blur') {
  3466. if (nSel.rangeCount > 0) {
  3467. me._bakRange = nSel.getRangeAt(0);
  3468. }
  3469. } else {
  3470. try {
  3471. me._bakRange && nSel.addRange(me._bakRange)
  3472. } catch (e) {
  3473. }
  3474. me._bakRange = null;
  3475. }
  3476. });
  3477. }
  3478. !options.isShow && me.setHide();
  3479. options.readonly && me.setDisabled();
  3480. },
  3481. /**
  3482. * 同步编辑器的数据,为提交数据做准备,主要用于你是手动提交的情况
  3483. * @name sync
  3484. * @grammar editor.sync(); //从编辑器的容器向上查找,如果找到就同步数据
  3485. * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  3486. * @desc
  3487. * 后台取得数据得键值使用你容器上得''name''属性,如果没有就使用参数传入的''textarea''
  3488. * @example
  3489. * editor.sync();
  3490. * form.sumbit(); //form变量已经指向了form元素
  3491. *
  3492. */
  3493. sync: function (formId) {
  3494. var me = this,
  3495. form = formId ? document.getElementById(formId) :
  3496. domUtils.findParent(me.body.parentNode, function (node) {
  3497. return node.tagName == 'FORM'
  3498. }, true);
  3499. form && setValue(form, me);
  3500. },
  3501. /**
  3502. * 设置编辑器高度
  3503. * @name setHeight
  3504. * @grammar editor.setHeight(number); //纯数值,不带单位
  3505. */
  3506. setHeight: function (height, notSetHeight) {
  3507. !notSetHeight && (this.options.initialFrameHeight = height);
  3508. if (this.options.autoHeightEnabled) {
  3509. $(this.body).css({
  3510. 'min-height': height + 'px'
  3511. });
  3512. if (browser.ie && browser.version <= 6 && this.container) {
  3513. this.container.style.height = height;
  3514. this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3515. }
  3516. } else {
  3517. $(this.body).height(height)
  3518. }
  3519. this.fireEvent('resize');
  3520. },
  3521. /**
  3522. * 设置编辑器宽度
  3523. * @name setWidth
  3524. * @grammar editor.setWidth(number); //纯数值,不带单位
  3525. */
  3526. setWidth: function (width) {
  3527. this.$container && this.$container.width(width);
  3528. $(this.body).width(width - $(this.body).css('padding-left').replace('px', '') * 1 - $(this.body).css('padding-right').replace('px', '') * 1);
  3529. this.fireEvent('resize');
  3530. },
  3531. addshortcutkey: function (cmd, keys) {
  3532. var obj = {};
  3533. if (keys) {
  3534. obj[cmd] = keys
  3535. } else {
  3536. obj = cmd;
  3537. }
  3538. utils.extend(this.shortcutkeys, obj)
  3539. },
  3540. _bindshortcutKeys: function () {
  3541. var me = this, shortcutkeys = this.shortcutkeys;
  3542. me.addListener('keydown', function (type, e) {
  3543. var keyCode = e.keyCode || e.which;
  3544. for (var i in shortcutkeys) {
  3545. var tmp = shortcutkeys[i].split(',');
  3546. for (var t = 0, ti; ti = tmp[t++];) {
  3547. ti = ti.split(':');
  3548. var key = ti[0], param = ti[1];
  3549. if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
  3550. if (((RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
  3551. && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
  3552. && keyCode == RegExp.$3
  3553. ) ||
  3554. keyCode == RegExp.$1
  3555. ) {
  3556. if (me.queryCommandState(i, param) != -1)
  3557. me.execCommand(i, param);
  3558. domUtils.preventDefault(e);
  3559. }
  3560. }
  3561. }
  3562. }
  3563. });
  3564. },
  3565. /**
  3566. * 获取编辑器内容
  3567. * @name getContent
  3568. * @grammar editor.getContent() => String //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。
  3569. * @grammar editor.getContent(fn) => String
  3570. * @example
  3571. * getContent默认是会现调用hasContents来判断编辑器是否为空,如果是,就直接返回空字符串
  3572. * 你也可以传入一个fn来接替hasContents的工作,定制判断的规则
  3573. * editor.getContent(function(){
  3574. * return false //编辑器没有内容 ,getContent直接返回空
  3575. * })
  3576. */
  3577. getContent: function (cmd, fn, notSetCursor, ignoreBlank, formatter) {
  3578. var me = this;
  3579. if (cmd && utils.isFunction(cmd)) {
  3580. fn = cmd;
  3581. cmd = '';
  3582. }
  3583. if (fn ? !fn() : !this.hasContents()) {
  3584. return '';
  3585. }
  3586. me.fireEvent('beforegetcontent');
  3587. var root = UM.htmlparser(me.body.innerHTML, ignoreBlank);
  3588. me.filterOutputRule(root);
  3589. me.fireEvent('aftergetcontent', root);
  3590. return root.toHtml(formatter);
  3591. },
  3592. /**
  3593. * 取得完整的html代码,可以直接显示成完整的html文档
  3594. * @name getAllHtml
  3595. * @grammar editor.getAllHtml() => String
  3596. */
  3597. getAllHtml: function () {
  3598. var me = this,
  3599. headHtml = [],
  3600. html = '';
  3601. me.fireEvent('getAllHtml', headHtml);
  3602. if (browser.ie && browser.version > 8) {
  3603. var headHtmlForIE9 = '';
  3604. utils.each(me.document.styleSheets, function (si) {
  3605. headHtmlForIE9 += (si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
  3606. });
  3607. utils.each(me.document.getElementsByTagName('script'), function (si) {
  3608. headHtmlForIE9 += si.outerHTML;
  3609. });
  3610. }
  3611. return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
  3612. + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
  3613. + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
  3614. },
  3615. /**
  3616. * 得到编辑器的纯文本内容,但会保留段落格式
  3617. * @name getPlainTxt
  3618. * @grammar editor.getPlainTxt() => String
  3619. */
  3620. getPlainTxt: function () {
  3621. var reg = new RegExp(domUtils.fillChar, 'g'),
  3622. html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
  3623. html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  3624. .replace(/<br\/?>/gi, '\n')
  3625. .replace(/<[^>/]+>/g, '')
  3626. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  3627. return dtd.$block[c] ? '\n' : b ? b : '';
  3628. });
  3629. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3630. return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
  3631. },
  3632. /**
  3633. * 获取编辑器中的纯文本内容,没有段落格式
  3634. * @name getContentTxt
  3635. * @grammar editor.getContentTxt() => String
  3636. */
  3637. getContentTxt: function () {
  3638. var reg = new RegExp(domUtils.fillChar, 'g');
  3639. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3640. return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
  3641. },
  3642. /**
  3643. * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值,则必须放在ready方法内部执行
  3644. * @name setContent
  3645. * @grammar editor.setContent(html)
  3646. * @example
  3647. * var editor = new UM.ui.Editor()
  3648. * editor.ready(function(){
  3649. * //需要ready后执行,否则可能报错
  3650. * editor.setContent("欢迎使用UEditor!");
  3651. * })
  3652. */
  3653. setContent: function (html, isAppendTo, notFireSelectionchange) {
  3654. var me = this;
  3655. me.fireEvent('beforesetcontent', html);
  3656. var root = UM.htmlparser(html);
  3657. me.filterInputRule(root);
  3658. html = root.toHtml();
  3659. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  3660. function isCdataDiv(node) {
  3661. return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
  3662. }
  3663. //给文本或者inline节点套p标签
  3664. if (me.options.enterTag == 'p') {
  3665. var child = this.body.firstChild, tmpNode;
  3666. if (!child || child.nodeType == 1 &&
  3667. (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
  3668. domUtils.isCustomeNode(child)
  3669. )
  3670. && child === this.body.lastChild) {
  3671. this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  3672. } else {
  3673. var p = me.document.createElement('p');
  3674. while (child) {
  3675. while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
  3676. tmpNode = child.nextSibling;
  3677. p.appendChild(child);
  3678. child = tmpNode;
  3679. }
  3680. if (p.firstChild) {
  3681. if (!child) {
  3682. me.body.appendChild(p);
  3683. break;
  3684. } else {
  3685. child.parentNode.insertBefore(p, child);
  3686. p = me.document.createElement('p');
  3687. }
  3688. }
  3689. child = child.nextSibling;
  3690. }
  3691. }
  3692. }
  3693. me.fireEvent('aftersetcontent');
  3694. me.fireEvent('contentchange');
  3695. !notFireSelectionchange && me._selectionChange();
  3696. //清除保存的选区
  3697. me._bakRange = me._bakIERange = me._bakNativeRange = null;
  3698. //trace:1742 setContent后gecko能得到焦点问题
  3699. var geckoSel;
  3700. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3701. geckoSel.removeAllRanges();
  3702. }
  3703. if (me.options.autoSyncData) {
  3704. me.form && setValue(me.form, me);
  3705. }
  3706. },
  3707. /**
  3708. * 让编辑器获得焦点,toEnd确定focus位置
  3709. * @name focus
  3710. * @grammar editor.focus([toEnd]) //默认focus到编辑器头部,toEnd为true时focus到内容尾部
  3711. */
  3712. focus: function (toEnd) {
  3713. try {
  3714. var me = this,
  3715. rng = me.selection.getRange();
  3716. if (toEnd) {
  3717. rng.setStartAtLast(me.body.lastChild).setCursor(false, true);
  3718. } else {
  3719. rng.select(true);
  3720. }
  3721. this.fireEvent('focus');
  3722. } catch (e) {
  3723. }
  3724. },
  3725. /**
  3726. * 使编辑区域失去焦点
  3727. */
  3728. blur: function () {
  3729. var sel = this.selection.getNative();
  3730. sel.empty ? sel.empty() : sel.removeAllRanges();
  3731. this.fireEvent('blur')
  3732. },
  3733. /**
  3734. * 判断编辑器当前是否获得了焦点
  3735. */
  3736. isFocus: function () {
  3737. if (this.fireEvent('isfocus') === true) {
  3738. return true;
  3739. }
  3740. return this.selection.isFocus();
  3741. },
  3742. /**
  3743. * 初始化UE事件及部分事件代理
  3744. * @private
  3745. * @ignore
  3746. */
  3747. _initEvents: function () {
  3748. var me = this,
  3749. cont = me.body,
  3750. _proxyDomEvent = function () {
  3751. me._proxyDomEvent.apply(me, arguments);
  3752. };
  3753. $(cont)
  3754. .on('click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)
  3755. .on('focus blur', _proxyDomEvent)
  3756. .on('mouseup keydown', function (evt) {
  3757. //特殊键不触发selectionchange
  3758. if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
  3759. return;
  3760. }
  3761. if (evt.button == 2) return;
  3762. me._selectionChange(250, evt);
  3763. });
  3764. },
  3765. /**
  3766. * 触发事件代理
  3767. * @private
  3768. * @ignore
  3769. */
  3770. _proxyDomEvent: function (evt) {
  3771. return this.fireEvent(evt.type.replace(/^on/, ''), evt);
  3772. },
  3773. /**
  3774. * 变化选区
  3775. * @private
  3776. * @ignore
  3777. */
  3778. _selectionChange: function (delay, evt) {
  3779. var me = this;
  3780. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  3781. // if ( !me.selection.isFocus() ){
  3782. // return;
  3783. // }
  3784. var hackForMouseUp = false;
  3785. var mouseX, mouseY;
  3786. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  3787. var range = this.selection.getRange();
  3788. if (!range.collapsed) {
  3789. hackForMouseUp = true;
  3790. mouseX = evt.clientX;
  3791. mouseY = evt.clientY;
  3792. }
  3793. }
  3794. clearTimeout(_selectionChangeTimer);
  3795. _selectionChangeTimer = setTimeout(function () {
  3796. if (!me.selection.getNative()) {
  3797. return;
  3798. }
  3799. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  3800. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  3801. var ieRange;
  3802. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  3803. ieRange = me.document.body.createTextRange();
  3804. try {
  3805. ieRange.moveToPoint(mouseX, mouseY);
  3806. } catch (ex) {
  3807. ieRange = null;
  3808. }
  3809. }
  3810. var bakGetIERange;
  3811. if (ieRange) {
  3812. bakGetIERange = me.selection.getIERange;
  3813. me.selection.getIERange = function () {
  3814. return ieRange;
  3815. };
  3816. }
  3817. me.selection.cache();
  3818. if (bakGetIERange) {
  3819. me.selection.getIERange = bakGetIERange;
  3820. }
  3821. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  3822. me.fireEvent('beforeselectionchange');
  3823. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  3824. me.fireEvent('selectionchange', !!evt);
  3825. me.fireEvent('afterselectionchange');
  3826. me.selection.clear();
  3827. }
  3828. }, delay || 50);
  3829. },
  3830. _callCmdFn: function (fnName, args) {
  3831. args = Array.prototype.slice.call(args, 0);
  3832. var cmdName = args.shift().toLowerCase(),
  3833. cmd, cmdFn;
  3834. cmd = this.commands[cmdName] || UM.commands[cmdName];
  3835. cmdFn = cmd && cmd[fnName];
  3836. //没有querycommandstate或者没有command的都默认返回0
  3837. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  3838. return 0;
  3839. } else if (cmdFn) {
  3840. return cmdFn.apply(this, [cmdName].concat(args));
  3841. }
  3842. },
  3843. /**
  3844. * 执行编辑命令cmdName,完成富文本编辑效果
  3845. * @name execCommand
  3846. * @grammar editor.execCommand(cmdName) => {*}
  3847. */
  3848. execCommand: function (cmdName) {
  3849. if (!this.isFocus()) {
  3850. var bakRange = this.selection._bakRange;
  3851. if (bakRange) {
  3852. bakRange.select()
  3853. } else {
  3854. this.focus(true)
  3855. }
  3856. }
  3857. cmdName = cmdName.toLowerCase();
  3858. var me = this,
  3859. result,
  3860. cmd = me.commands[cmdName] || UM.commands[cmdName];
  3861. if (!cmd || !cmd.execCommand) {
  3862. return null;
  3863. }
  3864. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  3865. me.__hasEnterExecCommand = true;
  3866. if (me.queryCommandState.apply(me, arguments) != -1) {
  3867. me.fireEvent('saveScene');
  3868. me.fireEvent('beforeexeccommand', cmdName);
  3869. result = this._callCmdFn('execCommand', arguments);
  3870. (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  3871. me.fireEvent('afterexeccommand', cmdName);
  3872. me.fireEvent('saveScene');
  3873. }
  3874. me.__hasEnterExecCommand = false;
  3875. } else {
  3876. result = this._callCmdFn('execCommand', arguments);
  3877. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
  3878. }
  3879. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
  3880. return result;
  3881. },
  3882. /**
  3883. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  3884. * @name queryCommandState
  3885. * @grammar editor.queryCommandState(cmdName) => (-1|0|1)
  3886. * @desc
  3887. * * ''-1'' 当前命令不可用
  3888. * * ''0'' 当前命令可用
  3889. * * ''1'' 当前命令已经执行过了
  3890. */
  3891. queryCommandState: function (cmdName) {
  3892. try {
  3893. return this._callCmdFn('queryCommandState', arguments);
  3894. } catch (e) {
  3895. return 0
  3896. }
  3897. },
  3898. /**
  3899. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  3900. * @name queryCommandValue
  3901. * @grammar editor.queryCommandValue(cmdName) => {*}
  3902. */
  3903. queryCommandValue: function (cmdName) {
  3904. try {
  3905. return this._callCmdFn('queryCommandValue', arguments);
  3906. } catch (e) {
  3907. return null
  3908. }
  3909. },
  3910. /**
  3911. * 检查编辑区域中是否有内容,若包含tags中的节点类型,直接返回true
  3912. * @name hasContents
  3913. * @desc
  3914. * 默认有文本内容,或者有以下节点都不认为是空
  3915. * <code>{table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1}</code>
  3916. * @grammar editor.hasContents() => (true|false)
  3917. * @grammar editor.hasContents(tags) => (true|false) //若文档中包含tags数组里对应的tag,直接返回true
  3918. * @example
  3919. * editor.hasContents(['span']) //如果编辑器里有这些,不认为是空
  3920. */
  3921. hasContents: function (tags) {
  3922. if (tags) {
  3923. for (var i = 0, ci; ci = tags[i++];) {
  3924. if (this.body.getElementsByTagName(ci).length > 0) {
  3925. return true;
  3926. }
  3927. }
  3928. }
  3929. if (!domUtils.isEmptyBlock(this.body)) {
  3930. return true
  3931. }
  3932. //随时添加,定义的特殊标签如果存在,不能认为是空
  3933. tags = ['div'];
  3934. for (i = 0; ci = tags[i++];) {
  3935. var nodes = domUtils.getElementsByTagName(this.body, ci);
  3936. for (var n = 0, cn; cn = nodes[n++];) {
  3937. if (domUtils.isCustomeNode(cn)) {
  3938. return true;
  3939. }
  3940. }
  3941. }
  3942. return false;
  3943. },
  3944. /**
  3945. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  3946. * @name reset
  3947. * @desc
  3948. * * 清空编辑器内容
  3949. * * 清空回退列表
  3950. * @grammar editor.reset()
  3951. */
  3952. reset: function () {
  3953. this.fireEvent('reset');
  3954. },
  3955. isEnabled: function () {
  3956. return this._isEnabled != true;
  3957. },
  3958. setEnabled: function () {
  3959. var me = this, range;
  3960. me.body.contentEditable = true;
  3961. /* 恢复选区 */
  3962. if (me.lastBk) {
  3963. range = me.selection.getRange();
  3964. try {
  3965. range.moveToBookmark(me.lastBk);
  3966. delete me.lastBk
  3967. } catch (e) {
  3968. range.setStartAtFirst(me.body).collapse(true)
  3969. }
  3970. range.select(true);
  3971. }
  3972. /* 恢复query函数 */
  3973. if (me.bkqueryCommandState) {
  3974. me.queryCommandState = me.bkqueryCommandState;
  3975. delete me.bkqueryCommandState;
  3976. }
  3977. /* 恢复原生事件 */
  3978. if (me._bkproxyDomEvent) {
  3979. me._proxyDomEvent = me._bkproxyDomEvent;
  3980. delete me._bkproxyDomEvent;
  3981. }
  3982. /* 触发事件 */
  3983. me.fireEvent('setEnabled');
  3984. },
  3985. /**
  3986. * 设置当前编辑区域可以编辑
  3987. * @name enable
  3988. * @grammar editor.enable()
  3989. */
  3990. enable: function () {
  3991. return this.setEnabled();
  3992. },
  3993. setDisabled: function (except, keepDomEvent) {
  3994. var me = this;
  3995. me.body.contentEditable = false;
  3996. me._except = except ? utils.isArray(except) ? except : [except] : [];
  3997. /* 备份最后的选区 */
  3998. if (!me.lastBk) {
  3999. me.lastBk = me.selection.getRange().createBookmark(true);
  4000. }
  4001. /* 备份并重置query函数 */
  4002. if (!me.bkqueryCommandState) {
  4003. me.bkqueryCommandState = me.queryCommandState;
  4004. me.queryCommandState = function (type) {
  4005. if (utils.indexOf(me._except, type) != -1) {
  4006. return me.bkqueryCommandState.apply(me, arguments);
  4007. }
  4008. return -1;
  4009. };
  4010. }
  4011. /* 备份并墙原生事件 */
  4012. if (!keepDomEvent && !me._bkproxyDomEvent) {
  4013. me._bkproxyDomEvent = me._proxyDomEvent;
  4014. me._proxyDomEvent = function () {
  4015. return false;
  4016. };
  4017. }
  4018. /* 触发事件 */
  4019. me.fireEvent('selectionchange');
  4020. me.fireEvent('setDisabled', me._except);
  4021. },
  4022. /** 设置当前编辑区域不可编辑,except中的命令除外
  4023. * @name disable
  4024. * @grammar editor.disable()
  4025. * @grammar editor.disable(except) //例外的命令,也即即使设置了disable,此处配置的命令仍然可以执行
  4026. * @example
  4027. * //禁用工具栏中除加粗和插入图片之外的所有功能
  4028. * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array
  4029. */
  4030. disable: function (except) {
  4031. return this.setDisabled(except);
  4032. },
  4033. /**
  4034. * 设置默认内容
  4035. * @ignore
  4036. * @private
  4037. * @param {String} cont 要存入的内容
  4038. */
  4039. _setDefaultContent: function () {
  4040. function clear() {
  4041. var me = this;
  4042. if (me.document.getElementById('initContent')) {
  4043. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
  4044. me.removeListener('firstBeforeExecCommand focus', clear);
  4045. setTimeout(function () {
  4046. me.focus();
  4047. me._selectionChange();
  4048. }, 0)
  4049. }
  4050. }
  4051. return function (cont) {
  4052. var me = this;
  4053. me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  4054. me.addListener('firstBeforeExecCommand focus', clear);
  4055. }
  4056. }(),
  4057. /**
  4058. * show方法的兼容版本
  4059. * @private
  4060. * @ignore
  4061. */
  4062. setShow: function () {
  4063. var me = this, range = me.selection.getRange();
  4064. if (me.container.style.display == 'none') {
  4065. //有可能内容丢失了
  4066. try {
  4067. range.moveToBookmark(me.lastBk);
  4068. delete me.lastBk
  4069. } catch (e) {
  4070. range.setStartAtFirst(me.body).collapse(true)
  4071. }
  4072. //ie下focus实效,所以做了个延迟
  4073. setTimeout(function () {
  4074. range.select(true);
  4075. }, 100);
  4076. me.container.style.display = '';
  4077. }
  4078. },
  4079. /**
  4080. * 显示编辑器
  4081. * @name show
  4082. * @grammar editor.show()
  4083. */
  4084. show: function () {
  4085. return this.setShow();
  4086. },
  4087. /**
  4088. * hide方法的兼容版本
  4089. * @private
  4090. * @ignore
  4091. */
  4092. setHide: function () {
  4093. var me = this;
  4094. if (!me.lastBk) {
  4095. me.lastBk = me.selection.getRange().createBookmark(true);
  4096. }
  4097. me.container.style.display = 'none'
  4098. },
  4099. /**
  4100. * 隐藏编辑器
  4101. * @name hide
  4102. * @grammar editor.hide()
  4103. */
  4104. hide: function () {
  4105. return this.setHide();
  4106. },
  4107. /**
  4108. * 根据制定的路径,获取对应的语言资源
  4109. * @name getLang
  4110. * @grammar editor.getLang(path) => (JSON|String) 路径根据的是lang目录下的语言文件的路径结构
  4111. * @example
  4112. * editor.getLang('contextMenu.delete') //如果当前是中文,那返回是的是删除
  4113. */
  4114. getLang: function (path) {
  4115. var lang = UM.I18N[this.options.lang];
  4116. if (!lang) {
  4117. throw Error("not import language file");
  4118. }
  4119. path = (path || "").split(".");
  4120. for (var i = 0, ci; ci = path[i++];) {
  4121. lang = lang[ci];
  4122. if (!lang) break;
  4123. }
  4124. return lang;
  4125. },
  4126. /**
  4127. * 计算编辑器当前内容的长度
  4128. * @name getContentLength
  4129. * @grammar editor.getContentLength(ingoneHtml,tagNames) =>
  4130. * @example
  4131. * editor.getLang(true)
  4132. */
  4133. getContentLength: function (ingoneHtml, tagNames) {
  4134. var count = this.getContent(false, false, true).length;
  4135. if (ingoneHtml) {
  4136. tagNames = (tagNames || []).concat(['hr', 'img', 'iframe']);
  4137. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
  4138. for (var i = 0, ci; ci = tagNames[i++];) {
  4139. count += this.body.getElementsByTagName(ci).length;
  4140. }
  4141. }
  4142. return count;
  4143. },
  4144. addInputRule: function (rule, ignoreUndo) {
  4145. rule.ignoreUndo = ignoreUndo;
  4146. this.inputRules.push(rule);
  4147. },
  4148. filterInputRule: function (root, isUndoLoad) {
  4149. for (var i = 0, ci; ci = this.inputRules[i++];) {
  4150. if (isUndoLoad && ci.ignoreUndo) {
  4151. continue;
  4152. }
  4153. ci.call(this, root)
  4154. }
  4155. },
  4156. addOutputRule: function (rule, ignoreUndo) {
  4157. rule.ignoreUndo = ignoreUndo;
  4158. this.outputRules.push(rule)
  4159. },
  4160. filterOutputRule: function (root, isUndoLoad) {
  4161. for (var i = 0, ci; ci = this.outputRules[i++];) {
  4162. if (isUndoLoad && ci.ignoreUndo) {
  4163. continue;
  4164. }
  4165. ci.call(this, root)
  4166. }
  4167. }
  4168. };
  4169. utils.inherits(Editor, EventBase);
  4170. })();
  4171. /**
  4172. * @file
  4173. * @name UM.filterWord
  4174. * @short filterWord
  4175. * @desc 用来过滤word粘贴过来的字符串
  4176. * @import editor.js,core/utils.js
  4177. * @anthor zhanyi
  4178. */
  4179. var filterWord = UM.filterWord = function () {
  4180. //是否是word过来的内容
  4181. function isWordDocument(str) {
  4182. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test(str);
  4183. }
  4184. //去掉小数
  4185. function transUnit(v) {
  4186. v = v.replace(/[\d.]+\w+/g, function (m) {
  4187. return utils.transUnitToPx(m);
  4188. });
  4189. return v;
  4190. }
  4191. function filterPasteWord(str) {
  4192. return str.replace(/[\t\r\n]+/g, ' ')
  4193. .replace(/<!--[\s\S]*?-->/ig, "")
  4194. //转换图片
  4195. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi, function (str) {
  4196. //opera能自己解析出image所这里直接返回空
  4197. if (browser.opera) {
  4198. return '';
  4199. }
  4200. try {
  4201. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  4202. if (/Bitmap/i.test(str)) {
  4203. return '';
  4204. }
  4205. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  4206. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  4207. src = str.match(/src=\s*"([^"]*)"/i)[1];
  4208. return '<img width="' + transUnit(width) + '" height="' + transUnit(height) + '" src="' + src + '" />';
  4209. } catch (e) {
  4210. return '';
  4211. }
  4212. })
  4213. //针对wps添加的多余标签处理
  4214. .replace(/<\/?div[^>]*>/g, '')
  4215. //去掉多余的属性
  4216. .replace(/v:\w+=(["']?)[^'"]+\1/g, '')
  4217. .replace(/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "")
  4218. .replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>")
  4219. //去掉多余的属性
  4220. .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function (str, name, marks, val) {
  4221. //保留list的标示
  4222. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  4223. })
  4224. //清除多余的font/span不能匹配&nbsp;有可能是空格
  4225. .replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function (a, b, c) {
  4226. return c.replace(/[\t\r\n ]+/g, ' ')
  4227. })
  4228. //处理style的问题
  4229. .replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function (str, tag, tmp, style) {
  4230. var n = [],
  4231. s = style.replace(/^\s+|\s+$/, '')
  4232. .replace(/&#39;/g, '\'')
  4233. .replace(/&quot;/gi, "'")
  4234. .split(/;\s*/g);
  4235. for (var i = 0, v; v = s[i]; i++) {
  4236. var name, value,
  4237. parts = v.split(":");
  4238. if (parts.length == 2) {
  4239. name = parts[0].toLowerCase();
  4240. value = parts[1].toLowerCase();
  4241. if (/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g, '').length == 0
  4242. ||
  4243. /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
  4244. ) {
  4245. continue;
  4246. }
  4247. switch (name) {
  4248. case "mso-padding-alt":
  4249. case "mso-padding-top-alt":
  4250. case "mso-padding-right-alt":
  4251. case "mso-padding-bottom-alt":
  4252. case "mso-padding-left-alt":
  4253. case "mso-margin-alt":
  4254. case "mso-margin-top-alt":
  4255. case "mso-margin-right-alt":
  4256. case "mso-margin-bottom-alt":
  4257. case "mso-margin-left-alt":
  4258. //ie下会出现挤到一起的情况
  4259. //case "mso-table-layout-alt":
  4260. case "mso-height":
  4261. case "mso-width":
  4262. case "mso-vertical-align-alt":
  4263. //trace:1819 ff下会解析出padding在table上
  4264. if (!/<table/.test(tag))
  4265. n[i] = name.replace(/^mso-|-alt$/g, "") + ":" + transUnit(value);
  4266. continue;
  4267. case "horiz-align":
  4268. n[i] = "text-align:" + value;
  4269. continue;
  4270. case "vert-align":
  4271. n[i] = "vertical-align:" + value;
  4272. continue;
  4273. case "font-color":
  4274. case "mso-foreground":
  4275. n[i] = "color:" + value;
  4276. continue;
  4277. case "mso-background":
  4278. case "mso-highlight":
  4279. n[i] = "background:" + value;
  4280. continue;
  4281. case "mso-default-height":
  4282. n[i] = "min-height:" + transUnit(value);
  4283. continue;
  4284. case "mso-default-width":
  4285. n[i] = "min-width:" + transUnit(value);
  4286. continue;
  4287. case "mso-padding-between-alt":
  4288. n[i] = "border-collapse:separate;border-spacing:" + transUnit(value);
  4289. continue;
  4290. case "text-line-through":
  4291. if ((value == "single") || (value == "double")) {
  4292. n[i] = "text-decoration:line-through";
  4293. }
  4294. continue;
  4295. case "mso-zero-height":
  4296. if (value == "yes") {
  4297. n[i] = "display:none";
  4298. }
  4299. continue;
  4300. // case 'background':
  4301. // break;
  4302. case 'margin':
  4303. if (!/[1-9]/.test(value)) {
  4304. continue;
  4305. }
  4306. }
  4307. 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)
  4308. ||
  4309. /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
  4310. ) {
  4311. continue;
  4312. }
  4313. n[i] = name + ":" + parts[1];
  4314. }
  4315. }
  4316. return tag + (n.length ? ' style="' + n.join(';').replace(/;{2,}/g, ';') + '"' : '');
  4317. })
  4318. .replace(/[\d.]+(cm|pt)/g, function (str) {
  4319. return utils.transUnitToPx(str)
  4320. })
  4321. }
  4322. return function (html) {
  4323. return (isWordDocument(html) ? filterPasteWord(html) : html);
  4324. };
  4325. }();
  4326. ///import editor.js
  4327. ///import core/utils.js
  4328. ///import core/dom/dom.js
  4329. ///import core/dom/dtd.js
  4330. ///import core/htmlparser.js
  4331. //模拟的节点类
  4332. //by zhanyi
  4333. (function () {
  4334. var uNode = UM.uNode = function (obj) {
  4335. this.type = obj.type;
  4336. this.data = obj.data;
  4337. this.tagName = obj.tagName;
  4338. this.parentNode = obj.parentNode;
  4339. this.attrs = obj.attrs || {};
  4340. this.children = obj.children;
  4341. };
  4342. var notTransAttrs = {
  4343. 'href': 1,
  4344. 'src': 1,
  4345. '_src': 1,
  4346. '_href': 1,
  4347. 'cdata_data': 1
  4348. };
  4349. var notTransTagName = {
  4350. style: 1,
  4351. script: 1
  4352. };
  4353. var indentChar = ' ',
  4354. breakChar = '\n';
  4355. function insertLine(arr, current, begin) {
  4356. arr.push(breakChar);
  4357. return current + (begin ? 1 : -1);
  4358. }
  4359. function insertIndent(arr, current) {
  4360. //插入缩进
  4361. for (var i = 0; i < current; i++) {
  4362. arr.push(indentChar);
  4363. }
  4364. }
  4365. //创建uNode的静态方法
  4366. //支持标签和html
  4367. uNode.createElement = function (html) {
  4368. if (/[<>]/.test(html)) {
  4369. return UM.htmlparser(html).children[0]
  4370. } else {
  4371. return new uNode({
  4372. type: 'element',
  4373. children: [],
  4374. tagName: html
  4375. })
  4376. }
  4377. };
  4378. uNode.createText = function (data, noTrans) {
  4379. return new UM.uNode({
  4380. type: 'text',
  4381. 'data': noTrans ? data : utils.unhtml(data || '')
  4382. })
  4383. };
  4384. function nodeToHtml(node, arr, formatter, current) {
  4385. switch (node.type) {
  4386. case 'root':
  4387. for (var i = 0, ci; ci = node.children[i++];) {
  4388. //插入新行
  4389. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4390. insertLine(arr, current, true);
  4391. insertIndent(arr, current)
  4392. }
  4393. nodeToHtml(ci, arr, formatter, current)
  4394. }
  4395. break;
  4396. case 'text':
  4397. isText(node, arr);
  4398. break;
  4399. case 'element':
  4400. isElement(node, arr, formatter, current);
  4401. break;
  4402. case 'comment':
  4403. isComment(node, arr, formatter);
  4404. }
  4405. return arr;
  4406. }
  4407. function isText(node, arr) {
  4408. if (node.parentNode.tagName == 'pre') {
  4409. //源码模式下输入html标签,不能做转换处理,直接输出
  4410. arr.push(node.data)
  4411. } else {
  4412. arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g, ' &nbsp;'))
  4413. }
  4414. }
  4415. function isElement(node, arr, formatter, current) {
  4416. var attrhtml = '';
  4417. if (node.attrs) {
  4418. attrhtml = [];
  4419. var attrs = node.attrs;
  4420. for (var a in attrs) {
  4421. //这里就针对
  4422. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  4423. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  4424. //有可能做的不够
  4425. attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  4426. return '&quot;'
  4427. }) : utils.unhtml(attrs[a])) + '"' : ''))
  4428. }
  4429. attrhtml = attrhtml.join(' ');
  4430. }
  4431. arr.push('<' + node.tagName +
  4432. (attrhtml ? ' ' + attrhtml : '') +
  4433. (dtd.$empty[node.tagName] ? '\/' : '') + '>'
  4434. );
  4435. //插入新行
  4436. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4437. if (node.children && node.children.length) {
  4438. current = insertLine(arr, current, true);
  4439. insertIndent(arr, current)
  4440. }
  4441. }
  4442. if (node.children && node.children.length) {
  4443. for (var i = 0, ci; ci = node.children[i++];) {
  4444. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4445. insertLine(arr, current);
  4446. insertIndent(arr, current)
  4447. }
  4448. nodeToHtml(ci, arr, formatter, current)
  4449. }
  4450. }
  4451. if (!dtd.$empty[node.tagName]) {
  4452. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4453. if (node.children && node.children.length) {
  4454. current = insertLine(arr, current);
  4455. insertIndent(arr, current)
  4456. }
  4457. }
  4458. arr.push('<\/' + node.tagName + '>');
  4459. }
  4460. }
  4461. function isComment(node, arr) {
  4462. arr.push('<!--' + node.data + '-->');
  4463. }
  4464. function getNodeById(root, id) {
  4465. var node;
  4466. if (root.type == 'element' && root.getAttr('id') == id) {
  4467. return root;
  4468. }
  4469. if (root.children && root.children.length) {
  4470. for (var i = 0, ci; ci = root.children[i++];) {
  4471. if (node = getNodeById(ci, id)) {
  4472. return node;
  4473. }
  4474. }
  4475. }
  4476. }
  4477. function getNodesByTagName(node, tagName, arr) {
  4478. if (node.type == 'element' && node.tagName == tagName) {
  4479. arr.push(node);
  4480. }
  4481. if (node.children && node.children.length) {
  4482. for (var i = 0, ci; ci = node.children[i++];) {
  4483. getNodesByTagName(ci, tagName, arr)
  4484. }
  4485. }
  4486. }
  4487. function nodeTraversal(root, fn) {
  4488. if (root.children && root.children.length) {
  4489. for (var i = 0, ci; ci = root.children[i];) {
  4490. nodeTraversal(ci, fn);
  4491. //ci被替换的情况,这里就不再走 fn了
  4492. if (ci.parentNode) {
  4493. if (ci.children && ci.children.length) {
  4494. fn(ci)
  4495. }
  4496. if (ci.parentNode) i++
  4497. }
  4498. }
  4499. } else {
  4500. fn(root)
  4501. }
  4502. }
  4503. uNode.prototype = {
  4504. /**
  4505. * 当前节点对象,转换成html文本
  4506. * @method toHtml
  4507. * @return { String } 返回转换后的html字符串
  4508. * @example
  4509. * ```javascript
  4510. * node.toHtml();
  4511. * ```
  4512. */
  4513. /**
  4514. * 当前节点对象,转换成html文本
  4515. * @method toHtml
  4516. * @param { Boolean } formatter 是否格式化返回值
  4517. * @return { String } 返回转换后的html字符串
  4518. * @example
  4519. * ```javascript
  4520. * node.toHtml( true );
  4521. * ```
  4522. */
  4523. toHtml: function (formatter) {
  4524. var arr = [];
  4525. nodeToHtml(this, arr, formatter, 0);
  4526. return arr.join('')
  4527. },
  4528. /**
  4529. * 获取节点的html内容
  4530. * @method innerHTML
  4531. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4532. * @return { String } 返回节点的html内容
  4533. * @example
  4534. * ```javascript
  4535. * var htmlstr = node.innerHTML();
  4536. * ```
  4537. */
  4538. /**
  4539. * 设置节点的html内容
  4540. * @method innerHTML
  4541. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4542. * @param { String } htmlstr 传入要设置的html内容
  4543. * @return { UM.uNode } 返回节点本身
  4544. * @example
  4545. * ```javascript
  4546. * node.innerHTML('<span>text</span>');
  4547. * ```
  4548. */
  4549. innerHTML: function (htmlstr) {
  4550. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4551. return this;
  4552. }
  4553. if (utils.isString(htmlstr)) {
  4554. if (this.children) {
  4555. for (var i = 0, ci; ci = this.children[i++];) {
  4556. ci.parentNode = null;
  4557. }
  4558. }
  4559. this.children = [];
  4560. var tmpRoot = UM.htmlparser(htmlstr);
  4561. for (var i = 0, ci; ci = tmpRoot.children[i++];) {
  4562. this.children.push(ci);
  4563. ci.parentNode = this;
  4564. }
  4565. return this;
  4566. } else {
  4567. var tmpRoot = new UM.uNode({
  4568. type: 'root',
  4569. children: this.children
  4570. });
  4571. return tmpRoot.toHtml();
  4572. }
  4573. },
  4574. /**
  4575. * 获取节点的纯文本内容
  4576. * @method innerText
  4577. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4578. * @return { String } 返回节点的存文本内容
  4579. * @example
  4580. * ```javascript
  4581. * var textStr = node.innerText();
  4582. * ```
  4583. */
  4584. /**
  4585. * 设置节点的纯文本内容
  4586. * @method innerText
  4587. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4588. * @param { String } textStr 传入要设置的文本内容
  4589. * @return { UM.uNode } 返回节点本身
  4590. * @example
  4591. * ```javascript
  4592. * node.innerText('<span>text</span>');
  4593. * ```
  4594. */
  4595. innerText: function (textStr, noTrans) {
  4596. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4597. return this;
  4598. }
  4599. if (textStr) {
  4600. if (this.children) {
  4601. for (var i = 0, ci; ci = this.children[i++];) {
  4602. ci.parentNode = null;
  4603. }
  4604. }
  4605. this.children = [];
  4606. this.appendChild(uNode.createText(textStr, noTrans));
  4607. return this;
  4608. } else {
  4609. return this.toHtml().replace(/<[^>]+>/g, '');
  4610. }
  4611. },
  4612. /**
  4613. * 获取当前对象的data属性
  4614. * @method getData
  4615. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  4616. * @example
  4617. * ```javascript
  4618. * node.getData();
  4619. * ```
  4620. */
  4621. getData: function () {
  4622. if (this.type == 'element')
  4623. return '';
  4624. return this.data
  4625. },
  4626. /**
  4627. * 获取当前节点下的第一个子节点
  4628. * @method firstChild
  4629. * @return { UM.uNode } 返回第一个子节点
  4630. * @example
  4631. * ```javascript
  4632. * node.firstChild(); //返回第一个子节点
  4633. * ```
  4634. */
  4635. firstChild: function () {
  4636. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4637. // return this;
  4638. // }
  4639. return this.children ? this.children[0] : null;
  4640. },
  4641. /**
  4642. * 获取当前节点下的最后一个子节点
  4643. * @method lastChild
  4644. * @return { UM.uNode } 返回最后一个子节点
  4645. * @example
  4646. * ```javascript
  4647. * node.lastChild(); //返回最后一个子节点
  4648. * ```
  4649. */
  4650. lastChild: function () {
  4651. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  4652. // return this;
  4653. // }
  4654. return this.children ? this.children[this.children.length - 1] : null;
  4655. },
  4656. /**
  4657. * 获取和当前节点有相同父亲节点的前一个节点
  4658. * @method previousSibling
  4659. * @return { UM.uNode } 返回前一个节点
  4660. * @example
  4661. * ```javascript
  4662. * node.children[2].previousSibling(); //返回子节点node.children[1]
  4663. * ```
  4664. */
  4665. previousSibling: function () {
  4666. var parent = this.parentNode;
  4667. for (var i = 0, ci; ci = parent.children[i]; i++) {
  4668. if (ci === this) {
  4669. return i == 0 ? null : parent.children[i - 1];
  4670. }
  4671. }
  4672. },
  4673. /**
  4674. * 获取和当前节点有相同父亲节点的后一个节点
  4675. * @method nextSibling
  4676. * @return { UM.uNode } 返回后一个节点,找不到返回null
  4677. * @example
  4678. * ```javascript
  4679. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  4680. * ```
  4681. */
  4682. nextSibling: function () {
  4683. var parent = this.parentNode;
  4684. for (var i = 0, ci; ci = parent.children[i++];) {
  4685. if (ci === this) {
  4686. return parent.children[i];
  4687. }
  4688. }
  4689. },
  4690. /**
  4691. * 用新的节点替换当前节点
  4692. * @method replaceChild
  4693. * @param { UM.uNode } target 要替换成该节点参数
  4694. * @param { UM.uNode } source 要被替换掉的节点
  4695. * @return { UM.uNode } 返回替换之后的节点对象
  4696. * @example
  4697. * ```javascript
  4698. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  4699. * ```
  4700. */
  4701. replaceChild: function (target, source) {
  4702. if (this.children) {
  4703. if (target.parentNode) {
  4704. target.parentNode.removeChild(target);
  4705. }
  4706. for (var i = 0, ci; ci = this.children[i]; i++) {
  4707. if (ci === source) {
  4708. this.children.splice(i, 1, target);
  4709. source.parentNode = null;
  4710. target.parentNode = this;
  4711. return target;
  4712. }
  4713. }
  4714. }
  4715. },
  4716. /**
  4717. * 在节点的子节点列表最后位置插入一个节点
  4718. * @method appendChild
  4719. * @param { UM.uNode } node 要插入的节点
  4720. * @return { UM.uNode } 返回刚插入的子节点
  4721. * @example
  4722. * ```javascript
  4723. * node.appendChild( newNode ); //在node内插入子节点newNode
  4724. * ```
  4725. */
  4726. appendChild: function (node) {
  4727. if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
  4728. if (!this.children) {
  4729. this.children = []
  4730. }
  4731. if (node.parentNode) {
  4732. node.parentNode.removeChild(node);
  4733. }
  4734. for (var i = 0, ci; ci = this.children[i]; i++) {
  4735. if (ci === node) {
  4736. this.children.splice(i, 1);
  4737. break;
  4738. }
  4739. }
  4740. this.children.push(node);
  4741. node.parentNode = this;
  4742. return node;
  4743. }
  4744. },
  4745. /**
  4746. * 在传入节点的前面插入一个节点
  4747. * @method insertBefore
  4748. * @param { UM.uNode } target 要插入的节点
  4749. * @param { UM.uNode } source 在该参数节点前面插入
  4750. * @return { UM.uNode } 返回刚插入的子节点
  4751. * @example
  4752. * ```javascript
  4753. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  4754. * ```
  4755. */
  4756. insertBefore: function (target, source) {
  4757. if (this.children) {
  4758. if (target.parentNode) {
  4759. target.parentNode.removeChild(target);
  4760. }
  4761. for (var i = 0, ci; ci = this.children[i]; i++) {
  4762. if (ci === source) {
  4763. this.children.splice(i, 0, target);
  4764. target.parentNode = this;
  4765. return target;
  4766. }
  4767. }
  4768. }
  4769. },
  4770. /**
  4771. * 在传入节点的后面插入一个节点
  4772. * @method insertAfter
  4773. * @param { UM.uNode } target 要插入的节点
  4774. * @param { UM.uNode } source 在该参数节点后面插入
  4775. * @return { UM.uNode } 返回刚插入的子节点
  4776. * @example
  4777. * ```javascript
  4778. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  4779. * ```
  4780. */
  4781. insertAfter: function (target, source) {
  4782. if (this.children) {
  4783. if (target.parentNode) {
  4784. target.parentNode.removeChild(target);
  4785. }
  4786. for (var i = 0, ci; ci = this.children[i]; i++) {
  4787. if (ci === source) {
  4788. this.children.splice(i + 1, 0, target);
  4789. target.parentNode = this;
  4790. return target;
  4791. }
  4792. }
  4793. }
  4794. },
  4795. /**
  4796. * 从当前节点的子节点列表中,移除节点
  4797. * @method removeChild
  4798. * @param { UM.uNode } node 要移除的节点引用
  4799. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  4800. * @return { * } 返回刚移除的子节点
  4801. * @example
  4802. * ```javascript
  4803. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  4804. * ```
  4805. */
  4806. removeChild: function (node, keepChildren) {
  4807. if (this.children) {
  4808. for (var i = 0, ci; ci = this.children[i]; i++) {
  4809. if (ci === node) {
  4810. this.children.splice(i, 1);
  4811. ci.parentNode = null;
  4812. if (keepChildren && ci.children && ci.children.length) {
  4813. for (var j = 0, cj; cj = ci.children[j]; j++) {
  4814. this.children.splice(i + j, 0, cj);
  4815. cj.parentNode = this;
  4816. }
  4817. }
  4818. return ci;
  4819. }
  4820. }
  4821. }
  4822. },
  4823. /**
  4824. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  4825. * @method getAttr
  4826. * @param { String } attrName 要获取的属性名称
  4827. * @return { * } 返回attrs对象下的属性值
  4828. * @example
  4829. * ```javascript
  4830. * node.getAttr('title');
  4831. * ```
  4832. */
  4833. getAttr: function (attrName) {
  4834. return this.attrs && this.attrs[attrName.toLowerCase()]
  4835. },
  4836. /**
  4837. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  4838. * @method setAttr
  4839. * @param { String } attrName 要设置的属性名称
  4840. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  4841. * @return { * } 返回attrs对象下的属性值
  4842. * @example
  4843. * ```javascript
  4844. * node.setAttr('title','标题');
  4845. * ```
  4846. */
  4847. setAttr: function (attrName, attrVal) {
  4848. if (!attrName) {
  4849. delete this.attrs;
  4850. return;
  4851. }
  4852. if (!this.attrs) {
  4853. this.attrs = {};
  4854. }
  4855. if (utils.isObject(attrName)) {
  4856. for (var a in attrName) {
  4857. if (!attrName[a]) {
  4858. delete this.attrs[a]
  4859. } else {
  4860. this.attrs[a.toLowerCase()] = attrName[a];
  4861. }
  4862. }
  4863. } else {
  4864. if (!attrVal) {
  4865. delete this.attrs[attrName]
  4866. } else {
  4867. this.attrs[attrName.toLowerCase()] = attrVal;
  4868. }
  4869. }
  4870. },
  4871. hasAttr: function (attrName) {
  4872. var attrVal = this.getAttr(attrName);
  4873. return (attrVal !== null) && (attrVal !== undefined);
  4874. },
  4875. /**
  4876. * 获取当前节点在父节点下的位置索引
  4877. * @method getIndex
  4878. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  4879. * @example
  4880. * ```javascript
  4881. * node.getIndex();
  4882. * ```
  4883. */
  4884. getIndex: function () {
  4885. var parent = this.parentNode;
  4886. for (var i = 0, ci; ci = parent.children[i]; i++) {
  4887. if (ci === this) {
  4888. return i;
  4889. }
  4890. }
  4891. return -1;
  4892. },
  4893. /**
  4894. * 在当前节点下,根据id查找节点
  4895. * @method getNodeById
  4896. * @param { String } id 要查找的id
  4897. * @return { UM.uNode } 返回找到的节点
  4898. * @example
  4899. * ```javascript
  4900. * node.getNodeById('textId');
  4901. * ```
  4902. */
  4903. getNodeById: function (id) {
  4904. var node;
  4905. if (this.children && this.children.length) {
  4906. for (var i = 0, ci; ci = this.children[i++];) {
  4907. if (node = getNodeById(ci, id)) {
  4908. return node;
  4909. }
  4910. }
  4911. }
  4912. },
  4913. /**
  4914. * 在当前节点下,根据元素名称查找节点列表
  4915. * @method getNodesByTagName
  4916. * @param { String } tagNames 要查找的元素名称
  4917. * @return { Array } 返回找到的节点列表
  4918. * @example
  4919. * ```javascript
  4920. * node.getNodesByTagName('span');
  4921. * ```
  4922. */
  4923. getNodesByTagName: function (tagNames) {
  4924. tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
  4925. var arr = [], me = this;
  4926. utils.each(tagNames, function (tagName) {
  4927. if (me.children && me.children.length) {
  4928. for (var i = 0, ci; ci = me.children[i++];) {
  4929. getNodesByTagName(ci, tagName, arr)
  4930. }
  4931. }
  4932. });
  4933. return arr;
  4934. },
  4935. /**
  4936. * 根据样式名称,获取节点的样式值
  4937. * @method getStyle
  4938. * @param { String } name 要获取的样式名称
  4939. * @return { String } 返回样式值
  4940. * @example
  4941. * ```javascript
  4942. * node.getStyle('font-size');
  4943. * ```
  4944. */
  4945. getStyle: function (name) {
  4946. var cssStyle = this.getAttr('style');
  4947. if (!cssStyle) {
  4948. return ''
  4949. }
  4950. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)', 'i');
  4951. var match = cssStyle.match(reg);
  4952. if (match && match[0]) {
  4953. return match[2]
  4954. }
  4955. return '';
  4956. },
  4957. /**
  4958. * 给节点设置样式
  4959. * @method setStyle
  4960. * @param { String } name 要设置的的样式名称
  4961. * @param { String } val 要设置的的样值
  4962. * @example
  4963. * ```javascript
  4964. * node.setStyle('font-size', '12px');
  4965. * ```
  4966. */
  4967. setStyle: function (name, val) {
  4968. function exec(name, val) {
  4969. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
  4970. cssStyle = cssStyle.replace(reg, '$1');
  4971. if (val) {
  4972. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  4973. }
  4974. }
  4975. var cssStyle = this.getAttr('style');
  4976. if (!cssStyle) {
  4977. cssStyle = '';
  4978. }
  4979. if (utils.isObject(name)) {
  4980. for (var a in name) {
  4981. exec(a, name[a])
  4982. }
  4983. } else {
  4984. exec(name, val)
  4985. }
  4986. this.setAttr('style', utils.trim(cssStyle))
  4987. },
  4988. hasClass: function (className) {
  4989. if (this.hasAttr('class')) {
  4990. var classNames = this.getAttr('class').split(/\s+/),
  4991. hasClass = false;
  4992. $.each(classNames, function (key, item) {
  4993. if (item === className) {
  4994. hasClass = true;
  4995. }
  4996. });
  4997. return hasClass;
  4998. } else {
  4999. return false;
  5000. }
  5001. },
  5002. addClass: function (className) {
  5003. var classes = null,
  5004. hasClass = false;
  5005. if (this.hasAttr('class')) {
  5006. classes = this.getAttr('class');
  5007. classes = classes.split(/\s+/);
  5008. classes.forEach(function (item) {
  5009. if (item === className) {
  5010. hasClass = true;
  5011. return;
  5012. }
  5013. });
  5014. !hasClass && classes.push(className);
  5015. this.setAttr('class', classes.join(" "));
  5016. } else {
  5017. this.setAttr('class', className);
  5018. }
  5019. },
  5020. removeClass: function (className) {
  5021. if (this.hasAttr('class')) {
  5022. var cl = this.getAttr('class');
  5023. cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'), '');
  5024. this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g, ' '));
  5025. }
  5026. },
  5027. /**
  5028. * 传入一个函数,递归遍历当前节点下的所有节点
  5029. * @method traversal
  5030. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  5031. * @example
  5032. * ```javascript
  5033. * traversal(node, function(){
  5034. * console.log(node.type);
  5035. * });
  5036. * ```
  5037. */
  5038. traversal: function (fn) {
  5039. if (this.children && this.children.length) {
  5040. nodeTraversal(this, fn);
  5041. }
  5042. return this;
  5043. }
  5044. }
  5045. })();
  5046. //html字符串转换成uNode节点
  5047. //by zhanyi
  5048. var htmlparser = UM.htmlparser = function (htmlstr, ignoreBlank) {
  5049. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  5050. //先去掉了,加上的原因忘了,这里先记录
  5051. var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  5052. re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  5053. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  5054. var allowEmptyTags = {
  5055. b: 1, code: 1, i: 1, u: 1, strike: 1, s: 1, tt: 1, strong: 1, q: 1, samp: 1, em: 1, span: 1,
  5056. sub: 1, img: 1, sup: 1, font: 1, big: 1, small: 1, iframe: 1, a: 1, br: 1, pre: 1
  5057. };
  5058. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
  5059. if (!ignoreBlank) {
  5060. htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n' + (ignoreBlank ? '' : ' ') + ']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n' + (ignoreBlank ? '' : ' ') + ']*', 'g'), function (a, b) {
  5061. //br暂时单独处理
  5062. if (b && allowEmptyTags[b.toLowerCase()]) {
  5063. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, '');
  5064. }
  5065. return a.replace(new RegExp('^[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+'), '').replace(new RegExp('[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+$'), '');
  5066. });
  5067. }
  5068. var notTransAttrs = {
  5069. 'href': 1,
  5070. 'src': 1
  5071. };
  5072. var uNode = UM.uNode,
  5073. needParentNode = {
  5074. 'td': 'tr',
  5075. 'tr': ['tbody', 'thead', 'tfoot'],
  5076. 'tbody': 'table',
  5077. 'th': 'tr',
  5078. 'thead': 'table',
  5079. 'tfoot': 'table',
  5080. 'caption': 'table',
  5081. 'li': ['ul', 'ol'],
  5082. 'dt': 'dl',
  5083. 'dd': 'dl',
  5084. 'option': 'select'
  5085. },
  5086. needChild = {
  5087. 'ol': 'li',
  5088. 'ul': 'li'
  5089. };
  5090. function text(parent, data) {
  5091. if (needChild[parent.tagName]) {
  5092. var tmpNode = uNode.createElement(needChild[parent.tagName]);
  5093. parent.appendChild(tmpNode);
  5094. tmpNode.appendChild(uNode.createText(data));
  5095. parent = tmpNode;
  5096. } else {
  5097. parent.appendChild(uNode.createText(data));
  5098. }
  5099. }
  5100. function element(parent, tagName, htmlattr) {
  5101. var needParentTag;
  5102. if (needParentTag = needParentNode[tagName]) {
  5103. var tmpParent = parent, hasParent;
  5104. while (tmpParent.type != 'root') {
  5105. if (utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName) {
  5106. parent = tmpParent;
  5107. hasParent = true;
  5108. break;
  5109. }
  5110. tmpParent = tmpParent.parentNode;
  5111. }
  5112. if (!hasParent) {
  5113. parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
  5114. }
  5115. }
  5116. //按dtd处理嵌套
  5117. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  5118. // parent = parent.parentNode;
  5119. var elm = new uNode({
  5120. parentNode: parent,
  5121. type: 'element',
  5122. tagName: tagName.toLowerCase(),
  5123. //是自闭合的处理一下
  5124. children: dtd.$empty[tagName] ? null : []
  5125. });
  5126. //如果属性存在,处理属性
  5127. if (htmlattr) {
  5128. var attrs = {}, match;
  5129. while (match = re_attr.exec(htmlattr)) {
  5130. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
  5131. }
  5132. elm.attrs = attrs;
  5133. }
  5134. parent.children.push(elm);
  5135. //如果是自闭合节点返回父亲节点
  5136. return dtd.$empty[tagName] ? parent : elm
  5137. }
  5138. function comment(parent, data) {
  5139. parent.children.push(new uNode({
  5140. type: 'comment',
  5141. data: data,
  5142. parentNode: parent
  5143. }));
  5144. }
  5145. var match, currentIndex = 0, nextIndex = 0;
  5146. //设置根节点
  5147. var root = new uNode({
  5148. type: 'root',
  5149. children: []
  5150. });
  5151. var currentParent = root;
  5152. while (match = re_tag.exec(htmlstr)) {
  5153. currentIndex = match.index;
  5154. try {
  5155. if (currentIndex > nextIndex) {
  5156. //text node
  5157. text(currentParent, htmlstr.slice(nextIndex, currentIndex));
  5158. }
  5159. if (match[3]) {
  5160. if (dtd.$cdata[currentParent.tagName]) {
  5161. text(currentParent, match[0]);
  5162. } else {
  5163. //start tag
  5164. currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
  5165. }
  5166. } else if (match[1]) {
  5167. if (currentParent.type != 'root') {
  5168. if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
  5169. text(currentParent, match[0]);
  5170. } else {
  5171. var tmpParent = currentParent;
  5172. while (currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()) {
  5173. currentParent = currentParent.parentNode;
  5174. if (currentParent.type == 'root') {
  5175. currentParent = tmpParent;
  5176. throw 'break'
  5177. }
  5178. }
  5179. //end tag
  5180. currentParent = currentParent.parentNode;
  5181. }
  5182. }
  5183. } else if (match[2]) {
  5184. //comment
  5185. comment(currentParent, match[2])
  5186. }
  5187. } catch (e) {
  5188. }
  5189. nextIndex = re_tag.lastIndex;
  5190. }
  5191. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  5192. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  5193. if (nextIndex < htmlstr.length) {
  5194. text(currentParent, htmlstr.slice(nextIndex));
  5195. }
  5196. return root;
  5197. };
  5198. /**
  5199. * @file
  5200. * @name UM.filterNode
  5201. * @short filterNode
  5202. * @desc 根据给定的规则过滤节点
  5203. * @import editor.js,core/utils.js
  5204. * @anthor zhanyi
  5205. */
  5206. var filterNode = UM.filterNode = function () {
  5207. function filterNode(node, rules) {
  5208. switch (node.type) {
  5209. case 'text':
  5210. break;
  5211. case 'element':
  5212. var val;
  5213. if (val = rules[node.tagName]) {
  5214. if (val === '-') {
  5215. node.parentNode.removeChild(node)
  5216. } else if (utils.isFunction(val)) {
  5217. var parentNode = node.parentNode,
  5218. index = node.getIndex();
  5219. val(node);
  5220. if (node.parentNode) {
  5221. if (node.children) {
  5222. for (var i = 0, ci; ci = node.children[i];) {
  5223. filterNode(ci, rules);
  5224. if (ci.parentNode) {
  5225. i++;
  5226. }
  5227. }
  5228. }
  5229. } else {
  5230. for (var i = index, ci; ci = parentNode.children[i];) {
  5231. filterNode(ci, rules);
  5232. if (ci.parentNode) {
  5233. i++;
  5234. }
  5235. }
  5236. }
  5237. } else {
  5238. var attrs = val['$'];
  5239. if (attrs && node.attrs) {
  5240. var tmpAttrs = {}, tmpVal;
  5241. for (var a in attrs) {
  5242. tmpVal = node.getAttr(a);
  5243. //todo 只先对style单独处理
  5244. if (a == 'style' && utils.isArray(attrs[a])) {
  5245. var tmpCssStyle = [];
  5246. utils.each(attrs[a], function (v) {
  5247. var tmp;
  5248. if (tmp = node.getStyle(v)) {
  5249. tmpCssStyle.push(v + ':' + tmp);
  5250. }
  5251. });
  5252. tmpVal = tmpCssStyle.join(';')
  5253. }
  5254. if (tmpVal) {
  5255. tmpAttrs[a] = tmpVal;
  5256. }
  5257. }
  5258. node.attrs = tmpAttrs;
  5259. }
  5260. if (node.children) {
  5261. for (var i = 0, ci; ci = node.children[i];) {
  5262. filterNode(ci, rules);
  5263. if (ci.parentNode) {
  5264. i++;
  5265. }
  5266. }
  5267. }
  5268. }
  5269. } else {
  5270. //如果不在名单里扣出子节点并删除该节点,cdata除外
  5271. if (dtd.$cdata[node.tagName]) {
  5272. node.parentNode.removeChild(node)
  5273. } else {
  5274. var parentNode = node.parentNode,
  5275. index = node.getIndex();
  5276. node.parentNode.removeChild(node, true);
  5277. for (var i = index, ci; ci = parentNode.children[i];) {
  5278. filterNode(ci, rules);
  5279. if (ci.parentNode) {
  5280. i++;
  5281. }
  5282. }
  5283. }
  5284. }
  5285. break;
  5286. case 'comment':
  5287. node.parentNode.removeChild(node)
  5288. }
  5289. }
  5290. return function (root, rules) {
  5291. if (utils.isEmptyObject(rules)) {
  5292. return root;
  5293. }
  5294. var val;
  5295. if (val = rules['-']) {
  5296. utils.each(val.split(' '), function (k) {
  5297. rules[k] = '-'
  5298. })
  5299. }
  5300. for (var i = 0, ci; ci = root.children[i];) {
  5301. filterNode(ci, rules);
  5302. if (ci.parentNode) {
  5303. i++;
  5304. }
  5305. }
  5306. return root;
  5307. }
  5308. }();
  5309. ///import core
  5310. /**
  5311. * @description 插入内容
  5312. * @name baidu.editor.execCommand
  5313. * @param {String} cmdName inserthtml插入内容的命令
  5314. * @param {String} html 要插入的内容
  5315. * @author zhanyi
  5316. */
  5317. UM.commands['inserthtml'] = {
  5318. execCommand: function (command, html, notNeedFilter) {
  5319. var me = this,
  5320. range,
  5321. div;
  5322. if (!html) {
  5323. return;
  5324. }
  5325. if (me.fireEvent('beforeinserthtml', html) === true) {
  5326. return;
  5327. }
  5328. range = me.selection.getRange();
  5329. div = range.document.createElement('div');
  5330. div.style.display = 'inline';
  5331. if (!notNeedFilter) {
  5332. var root = UM.htmlparser(html);
  5333. //如果给了过滤规则就先进行过滤
  5334. if (me.options.filterRules) {
  5335. UM.filterNode(root, me.options.filterRules);
  5336. }
  5337. //执行默认的处理
  5338. me.filterInputRule(root);
  5339. html = root.toHtml()
  5340. }
  5341. div.innerHTML = utils.trim(html);
  5342. if (!range.collapsed) {
  5343. var tmpNode = range.startContainer;
  5344. if (domUtils.isFillChar(tmpNode)) {
  5345. range.setStartBefore(tmpNode)
  5346. }
  5347. tmpNode = range.endContainer;
  5348. if (domUtils.isFillChar(tmpNode)) {
  5349. range.setEndAfter(tmpNode)
  5350. }
  5351. range.txtToElmBoundary();
  5352. //结束边界可能放到了br的前边,要把br包含进来
  5353. // x[xxx]<br/>
  5354. if (range.endContainer && range.endContainer.nodeType == 1) {
  5355. tmpNode = range.endContainer.childNodes[range.endOffset];
  5356. if (tmpNode && domUtils.isBr(tmpNode)) {
  5357. range.setEndAfter(tmpNode);
  5358. }
  5359. }
  5360. if (range.startOffset == 0) {
  5361. tmpNode = range.startContainer;
  5362. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  5363. tmpNode = range.endContainer;
  5364. if (range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode, 'lastChild')) {
  5365. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  5366. range.setStart(me.body.firstChild, 0).collapse(true)
  5367. }
  5368. }
  5369. }
  5370. !range.collapsed && range.deleteContents();
  5371. if (range.startContainer.nodeType == 1) {
  5372. var child = range.startContainer.childNodes[range.startOffset], pre;
  5373. if (child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)) {
  5374. range.setEnd(pre, pre.childNodes.length).collapse();
  5375. while (child.firstChild) {
  5376. pre.appendChild(child.firstChild);
  5377. }
  5378. domUtils.remove(child);
  5379. }
  5380. }
  5381. }
  5382. var child, parent, pre, tmp, hadBreak = 0, nextNode;
  5383. //如果当前位置选中了fillchar要干掉,要不会产生空行
  5384. if (range.inFillChar()) {
  5385. child = range.startContainer;
  5386. if (domUtils.isFillChar(child)) {
  5387. range.setStartBefore(child).collapse(true);
  5388. domUtils.remove(child);
  5389. } else if (domUtils.isFillChar(child, true)) {
  5390. child.nodeValue = child.nodeValue.replace(fillCharReg, '');
  5391. range.startOffset--;
  5392. range.collapsed && range.collapse(true)
  5393. }
  5394. }
  5395. while (child = div.firstChild) {
  5396. if (hadBreak) {
  5397. var p = me.document.createElement('p');
  5398. while (child && (child.nodeType == 3 || !dtd.$block[child.tagName])) {
  5399. nextNode = child.nextSibling;
  5400. p.appendChild(child);
  5401. child = nextNode;
  5402. }
  5403. if (p.firstChild) {
  5404. child = p
  5405. }
  5406. }
  5407. range.insertNode(child);
  5408. nextNode = child.nextSibling;
  5409. if (!hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm(child)) {
  5410. parent = domUtils.findParent(child, function (node) {
  5411. return domUtils.isBlockElm(node);
  5412. });
  5413. if (parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)) {
  5414. if (!dtd[parent.tagName][child.nodeName]) {
  5415. pre = parent;
  5416. } else {
  5417. tmp = child.parentNode;
  5418. while (tmp !== parent) {
  5419. pre = tmp;
  5420. tmp = tmp.parentNode;
  5421. }
  5422. }
  5423. domUtils.breakParent(child, pre || tmp);
  5424. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  5425. var pre = child.previousSibling;
  5426. domUtils.trimWhiteTextNode(pre);
  5427. if (!pre.childNodes.length) {
  5428. domUtils.remove(pre);
  5429. }
  5430. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  5431. if (!browser.ie &&
  5432. (next = child.nextSibling) &&
  5433. domUtils.isBlockElm(next) &&
  5434. next.lastChild &&
  5435. !domUtils.isBr(next.lastChild)) {
  5436. next.appendChild(me.document.createElement('br'));
  5437. }
  5438. hadBreak = 1;
  5439. }
  5440. }
  5441. var next = child.nextSibling;
  5442. if (!div.firstChild && next && domUtils.isBlockElm(next)) {
  5443. range.setStart(next, 0).collapse(true);
  5444. break;
  5445. }
  5446. range.setEndAfter(child).collapse();
  5447. }
  5448. child = range.startContainer;
  5449. if (nextNode && domUtils.isBr(nextNode)) {
  5450. domUtils.remove(nextNode)
  5451. }
  5452. //用chrome可能有空白展位符
  5453. if (domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)) {
  5454. if (nextNode = child.nextSibling) {
  5455. domUtils.remove(child);
  5456. if (nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]) {
  5457. range.setStart(nextNode, 0).collapse(true).shrinkBoundary()
  5458. }
  5459. } else {
  5460. try {
  5461. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  5462. } catch (e) {
  5463. range.setStartBefore(child);
  5464. domUtils.remove(child)
  5465. }
  5466. }
  5467. }
  5468. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  5469. try {
  5470. if (browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]) {
  5471. var start = range.startContainer, pre = start.childNodes[range.startOffset - 1];
  5472. if (pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]) {
  5473. var txt = this.document.createTextNode(domUtils.fillChar);
  5474. range.insertNode(txt).setStart(txt, 0).collapse(true);
  5475. }
  5476. }
  5477. setTimeout(function () {
  5478. range.select(true);
  5479. })
  5480. } catch (e) {
  5481. }
  5482. setTimeout(function () {
  5483. range = me.selection.getRange();
  5484. range.scrollIntoView();
  5485. me.fireEvent('afterinserthtml');
  5486. }, 200);
  5487. }
  5488. };
  5489. ///import core
  5490. ///import plugins\inserthtml.js
  5491. ///commands 插入图片,操作图片的对齐方式
  5492. ///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter
  5493. ///commandsTitle 图片,默认,居左,居右,居中
  5494. ///commandsDialog dialogs\image
  5495. /**
  5496. * Created by .
  5497. * User: zhanyi
  5498. * for image
  5499. */
  5500. UM.commands['insertimage'] = {
  5501. execCommand: function (cmd, opt) {
  5502. opt = utils.isArray(opt) ? opt : [opt];
  5503. if (!opt.length) {
  5504. return;
  5505. }
  5506. var me = this;
  5507. var html = [], str = '', ci;
  5508. ci = opt[0];
  5509. if (opt.length == 1) {
  5510. str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5511. (ci.width ? 'width="' + ci.width + '" ' : '') +
  5512. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5513. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
  5514. (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
  5515. (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
  5516. (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
  5517. (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
  5518. (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
  5519. if (ci['floatStyle'] == 'center') {
  5520. str = '<p style="text-align: center">' + str + '</p>';
  5521. }
  5522. html.push(str);
  5523. } else {
  5524. for (var i = 0; ci = opt[i++];) {
  5525. str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
  5526. (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5527. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5528. ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
  5529. (ci.border || '') + '" ' +
  5530. (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
  5531. html.push(str);
  5532. }
  5533. }
  5534. me.execCommand('insertHtml', html.join(''), true);
  5535. }
  5536. };
  5537. ///import core
  5538. ///commands 段落格式,居左,居右,居中,两端对齐
  5539. ///commandsName JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify
  5540. ///commandsTitle 居左对齐,居中对齐,居右对齐,两端对齐
  5541. /**
  5542. * @description 居左右中
  5543. * @name UM.execCommand
  5544. * @param {String} cmdName justify执行对齐方式的命令
  5545. * @param {String} align 对齐方式:left居左,right居右,center居中,justify两端对齐
  5546. * @author zhanyi
  5547. */
  5548. UM.plugins['justify'] = function () {
  5549. var me = this;
  5550. $.each('justifyleft justifyright justifycenter justifyfull'.split(' '), function (i, cmdName) {
  5551. me.commands[cmdName] = {
  5552. execCommand: function (cmdName) {
  5553. return this.document.execCommand(cmdName);
  5554. },
  5555. queryCommandValue: function (cmdName) {
  5556. var val = this.document.queryCommandValue(cmdName);
  5557. return val === true || val === 'true' ? cmdName.replace(/justify/, '') : '';
  5558. },
  5559. queryCommandState: function (cmdName) {
  5560. return this.document.queryCommandState(cmdName) ? 1 : 0
  5561. }
  5562. };
  5563. })
  5564. };
  5565. ///import core
  5566. ///import plugins\removeformat.js
  5567. ///commands 字体颜色,背景色,字号,字体,下划线,删除线
  5568. ///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough
  5569. ///commandsTitle 字体颜色,背景色,字号,字体,下划线,删除线
  5570. /**
  5571. * @description 字体
  5572. * @name UM.execCommand
  5573. * @param {String} cmdName 执行的功能名称
  5574. * @param {String} value 传入的值
  5575. */
  5576. UM.plugins['font'] = function () {
  5577. var me = this,
  5578. fonts = {
  5579. 'forecolor': 'forecolor',
  5580. 'backcolor': 'backcolor',
  5581. 'fontsize': 'fontsize',
  5582. 'fontfamily': 'fontname'
  5583. },
  5584. cmdNameToStyle = {
  5585. 'forecolor': 'color',
  5586. 'backcolor': 'background-color',
  5587. 'fontsize': 'font-size',
  5588. 'fontfamily': 'font-family'
  5589. },
  5590. cmdNameToAttr = {
  5591. 'forecolor': 'color',
  5592. 'fontsize': 'size',
  5593. 'fontfamily': 'face'
  5594. };
  5595. me.setOpt({
  5596. 'fontfamily': [
  5597. {name: 'songti', val: '宋体,SimSun'},
  5598. {name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
  5599. {name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
  5600. {name: 'heiti', val: '黑体, SimHei'},
  5601. {name: 'lishu', val: '隶书, SimLi'},
  5602. {name: 'andaleMono', val: 'andale mono'},
  5603. {name: 'arial', val: 'arial, helvetica,sans-serif'},
  5604. {name: 'arialBlack', val: 'arial black,avant garde'},
  5605. {name: 'comicSansMs', val: 'comic sans ms'},
  5606. {name: 'impact', val: 'impact,chicago'},
  5607. {name: 'timesNewRoman', val: 'times new roman'},
  5608. {name: 'sans-serif', val: 'sans-serif'}
  5609. ],
  5610. 'fontsize': [10, 12, 16, 18, 24, 32, 48]
  5611. });
  5612. me.addOutputRule(function (root) {
  5613. utils.each(root.getNodesByTagName('font'), function (node) {
  5614. if (node.tagName == 'font') {
  5615. var cssStyle = [];
  5616. for (var p in node.attrs) {
  5617. switch (p) {
  5618. case 'size':
  5619. var val = node.attrs[p];
  5620. $.each({
  5621. '10': '1',
  5622. '12': '2',
  5623. '16': '3',
  5624. '18': '4',
  5625. '24': '5',
  5626. '32': '6',
  5627. '48': '7'
  5628. }, function (k, v) {
  5629. if (v == val) {
  5630. val = k;
  5631. return false;
  5632. }
  5633. });
  5634. cssStyle.push('font-size:' + val + 'px');
  5635. break;
  5636. case 'color':
  5637. cssStyle.push('color:' + node.attrs[p]);
  5638. break;
  5639. case 'face':
  5640. cssStyle.push('font-family:' + node.attrs[p]);
  5641. break;
  5642. case 'style':
  5643. cssStyle.push(node.attrs[p]);
  5644. }
  5645. }
  5646. node.attrs = {
  5647. 'style': cssStyle.join(';')
  5648. };
  5649. }
  5650. node.tagName = 'span';
  5651. if (node.parentNode.tagName == 'span' && node.parentNode.children.length == 1) {
  5652. $.each(node.attrs, function (k, v) {
  5653. node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;
  5654. })
  5655. node.parentNode.removeChild(node, true);
  5656. }
  5657. });
  5658. });
  5659. for (var p in fonts) {
  5660. (function (cmd) {
  5661. me.commands[cmd] = {
  5662. execCommand: function (cmdName, value) {
  5663. if (value == 'transparent') {
  5664. return;
  5665. }
  5666. var rng = this.selection.getRange();
  5667. if (rng.collapsed) {
  5668. var span = $('<span></span>').css(cmdNameToStyle[cmdName], value)[0];
  5669. rng.insertNode(span).setStart(span, 0).setCursor();
  5670. } else {
  5671. if (cmdName == 'fontsize') {
  5672. value = {
  5673. '10': '1',
  5674. '12': '2',
  5675. '16': '3',
  5676. '18': '4',
  5677. '24': '5',
  5678. '32': '6',
  5679. '48': '7'
  5680. }[(value + "").replace(/px/, '')]
  5681. }
  5682. this.document.execCommand(fonts[cmdName], false, value);
  5683. if (browser.gecko) {
  5684. $.each(this.$body.find('a'), function (i, a) {
  5685. var parent = a.parentNode;
  5686. if (parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)) {
  5687. var cloneNode = parent.cloneNode(false);
  5688. cloneNode.innerHTML = a.innerHTML;
  5689. $(a).html('').append(cloneNode).insertBefore(parent);
  5690. $(parent).remove();
  5691. }
  5692. });
  5693. }
  5694. if (!browser.ie) {
  5695. var nativeRange = this.selection.getNative().getRangeAt(0);
  5696. var common = nativeRange.commonAncestorContainer;
  5697. var rng = this.selection.getRange(),
  5698. bk = rng.createBookmark(true);
  5699. $(common).find('a').each(function (i, n) {
  5700. var parent = n.parentNode;
  5701. if (parent.nodeName == 'FONT') {
  5702. var font = parent.cloneNode(false);
  5703. font.innerHTML = n.innerHTML;
  5704. $(n).html('').append(font);
  5705. }
  5706. });
  5707. rng.moveToBookmark(bk).select()
  5708. }
  5709. return true
  5710. }
  5711. },
  5712. queryCommandValue: function (cmdName) {
  5713. var start = me.selection.getStart();
  5714. var val = $(start).css(cmdNameToStyle[cmdName]);
  5715. if (val === undefined) {
  5716. val = $(start).attr(cmdNameToAttr[cmdName])
  5717. }
  5718. return val ? utils.fixColor(cmdName, val).replace(/px/, '') : '';
  5719. },
  5720. queryCommandState: function (cmdName) {
  5721. return this.queryCommandValue(cmdName)
  5722. }
  5723. };
  5724. })(p);
  5725. }
  5726. };
  5727. ///import core
  5728. ///commands 超链接,取消链接
  5729. ///commandsName Link,Unlink
  5730. ///commandsTitle 超链接,取消链接
  5731. ///commandsDialog dialogs\link
  5732. /**
  5733. * 超链接
  5734. * @function
  5735. * @name UM.execCommand
  5736. * @param {String} cmdName link插入超链接
  5737. * @param {Object} options url地址,title标题,target是否打开新页
  5738. * @author zhanyi
  5739. */
  5740. /**
  5741. * 取消链接
  5742. * @function
  5743. * @name UM.execCommand
  5744. * @param {String} cmdName unlink取消链接
  5745. * @author zhanyi
  5746. */
  5747. UM.plugins['link'] = function () {
  5748. var me = this;
  5749. me.setOpt('autourldetectinie', false);
  5750. //在ie下禁用autolink
  5751. if (browser.ie && this.options.autourldetectinie === false) {
  5752. this.addListener('keyup', function (cmd, evt) {
  5753. var me = this, keyCode = evt.keyCode;
  5754. if (keyCode == 13 || keyCode == 32) {
  5755. var rng = me.selection.getRange();
  5756. var start = rng.startContainer;
  5757. if (keyCode == 13) {
  5758. if (start.nodeName == 'P') {
  5759. var pre = start.previousSibling;
  5760. if (pre && pre.nodeType == 1) {
  5761. var pre = pre.lastChild;
  5762. if (pre && pre.nodeName == 'A' && !pre.getAttribute('_href')) {
  5763. domUtils.remove(pre, true);
  5764. }
  5765. }
  5766. }
  5767. } else if (keyCode == 32) {
  5768. if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) {
  5769. start = start.previousSibling;
  5770. if (start && start.nodeName == 'A' && !start.getAttribute('_href')) {
  5771. domUtils.remove(start, true);
  5772. }
  5773. }
  5774. }
  5775. }
  5776. });
  5777. }
  5778. this.addOutputRule(function (root) {
  5779. $.each(root.getNodesByTagName('a'), function (i, a) {
  5780. var _href = a.getAttr('href');
  5781. if (!/^(ftp|https?|\/|file)/.test(_href)) {
  5782. _href = 'http://' + _href;
  5783. }
  5784. if (_href != 'http://undefined') a.setAttr('href', _href);
  5785. a.setAttr('href', _href);
  5786. a.setAttr('_href')
  5787. if (a.getAttr('title') == '') {
  5788. a.setAttr('title')
  5789. }
  5790. })
  5791. });
  5792. this.addInputRule(function (root) {
  5793. $.each(root.getNodesByTagName('a'), function (i, a) {
  5794. a.setAttr('_href', a.getAttr('href'));
  5795. })
  5796. });
  5797. me.commands['link'] = {
  5798. execCommand: function (cmdName, opt) {
  5799. var me = this;
  5800. var rng = me.selection.getRange();
  5801. opt._href && (opt._href = utils.unhtml(opt._href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g));
  5802. opt.href && (opt.href = utils.unhtml(opt.href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g));
  5803. if (rng.collapsed) {
  5804. var start = rng.startContainer;
  5805. if (start = domUtils.findParentByTagName(start, 'a', true)) {
  5806. $(start).attr(opt);
  5807. rng.selectNode(start).select()
  5808. } else {
  5809. rng.insertNode($('<a>' + opt.href + '</a>').attr(opt)[0]).select()
  5810. }
  5811. } else {
  5812. me.document.execCommand('createlink', false, '_umeditor_link');
  5813. utils.each(domUtils.getElementsByTagName(me.body, 'a', function (n) {
  5814. return n.getAttribute('href') == '_umeditor_link'
  5815. }), function (l) {
  5816. if ($(l).text() == '_umeditor_link') {
  5817. $(l).text(opt.href);
  5818. }
  5819. domUtils.setAttributes(l, opt);
  5820. rng.selectNode(l).select()
  5821. })
  5822. }
  5823. },
  5824. queryCommandState: function () {
  5825. return this.queryCommandValue('link') ? 1 : 0;
  5826. },
  5827. queryCommandValue: function () {
  5828. var path = this.selection.getStartElementPath();
  5829. var result;
  5830. $.each(path, function (i, n) {
  5831. if (n.nodeName == "A") {
  5832. result = n;
  5833. return false;
  5834. }
  5835. })
  5836. return result;
  5837. }
  5838. };
  5839. me.commands['unlink'] = {
  5840. execCommand: function () {
  5841. this.document.execCommand('unlink');
  5842. }
  5843. };
  5844. };
  5845. ///import core
  5846. ///commands 打印
  5847. ///commandsName Print
  5848. ///commandsTitle 打印
  5849. /**
  5850. * @description 打印
  5851. * @name baidu.editor.execCommand
  5852. * @param {String} cmdName print打印编辑器内容
  5853. * @author zhanyi
  5854. */
  5855. UM.commands['print'] = {
  5856. execCommand: function () {
  5857. var me = this,
  5858. id = 'editor_print_' + +new Date();
  5859. $('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').attr('id', id)
  5860. .css({
  5861. width: '0px',
  5862. height: '0px',
  5863. 'overflow': 'hidden',
  5864. 'float': 'left',
  5865. 'position': 'absolute',
  5866. top: '-10000px',
  5867. left: '-10000px'
  5868. })
  5869. .appendTo(me.$container.find('.edui-dialog-container'));
  5870. var w = window.open('', id, ''),
  5871. d = w.document;
  5872. d.open();
  5873. d.write('<html><head></head><body><div>' + this.getContent(null, null, true) + '</div><script>' +
  5874. "setTimeout(function(){" +
  5875. "window.print();" +
  5876. "setTimeout(function(){" +
  5877. "window.parent.$('#" + id + "').remove();" +
  5878. "},100);" +
  5879. "},200);" +
  5880. '</script></body></html>');
  5881. d.close();
  5882. },
  5883. notNeedUndo: 1
  5884. };
  5885. ///import core
  5886. ///commands 格式
  5887. ///commandsName Paragraph
  5888. ///commandsTitle 段落格式
  5889. /**
  5890. * 段落样式
  5891. * @function
  5892. * @name UM.execCommand
  5893. * @param {String} cmdName paragraph插入段落执行命令
  5894. * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
  5895. * @param {String} attrs 标签的属性
  5896. * @author zhanyi
  5897. */
  5898. UM.plugins['paragraph'] = function () {
  5899. var me = this;
  5900. me.setOpt('paragraph', {'p': '', 'h1': '', 'h2': '', 'h3': '', 'h4': '', 'h5': '', 'h6': ''});
  5901. me.commands['paragraph'] = {
  5902. execCommand: function (cmdName, style) {
  5903. return this.document.execCommand('formatBlock', false, '<' + style + '>');
  5904. },
  5905. queryCommandValue: function () {
  5906. try {
  5907. var val = this.document.queryCommandValue('formatBlock')
  5908. } catch (e) {
  5909. }
  5910. return val;
  5911. }
  5912. };
  5913. };
  5914. ///import core
  5915. ///import plugins\inserthtml.js
  5916. ///commands 分割线
  5917. ///commandsName Horizontal
  5918. ///commandsTitle 分隔线
  5919. /**
  5920. * 分割线
  5921. * @function
  5922. * @name UM.execCommand
  5923. * @param {String} cmdName horizontal插入分割线
  5924. */
  5925. UM.plugins['horizontal'] = function () {
  5926. var me = this;
  5927. me.commands['horizontal'] = {
  5928. execCommand: function () {
  5929. this.document.execCommand('insertHorizontalRule');
  5930. var rng = me.selection.getRange().txtToElmBoundary(true),
  5931. start = rng.startContainer;
  5932. if (domUtils.isBody(rng.startContainer)) {
  5933. var next = rng.startContainer.childNodes[rng.startOffset];
  5934. if (!next) {
  5935. next = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[0]
  5936. }
  5937. rng.setStart(next, 0).setCursor()
  5938. } else {
  5939. while (dtd.$inline[start.tagName] && start.lastChild === start.firstChild) {
  5940. var parent = start.parentNode;
  5941. parent.appendChild(start.firstChild);
  5942. parent.removeChild(start);
  5943. start = parent;
  5944. }
  5945. while (dtd.$inline[start.tagName]) {
  5946. start = start.parentNode;
  5947. }
  5948. if (start.childNodes.length == 1 && start.lastChild.nodeName == 'HR') {
  5949. var hr = start.lastChild;
  5950. $(hr).insertBefore(start);
  5951. rng.setStart(start, 0).setCursor();
  5952. } else {
  5953. hr = $('hr', start)[0];
  5954. domUtils.breakParent(hr, start);
  5955. var pre = hr.previousSibling;
  5956. if (pre && domUtils.isEmptyBlock(pre)) {
  5957. $(pre).remove()
  5958. }
  5959. rng.setStart(hr.nextSibling, 0).setCursor();
  5960. }
  5961. }
  5962. }
  5963. };
  5964. };
  5965. ///import core
  5966. ///commands 清空文档
  5967. ///commandsName ClearDoc
  5968. ///commandsTitle 清空文档
  5969. /**
  5970. *
  5971. * 清空文档
  5972. * @function
  5973. * @name UM.execCommand
  5974. * @param {String} cmdName cleardoc清空文档
  5975. */
  5976. UM.commands['cleardoc'] = {
  5977. execCommand: function () {
  5978. var me = this,
  5979. range = me.selection.getRange();
  5980. me.body.innerHTML = "<p>" + (ie ? "" : "<br/>") + "</p>";
  5981. range.setStart(me.body.firstChild, 0).setCursor(false, true);
  5982. setTimeout(function () {
  5983. me.fireEvent("clearDoc");
  5984. }, 0);
  5985. }
  5986. };
  5987. ///import core
  5988. ///commands 撤销和重做
  5989. ///commandsName Undo,Redo
  5990. ///commandsTitle 撤销,重做
  5991. /**
  5992. * @description 回退
  5993. * @author zhanyi
  5994. */
  5995. UM.plugins['undo'] = function () {
  5996. var saveSceneTimer;
  5997. var me = this,
  5998. maxUndoCount = me.options.maxUndoCount || 20,
  5999. maxInputCount = me.options.maxInputCount || 20,
  6000. fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
  6001. var noNeedFillCharTags = {
  6002. ol: 1, ul: 1, table: 1, tbody: 1, tr: 1, body: 1
  6003. };
  6004. var orgState = me.options.autoClearEmptyNode;
  6005. function compareAddr(indexA, indexB) {
  6006. if (indexA.length != indexB.length)
  6007. return 0;
  6008. for (var i = 0, l = indexA.length; i < l; i++) {
  6009. if (indexA[i] != indexB[i])
  6010. return 0
  6011. }
  6012. return 1;
  6013. }
  6014. function compareRangeAddress(rngAddrA, rngAddrB) {
  6015. if (rngAddrA.collapsed != rngAddrB.collapsed) {
  6016. return 0;
  6017. }
  6018. if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
  6019. return 0;
  6020. }
  6021. return 1;
  6022. }
  6023. function UndoManager() {
  6024. this.list = [];
  6025. this.index = 0;
  6026. this.hasUndo = false;
  6027. this.hasRedo = false;
  6028. this.undo = function () {
  6029. if (this.hasUndo) {
  6030. if (!this.list[this.index - 1] && this.list.length == 1) {
  6031. this.reset();
  6032. return;
  6033. }
  6034. while (this.list[this.index].content == this.list[this.index - 1].content) {
  6035. this.index--;
  6036. if (this.index == 0) {
  6037. return this.restore(0);
  6038. }
  6039. }
  6040. this.restore(--this.index);
  6041. }
  6042. };
  6043. this.redo = function () {
  6044. if (this.hasRedo) {
  6045. while (this.list[this.index].content == this.list[this.index + 1].content) {
  6046. this.index++;
  6047. if (this.index == this.list.length - 1) {
  6048. return this.restore(this.index);
  6049. }
  6050. }
  6051. this.restore(++this.index);
  6052. }
  6053. };
  6054. this.restore = function () {
  6055. var me = this.editor;
  6056. var scene = this.list[this.index];
  6057. var root = UM.htmlparser(scene.content.replace(fillchar, ''));
  6058. me.options.autoClearEmptyNode = false;
  6059. me.filterInputRule(root, true);
  6060. me.options.autoClearEmptyNode = orgState;
  6061. //trace:873
  6062. //去掉展位符
  6063. me.body.innerHTML = root.toHtml();
  6064. me.fireEvent('afterscencerestore');
  6065. //处理undo后空格不展位的问题
  6066. if (browser.ie) {
  6067. utils.each(domUtils.getElementsByTagName(me.document, 'td th caption p'), function (node) {
  6068. if (domUtils.isEmptyNode(node)) {
  6069. domUtils.fillNode(me.document, node);
  6070. }
  6071. })
  6072. }
  6073. try {
  6074. var rng = new dom.Range(me.document, me.body).moveToAddress(scene.address);
  6075. if (browser.ie && rng.collapsed && rng.startContainer.nodeType == 1) {
  6076. var tmpNode = rng.startContainer.childNodes[rng.startOffset];
  6077. if (!tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]) {
  6078. rng.insertNode(me.document.createTextNode(' ')).collapse(true);
  6079. }
  6080. }
  6081. rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
  6082. } catch (e) {
  6083. }
  6084. this.update();
  6085. this.clearKey();
  6086. //不能把自己reset了
  6087. me.fireEvent('reset', true);
  6088. };
  6089. this.getScene = function () {
  6090. var me = this.editor;
  6091. var rng = me.selection.getRange(),
  6092. rngAddress = rng.createAddress(false, true);
  6093. me.fireEvent('beforegetscene');
  6094. var root = UM.htmlparser(me.body.innerHTML, true);
  6095. me.options.autoClearEmptyNode = false;
  6096. me.filterOutputRule(root, true);
  6097. me.options.autoClearEmptyNode = orgState;
  6098. var cont = root.toHtml();
  6099. browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
  6100. me.fireEvent('aftergetscene');
  6101. return {
  6102. address: rngAddress,
  6103. content: cont
  6104. }
  6105. };
  6106. this.save = function (notCompareRange, notSetCursor) {
  6107. clearTimeout(saveSceneTimer);
  6108. var currentScene = this.getScene(notSetCursor),
  6109. lastScene = this.list[this.index];
  6110. //内容相同位置相同不存
  6111. if (lastScene && lastScene.content == currentScene.content &&
  6112. (notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address))
  6113. ) {
  6114. return;
  6115. }
  6116. this.list = this.list.slice(0, this.index + 1);
  6117. this.list.push(currentScene);
  6118. //如果大于最大数量了,就把最前的剔除
  6119. if (this.list.length > maxUndoCount) {
  6120. this.list.shift();
  6121. }
  6122. this.index = this.list.length - 1;
  6123. this.clearKey();
  6124. //跟新undo/redo状态
  6125. this.update();
  6126. };
  6127. this.update = function () {
  6128. this.hasRedo = !!this.list[this.index + 1];
  6129. this.hasUndo = !!this.list[this.index - 1];
  6130. };
  6131. this.reset = function () {
  6132. this.list = [];
  6133. this.index = 0;
  6134. this.hasUndo = false;
  6135. this.hasRedo = false;
  6136. this.clearKey();
  6137. };
  6138. this.clearKey = function () {
  6139. keycont = 0;
  6140. lastKeyCode = null;
  6141. };
  6142. }
  6143. me.undoManger = new UndoManager();
  6144. me.undoManger.editor = me;
  6145. function saveScene() {
  6146. this.undoManger.save();
  6147. }
  6148. me.addListener('saveScene', function () {
  6149. var args = Array.prototype.splice.call(arguments, 1);
  6150. this.undoManger.save.apply(this.undoManger, args);
  6151. });
  6152. me.addListener('beforeexeccommand', saveScene);
  6153. me.addListener('afterexeccommand', saveScene);
  6154. me.addListener('reset', function (type, exclude) {
  6155. if (!exclude) {
  6156. this.undoManger.reset();
  6157. }
  6158. });
  6159. me.commands['redo'] = me.commands['undo'] = {
  6160. execCommand: function (cmdName) {
  6161. this.undoManger[cmdName]();
  6162. },
  6163. queryCommandState: function (cmdName) {
  6164. return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
  6165. },
  6166. notNeedUndo: 1
  6167. };
  6168. var keys = {
  6169. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  6170. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  6171. 37: 1, 38: 1, 39: 1, 40: 1
  6172. },
  6173. keycont = 0,
  6174. lastKeyCode;
  6175. //输入法状态下不计算字符数
  6176. var inputType = false;
  6177. me.addListener('ready', function () {
  6178. $(this.body).on('compositionstart', function () {
  6179. inputType = true;
  6180. }).on('compositionend', function () {
  6181. inputType = false;
  6182. })
  6183. });
  6184. //快捷键
  6185. me.addshortcutkey({
  6186. "Undo": "ctrl+90", //undo
  6187. "Redo": "ctrl+89,shift+ctrl+z" //redo
  6188. });
  6189. var isCollapsed = true;
  6190. me.addListener('keydown', function (type, evt) {
  6191. var me = this;
  6192. var keyCode = evt.keyCode || evt.which;
  6193. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6194. if (inputType)
  6195. return;
  6196. if (!me.selection.getRange().collapsed) {
  6197. me.undoManger.save(false, true);
  6198. isCollapsed = false;
  6199. return;
  6200. }
  6201. if (me.undoManger.list.length == 0) {
  6202. me.undoManger.save(true);
  6203. }
  6204. clearTimeout(saveSceneTimer);
  6205. function save(cont) {
  6206. if (cont.selection.getRange().collapsed)
  6207. cont.fireEvent('contentchange');
  6208. cont.undoManger.save(false, true);
  6209. cont.fireEvent('selectionchange');
  6210. }
  6211. saveSceneTimer = setTimeout(function () {
  6212. if (inputType) {
  6213. var interalTimer = setInterval(function () {
  6214. if (!inputType) {
  6215. save(me);
  6216. clearInterval(interalTimer)
  6217. }
  6218. }, 300)
  6219. return;
  6220. }
  6221. save(me);
  6222. }, 200);
  6223. lastKeyCode = keyCode;
  6224. keycont++;
  6225. if (keycont >= maxInputCount) {
  6226. save(me)
  6227. }
  6228. }
  6229. });
  6230. me.addListener('keyup', function (type, evt) {
  6231. var keyCode = evt.keyCode || evt.which;
  6232. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6233. if (inputType)
  6234. return;
  6235. if (!isCollapsed) {
  6236. this.undoManger.save(false, true);
  6237. isCollapsed = true;
  6238. }
  6239. }
  6240. });
  6241. };
  6242. ///import core
  6243. ///import plugins/inserthtml.js
  6244. ///import plugins/undo.js
  6245. ///import plugins/serialize.js
  6246. ///commands 粘贴
  6247. ///commandsName PastePlain
  6248. ///commandsTitle 纯文本粘贴模式
  6249. /**
  6250. * @description 粘贴
  6251. * @author zhanyi
  6252. */
  6253. UM.plugins['paste'] = function () {
  6254. function getClipboardData(callback) {
  6255. var doc = this.document;
  6256. if (doc.getElementById('baidu_pastebin')) {
  6257. return;
  6258. }
  6259. var range = this.selection.getRange(),
  6260. bk = range.createBookmark(),
  6261. //创建剪贴的容器div
  6262. pastebin = doc.createElement('div');
  6263. pastebin.id = 'baidu_pastebin';
  6264. // Safari 要求div必须有内容,才能粘贴内容进来
  6265. browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
  6266. this.body.appendChild(pastebin);
  6267. //trace:717 隐藏的span不能得到top
  6268. //bk.start.innerHTML = '&nbsp;';
  6269. bk.start.style.display = '';
  6270. pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
  6271. //要在现在光标平行的位置加入,否则会出现跳动的问题
  6272. $(bk.start).position().top + 'px';
  6273. range.selectNodeContents(pastebin).select(true);
  6274. setTimeout(function () {
  6275. if (browser.webkit) {
  6276. for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
  6277. if (domUtils.isEmptyNode(pi)) {
  6278. domUtils.remove(pi);
  6279. } else {
  6280. pastebin = pi;
  6281. break;
  6282. }
  6283. }
  6284. }
  6285. try {
  6286. pastebin.parentNode.removeChild(pastebin);
  6287. } catch (e) {
  6288. }
  6289. range.moveToBookmark(bk).select(true);
  6290. callback(pastebin);
  6291. }, 0);
  6292. }
  6293. var me = this;
  6294. function filter(div) {
  6295. var html;
  6296. if (div.firstChild) {
  6297. //去掉cut中添加的边界值
  6298. var nodes = domUtils.getElementsByTagName(div, 'span');
  6299. for (var i = 0, ni; ni = nodes[i++];) {
  6300. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  6301. domUtils.remove(ni);
  6302. }
  6303. }
  6304. if (browser.webkit) {
  6305. var brs = div.querySelectorAll('div br');
  6306. for (var i = 0, bi; bi = brs[i++];) {
  6307. var pN = bi.parentNode;
  6308. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  6309. pN.innerHTML = '<p><br/></p>';
  6310. domUtils.remove(pN);
  6311. }
  6312. }
  6313. var divs = div.querySelectorAll('#baidu_pastebin');
  6314. for (var i = 0, di; di = divs[i++];) {
  6315. var tmpP = me.document.createElement('p');
  6316. di.parentNode.insertBefore(tmpP, di);
  6317. while (di.firstChild) {
  6318. tmpP.appendChild(di.firstChild);
  6319. }
  6320. domUtils.remove(di);
  6321. }
  6322. var metas = div.querySelectorAll('meta');
  6323. for (var i = 0, ci; ci = metas[i++];) {
  6324. domUtils.remove(ci);
  6325. }
  6326. var brs = div.querySelectorAll('br');
  6327. for (i = 0; ci = brs[i++];) {
  6328. if (/^apple-/i.test(ci.className)) {
  6329. domUtils.remove(ci);
  6330. }
  6331. }
  6332. }
  6333. if (browser.gecko) {
  6334. var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
  6335. for (i = 0; ci = dirtyNodes[i++];) {
  6336. ci.removeAttribute('_moz_dirty');
  6337. }
  6338. }
  6339. if (!browser.ie) {
  6340. var spans = div.querySelectorAll('span.Apple-style-span');
  6341. for (var i = 0, ci; ci = spans[i++];) {
  6342. domUtils.remove(ci, true);
  6343. }
  6344. }
  6345. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  6346. html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  6347. //过滤word粘贴过来的冗余属性
  6348. html = UM.filterWord(html);
  6349. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  6350. var root = UM.htmlparser(html);
  6351. //如果给了过滤规则就先进行过滤
  6352. if (me.options.filterRules) {
  6353. UM.filterNode(root, me.options.filterRules);
  6354. }
  6355. //执行默认的处理
  6356. me.filterInputRule(root);
  6357. //针对chrome的处理
  6358. if (browser.webkit) {
  6359. var br = root.lastChild();
  6360. if (br && br.type == 'element' && br.tagName == 'br') {
  6361. root.removeChild(br)
  6362. }
  6363. utils.each(me.body.querySelectorAll('div'), function (node) {
  6364. if (domUtils.isEmptyBlock(node)) {
  6365. domUtils.remove(node)
  6366. }
  6367. })
  6368. }
  6369. html = {'html': root.toHtml()};
  6370. me.fireEvent('beforepaste', html, root);
  6371. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  6372. if (!html.html) {
  6373. return;
  6374. }
  6375. me.execCommand('insertHtml', html.html, true);
  6376. me.fireEvent("afterpaste", html);
  6377. }
  6378. }
  6379. me.addListener('ready', function () {
  6380. $(me.body).on('cut', function () {
  6381. var range = me.selection.getRange();
  6382. if (!range.collapsed && me.undoManger) {
  6383. me.undoManger.save();
  6384. }
  6385. }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
  6386. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  6387. if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
  6388. return;
  6389. }
  6390. getClipboardData.call(me, function (div) {
  6391. filter(div);
  6392. });
  6393. });
  6394. });
  6395. };
  6396. ///import core
  6397. ///commands 有序列表,无序列表
  6398. ///commandsName InsertOrderedList,InsertUnorderedList
  6399. ///commandsTitle 有序列表,无序列表
  6400. /**
  6401. * 有序列表
  6402. * @function
  6403. * @name UM.execCommand
  6404. * @param {String} cmdName insertorderlist插入有序列表
  6405. * @param {String} style 值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman
  6406. * @author zhanyi
  6407. */
  6408. /**
  6409. * 无序链接
  6410. * @function
  6411. * @name UM.execCommand
  6412. * @param {String} cmdName insertunorderlist插入无序列表
  6413. * * @param {String} style 值为:circle,disc,square
  6414. * @author zhanyi
  6415. */
  6416. UM.plugins['list'] = function () {
  6417. var me = this;
  6418. me.setOpt({
  6419. 'insertorderedlist': {
  6420. 'decimal': '',
  6421. 'lower-alpha': '',
  6422. 'lower-roman': '',
  6423. 'upper-alpha': '',
  6424. 'upper-roman': ''
  6425. },
  6426. 'insertunorderedlist': {
  6427. 'circle': '',
  6428. 'disc': '',
  6429. 'square': ''
  6430. }
  6431. });
  6432. this.addInputRule(function (root) {
  6433. utils.each(root.getNodesByTagName('li'), function (node) {
  6434. if (node.children.length == 0) {
  6435. node.parentNode.removeChild(node);
  6436. }
  6437. })
  6438. });
  6439. me.commands['insertorderedlist'] =
  6440. me.commands['insertunorderedlist'] = {
  6441. execCommand: function (cmdName) {
  6442. this.document.execCommand(cmdName);
  6443. var rng = this.selection.getRange(),
  6444. bk = rng.createBookmark(true);
  6445. this.$body.find('ol,ul').each(function (i, n) {
  6446. var parent = n.parentNode;
  6447. if (parent.tagName == 'P' && parent.lastChild === parent.firstChild) {
  6448. $(n).children().each(function (j, li) {
  6449. var p = parent.cloneNode(false);
  6450. $(p).append(li.innerHTML);
  6451. $(li).html('').append(p);
  6452. });
  6453. $(n).insertBefore(parent);
  6454. $(parent).remove();
  6455. }
  6456. if (dtd.$inline[parent.tagName]) {
  6457. if (parent.tagName == 'SPAN') {
  6458. $(n).children().each(function (k, li) {
  6459. var span = parent.cloneNode(false);
  6460. if (li.firstChild.nodeName != 'P') {
  6461. while (li.firstChild) {
  6462. span.appendChild(li.firstChild)
  6463. }
  6464. ;
  6465. $('<p></p>').appendTo(li).append(span);
  6466. } else {
  6467. while (li.firstChild) {
  6468. span.appendChild(li.firstChild)
  6469. }
  6470. ;
  6471. $(li.firstChild).append(span);
  6472. }
  6473. })
  6474. }
  6475. domUtils.remove(parent, true)
  6476. }
  6477. });
  6478. rng.moveToBookmark(bk).select();
  6479. return true;
  6480. },
  6481. queryCommandState: function (cmdName) {
  6482. return this.document.queryCommandState(cmdName);
  6483. }
  6484. };
  6485. };
  6486. ///import core
  6487. ///import plugins/serialize.js
  6488. ///import plugins/undo.js
  6489. ///commands 查看源码
  6490. ///commandsName Source
  6491. ///commandsTitle 查看源码
  6492. (function () {
  6493. var sourceEditors = {
  6494. textarea: function (editor, holder) {
  6495. var textarea = holder.ownerDocument.createElement('textarea');
  6496. textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';
  6497. // todo: IE下只有onresize属性可用... 很纠结
  6498. if (browser.ie && browser.version < 8) {
  6499. textarea.style.width = holder.offsetWidth + 'px';
  6500. textarea.style.height = holder.offsetHeight + 'px';
  6501. holder.onresize = function () {
  6502. textarea.style.width = holder.offsetWidth + 'px';
  6503. textarea.style.height = holder.offsetHeight + 'px';
  6504. };
  6505. }
  6506. holder.appendChild(textarea);
  6507. return {
  6508. container: textarea,
  6509. setContent: function (content) {
  6510. textarea.value = content;
  6511. },
  6512. getContent: function () {
  6513. return textarea.value;
  6514. },
  6515. select: function () {
  6516. var range;
  6517. if (browser.ie) {
  6518. range = textarea.createTextRange();
  6519. range.collapse(true);
  6520. range.select();
  6521. } else {
  6522. //todo: chrome下无法设置焦点
  6523. textarea.setSelectionRange(0, 0);
  6524. textarea.focus();
  6525. }
  6526. },
  6527. dispose: function () {
  6528. holder.removeChild(textarea);
  6529. // todo
  6530. holder.onresize = null;
  6531. textarea = null;
  6532. holder = null;
  6533. }
  6534. };
  6535. }
  6536. };
  6537. UM.plugins['source'] = function () {
  6538. var me = this;
  6539. var opt = this.options;
  6540. var sourceMode = false;
  6541. var sourceEditor;
  6542. opt.sourceEditor = 'textarea';
  6543. me.setOpt({
  6544. sourceEditorFirst: false
  6545. });
  6546. function createSourceEditor(holder) {
  6547. return sourceEditors.textarea(me, holder);
  6548. }
  6549. var bakCssText;
  6550. //解决在源码模式下getContent不能得到最新的内容问题
  6551. var oldGetContent = me.getContent,
  6552. bakAddress;
  6553. me.commands['source'] = {
  6554. execCommand: function () {
  6555. sourceMode = !sourceMode;
  6556. if (sourceMode) {
  6557. bakAddress = me.selection.getRange().createAddress(false, true);
  6558. me.undoManger && me.undoManger.save(true);
  6559. if (browser.gecko) {
  6560. me.body.contentEditable = false;
  6561. }
  6562. // bakCssText = me.body.style.cssText;
  6563. me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';
  6564. me.fireEvent('beforegetcontent');
  6565. var root = UM.htmlparser(me.body.innerHTML);
  6566. me.filterOutputRule(root);
  6567. root.traversal(function (node) {
  6568. if (node.type == 'element') {
  6569. switch (node.tagName) {
  6570. case 'td':
  6571. case 'th':
  6572. case 'caption':
  6573. if (node.children && node.children.length == 1) {
  6574. if (node.firstChild().tagName == 'br') {
  6575. node.removeChild(node.firstChild())
  6576. }
  6577. }
  6578. ;
  6579. break;
  6580. case 'pre':
  6581. node.innerText(node.innerText().replace(/&nbsp;/g, ' '))
  6582. }
  6583. }
  6584. });
  6585. me.fireEvent('aftergetcontent');
  6586. var content = root.toHtml(true);
  6587. sourceEditor = createSourceEditor(me.body.parentNode);
  6588. sourceEditor.setContent(content);
  6589. var getStyleValue = function (attr) {
  6590. return parseInt($(me.body).css(attr));
  6591. };
  6592. $(sourceEditor.container).width($(me.body).width() + getStyleValue("padding-left") + getStyleValue("padding-right"))
  6593. .height($(me.body).height());
  6594. setTimeout(function () {
  6595. sourceEditor.select();
  6596. });
  6597. //重置getContent,源码模式下取值也能是最新的数据
  6598. me.getContent = function () {
  6599. return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  6600. };
  6601. } else {
  6602. me.$body.css({
  6603. 'position': '',
  6604. 'left': '',
  6605. 'top': ''
  6606. });
  6607. // me.body.style.cssText = bakCssText;
  6608. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  6609. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  6610. cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>', 'g'), function (a, b) {
  6611. if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
  6612. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, '');
  6613. }
  6614. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, '')
  6615. });
  6616. me.setContent(cont);
  6617. sourceEditor.dispose();
  6618. sourceEditor = null;
  6619. //还原getContent方法
  6620. me.getContent = oldGetContent;
  6621. var first = me.body.firstChild;
  6622. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  6623. if (!first) {
  6624. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  6625. }
  6626. //要在ifm为显示时ff才能取到selection,否则报错
  6627. //这里不能比较位置了
  6628. me.undoManger && me.undoManger.save(true);
  6629. if (browser.gecko) {
  6630. me.body.contentEditable = true;
  6631. }
  6632. try {
  6633. me.selection.getRange().moveToAddress(bakAddress).select();
  6634. } catch (e) {
  6635. }
  6636. }
  6637. this.fireEvent('sourcemodechanged', sourceMode);
  6638. },
  6639. queryCommandState: function () {
  6640. return sourceMode | 0;
  6641. },
  6642. notNeedUndo: 1
  6643. };
  6644. var oldQueryCommandState = me.queryCommandState;
  6645. me.queryCommandState = function (cmdName) {
  6646. cmdName = cmdName.toLowerCase();
  6647. if (sourceMode) {
  6648. //源码模式下可以开启的命令
  6649. return cmdName in {
  6650. 'source': 1,
  6651. 'fullscreen': 1
  6652. } ? oldQueryCommandState.apply(this, arguments) : -1
  6653. }
  6654. return oldQueryCommandState.apply(this, arguments);
  6655. };
  6656. };
  6657. })();
  6658. ///import core
  6659. ///import plugins/undo.js
  6660. ///commands 设置回车标签p或br
  6661. ///commandsName EnterKey
  6662. ///commandsTitle 设置回车标签p或br
  6663. /**
  6664. * @description 处理回车
  6665. * @author zhanyi
  6666. */
  6667. UM.plugins['enterkey'] = function () {
  6668. var hTag,
  6669. me = this,
  6670. tag = me.options.enterTag;
  6671. me.addListener('keyup', function (type, evt) {
  6672. var keyCode = evt.keyCode || evt.which;
  6673. if (keyCode == 13) {
  6674. var range = me.selection.getRange(),
  6675. start = range.startContainer,
  6676. doSave;
  6677. //修正在h1-h6里边回车后不能嵌套p的问题
  6678. if (!browser.ie) {
  6679. if (/h\d/i.test(hTag)) {
  6680. if (browser.gecko) {
  6681. var h = domUtils.findParentByTagName(start, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption', 'table'], true);
  6682. if (!h) {
  6683. me.document.execCommand('formatBlock', false, '<p>');
  6684. doSave = 1;
  6685. }
  6686. } else {
  6687. //chrome remove div
  6688. if (start.nodeType == 1) {
  6689. var tmp = me.document.createTextNode(''), div;
  6690. range.insertNode(tmp);
  6691. div = domUtils.findParentByTagName(tmp, 'div', true);
  6692. if (div) {
  6693. var p = me.document.createElement('p');
  6694. while (div.firstChild) {
  6695. p.appendChild(div.firstChild);
  6696. }
  6697. div.parentNode.insertBefore(p, div);
  6698. domUtils.remove(div);
  6699. range.setStartBefore(tmp).setCursor();
  6700. doSave = 1;
  6701. }
  6702. domUtils.remove(tmp);
  6703. }
  6704. }
  6705. if (me.undoManger && doSave) {
  6706. me.undoManger.save();
  6707. }
  6708. }
  6709. //没有站位符,会出现多行的问题
  6710. browser.opera && range.select();
  6711. } else {
  6712. me.fireEvent('saveScene', true, true)
  6713. }
  6714. }
  6715. });
  6716. me.addListener('keydown', function (type, evt) {
  6717. var keyCode = evt.keyCode || evt.which;
  6718. if (keyCode == 13) {//回车
  6719. if (me.fireEvent('beforeenterkeydown')) {
  6720. domUtils.preventDefault(evt);
  6721. return;
  6722. }
  6723. me.fireEvent('saveScene', true, true);
  6724. hTag = '';
  6725. var range = me.selection.getRange();
  6726. if (!range.collapsed) {
  6727. //跨td不能删
  6728. var start = range.startContainer,
  6729. end = range.endContainer,
  6730. startTd = domUtils.findParentByTagName(start, 'td', true),
  6731. endTd = domUtils.findParentByTagName(end, 'td', true);
  6732. if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
  6733. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  6734. return;
  6735. }
  6736. }
  6737. if (tag == 'p') {
  6738. if (!browser.ie) {
  6739. start = domUtils.findParentByTagName(range.startContainer, ['ol', 'ul', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption'], true);
  6740. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  6741. //trace:2431
  6742. if (!start && !browser.opera) {
  6743. me.document.execCommand('formatBlock', false, '<p>');
  6744. if (browser.gecko) {
  6745. range = me.selection.getRange();
  6746. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  6747. start && domUtils.removeDirtyAttr(start);
  6748. }
  6749. } else {
  6750. hTag = start.tagName;
  6751. start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
  6752. }
  6753. }
  6754. }
  6755. }
  6756. });
  6757. browser.ie && me.addListener('setDisabled', function () {
  6758. $(me.body).find('p').each(function (i, p) {
  6759. if (domUtils.isEmptyBlock(p)) {
  6760. p.innerHTML = '&nbsp;'
  6761. }
  6762. })
  6763. })
  6764. };
  6765. ///import core
  6766. ///commands 预览
  6767. ///commandsName Preview
  6768. ///commandsTitle 预览
  6769. /**
  6770. * 预览
  6771. * @function
  6772. * @name UM.execCommand
  6773. * @param {String} cmdName preview预览编辑器内容
  6774. */
  6775. UM.commands['preview'] = {
  6776. execCommand: function () {
  6777. var w = window.open('', '_blank', ''),
  6778. d = w.document,
  6779. c = this.getContent(null, null, true),
  6780. path = this.getOpt('UMEDITOR_HOME_URL'),
  6781. formula = c.indexOf('mathquill-embedded-latex') != -1 ?
  6782. '<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +
  6783. '<script src="' + path + 'third-party/jquery.min.js"></script>' +
  6784. '<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>' : '';
  6785. d.open();
  6786. d.write('<html><head>' + formula + '</head><body><div>' + c + '</div></body></html>');
  6787. d.close();
  6788. },
  6789. notNeedUndo: 1
  6790. };
  6791. ///import core
  6792. ///commands 加粗,斜体,上标,下标
  6793. ///commandsName Bold,Italic,Subscript,Superscript
  6794. ///commandsTitle 加粗,加斜,下标,上标
  6795. /**
  6796. * b u i等基础功能实现
  6797. * @function
  6798. * @name UM.execCommands
  6799. * @param {String} cmdName bold加粗。italic斜体。subscript上标。superscript下标。
  6800. */
  6801. UM.plugins['basestyle'] = function () {
  6802. var basestyles = ['bold', 'underline', 'superscript', 'subscript', 'italic', 'strikethrough'],
  6803. me = this;
  6804. //添加快捷键
  6805. me.addshortcutkey({
  6806. "Bold": "ctrl+66",//^B
  6807. "Italic": "ctrl+73", //^I
  6808. "Underline": "ctrl+shift+85",//^U
  6809. "strikeThrough": 'ctrl+shift+83' //^s
  6810. });
  6811. //过滤最后的产出数据
  6812. me.addOutputRule(function (root) {
  6813. $.each(root.getNodesByTagName('b i u strike s'), function (i, node) {
  6814. switch (node.tagName) {
  6815. case 'b':
  6816. node.tagName = 'strong';
  6817. break;
  6818. case 'i':
  6819. node.tagName = 'em';
  6820. break;
  6821. case 'u':
  6822. node.tagName = 'span';
  6823. node.setStyle('text-decoration', 'underline');
  6824. break;
  6825. case 's':
  6826. case 'strike':
  6827. node.tagName = 'span';
  6828. node.setStyle('text-decoration', 'line-through')
  6829. }
  6830. });
  6831. });
  6832. $.each(basestyles, function (i, cmd) {
  6833. me.commands[cmd] = {
  6834. execCommand: function (cmdName) {
  6835. var rng = this.selection.getRange();
  6836. if (rng.collapsed && this.queryCommandState(cmdName) != 1) {
  6837. var node = this.document.createElement({
  6838. 'bold': 'strong',
  6839. 'underline': 'u',
  6840. 'superscript': 'sup',
  6841. 'subscript': 'sub',
  6842. 'italic': 'em',
  6843. 'strikethrough': 'strike'
  6844. }[cmdName]);
  6845. rng.insertNode(node).setStart(node, 0).setCursor(false);
  6846. return true;
  6847. } else {
  6848. return this.document.execCommand(cmdName)
  6849. }
  6850. },
  6851. queryCommandState: function (cmdName) {
  6852. if (browser.gecko) {
  6853. return this.document.queryCommandState(cmdName)
  6854. }
  6855. var path = this.selection.getStartElementPath(), result = false;
  6856. $.each(path, function (i, n) {
  6857. switch (cmdName) {
  6858. case 'bold':
  6859. if (n.nodeName == 'STRONG' || n.nodeName == 'B') {
  6860. result = 1;
  6861. return false;
  6862. }
  6863. break;
  6864. case 'underline':
  6865. if (n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline') {
  6866. result = 1;
  6867. return false;
  6868. }
  6869. break;
  6870. case 'superscript':
  6871. if (n.nodeName == 'SUP') {
  6872. result = 1;
  6873. return false;
  6874. }
  6875. break;
  6876. case 'subscript':
  6877. if (n.nodeName == 'SUB') {
  6878. result = 1;
  6879. return false;
  6880. }
  6881. break;
  6882. case 'italic':
  6883. if (n.nodeName == 'EM' || n.nodeName == 'I') {
  6884. result = 1;
  6885. return false;
  6886. }
  6887. break;
  6888. case 'strikethrough':
  6889. if (n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through') {
  6890. result = 1;
  6891. return false;
  6892. }
  6893. break;
  6894. }
  6895. })
  6896. return result
  6897. }
  6898. };
  6899. })
  6900. };
  6901. ///import core
  6902. ///import plugins/inserthtml.js
  6903. ///commands 视频
  6904. ///commandsName InsertVideo
  6905. ///commandsTitle 插入视频
  6906. ///commandsDialog dialogs\video
  6907. UM.plugins['video'] = function () {
  6908. var me = this,
  6909. div;
  6910. /**
  6911. * 创建插入视频字符窜
  6912. * @param url 视频地址
  6913. * @param width 视频宽度
  6914. * @param height 视频高度
  6915. * @param align 视频对齐
  6916. * @param toEmbed 是否以flash代替显示
  6917. * @param addParagraph 是否需要添加P 标签
  6918. */
  6919. function creatInsertStr(url, width, height, id, align, toEmbed) {
  6920. return !toEmbed ?
  6921. '<img ' + (id ? 'id="' + id + '"' : '') + ' width="' + width + '" height="' + height + '" _url="' + url + '" class="edui-faked-video"' +
  6922. ' src="' + me.options.UMEDITOR_HOME_URL + 'themes/default/images/spacer.gif" style="background:url(' + me.options.UMEDITOR_HOME_URL + 'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;' + (align ? 'float:' + align + ';' : '') + '" />'
  6923. :
  6924. '<video class="edui-faked-video" ' +
  6925. ' src="' + url + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"' : '') +
  6926. ' controls autoplay preload="auto"></video>';
  6927. }
  6928. function switchImgAndEmbed(root, img2embed) {
  6929. utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'), function (node) {
  6930. if (node.getAttr('class') == 'edui-faked-video') {
  6931. var html = creatInsertStr(img2embed ? node.getAttr('_url') : node.getAttr('src'), node.getAttr('width'), node.getAttr('height'), null, node.getStyle('float') || '', img2embed);
  6932. node.parentNode.replaceChild(UM.uNode.createElement(html), node)
  6933. }
  6934. })
  6935. }
  6936. me.addOutputRule(function (root) {
  6937. switchImgAndEmbed(root, true)
  6938. });
  6939. me.addInputRule(function (root) {
  6940. switchImgAndEmbed(root)
  6941. });
  6942. me.commands["insertvideo"] = {
  6943. execCommand: function (cmd, videoObjs) {
  6944. videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs];
  6945. var html = [], id = 'tmpVedio';
  6946. for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
  6947. vi = videoObjs[i];
  6948. vi.url = utils.unhtml(vi.url, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g);
  6949. html.push(creatInsertStr(vi.url, vi.width || 420, vi.height || 280, id + i, vi.align, false));
  6950. }
  6951. me.execCommand("inserthtml", html.join(""), true);
  6952. },
  6953. queryCommandState: function () {
  6954. var img = me.selection.getRange().getClosedNode(),
  6955. flag = img && (img.className == "edui-faked-video");
  6956. return flag ? 1 : 0;
  6957. }
  6958. };
  6959. };
  6960. ///import core
  6961. ///commands 全选
  6962. ///commandsName SelectAll
  6963. ///commandsTitle 全选
  6964. /**
  6965. * 选中所有
  6966. * @function
  6967. * @name UM.execCommand
  6968. * @param {String} cmdName selectall选中编辑器里的所有内容
  6969. * @author zhanyi
  6970. */
  6971. UM.plugins['selectall'] = function () {
  6972. var me = this;
  6973. me.commands['selectall'] = {
  6974. execCommand: function () {
  6975. //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
  6976. var me = this, body = me.body,
  6977. range = me.selection.getRange();
  6978. range.selectNodeContents(body);
  6979. if (domUtils.isEmptyBlock(body)) {
  6980. //opera不能自动合并到元素的里边,要手动处理一下
  6981. if (browser.opera && body.firstChild && body.firstChild.nodeType == 1) {
  6982. range.setStartAtFirst(body.firstChild);
  6983. }
  6984. range.collapse(true);
  6985. }
  6986. range.select(true);
  6987. },
  6988. notNeedUndo: 1
  6989. };
  6990. //快捷键
  6991. me.addshortcutkey({
  6992. "selectAll": "ctrl+65"
  6993. });
  6994. };
  6995. //UM.plugins['removeformat'] = function () {
  6996. // var me = this;
  6997. // me.commands['removeformat'] = {
  6998. // execCommand: function () {
  6999. // me.document.execCommand('removeformat');
  7000. //
  7001. // /* 处理ie8和firefox选区有链接时,清除格式的bug */
  7002. // if (browser.gecko || browser.ie8 || browser.webkit) {
  7003. // var nativeRange = this.selection.getNative().getRangeAt(0),
  7004. // common = nativeRange.commonAncestorContainer,
  7005. // rng = me.selection.getRange(),
  7006. // bk = rng.createBookmark();
  7007. //
  7008. // function isEleInBookmark(node, bk){
  7009. // if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&
  7010. // (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {
  7011. // return true;
  7012. // } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||
  7013. // (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {
  7014. // return true;
  7015. // }
  7016. // return false;
  7017. // }
  7018. //
  7019. // $(common).find('a').each(function (k, a) {
  7020. // if ( isEleInBookmark(a, bk) ) {
  7021. // a.removeAttribute('style');
  7022. // }
  7023. // });
  7024. //
  7025. // }
  7026. // }
  7027. // };
  7028. //
  7029. //};
  7030. //
  7031. UM.plugins['removeformat'] = function () {
  7032. var me = this;
  7033. me.setOpt({
  7034. 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
  7035. 'removeFormatAttributes': 'class,style,lang,width,height,align,hspace,valign'
  7036. });
  7037. me.commands['removeformat'] = {
  7038. execCommand: function (cmdName, tags, style, attrs, notIncludeA) {
  7039. var tagReg = new RegExp('^(?:' + (tags || this.options.removeFormatTags).replace(/,/g, '|') + ')$', 'i'),
  7040. removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split(','),
  7041. range = new dom.Range(this.document),
  7042. bookmark, node, parent,
  7043. filter = function (node) {
  7044. return node.nodeType == 1;
  7045. };
  7046. function isRedundantSpan(node) {
  7047. if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span') {
  7048. return 0;
  7049. }
  7050. if (browser.ie) {
  7051. //ie 下判断实效,所以只能简单用style来判断
  7052. //return node.style.cssText == '' ? 1 : 0;
  7053. var attrs = node.attributes;
  7054. if (attrs.length) {
  7055. for (var i = 0, l = attrs.length; i < l; i++) {
  7056. if (attrs[i].specified) {
  7057. return 0;
  7058. }
  7059. }
  7060. return 1;
  7061. }
  7062. }
  7063. return !node.attributes.length;
  7064. }
  7065. function doRemove(range) {
  7066. var bookmark1 = range.createBookmark();
  7067. if (range.collapsed) {
  7068. range.enlarge(true);
  7069. }
  7070. //不能把a标签切了
  7071. if (!notIncludeA) {
  7072. var aNode = domUtils.findParentByTagName(range.startContainer, 'a', true);
  7073. if (aNode) {
  7074. range.setStartBefore(aNode);
  7075. }
  7076. aNode = domUtils.findParentByTagName(range.endContainer, 'a', true);
  7077. if (aNode) {
  7078. range.setEndAfter(aNode);
  7079. }
  7080. }
  7081. bookmark = range.createBookmark();
  7082. node = bookmark.start;
  7083. //切开始
  7084. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  7085. domUtils.breakParent(node, parent);
  7086. domUtils.clearEmptySibling(node);
  7087. }
  7088. if (bookmark.end) {
  7089. //切结束
  7090. node = bookmark.end;
  7091. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  7092. domUtils.breakParent(node, parent);
  7093. domUtils.clearEmptySibling(node);
  7094. }
  7095. //开始去除样式
  7096. var current = domUtils.getNextDomNode(bookmark.start, false, filter),
  7097. next;
  7098. while (current) {
  7099. if (current == bookmark.end) {
  7100. break;
  7101. }
  7102. next = domUtils.getNextDomNode(current, true, filter);
  7103. if (!dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode(current)) {
  7104. if (tagReg.test(current.tagName)) {
  7105. if (style) {
  7106. domUtils.removeStyle(current, style);
  7107. if (isRedundantSpan(current) && style != 'text-decoration') {
  7108. domUtils.remove(current, true);
  7109. }
  7110. } else {
  7111. domUtils.remove(current, true);
  7112. }
  7113. } else {
  7114. //trace:939 不能把list上的样式去掉
  7115. if (!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]) {
  7116. domUtils.removeAttributes(current, removeFormatAttributes);
  7117. if (isRedundantSpan(current)) {
  7118. domUtils.remove(current, true);
  7119. }
  7120. }
  7121. }
  7122. }
  7123. current = next;
  7124. }
  7125. }
  7126. //trace:1035
  7127. //trace:1096 不能把td上的样式去掉,比如边框
  7128. var pN = bookmark.start.parentNode;
  7129. if (domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
  7130. var remove_check = pN.getAttribute('class'); //新增
  7131. if (remove_check && remove_check.indexOf('edui-body-container') > -1) {
  7132. } else {
  7133. domUtils.removeAttributes(pN, removeFormatAttributes);
  7134. }
  7135. }
  7136. pN = bookmark.end.parentNode;
  7137. if (bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
  7138. var remove_check = pN.getAttribute('class'); //新增
  7139. if (remove_check && remove_check.indexOf('edui-body-container') > -1) {
  7140. } else {
  7141. domUtils.removeAttributes(pN, removeFormatAttributes);
  7142. }
  7143. }
  7144. range.moveToBookmark(bookmark).moveToBookmark(bookmark1);
  7145. //清除冗余的代码 <b><bookmark></b>
  7146. var node = range.startContainer,
  7147. tmp,
  7148. collapsed = range.collapsed;
  7149. console.log(node);
  7150. while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
  7151. tmp = node.parentNode;
  7152. range.setStartBefore(node);
  7153. //trace:937
  7154. //更新结束边界
  7155. if (range.startContainer === range.endContainer) {
  7156. range.endOffset--;
  7157. }
  7158. domUtils.remove(node);
  7159. node = tmp;
  7160. }
  7161. if (!collapsed) {
  7162. node = range.endContainer;
  7163. while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
  7164. tmp = node.parentNode;
  7165. range.setEndBefore(node);
  7166. domUtils.remove(node);
  7167. node = tmp;
  7168. }
  7169. }
  7170. }
  7171. range = this.selection.getRange();
  7172. if (!range.collapsed) {
  7173. doRemove(range);
  7174. range.select();
  7175. }
  7176. }
  7177. };
  7178. };
  7179. /*
  7180. * 处理特殊键的兼容性问题
  7181. */
  7182. UM.plugins['keystrokes'] = function () {
  7183. var me = this;
  7184. var collapsed = true;
  7185. me.addListener('keydown', function (type, evt) {
  7186. var keyCode = evt.keyCode || evt.which,
  7187. rng = me.selection.getRange();
  7188. //处理全选的情况
  7189. if (!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <= 90
  7190. || keyCode >= 48 && keyCode <= 57 ||
  7191. keyCode >= 96 && keyCode <= 111 || {
  7192. 13: 1,
  7193. 8: 1,
  7194. 46: 1
  7195. }[keyCode])
  7196. ) {
  7197. var tmpNode = rng.startContainer;
  7198. if (domUtils.isFillChar(tmpNode)) {
  7199. rng.setStartBefore(tmpNode)
  7200. }
  7201. tmpNode = rng.endContainer;
  7202. if (domUtils.isFillChar(tmpNode)) {
  7203. rng.setEndAfter(tmpNode)
  7204. }
  7205. rng.txtToElmBoundary();
  7206. //结束边界可能放到了br的前边,要把br包含进来
  7207. // x[xxx]<br/>
  7208. if (rng.endContainer && rng.endContainer.nodeType == 1) {
  7209. tmpNode = rng.endContainer.childNodes[rng.endOffset];
  7210. if (tmpNode && domUtils.isBr(tmpNode)) {
  7211. rng.setEndAfter(tmpNode);
  7212. }
  7213. }
  7214. if (rng.startOffset == 0) {
  7215. tmpNode = rng.startContainer;
  7216. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  7217. tmpNode = rng.endContainer;
  7218. if (rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode, 'lastChild')) {
  7219. me.fireEvent('saveScene');
  7220. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  7221. rng.setStart(me.body.firstChild, 0).setCursor(false, true);
  7222. me._selectionChange();
  7223. return;
  7224. }
  7225. }
  7226. }
  7227. }
  7228. //处理backspace
  7229. if (keyCode == 8) {
  7230. rng = me.selection.getRange();
  7231. collapsed = rng.collapsed;
  7232. if (me.fireEvent('delkeydown', evt)) {
  7233. return;
  7234. }
  7235. var start, end;
  7236. //避免按两次删除才能生效的问题
  7237. if (rng.collapsed && rng.inFillChar()) {
  7238. start = rng.startContainer;
  7239. if (domUtils.isFillChar(start)) {
  7240. rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
  7241. domUtils.remove(start)
  7242. } else {
  7243. start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '');
  7244. rng.startOffset--;
  7245. rng.collapse(true).select(true)
  7246. }
  7247. }
  7248. //解决选中control元素不能删除的问题
  7249. if (start = rng.getClosedNode()) {
  7250. me.fireEvent('saveScene');
  7251. rng.setStartBefore(start);
  7252. domUtils.remove(start);
  7253. rng.setCursor();
  7254. me.fireEvent('saveScene');
  7255. domUtils.preventDefault(evt);
  7256. return;
  7257. }
  7258. //阻止在table上的删除
  7259. if (!browser.ie) {
  7260. start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
  7261. end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
  7262. if (start && !end || !start && end || start !== end) {
  7263. evt.preventDefault();
  7264. return;
  7265. }
  7266. }
  7267. start = rng.startContainer;
  7268. if (rng.collapsed && start.nodeType == 1) {
  7269. var currentNode = start.childNodes[rng.startOffset - 1];
  7270. if (currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR') {
  7271. me.fireEvent('saveScene');
  7272. rng.setStartBefore(currentNode).collapse(true);
  7273. domUtils.remove(currentNode);
  7274. rng.select();
  7275. me.fireEvent('saveScene');
  7276. }
  7277. }
  7278. //trace:3613
  7279. if (browser.chrome) {
  7280. if (rng.collapsed) {
  7281. while (rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)) {
  7282. rng.setStartBefore(rng.startContainer)
  7283. }
  7284. var pre = rng.startContainer.childNodes[rng.startOffset - 1];
  7285. if (pre && pre.nodeName == 'BR') {
  7286. rng.setStartBefore(pre);
  7287. me.fireEvent('saveScene');
  7288. $(pre).remove();
  7289. rng.setCursor();
  7290. me.fireEvent('saveScene');
  7291. }
  7292. }
  7293. }
  7294. }
  7295. //trace:1634
  7296. //ff的del键在容器空的时候,也会删除
  7297. if (browser.gecko && keyCode == 46) {
  7298. var range = me.selection.getRange();
  7299. if (range.collapsed) {
  7300. start = range.startContainer;
  7301. if (domUtils.isEmptyBlock(start)) {
  7302. var parent = start.parentNode;
  7303. while (domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)) {
  7304. start = parent;
  7305. parent = parent.parentNode;
  7306. }
  7307. if (start === parent.lastChild)
  7308. evt.preventDefault();
  7309. return;
  7310. }
  7311. }
  7312. }
  7313. });
  7314. me.addListener('keyup', function (type, evt) {
  7315. var keyCode = evt.keyCode || evt.which,
  7316. rng, me = this;
  7317. if (keyCode == 8) {
  7318. if (me.fireEvent('delkeyup')) {
  7319. return;
  7320. }
  7321. rng = me.selection.getRange();
  7322. if (rng.collapsed) {
  7323. var tmpNode,
  7324. autoClearTagName = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
  7325. if (tmpNode = domUtils.findParentByTagName(rng.startContainer, autoClearTagName, true)) {
  7326. if (domUtils.isEmptyBlock(tmpNode)) {
  7327. var pre = tmpNode.previousSibling;
  7328. if (pre && pre.nodeName != 'TABLE') {
  7329. domUtils.remove(tmpNode);
  7330. rng.setStartAtLast(pre).setCursor(false, true);
  7331. return;
  7332. } else {
  7333. var next = tmpNode.nextSibling;
  7334. if (next && next.nodeName != 'TABLE') {
  7335. domUtils.remove(tmpNode);
  7336. rng.setStartAtFirst(next).setCursor(false, true);
  7337. return;
  7338. }
  7339. }
  7340. }
  7341. }
  7342. //处理当删除到body时,要重新给p标签展位
  7343. if (domUtils.isBody(rng.startContainer)) {
  7344. var tmpNode = domUtils.createElement(me.document, 'p', {
  7345. 'innerHTML': browser.ie ? domUtils.fillChar : '<br/>'
  7346. });
  7347. rng.insertNode(tmpNode).setStart(tmpNode, 0).setCursor(false, true);
  7348. }
  7349. }
  7350. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  7351. if (!collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))) {
  7352. if (browser.ie) {
  7353. var span = rng.document.createElement('span');
  7354. rng.insertNode(span).setStartBefore(span).collapse(true);
  7355. rng.select();
  7356. domUtils.remove(span)
  7357. } else {
  7358. rng.select()
  7359. }
  7360. }
  7361. }
  7362. })
  7363. };
  7364. /**
  7365. * 自动保存草稿
  7366. */
  7367. UM.plugins['autosave'] = function () {
  7368. var me = this,
  7369. //无限循环保护
  7370. lastSaveTime = new Date(),
  7371. //最小保存间隔时间
  7372. MIN_TIME = 20,
  7373. //auto save key
  7374. saveKey = null;
  7375. //默认间隔时间
  7376. me.setOpt('saveInterval', 500);
  7377. //存储媒介封装
  7378. var LocalStorage = UM.LocalStorage = (function () {
  7379. var storage = window.localStorage || getUserData() || null,
  7380. LOCAL_FILE = "localStorage";
  7381. return {
  7382. saveLocalData: function (key, data) {
  7383. if (storage && data) {
  7384. storage.setItem(key, data);
  7385. return true;
  7386. }
  7387. return false;
  7388. },
  7389. getLocalData: function (key) {
  7390. if (storage) {
  7391. return storage.getItem(key);
  7392. }
  7393. return null;
  7394. },
  7395. removeItem: function (key) {
  7396. storage && storage.removeItem(key);
  7397. }
  7398. };
  7399. function getUserData() {
  7400. var container = document.createElement("div");
  7401. container.style.display = "none";
  7402. if (!container.addBehavior) {
  7403. return null;
  7404. }
  7405. container.addBehavior("#default#userdata");
  7406. return {
  7407. getItem: function (key) {
  7408. var result = null;
  7409. try {
  7410. document.body.appendChild(container);
  7411. container.load(LOCAL_FILE);
  7412. result = container.getAttribute(key);
  7413. document.body.removeChild(container);
  7414. } catch (e) {
  7415. }
  7416. return result;
  7417. },
  7418. setItem: function (key, value) {
  7419. document.body.appendChild(container);
  7420. container.setAttribute(key, value);
  7421. container.save(LOCAL_FILE);
  7422. document.body.removeChild(container);
  7423. },
  7424. // 暂时没有用到
  7425. // clear: function () {
  7426. //
  7427. // var expiresTime = new Date();
  7428. // expiresTime.setFullYear( expiresTime.getFullYear() - 1 );
  7429. // document.body.appendChild( container );
  7430. // container.expires = expiresTime.toUTCString();
  7431. // container.save( LOCAL_FILE );
  7432. // document.body.removeChild( container );
  7433. //
  7434. // },
  7435. removeItem: function (key) {
  7436. document.body.appendChild(container);
  7437. container.removeAttribute(key);
  7438. container.save(LOCAL_FILE);
  7439. document.body.removeChild(container);
  7440. }
  7441. };
  7442. }
  7443. })();
  7444. function save(editor) {
  7445. var saveData = null;
  7446. if (new Date() - lastSaveTime < MIN_TIME) {
  7447. return;
  7448. }
  7449. if (!editor.hasContents()) {
  7450. //这里不能调用命令来删除, 会造成事件死循环
  7451. saveKey && LocalStorage.removeItem(saveKey);
  7452. return;
  7453. }
  7454. lastSaveTime = new Date();
  7455. editor._saveFlag = null;
  7456. saveData = me.body.innerHTML;
  7457. if (editor.fireEvent("beforeautosave", {
  7458. content: saveData
  7459. }) === false) {
  7460. return;
  7461. }
  7462. LocalStorage.saveLocalData(saveKey, saveData);
  7463. editor.fireEvent("afterautosave", {
  7464. content: saveData
  7465. });
  7466. }
  7467. me.addListener('ready', function () {
  7468. var _suffix = "-drafts-data",
  7469. key = null;
  7470. if (me.key) {
  7471. key = me.key + _suffix;
  7472. } else {
  7473. key = (me.container.parentNode.id || 'ue-common') + _suffix;
  7474. }
  7475. //页面地址+编辑器ID 保持唯一
  7476. saveKey = (location.protocol + location.host + location.pathname).replace(/[.:\/]/g, '_') + key;
  7477. });
  7478. me.addListener('contentchange', function () {
  7479. if (!saveKey) {
  7480. return;
  7481. }
  7482. if (me._saveFlag) {
  7483. window.clearTimeout(me._saveFlag);
  7484. }
  7485. if (me.options.saveInterval > 0) {
  7486. me._saveFlag = window.setTimeout(function () {
  7487. save(me);
  7488. }, me.options.saveInterval);
  7489. } else {
  7490. save(me);
  7491. }
  7492. })
  7493. me.commands['clearlocaldata'] = {
  7494. execCommand: function (cmd, name) {
  7495. if (saveKey && LocalStorage.getLocalData(saveKey)) {
  7496. LocalStorage.removeItem(saveKey)
  7497. }
  7498. },
  7499. notNeedUndo: true,
  7500. ignoreContentChange: true
  7501. };
  7502. me.commands['getlocaldata'] = {
  7503. execCommand: function (cmd, name) {
  7504. return saveKey ? LocalStorage.getLocalData(saveKey) || '' : '';
  7505. },
  7506. notNeedUndo: true,
  7507. ignoreContentChange: true
  7508. };
  7509. me.commands['drafts'] = {
  7510. execCommand: function (cmd, name) {
  7511. if (saveKey) {
  7512. me.body.innerHTML = LocalStorage.getLocalData(saveKey) || '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>';
  7513. me.focus(true);
  7514. }
  7515. },
  7516. queryCommandState: function () {
  7517. return saveKey ? (LocalStorage.getLocalData(saveKey) === null ? -1 : 0) : -1;
  7518. },
  7519. notNeedUndo: true,
  7520. ignoreContentChange: true
  7521. }
  7522. };
  7523. /**
  7524. * @description
  7525. * 1.拖放文件到编辑区域,自动上传并插入到选区
  7526. * 2.插入粘贴板的图片,自动上传并插入到选区
  7527. * @author Jinqn
  7528. * @date 2013-10-14
  7529. */
  7530. UM.plugins['autoupload'] = function () {
  7531. var me = this;
  7532. me.setOpt('pasteImageEnabled', true);
  7533. me.setOpt('dropFileEnabled', true);
  7534. var sendAndInsertImage = function (file, editor) {
  7535. //模拟数据
  7536. var fd = new FormData();
  7537. fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));
  7538. fd.append('type', 'ajax');
  7539. var xhr = new XMLHttpRequest();
  7540. xhr.open("post", me.options.imageUrl, true);
  7541. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  7542. xhr.addEventListener('load', function (e) {
  7543. try {
  7544. var json = eval('(' + e.target.response + ')'),
  7545. link = json.url,
  7546. picLink = me.options.imagePath + link;
  7547. editor.execCommand('insertimage', {
  7548. src: picLink,
  7549. _src: picLink
  7550. });
  7551. } catch (er) {
  7552. }
  7553. });
  7554. xhr.send(fd);
  7555. };
  7556. function getPasteImage(e) {
  7557. return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
  7558. }
  7559. function getDropImage(e) {
  7560. return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
  7561. }
  7562. me.addListener('ready', function () {
  7563. if (window.FormData && window.FileReader) {
  7564. var autoUploadHandler = function (e) {
  7565. var hasImg = false,
  7566. items;
  7567. //获取粘贴板文件列表或者拖放文件列表
  7568. items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);
  7569. if (items) {
  7570. var len = items.length,
  7571. file;
  7572. while (len--) {
  7573. file = items[len];
  7574. if (file.getAsFile) file = file.getAsFile();
  7575. if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {
  7576. sendAndInsertImage(file, me);
  7577. hasImg = true;
  7578. }
  7579. }
  7580. if (hasImg) return false;
  7581. }
  7582. };
  7583. me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);
  7584. me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);
  7585. //取消拖放图片时出现的文字光标位置提示
  7586. me.$body.on('dragover', function (e) {
  7587. if (e.originalEvent.dataTransfer.types[0] == 'Files') {
  7588. return false;
  7589. }
  7590. });
  7591. }
  7592. });
  7593. };
  7594. /**
  7595. * 公式插件
  7596. */
  7597. UM.plugins['formula'] = function () {
  7598. var me = this;
  7599. function getActiveIframe() {
  7600. return me.$body.find('iframe.edui-formula-active')[0] || null;
  7601. }
  7602. function blurActiveIframe() {
  7603. var iframe = getActiveIframe();
  7604. iframe && iframe.contentWindow.formula.blur();
  7605. }
  7606. me.addInputRule(function (root) {
  7607. $.each(root.getNodesByTagName('span'), function (i, node) {
  7608. if (node.hasClass('mathquill-embedded-latex')) {
  7609. var firstChild, latex = '';
  7610. while (firstChild = node.firstChild()) {
  7611. latex += firstChild.data;
  7612. node.removeChild(firstChild);
  7613. }
  7614. node.tagName = 'iframe';
  7615. node.setAttr({
  7616. 'frameborder': '0',
  7617. 'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',
  7618. 'data-latex': utils.unhtml(latex)
  7619. });
  7620. }
  7621. });
  7622. });
  7623. me.addOutputRule(function (root) {
  7624. $.each(root.getNodesByTagName('iframe'), function (i, node) {
  7625. if (node.hasClass('mathquill-embedded-latex')) {
  7626. node.tagName = 'span';
  7627. node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));
  7628. node.setAttr({
  7629. 'frameborder': '',
  7630. 'src': '',
  7631. 'data-latex': ''
  7632. });
  7633. }
  7634. });
  7635. });
  7636. me.addListener('click', function () {
  7637. blurActiveIframe();
  7638. });
  7639. me.addListener('afterexeccommand', function (type, cmd) {
  7640. if (cmd != 'formula') {
  7641. blurActiveIframe();
  7642. }
  7643. });
  7644. me.commands['formula'] = {
  7645. execCommand: function (cmd, latex) {
  7646. var iframe = getActiveIframe();
  7647. if (iframe) {
  7648. iframe.contentWindow.formula.insertLatex(latex);
  7649. } else {
  7650. me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');
  7651. browser.ie && browser.ie9below && setTimeout(function () {
  7652. var rng = me.selection.getRange(),
  7653. startContainer = rng.startContainer;
  7654. if (startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]) {
  7655. rng.insertNode(me.document.createTextNode(' '));
  7656. rng.setCursor()
  7657. }
  7658. }, 100)
  7659. }
  7660. },
  7661. queryCommandState: function (cmd) {
  7662. return 0;
  7663. },
  7664. queryCommandValue: function (cmd) {
  7665. var iframe = getActiveIframe();
  7666. return iframe && iframe.contentWindow.formula.getLatex();
  7667. }
  7668. }
  7669. };
  7670. /**
  7671. * @file xssFilter.js
  7672. * @desc xss过滤器
  7673. * @author robbenmu
  7674. */
  7675. UM.plugins.xssFilter = function () {
  7676. var config = UMEDITOR_CONFIG;
  7677. var whiteList = config.whiteList;
  7678. function filter(node) {
  7679. var tagName = node.tagName;
  7680. var attrs = node.attrs;
  7681. if (!whiteList.hasOwnProperty(tagName)) {
  7682. node.parentNode.removeChild(node);
  7683. return false;
  7684. }
  7685. UM.utils.each(attrs, function (val, key) {
  7686. if (whiteList[tagName].indexOf(key) === -1) {
  7687. node.setAttr(key);
  7688. }
  7689. });
  7690. }
  7691. // 添加inserthtml\paste等操作用的过滤规则
  7692. if (whiteList && config.xssFilterRules) {
  7693. this.options.filterRules = function () {
  7694. var result = {};
  7695. UM.utils.each(whiteList, function (val, key) {
  7696. result[key] = function (node) {
  7697. return filter(node);
  7698. };
  7699. });
  7700. return result;
  7701. }();
  7702. }
  7703. var tagList = [];
  7704. UM.utils.each(whiteList, function (val, key) {
  7705. tagList.push(key);
  7706. });
  7707. // 添加input过滤规则
  7708. //
  7709. if (whiteList && config.inputXssFilter) {
  7710. this.addInputRule(function (root) {
  7711. root.traversal(function (node) {
  7712. if (node.type !== 'element') {
  7713. return false;
  7714. }
  7715. filter(node);
  7716. });
  7717. });
  7718. }
  7719. // 添加output过滤规则
  7720. //
  7721. if (whiteList && config.outputXssFilter) {
  7722. this.addOutputRule(function (root) {
  7723. root.traversal(function (node) {
  7724. if (node.type !== 'element') {
  7725. return false;
  7726. }
  7727. filter(node);
  7728. });
  7729. });
  7730. }
  7731. };
  7732. (function ($) {
  7733. //对jquery的扩展
  7734. $.parseTmpl = function parse(str, data) {
  7735. var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g, function (match, code) {
  7736. return "'," + code.replace(/\\'/g, "'") + ",'";
  7737. }).replace(/<%([\s\S]+?)%>/g, function (match, code) {
  7738. return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
  7739. }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
  7740. var func = new Function('obj', tmpl);
  7741. return data ? func(data) : func;
  7742. };
  7743. $.extend2 = function (t, s) {
  7744. var a = arguments,
  7745. notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,
  7746. len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;
  7747. for (var i = 1; i < len; i++) {
  7748. var x = a[i];
  7749. for (var k in x) {
  7750. if (!notCover || !t.hasOwnProperty(k)) {
  7751. t[k] = x[k];
  7752. }
  7753. }
  7754. }
  7755. return t;
  7756. };
  7757. $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;
  7758. //所有ui的基类
  7759. var _eventHandler = [];
  7760. var _widget = function () {
  7761. };
  7762. var _prefix = 'edui';
  7763. _widget.prototype = {
  7764. on: function (ev, cb) {
  7765. this.root().on(ev, $.proxy(cb, this));
  7766. return this;
  7767. },
  7768. off: function (ev, cb) {
  7769. this.root().off(ev, $.proxy(cb, this));
  7770. return this;
  7771. },
  7772. trigger: function (ev, data) {
  7773. return this.root().trigger(ev, data) === false ? false : this;
  7774. },
  7775. root: function ($el) {
  7776. return this._$el || (this._$el = $el);
  7777. },
  7778. destroy: function () {
  7779. },
  7780. data: function (key, val) {
  7781. if (val !== undefined) {
  7782. this.root().data(_prefix + key, val);
  7783. return this;
  7784. } else {
  7785. return this.root().data(_prefix + key)
  7786. }
  7787. },
  7788. register: function (eventName, $el, fn) {
  7789. _eventHandler.push({
  7790. 'evtname': eventName,
  7791. '$els': $.isArray($el) ? $el : [$el],
  7792. handler: $.proxy(fn, $el)
  7793. })
  7794. }
  7795. };
  7796. //从jq实例上拿到绑定的widget实例
  7797. $.fn.edui = function (obj) {
  7798. return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');
  7799. };
  7800. function _createClass(ClassObj, properties, supperClass) {
  7801. ClassObj.prototype = $.extend2(
  7802. $.extend({}, properties),
  7803. (UM.ui[supperClass] || _widget).prototype,
  7804. true
  7805. );
  7806. ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;
  7807. //父class的defaultOpt 合并
  7808. if (UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt) {
  7809. var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,
  7810. subDefaultOptions = ClassObj.prototype.defaultOpt;
  7811. ClassObj.prototype.defaultOpt = $.extend({}, parentDefaultOptions, subDefaultOptions || {});
  7812. }
  7813. return ClassObj
  7814. }
  7815. var _guid = 1;
  7816. function mergeToJQ(ClassObj, className) {
  7817. $[_prefix + className] = ClassObj;
  7818. $.fn[_prefix + className] = function (opt) {
  7819. var result, args = Array.prototype.slice.call(arguments, 1);
  7820. this.each(function (i, el) {
  7821. var $this = $(el);
  7822. var obj = $this.edui();
  7823. if (!obj) {
  7824. ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);
  7825. $this.edui(obj)
  7826. }
  7827. if ($.type(opt) == 'string') {
  7828. if (opt == 'this') {
  7829. result = obj;
  7830. } else {
  7831. result = obj[opt].apply(obj, args);
  7832. if (result !== obj && result !== undefined) {
  7833. return false;
  7834. }
  7835. result = null;
  7836. }
  7837. }
  7838. });
  7839. return result !== null ? result : this;
  7840. }
  7841. }
  7842. UM.ui = {
  7843. define: function (className, properties, supperClass) {
  7844. var ClassObj = UM.ui[className] = _createClass(function (options, $el) {
  7845. var _obj = function () {
  7846. };
  7847. $.extend(_obj.prototype, ClassObj.prototype, {
  7848. guid: className + _guid++,
  7849. widgetName: className
  7850. }
  7851. );
  7852. var obj = new _obj;
  7853. if ($.type(options) == 'string') {
  7854. obj.init && obj.init({});
  7855. obj.root().edui(obj);
  7856. obj.root().find('a').click(function (evt) {
  7857. evt.preventDefault()
  7858. });
  7859. return obj.root()[_prefix + className].apply(obj.root(), arguments)
  7860. } else {
  7861. $el && obj.root($el);
  7862. obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);
  7863. try {
  7864. obj.root().find('a').click(function (evt) {
  7865. evt.preventDefault()
  7866. });
  7867. } catch (e) {
  7868. }
  7869. return obj.root().edui(obj);
  7870. }
  7871. }, properties, supperClass);
  7872. mergeToJQ(ClassObj, className);
  7873. }
  7874. };
  7875. $(function () {
  7876. $(document).on('click mouseup mousedown dblclick mouseover', function (evt) {
  7877. $.each(_eventHandler, function (i, obj) {
  7878. if (obj.evtname == evt.type) {
  7879. $.each(obj.$els, function (i, $el) {
  7880. if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {
  7881. obj.handler(evt);
  7882. }
  7883. })
  7884. }
  7885. })
  7886. })
  7887. })
  7888. })(jQuery);
  7889. //button 类
  7890. UM.ui.define('button', {
  7891. tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%><% if(title) {%> data-original-title="<%=title%>" <%};%>> ' +
  7892. '<% if(icon) {%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><% }; %><%if(text) {%><span unselectable="on" onmousedown="return false" class="edui-button-label"><%=text%></span><%}%>' +
  7893. '<%if(caret && text){%><span class="edui-button-spacing"></span><%}%>' +
  7894. '<% if(caret) {%><span unselectable="on" onmousedown="return false" class="edui-caret"></span><% };%></<%if(!texttype){%>div<%}else{%>a<%}%>>',
  7895. defaultOpt: {
  7896. text: '',
  7897. title: '',
  7898. icon: '',
  7899. width: '',
  7900. caret: false,
  7901. texttype: false,
  7902. click: function () {
  7903. }
  7904. },
  7905. init: function (options) {
  7906. var me = this;
  7907. me.root($($.parseTmpl(me.tpl, options)))
  7908. .click(function (evt) {
  7909. me.wrapclick(options.click, evt)
  7910. });
  7911. me.root().hover(function () {
  7912. if (!me.root().hasClass("edui-disabled")) {
  7913. me.root().toggleClass('edui-hover')
  7914. }
  7915. })
  7916. return me;
  7917. },
  7918. wrapclick: function (fn, evt) {
  7919. if (!this.disabled()) {
  7920. this.root().trigger('wrapclick');
  7921. $.proxy(fn, this, evt)()
  7922. }
  7923. return this;
  7924. },
  7925. label: function (text) {
  7926. if (text === undefined) {
  7927. return this.root().find('.edui-button-label').text();
  7928. } else {
  7929. this.root().find('.edui-button-label').text(text);
  7930. return this;
  7931. }
  7932. },
  7933. disabled: function (state) {
  7934. if (state === undefined) {
  7935. return this.root().hasClass('edui-disabled')
  7936. }
  7937. this.root().toggleClass('edui-disabled', state);
  7938. if (this.root().hasClass('edui-disabled')) {
  7939. this.root().removeClass('edui-hover')
  7940. }
  7941. return this;
  7942. },
  7943. active: function (state) {
  7944. if (state === undefined) {
  7945. return this.root().hasClass('edui-active')
  7946. }
  7947. this.root().toggleClass('edui-active', state)
  7948. return this;
  7949. },
  7950. mergeWith: function ($obj) {
  7951. var me = this;
  7952. me.data('$mergeObj', $obj);
  7953. $obj.edui().data('$mergeObj', me.root());
  7954. if (!$.contains(document.body, $obj[0])) {
  7955. $obj.appendTo(me.root());
  7956. }
  7957. me.on('click', function () {
  7958. me.wrapclick(function () {
  7959. $obj.edui().show();
  7960. })
  7961. }).register('click', me.root(), function (evt) {
  7962. $obj.hide()
  7963. });
  7964. }
  7965. });
  7966. //toolbar 类
  7967. (function () {
  7968. UM.ui.define('toolbar', {
  7969. tpl: '<div class="edui-toolbar" ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false" ></div></div>'
  7970. ,
  7971. init: function () {
  7972. var $root = this.root($(this.tpl));
  7973. this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))
  7974. },
  7975. appendToBtnmenu: function (data) {
  7976. var $cont = this.data('$btnToolbar');
  7977. data = $.isArray(data) ? data : [data];
  7978. $.each(data, function (i, $item) {
  7979. $cont.append($item)
  7980. })
  7981. }
  7982. });
  7983. })();
  7984. //menu 类
  7985. UM.ui.define('menu', {
  7986. show: function ($obj, dir, fnname, topOffset, leftOffset) {
  7987. fnname = fnname || 'position';
  7988. if (this.trigger('beforeshow') === false) {
  7989. return;
  7990. } else {
  7991. this.root().css($.extend({display: 'block'}, $obj ? {
  7992. top: $obj[fnname]().top + (dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),
  7993. left: $obj[fnname]().left + (dir == 'right' ? $obj.outerWidth() : 0) - (leftOffset || 0)
  7994. } : {}))
  7995. this.trigger('aftershow');
  7996. }
  7997. },
  7998. hide: function (all) {
  7999. var $parentmenu;
  8000. if (this.trigger('beforehide') === false) {
  8001. return;
  8002. } else {
  8003. if ($parentmenu = this.root().data('parentmenu')) {
  8004. if ($parentmenu.data('parentmenu') || all)
  8005. $parentmenu.edui().hide();
  8006. }
  8007. this.root().css('display', 'none');
  8008. this.trigger('afterhide');
  8009. }
  8010. },
  8011. attachTo: function ($obj) {
  8012. var me = this;
  8013. if (!$obj.data('$mergeObj')) {
  8014. $obj.data('$mergeObj', me.root());
  8015. $obj.on('wrapclick', function (evt) {
  8016. me.show()
  8017. });
  8018. me.register('click', $obj, function (evt) {
  8019. me.hide()
  8020. });
  8021. me.data('$mergeObj', $obj)
  8022. }
  8023. }
  8024. });
  8025. //dropmenu 类
  8026. UM.ui.define('dropmenu', {
  8027. tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +
  8028. '<%for(var i=0,ci;ci=data[i++];){%>' +
  8029. '<%if(ci.divider){%><li class="edui-divider"></li><%}else{%>' +
  8030. '<li <%if(ci.active||ci.disabled){%>class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' +
  8031. '<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em><%= ci.label%></a>' +
  8032. '</li><%}%>' +
  8033. '<%}%>' +
  8034. '</ul>',
  8035. defaultOpt: {
  8036. data: [],
  8037. click: function () {
  8038. }
  8039. },
  8040. init: function (options) {
  8041. var me = this;
  8042. var eventName = {
  8043. click: 1,
  8044. mouseover: 1,
  8045. mouseout: 1
  8046. };
  8047. this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]', function (evt) {
  8048. $.proxy(options.click, me, evt, $(this).data('value'), $(this))()
  8049. }).find('li').each(function (i, el) {
  8050. var $this = $(this);
  8051. if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {
  8052. var data = options.data[i];
  8053. $.each(eventName, function (k) {
  8054. data[k] && $this[k](function (evt) {
  8055. $.proxy(data[k], el)(evt, data, me.root)
  8056. })
  8057. })
  8058. }
  8059. })
  8060. },
  8061. disabled: function (cb) {
  8062. $('li[class!=edui-divider]', this.root()).each(function () {
  8063. var $el = $(this);
  8064. if (cb === true) {
  8065. $el.addClass('edui-disabled')
  8066. } else if ($.isFunction(cb)) {
  8067. $el.toggleClass('edui-disabled', cb(li))
  8068. } else {
  8069. $el.removeClass('edui-disabled')
  8070. }
  8071. });
  8072. },
  8073. val: function (val) {
  8074. var currentVal;
  8075. $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {
  8076. var $el = $(this);
  8077. if (val === undefined) {
  8078. if ($el.find('em.edui-dropmenu-checked').length) {
  8079. currentVal = $el.data('value');
  8080. return false
  8081. }
  8082. } else {
  8083. $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)
  8084. }
  8085. });
  8086. if (val === undefined) {
  8087. return currentVal
  8088. }
  8089. },
  8090. addSubmenu: function (label, menu, index) {
  8091. index = index || 0;
  8092. var $list = $('li[class!=edui-divider]', this.root());
  8093. var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);
  8094. if (index >= 0 && index < $list.length) {
  8095. $node.insertBefore($list[index]);
  8096. } else if (index < 0) {
  8097. $node.insertBefore($list[0]);
  8098. } else if (index >= $list.length) {
  8099. $node.appendTo($list);
  8100. }
  8101. }
  8102. }, 'menu');
  8103. //splitbutton 类
  8104. ///import button
  8105. UM.ui.define('splitbutton', {
  8106. tpl: '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><%if(text){%><%=text%><%}%></div>' +
  8107. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >' +
  8108. '<div unselectable="on" class="edui-caret"><\/div>' +
  8109. '</div>' +
  8110. '</div>',
  8111. defaultOpt: {
  8112. text: '',
  8113. title: '',
  8114. click: function () {
  8115. }
  8116. },
  8117. init: function (options) {
  8118. var me = this;
  8119. me.root($($.parseTmpl(me.tpl, options)));
  8120. me.root().find('.edui-btn:first').click(function (evt) {
  8121. if (!me.disabled()) {
  8122. $.proxy(options.click, me)();
  8123. }
  8124. });
  8125. me.root().find('.edui-dropdown-toggle').click(function () {
  8126. if (!me.disabled()) {
  8127. me.trigger('arrowclick')
  8128. }
  8129. });
  8130. me.root().hover(function () {
  8131. if (!me.root().hasClass("edui-disabled")) {
  8132. me.root().toggleClass('edui-hover')
  8133. }
  8134. });
  8135. return me;
  8136. },
  8137. wrapclick: function (fn, evt) {
  8138. if (!this.disabled()) {
  8139. $.proxy(fn, this, evt)()
  8140. }
  8141. return this;
  8142. },
  8143. disabled: function (state) {
  8144. if (state === undefined) {
  8145. return this.root().hasClass('edui-disabled')
  8146. }
  8147. this.root().toggleClass('edui-disabled', state).find('.edui-btn').toggleClass('edui-disabled', state);
  8148. return this;
  8149. },
  8150. active: function (state) {
  8151. if (state === undefined) {
  8152. return this.root().hasClass('edui-active')
  8153. }
  8154. this.root().toggleClass('edui-active', state).find('.edui-btn:first').toggleClass('edui-active', state);
  8155. return this;
  8156. },
  8157. mergeWith: function ($obj) {
  8158. var me = this;
  8159. me.data('$mergeObj', $obj);
  8160. $obj.edui().data('$mergeObj', me.root());
  8161. if (!$.contains(document.body, $obj[0])) {
  8162. $obj.appendTo(me.root());
  8163. }
  8164. me.root().delegate('.edui-dropdown-toggle', 'click', function () {
  8165. me.wrapclick(function () {
  8166. $obj.edui().show();
  8167. })
  8168. });
  8169. me.register('click', me.root().find('.edui-dropdown-toggle'), function (evt) {
  8170. $obj.hide()
  8171. });
  8172. }
  8173. });
  8174. /**
  8175. * Created with JetBrains PhpStorm.
  8176. * User: hn
  8177. * Date: 13-7-10
  8178. * Time: 下午3:07
  8179. * To change this template use File | Settings | File Templates.
  8180. */
  8181. UM.ui.define('colorsplitbutton', {
  8182. tpl: '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>" unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn" unselectable="on" ><%if(icon){%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><div class="edui-splitbutton-color-label" <%if (color) {%>style="background: <%=color%>"<%}%>></div><%if(text){%><%=text%><%}%></div>' +
  8183. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >' +
  8184. '<div unselectable="on" class="edui-caret"><\/div>' +
  8185. '</div>' +
  8186. '</div>',
  8187. defaultOpt: {
  8188. color: ''
  8189. },
  8190. init: function (options) {
  8191. var me = this;
  8192. me.supper.init.call(me, options);
  8193. },
  8194. colorLabel: function () {
  8195. return this.root().find('.edui-splitbutton-color-label');
  8196. }
  8197. }, 'splitbutton');
  8198. //popup 类
  8199. UM.ui.define('popup', {
  8200. tpl: '<div class="edui-dropdown-menu edui-popup"' +
  8201. '<%if(!<%=stopprop%>){%>onmousedown="return false"<%}%>' +
  8202. '><div class="edui-popup-body" unselectable="on" onmousedown="return false"><%=subtpl%></div>' +
  8203. '<div class="edui-popup-caret"></div>' +
  8204. '</div>',
  8205. defaultOpt: {
  8206. stopprop: false,
  8207. subtpl: '',
  8208. width: '',
  8209. height: ''
  8210. },
  8211. init: function (options) {
  8212. this.root($($.parseTmpl(this.tpl, options)));
  8213. return this;
  8214. },
  8215. mergeTpl: function (data) {
  8216. return $.parseTmpl(this.tpl, {subtpl: data});
  8217. },
  8218. show: function ($obj, posObj) {
  8219. if (!posObj) posObj = {};
  8220. var fnname = posObj.fnname || 'position';
  8221. if (this.trigger('beforeshow') === false) {
  8222. return;
  8223. } else {
  8224. this.root().css($.extend({display: 'block'}, $obj ? {
  8225. top: $obj[fnname]().top + (posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),
  8226. left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),
  8227. position: 'absolute'
  8228. } : {}));
  8229. this.root().find('.edui-popup-caret').css({
  8230. top: posObj.caretTop || 0,
  8231. left: posObj.caretLeft || 0,
  8232. position: 'absolute'
  8233. }).addClass(posObj.caretDir || "up")
  8234. }
  8235. this.trigger("aftershow");
  8236. },
  8237. hide: function () {
  8238. this.root().css('display', 'none');
  8239. this.trigger('afterhide')
  8240. },
  8241. attachTo: function ($obj, posObj) {
  8242. var me = this
  8243. if (!$obj.data('$mergeObj')) {
  8244. $obj.data('$mergeObj', me.root());
  8245. $obj.on('wrapclick', function (evt) {
  8246. me.show($obj, posObj)
  8247. });
  8248. me.register('click', $obj, function (evt) {
  8249. me.hide()
  8250. });
  8251. me.data('$mergeObj', $obj)
  8252. }
  8253. },
  8254. getBodyContainer: function () {
  8255. return this.root().find(".edui-popup-body");
  8256. }
  8257. });
  8258. //scale 类
  8259. UM.ui.define('scale', {
  8260. tpl: '<div class="edui-scale" unselectable="on">' +
  8261. '<span class="edui-scale-hand0"></span>' +
  8262. '<span class="edui-scale-hand1"></span>' +
  8263. '<span class="edui-scale-hand2"></span>' +
  8264. '<span class="edui-scale-hand3"></span>' +
  8265. '<span class="edui-scale-hand4"></span>' +
  8266. '<span class="edui-scale-hand5"></span>' +
  8267. '<span class="edui-scale-hand6"></span>' +
  8268. '<span class="edui-scale-hand7"></span>' +
  8269. '</div>',
  8270. defaultOpt: {
  8271. $doc: $(document),
  8272. $wrap: $(document)
  8273. },
  8274. init: function (options) {
  8275. if (options.$doc) this.defaultOpt.$doc = options.$doc;
  8276. if (options.$wrap) this.defaultOpt.$wrap = options.$wrap;
  8277. this.root($($.parseTmpl(this.tpl, options)));
  8278. this.initStyle();
  8279. this.startPos = this.prePos = {x: 0, y: 0};
  8280. this.dragId = -1;
  8281. return this;
  8282. },
  8283. initStyle: function () {
  8284. utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +
  8285. '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
  8286. + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
  8287. + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
  8288. + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
  8289. + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
  8290. + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
  8291. + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
  8292. + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
  8293. + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
  8294. },
  8295. _eventHandler: function (e) {
  8296. var me = this,
  8297. $doc = me.defaultOpt.$doc;
  8298. switch (e.type) {
  8299. case 'mousedown':
  8300. var hand = e.target || e.srcElement, hand;
  8301. if (hand.className.indexOf('edui-scale-hand') != -1) {
  8302. me.dragId = hand.className.slice(-1);
  8303. me.startPos.x = me.prePos.x = e.clientX;
  8304. me.startPos.y = me.prePos.y = e.clientY;
  8305. $doc.bind('mousemove', $.proxy(me._eventHandler, me));
  8306. }
  8307. break;
  8308. case 'mousemove':
  8309. if (me.dragId != -1) {
  8310. me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
  8311. me.prePos.x = e.clientX;
  8312. me.prePos.y = e.clientY;
  8313. me.updateTargetElement();
  8314. }
  8315. break;
  8316. case 'mouseup':
  8317. if (me.dragId != -1) {
  8318. me.dragId = -1;
  8319. me.updateTargetElement();
  8320. var $target = me.data('$scaleTarget');
  8321. if ($target.parent()) me.attachTo(me.data('$scaleTarget'));
  8322. }
  8323. $doc.unbind('mousemove', $.proxy(me._eventHandler, me));
  8324. break;
  8325. default:
  8326. break;
  8327. }
  8328. },
  8329. updateTargetElement: function () {
  8330. var me = this,
  8331. $root = me.root(),
  8332. $target = me.data('$scaleTarget');
  8333. $target.css({width: $root.width(), height: $root.height()});
  8334. me.attachTo($target);
  8335. },
  8336. updateContainerStyle: function (dir, offset) {
  8337. var me = this,
  8338. $dom = me.root(),
  8339. tmp,
  8340. rect = [
  8341. //[left, top, width, height]
  8342. [0, 0, -1, -1],
  8343. [0, 0, 0, -1],
  8344. [0, 0, 1, -1],
  8345. [0, 0, -1, 0],
  8346. [0, 0, 1, 0],
  8347. [0, 0, -1, 1],
  8348. [0, 0, 0, 1],
  8349. [0, 0, 1, 1]
  8350. ];
  8351. if (rect[dir][0] != 0) {
  8352. tmp = parseInt($dom.offset().left) + offset.x;
  8353. $dom.css('left', me._validScaledProp('left', tmp));
  8354. }
  8355. if (rect[dir][1] != 0) {
  8356. tmp = parseInt($dom.offset().top) + offset.y;
  8357. $dom.css('top', me._validScaledProp('top', tmp));
  8358. }
  8359. if (rect[dir][2] != 0) {
  8360. tmp = $dom.width() + rect[dir][2] * offset.x;
  8361. $dom.css('width', me._validScaledProp('width', tmp));
  8362. }
  8363. if (rect[dir][3] != 0) {
  8364. tmp = $dom.height() + rect[dir][3] * offset.y;
  8365. $dom.css('height', me._validScaledProp('height', tmp));
  8366. }
  8367. },
  8368. _validScaledProp: function (prop, value) {
  8369. var $ele = this.root(),
  8370. $wrap = this.defaultOpt.$doc,
  8371. calc = function (val, a, b) {
  8372. return (val + a) > b ? b - a : value;
  8373. };
  8374. value = isNaN(value) ? 0 : value;
  8375. switch (prop) {
  8376. case 'left':
  8377. return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());
  8378. case 'top':
  8379. return value < 0 ? 0 : calc(value, $ele.height(), $wrap.height());
  8380. case 'width':
  8381. return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());
  8382. case 'height':
  8383. return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());
  8384. }
  8385. },
  8386. show: function ($obj) {
  8387. var me = this;
  8388. if ($obj) me.attachTo($obj);
  8389. me.root().bind('mousedown', $.proxy(me._eventHandler, me));
  8390. me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));
  8391. me.root().show();
  8392. me.trigger("aftershow");
  8393. },
  8394. hide: function () {
  8395. var me = this;
  8396. me.root().unbind('mousedown', $.proxy(me._eventHandler, me));
  8397. me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));
  8398. me.root().hide();
  8399. me.trigger('afterhide')
  8400. },
  8401. attachTo: function ($obj) {
  8402. var me = this,
  8403. imgPos = $obj.offset(),
  8404. $root = me.root(),
  8405. $wrap = me.defaultOpt.$wrap,
  8406. posObj = $wrap.offset();
  8407. me.data('$scaleTarget', $obj);
  8408. me.root().css({
  8409. position: 'absolute',
  8410. width: $obj.width(),
  8411. height: $obj.height(),
  8412. left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),
  8413. top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))
  8414. });
  8415. },
  8416. getScaleTarget: function () {
  8417. return this.data('$scaleTarget')[0];
  8418. }
  8419. });
  8420. //colorpicker 类
  8421. UM.ui.define('colorpicker', {
  8422. tpl: function (opt) {
  8423. var COLORS = (
  8424. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  8425. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  8426. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  8427. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  8428. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  8429. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  8430. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  8431. var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if (name){%> edui-colorpicker-<%=name%><%}%>" >' +
  8432. '<table unselectable="on" onmousedown="return false">' +
  8433. '<tr><td colspan="10">' + opt.lang_themeColor + '</td> </tr>' +
  8434. '<tr class="edui-colorpicker-firstrow" >';
  8435. for (var i = 0; i < COLORS.length; i++) {
  8436. if (i && i % 10 === 0) {
  8437. html += '</tr>' + (i == 60 ? '<tr><td colspan="10">' + opt.lang_standardColor + '</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';
  8438. }
  8439. html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +
  8440. ' data-color="#' + COLORS[i] + '"' +
  8441. ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
  8442. (i < 10 || i >= 60 ? 'border-width:1px;' :
  8443. i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
  8444. 'border-width:0 1px 0 1px;') +
  8445. '"' +
  8446. '></a></td>' : '';
  8447. }
  8448. html += '</tr></table></div>';
  8449. return html;
  8450. },
  8451. init: function (options) {
  8452. var me = this;
  8453. me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)), options)));
  8454. me.root().on("click", function (e) {
  8455. me.trigger('pickcolor', $(e.target).data('color'));
  8456. });
  8457. }
  8458. }, 'popup');
  8459. /**
  8460. * Created with JetBrains PhpStorm.
  8461. * User: hn
  8462. * Date: 13-5-29
  8463. * Time: 下午8:01
  8464. * To change this template use File | Settings | File Templates.
  8465. */
  8466. (function () {
  8467. var widgetName = 'combobox',
  8468. itemClassName = 'edui-combobox-item',
  8469. HOVER_CLASS = 'edui-combobox-item-hover',
  8470. ICON_CLASS = 'edui-combobox-checked-icon',
  8471. labelClassName = 'edui-combobox-item-label';
  8472. UM.ui.define(widgetName, (function () {
  8473. return {
  8474. tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if (comboboxName!=='') {%> edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +
  8475. "<%if(autoRecord) {%>" +
  8476. "<%for( var i=0, len = recordStack.length; i<len; i++ ) {%>" +
  8477. "<%var index = recordStack[i];%>" +
  8478. "<li class=\"<%=itemClassName%><%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" +
  8479. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8480. "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ index ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=items[index]%></label>" +
  8481. "</li>" +
  8482. "<%}%>" +
  8483. "<%if( i ) {%>" +
  8484. "<li class=\"edui-combobox-item-separator\"></li>" +
  8485. "<%}%>" +
  8486. "<%}%>" +
  8487. "<%for( var i=0, label; label = items[i]; i++ ) {%>" +
  8488. "<li class=\"<%=itemClassName%><%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" +
  8489. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8490. "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ i ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=label%></label>" +
  8491. "</li>" +
  8492. "<%}%>" +
  8493. "</ul>",
  8494. defaultOpt: {
  8495. //记录栈初始列表
  8496. recordStack: [],
  8497. //可用项列表
  8498. items: [],
  8499. //item对应的值列表
  8500. value: [],
  8501. comboboxName: '',
  8502. selected: '',
  8503. //自动记录
  8504. autoRecord: true,
  8505. //最多记录条数
  8506. recordCount: 5
  8507. },
  8508. init: function (options) {
  8509. var me = this;
  8510. $.extend(me._optionAdaptation(options), me._createItemMapping(options.recordStack, options.items), {
  8511. itemClassName: itemClassName,
  8512. iconClass: ICON_CLASS,
  8513. labelClassName: labelClassName
  8514. });
  8515. this._transStack(options);
  8516. me.root($($.parseTmpl(me.tpl, options)));
  8517. this.data('options', options).initEvent();
  8518. },
  8519. initEvent: function () {
  8520. var me = this;
  8521. me.initSelectItem();
  8522. this.initItemActive();
  8523. },
  8524. /**
  8525. * 初始化选择项
  8526. */
  8527. initSelectItem: function () {
  8528. var me = this,
  8529. labelClass = "." + labelClassName;
  8530. me.root().delegate('.' + itemClassName, 'click', function () {
  8531. var $li = $(this),
  8532. index = $li.attr('data-item-index');
  8533. me.trigger('comboboxselect', {
  8534. index: index,
  8535. label: $li.find(labelClass).text(),
  8536. value: me.data('options').value[index]
  8537. }).select(index);
  8538. me.hide();
  8539. return false;
  8540. });
  8541. },
  8542. initItemActive: function () {
  8543. var fn = {
  8544. mouseenter: 'addClass',
  8545. mouseleave: 'removeClass'
  8546. };
  8547. if ($.IE6) {
  8548. this.root().delegate('.' + itemClassName, 'mouseenter mouseleave', function (evt) {
  8549. $(this)[fn[evt.type]](HOVER_CLASS);
  8550. }).one('afterhide', function () {
  8551. });
  8552. }
  8553. },
  8554. /**
  8555. * 选择给定索引的项
  8556. * @param index 项索引
  8557. * @returns {*} 如果存在对应索引的项,则返回该项;否则返回null
  8558. */
  8559. select: function (index) {
  8560. var itemCount = this.data('options').itemCount,
  8561. items = this.data('options').autowidthitem;
  8562. if (items && !items.length) {
  8563. items = this.data('options').items;
  8564. }
  8565. if (itemCount == 0) {
  8566. return null;
  8567. }
  8568. if (index < 0) {
  8569. index = itemCount + index % itemCount;
  8570. } else if (index >= itemCount) {
  8571. index = itemCount - 1;
  8572. }
  8573. this.trigger('changebefore', items[index]);
  8574. this._update(index);
  8575. this.trigger('changeafter', items[index]);
  8576. return null;
  8577. },
  8578. selectItemByLabel: function (label) {
  8579. var itemMapping = this.data('options').itemMapping,
  8580. me = this,
  8581. index = null;
  8582. !$.isArray(label) && (label = [label]);
  8583. $.each(label, function (i, item) {
  8584. index = itemMapping[item];
  8585. if (index !== undefined) {
  8586. me.select(index);
  8587. return false;
  8588. }
  8589. });
  8590. },
  8591. /**
  8592. * 转换记录栈
  8593. */
  8594. _transStack: function (options) {
  8595. var temp = [],
  8596. itemIndex = -1,
  8597. selected = -1;
  8598. $.each(options.recordStack, function (index, item) {
  8599. itemIndex = options.itemMapping[item];
  8600. if ($.isNumeric(itemIndex)) {
  8601. temp.push(itemIndex);
  8602. //selected的合法性检测
  8603. if (item == options.selected) {
  8604. selected = itemIndex;
  8605. }
  8606. }
  8607. });
  8608. options.recordStack = temp;
  8609. options.selected = selected;
  8610. temp = null;
  8611. },
  8612. _optionAdaptation: function (options) {
  8613. if (!('itemStyles' in options)) {
  8614. options.itemStyles = [];
  8615. for (var i = 0, len = options.items.length; i < len; i++) {
  8616. options.itemStyles.push('');
  8617. }
  8618. }
  8619. options.autowidthitem = options.autowidthitem || options.items;
  8620. options.itemCount = options.items.length;
  8621. return options;
  8622. },
  8623. _createItemMapping: function (stackItem, items) {
  8624. var temp = {},
  8625. result = {
  8626. recordStack: [],
  8627. mapping: {}
  8628. };
  8629. $.each(items, function (index, item) {
  8630. temp[item] = index;
  8631. });
  8632. result.itemMapping = temp;
  8633. $.each(stackItem, function (index, item) {
  8634. if (temp[item] !== undefined) {
  8635. result.recordStack.push(temp[item]);
  8636. result.mapping[item] = temp[item];
  8637. }
  8638. });
  8639. return result;
  8640. },
  8641. _update: function (index) {
  8642. var options = this.data("options"),
  8643. newStack = [],
  8644. newChilds = null;
  8645. $.each(options.recordStack, function (i, item) {
  8646. if (item != index) {
  8647. newStack.push(item);
  8648. }
  8649. });
  8650. //压入最新的记录
  8651. newStack.unshift(index);
  8652. if (newStack.length > options.recordCount) {
  8653. newStack.length = options.recordCount;
  8654. }
  8655. options.recordStack = newStack;
  8656. options.selected = index;
  8657. newChilds = $($.parseTmpl(this.tpl, options));
  8658. //重新渲染
  8659. this.root().html(newChilds.html());
  8660. newChilds = null;
  8661. newStack = null;
  8662. }
  8663. };
  8664. })(), 'menu');
  8665. })();
  8666. /**
  8667. * Combox 抽象基类
  8668. * User: hn
  8669. * Date: 13-5-29
  8670. * Time: 下午8:01
  8671. * To change this template use File | Settings | File Templates.
  8672. */
  8673. (function () {
  8674. var widgetName = 'buttoncombobox';
  8675. UM.ui.define(widgetName, (function () {
  8676. return {
  8677. defaultOpt: {
  8678. //按钮初始文字
  8679. label: '',
  8680. title: ''
  8681. },
  8682. init: function (options) {
  8683. var me = this;
  8684. var btnWidget = $.eduibutton({
  8685. caret: true,
  8686. name: options.comboboxName,
  8687. title: options.title,
  8688. text: options.label,
  8689. click: function () {
  8690. me.show(this.root());
  8691. }
  8692. });
  8693. me.supper.init.call(me, options);
  8694. //监听change, 改变button显示内容
  8695. me.on('changebefore', function (e, label) {
  8696. btnWidget.eduibutton('label', label);
  8697. });
  8698. me.data('button', btnWidget);
  8699. me.attachTo(btnWidget)
  8700. },
  8701. button: function () {
  8702. return this.data('button');
  8703. }
  8704. }
  8705. })(), 'combobox');
  8706. })();
  8707. /*modal 类*/
  8708. UM.ui.define('modal', {
  8709. tpl: '<div class="edui-modal" tabindex="-1" >' +
  8710. '<div class="edui-modal-header">' +
  8711. '<div class="edui-close" data-hide="modal"></div>' +
  8712. '<h3 class="edui-title"><%=title%></h3>' +
  8713. '</div>' +
  8714. '<div class="edui-modal-body" style="<%if(width){%>width:<%=width%>px;<%}%>' +
  8715. '<%if(height){%>height:<%=height%>px;<%}%>">' +
  8716. ' </div>' +
  8717. '<% if(cancellabel || oklabel) {%>' +
  8718. '<div class="edui-modal-footer">' +
  8719. '<div class="edui-modal-tip"></div>' +
  8720. '<%if(oklabel){%><div class="edui-btn edui-btn-primary" data-ok="modal"><%=oklabel%></div><%}%>' +
  8721. '<%if(cancellabel){%><div class="edui-btn" data-hide="modal"><%=cancellabel%></div><%}%>' +
  8722. '</div>' +
  8723. '<%}%></div>',
  8724. defaultOpt: {
  8725. title: "",
  8726. cancellabel: "",
  8727. oklabel: "",
  8728. width: '',
  8729. height: '',
  8730. backdrop: true,
  8731. keyboard: true
  8732. },
  8733. init: function (options) {
  8734. var me = this;
  8735. me.root($($.parseTmpl(me.tpl, options || {})));
  8736. me.data("options", options);
  8737. if (options.okFn) {
  8738. me.on('ok', $.proxy(options.okFn, me))
  8739. }
  8740. if (options.cancelFn) {
  8741. me.on('beforehide', $.proxy(options.cancelFn, me))
  8742. }
  8743. me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))
  8744. .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));
  8745. $('[data-hide="modal"],[data-ok="modal"]', me.root()).hover(function () {
  8746. $(this).toggleClass('edui-hover')
  8747. });
  8748. },
  8749. toggle: function () {
  8750. var me = this;
  8751. return me[!me.data("isShown") ? 'show' : 'hide']();
  8752. },
  8753. show: function () {
  8754. var me = this;
  8755. me.trigger("beforeshow");
  8756. if (me.data("isShown")) return;
  8757. me.data("isShown", true);
  8758. me.escape();
  8759. me.backdrop(function () {
  8760. me.autoCenter();
  8761. me.root()
  8762. .show()
  8763. .focus()
  8764. .trigger('aftershow');
  8765. })
  8766. },
  8767. showTip: function (text) {
  8768. $('.edui-modal-tip', this.root()).html(text).fadeIn();
  8769. },
  8770. hideTip: function (text) {
  8771. $('.edui-modal-tip', this.root()).fadeOut(function () {
  8772. $(this).html('');
  8773. });
  8774. },
  8775. autoCenter: function () {
  8776. //ie6下不用处理了
  8777. !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));
  8778. },
  8779. hide: function () {
  8780. var me = this;
  8781. me.trigger("beforehide");
  8782. if (!me.data("isShown")) return;
  8783. me.data("isShown", false);
  8784. me.escape();
  8785. me.hideModal();
  8786. },
  8787. escape: function () {
  8788. var me = this;
  8789. if (me.data("isShown") && me.data("options").keyboard) {
  8790. me.root().on('keyup', function (e) {
  8791. e.which == 27 && me.hide();
  8792. })
  8793. } else if (!me.data("isShown")) {
  8794. me.root().off('keyup');
  8795. }
  8796. },
  8797. hideModal: function () {
  8798. var me = this;
  8799. me.root().hide();
  8800. me.backdrop(function () {
  8801. me.removeBackdrop();
  8802. me.trigger('afterhide');
  8803. })
  8804. },
  8805. removeBackdrop: function () {
  8806. this.$backdrop && this.$backdrop.remove();
  8807. this.$backdrop = null;
  8808. },
  8809. backdrop: function (callback) {
  8810. var me = this;
  8811. if (me.data("isShown") && me.data("options").backdrop) {
  8812. me.$backdrop = $('<div class="edui-modal-backdrop" />').click(
  8813. me.data("options").backdrop == 'static' ?
  8814. $.proxy(me.root()[0].focus, me.root()[0])
  8815. : $.proxy(me.hide, me)
  8816. )
  8817. }
  8818. me.trigger('afterbackdrop');
  8819. callback && callback();
  8820. },
  8821. attachTo: function ($obj) {
  8822. var me = this
  8823. if (!$obj.data('$mergeObj')) {
  8824. $obj.data('$mergeObj', me.root());
  8825. $obj.on('click', function () {
  8826. me.toggle($obj)
  8827. });
  8828. me.data('$mergeObj', $obj)
  8829. }
  8830. },
  8831. ok: function () {
  8832. var me = this;
  8833. me.trigger('beforeok');
  8834. if (me.trigger("ok", me) === false) {
  8835. return;
  8836. }
  8837. me.hide();
  8838. },
  8839. getBodyContainer: function () {
  8840. return this.root().find('.edui-modal-body')
  8841. }
  8842. });
  8843. /*tooltip 类*/
  8844. UM.ui.define('tooltip', {
  8845. tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +
  8846. '<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +
  8847. '<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +
  8848. '</div>',
  8849. init: function (options) {
  8850. var me = this;
  8851. me.root($($.parseTmpl(me.tpl, options || {})));
  8852. },
  8853. content: function (e) {
  8854. var me = this,
  8855. title = $(e.currentTarget).attr("data-original-title");
  8856. me.root().find('.edui-tooltip-inner')['text'](title);
  8857. },
  8858. position: function (e) {
  8859. var me = this,
  8860. $obj = $(e.currentTarget);
  8861. me.root().css($.extend({display: 'block'}, $obj ? {
  8862. top: $obj.outerHeight(),
  8863. left: (($obj.outerWidth() - me.root().outerWidth()) / 2)
  8864. } : {}))
  8865. },
  8866. show: function (e) {
  8867. if ($(e.currentTarget).hasClass('edui-disabled')) return;
  8868. var me = this;
  8869. me.content(e);
  8870. me.root().appendTo($(e.currentTarget));
  8871. me.position(e);
  8872. me.root().css('display', 'block');
  8873. },
  8874. hide: function () {
  8875. var me = this;
  8876. me.root().css('display', 'none')
  8877. },
  8878. attachTo: function ($obj) {
  8879. var me = this;
  8880. function tmp($obj) {
  8881. var me = this;
  8882. if (!$.contains(document.body, me.root()[0])) {
  8883. me.root().appendTo($obj);
  8884. }
  8885. me.data('tooltip', me.root());
  8886. $obj.each(function () {
  8887. if ($(this).attr("data-original-title")) {
  8888. $(this).on('mouseenter', $.proxy(me.show, me))
  8889. .on('mouseleave click', $.proxy(me.hide, me))
  8890. }
  8891. });
  8892. }
  8893. if ($.type($obj) === "undefined") {
  8894. $("[data-original-title]").each(function (i, el) {
  8895. tmp.call(me, $(el));
  8896. })
  8897. } else {
  8898. if (!$obj.data('tooltip')) {
  8899. tmp.call(me, $obj);
  8900. }
  8901. }
  8902. }
  8903. });
  8904. /*tab 类*/
  8905. UM.ui.define('tab', {
  8906. init: function (options) {
  8907. var me = this,
  8908. slr = options.selector;
  8909. if ($.type(slr)) {
  8910. me.root($(slr, options.context));
  8911. me.data("context", options.context);
  8912. $(slr, me.data("context")).on('click', function (e) {
  8913. me.show(e);
  8914. });
  8915. }
  8916. },
  8917. show: function (e) {
  8918. var me = this,
  8919. $cur = $(e.target),
  8920. $ul = $cur.closest('ul'),
  8921. selector,
  8922. previous,
  8923. $target,
  8924. e;
  8925. selector = $cur.attr('data-context');
  8926. selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
  8927. var $tmp = $cur.parent('li');
  8928. if (!$tmp.length || $tmp.hasClass('edui-active')) return;
  8929. previous = $ul.find('.edui-active:last a')[0];
  8930. e = $.Event('beforeshow', {
  8931. target: $cur[0],
  8932. relatedTarget: previous
  8933. });
  8934. me.trigger(e);
  8935. if (e.isDefaultPrevented()) return;
  8936. $target = $(selector, me.data("context"));
  8937. me.activate($cur.parent('li'), $ul);
  8938. me.activate($target, $target.parent(), function () {
  8939. me.trigger({
  8940. type: 'aftershow', relatedTarget: previous
  8941. })
  8942. });
  8943. },
  8944. activate: function (element, container, callback) {
  8945. if (element === undefined) {
  8946. return $(".edui-tab-item.edui-active", this.root()).index();
  8947. }
  8948. var $active = container.find('> .edui-active');
  8949. $active.removeClass('edui-active');
  8950. element.addClass('edui-active');
  8951. callback && callback();
  8952. }
  8953. });
  8954. //button 类
  8955. UM.ui.define('separator', {
  8956. tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',
  8957. init: function (options) {
  8958. var me = this;
  8959. me.root($($.parseTmpl(me.tpl, options)));
  8960. return me;
  8961. }
  8962. });
  8963. /**
  8964. * @file adapter.js
  8965. * @desc adapt ui to editor
  8966. * @import core/Editor.js, core/utils.js
  8967. */
  8968. (function () {
  8969. var _editorUI = {},
  8970. _editors = {},
  8971. _readyFn = [],
  8972. _activeWidget = null,
  8973. _widgetData = {},
  8974. _widgetCallBack = {},
  8975. _cacheUI = {},
  8976. _maxZIndex = null;
  8977. utils.extend(UM, {
  8978. defaultWidth: 500,
  8979. defaultHeight: 500,
  8980. registerUI: function (name, fn) {
  8981. utils.each(name.split(/\s+/), function (uiname) {
  8982. _editorUI[uiname] = fn;
  8983. })
  8984. },
  8985. setEditor: function (editor) {
  8986. !_editors[editor.id] && (_editors[editor.id] = editor);
  8987. },
  8988. registerWidget: function (name, pro, cb) {
  8989. _widgetData[name] = $.extend2(pro, {
  8990. $root: '',
  8991. _preventDefault: false,
  8992. root: function ($el) {
  8993. return this.$root || (this.$root = $el);
  8994. },
  8995. preventDefault: function () {
  8996. this._preventDefault = true;
  8997. },
  8998. clear: false
  8999. });
  9000. if (cb) {
  9001. _widgetCallBack[name] = cb;
  9002. }
  9003. },
  9004. getWidgetData: function (name) {
  9005. return _widgetData[name]
  9006. },
  9007. setWidgetBody: function (name, $widget, editor) {
  9008. if (!editor._widgetData) {
  9009. utils.extend(editor, {
  9010. _widgetData: {},
  9011. getWidgetData: function (name) {
  9012. return this._widgetData[name];
  9013. },
  9014. getWidgetCallback: function (widgetName) {
  9015. var me = this;
  9016. return function () {
  9017. return _widgetCallBack[widgetName].apply(me, [me, $widget].concat(Array.prototype.slice.call(arguments, 0)))
  9018. }
  9019. }
  9020. })
  9021. }
  9022. var pro = _widgetData[name];
  9023. if (!pro) {
  9024. return null;
  9025. }
  9026. pro = editor._widgetData[name];
  9027. if (!pro) {
  9028. pro = _widgetData[name];
  9029. pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);
  9030. }
  9031. pro.root($widget.edui().getBodyContainer());
  9032. pro.initContent(editor, $widget);
  9033. if (!pro._preventDefault) {
  9034. pro.initEvent(editor, $widget);
  9035. }
  9036. pro.width && $widget.width(pro.width);
  9037. },
  9038. setActiveWidget: function ($widget) {
  9039. _activeWidget = $widget;
  9040. },
  9041. getEditor: function (id, options) {
  9042. var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));
  9043. _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex) : editor.getOpt('zIndex');
  9044. return editor;
  9045. },
  9046. setTopEditor: function (editor) {
  9047. $.each(_editors, function (i, o) {
  9048. if (editor == o) {
  9049. editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);
  9050. } else {
  9051. o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));
  9052. }
  9053. });
  9054. },
  9055. clearCache: function (id) {
  9056. if (_editors[id]) {
  9057. delete _editors[id]
  9058. }
  9059. },
  9060. delEditor: function (id) {
  9061. var editor;
  9062. if (editor = _editors[id]) {
  9063. editor.destroy();
  9064. }
  9065. },
  9066. ready: function (fn) {
  9067. _readyFn.push(fn);
  9068. },
  9069. createEditor: function (id, opt) {
  9070. var editor = new UM.Editor(opt);
  9071. var T = this;
  9072. editor.langIsReady ? $.proxy(renderUI, T)() : editor.addListener("langReady", $.proxy(renderUI, T));
  9073. function renderUI() {
  9074. var $container = this.createUI('#' + id, editor);
  9075. editor.key = id;
  9076. editor.ready(function () {
  9077. $.each(_readyFn, function (index, fn) {
  9078. $.proxy(fn, editor)();
  9079. });
  9080. });
  9081. var options = editor.options;
  9082. if (options.initialFrameWidth) {
  9083. options.minFrameWidth = options.initialFrameWidth
  9084. } else {
  9085. options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth;
  9086. }
  9087. $container.css({
  9088. width: options.initialFrameWidth,
  9089. zIndex: editor.getOpt('zIndex')
  9090. });
  9091. //ie6下缓存图片
  9092. UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);
  9093. editor.render(id);
  9094. //添加tooltip;
  9095. $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]", $container)).css('z-index', editor.getOpt('zIndex') + 1);
  9096. $container.find('a').click(function (evt) {
  9097. evt.preventDefault()
  9098. });
  9099. editor.fireEvent("afteruiready");
  9100. }
  9101. return editor;
  9102. },
  9103. createUI: function (id, editor) {
  9104. var $editorCont = $(id),
  9105. $container = $('<div class="edui-container"><div class="edui-editor-body"></div></div>').insertBefore($editorCont);
  9106. editor.$container = $container;
  9107. editor.container = $container[0];
  9108. editor.$body = $editorCont;
  9109. //修正在ie9+以上的版本中,自动长高收起时的,残影问题
  9110. if (browser.ie && browser.ie9above) {
  9111. var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');
  9112. $span.insertAfter($container);
  9113. }
  9114. //初始化注册的ui组件
  9115. $.each(_editorUI, function (n, v) {
  9116. var widget = v.call(editor, n);
  9117. if (widget) {
  9118. _cacheUI[n] = widget;
  9119. }
  9120. });
  9121. $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));
  9122. $container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));
  9123. return $container;
  9124. },
  9125. createToolbar: function (options, editor) {
  9126. var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();
  9127. //创建下来菜单列表
  9128. if (options.toolbar && options.toolbar.length) {
  9129. var btns = [];
  9130. $.each(options.toolbar, function (i, uiNames) {
  9131. $.each(uiNames.split(/\s+/), function (index, name) {
  9132. if (name == '|') {
  9133. $.eduiseparator && btns.push($.eduiseparator());
  9134. } else {
  9135. var ui = _cacheUI[name];
  9136. if (name == "fullscreen") {
  9137. ui && btns.unshift(ui);
  9138. } else {
  9139. ui && btns.push(ui);
  9140. }
  9141. }
  9142. });
  9143. btns.length && toolbar.appendToBtnmenu(btns);
  9144. });
  9145. } else {
  9146. $toolbar.find('.edui-btn-toolbar').remove()
  9147. }
  9148. return $toolbar;
  9149. }
  9150. })
  9151. })();
  9152. UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +
  9153. 'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',
  9154. function (name) {
  9155. var me = this;
  9156. var $btn = $.eduibutton({
  9157. icon: name,
  9158. click: function () {
  9159. me.execCommand(name);
  9160. },
  9161. title: this.getLang('labelMap')[name] || ''
  9162. });
  9163. this.addListener('selectionchange', function () {
  9164. var state = this.queryCommandState(name);
  9165. $btn.edui().disabled(state == -1).active(state == 1)
  9166. });
  9167. return $btn;
  9168. }
  9169. );
  9170. /**
  9171. * 全屏组件
  9172. */
  9173. (function () {
  9174. //状态缓存
  9175. var STATUS_CACHE = {},
  9176. //状态值列表
  9177. STATUS_LIST = ['width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY'],
  9178. CONTENT_AREA_STATUS = {},
  9179. //页面状态
  9180. DOCUMENT_STATUS = {},
  9181. DOCUMENT_ELEMENT_STATUS = {},
  9182. FULLSCREENS = {};
  9183. UM.registerUI('fullscreen', function (name) {
  9184. var me = this,
  9185. $button = $.eduibutton({
  9186. 'icon': 'fullscreen',
  9187. 'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9188. 'click': function () {
  9189. //切换
  9190. me.execCommand(name);
  9191. UM.setTopEditor(me);
  9192. }
  9193. });
  9194. me.addListener("selectionchange", function () {
  9195. var state = this.queryCommandState(name);
  9196. $button.edui().disabled(state == -1).active(state == 1);
  9197. });
  9198. //切换至全屏
  9199. me.addListener('ready', function () {
  9200. me.options.fullscreen && Fullscreen.getInstance(me).toggle();
  9201. });
  9202. return $button;
  9203. });
  9204. UM.commands['fullscreen'] = {
  9205. execCommand: function (cmdName) {
  9206. Fullscreen.getInstance(this).toggle();
  9207. },
  9208. queryCommandState: function (cmdName) {
  9209. return this._edui_fullscreen_status;
  9210. },
  9211. notNeedUndo: 1
  9212. };
  9213. function Fullscreen(editor) {
  9214. var me = this;
  9215. if (!editor) {
  9216. throw new Error('invalid params, notfound editor');
  9217. }
  9218. me.editor = editor;
  9219. //记录初始化的全屏组件
  9220. FULLSCREENS[editor.uid] = this;
  9221. editor.addListener('destroy', function () {
  9222. delete FULLSCREENS[editor.uid];
  9223. me.editor = null;
  9224. });
  9225. }
  9226. Fullscreen.prototype = {
  9227. /**
  9228. * 全屏状态切换
  9229. */
  9230. toggle: function () {
  9231. var editor = this.editor,
  9232. //当前编辑器的缩放状态
  9233. _edui_fullscreen_status = this.isFullState();
  9234. editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status);
  9235. //更新状态
  9236. this.update(!_edui_fullscreen_status);
  9237. !_edui_fullscreen_status ? this.enlarge() : this.revert();
  9238. editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status);
  9239. if (editor.body.contentEditable === 'true') {
  9240. editor.fireEvent('fullscreenchanged', !_edui_fullscreen_status);
  9241. }
  9242. editor.fireEvent('selectionchange');
  9243. },
  9244. /**
  9245. * 执行放大
  9246. */
  9247. enlarge: function () {
  9248. this.saveSataus();
  9249. this.setDocumentStatus();
  9250. this.resize();
  9251. },
  9252. /**
  9253. * 全屏还原
  9254. */
  9255. revert: function () {
  9256. //还原CSS表达式
  9257. var options = this.editor.options,
  9258. height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top") - this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));
  9259. $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  9260. //还原容器状态
  9261. this.revertContainerStatus();
  9262. this.revertContentAreaStatus();
  9263. this.revertDocumentStatus();
  9264. },
  9265. /**
  9266. * 更新状态
  9267. * @param isFull 当前状态是否是全屏状态
  9268. */
  9269. update: function (isFull) {
  9270. this.editor._edui_fullscreen_status = isFull;
  9271. },
  9272. /**
  9273. * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态, 则不做调整
  9274. */
  9275. resize: function () {
  9276. var $win = null,
  9277. height = 0,
  9278. width = 0,
  9279. borderWidth = 0,
  9280. paddingWidth = 0,
  9281. editor = this.editor,
  9282. me = this,
  9283. bound = null,
  9284. editorBody = null;
  9285. if (!this.isFullState()) {
  9286. return;
  9287. }
  9288. $win = $(window);
  9289. width = $win.width();
  9290. height = $win.height();
  9291. editorBody = this.getEditorHolder();
  9292. //文本编辑区border宽度
  9293. borderWidth = parseInt(domUtils.getComputedStyle(editorBody, 'border-width'), 10) || 0;
  9294. //容器border宽度
  9295. borderWidth += parseInt(domUtils.getComputedStyle(editor.container, 'border-width'), 10) || 0;
  9296. //容器padding
  9297. paddingWidth += parseInt(domUtils.getComputedStyle(editorBody, 'padding-left'), 10) + parseInt(domUtils.getComputedStyle(editorBody, 'padding-right'), 10) || 0;
  9298. //干掉css表达式
  9299. $.IE6 && editorBody.style.setExpression('height', null);
  9300. bound = this.getBound();
  9301. $(editor.container).css({
  9302. width: width + 'px',
  9303. height: height + 'px',
  9304. position: !$.IE6 ? 'fixed' : 'absolute',
  9305. top: bound.top,
  9306. left: bound.left,
  9307. margin: 0,
  9308. padding: 0,
  9309. overflowX: 'hidden',
  9310. overflowY: 'hidden'
  9311. });
  9312. $(editorBody).css({
  9313. width: width - 2 * borderWidth - paddingWidth + 'px',
  9314. height: height - 2 * borderWidth - (editor.options.withoutToolbar ? 0 : $('.edui-toolbar', editor.container).outerHeight()) - $('.edui-bottombar', editor.container).outerHeight() + 'px',
  9315. overflowX: 'hidden',
  9316. overflowY: 'auto'
  9317. });
  9318. },
  9319. /**
  9320. * 保存状态
  9321. */
  9322. saveSataus: function () {
  9323. var styles = this.editor.container.style,
  9324. tmp = null,
  9325. cache = {};
  9326. for (var i = 0, len = STATUS_LIST.length; i < len; i++) {
  9327. tmp = STATUS_LIST[i];
  9328. cache[tmp] = styles[tmp];
  9329. }
  9330. STATUS_CACHE[this.editor.uid] = cache;
  9331. this.saveContentAreaStatus();
  9332. this.saveDocumentStatus();
  9333. },
  9334. saveContentAreaStatus: function () {
  9335. var $holder = $(this.getEditorHolder());
  9336. CONTENT_AREA_STATUS[this.editor.uid] = {
  9337. width: $holder.css("width"),
  9338. overflowX: $holder.css("overflowX"),
  9339. overflowY: $holder.css("overflowY"),
  9340. height: $holder.css("height")
  9341. };
  9342. },
  9343. /**
  9344. * 保存与指定editor相关的页面的状态
  9345. */
  9346. saveDocumentStatus: function () {
  9347. var $doc = $(this.getEditorDocumentBody());
  9348. DOCUMENT_STATUS[this.editor.uid] = {
  9349. overflowX: $doc.css('overflowX'),
  9350. overflowY: $doc.css('overflowY')
  9351. };
  9352. DOCUMENT_ELEMENT_STATUS[this.editor.uid] = {
  9353. overflowX: $(this.getEditorDocumentElement()).css('overflowX'),
  9354. overflowY: $(this.getEditorDocumentElement()).css('overflowY')
  9355. };
  9356. },
  9357. /**
  9358. * 恢复容器状态
  9359. */
  9360. revertContainerStatus: function () {
  9361. $(this.editor.container).css(this.getEditorStatus());
  9362. },
  9363. /**
  9364. * 恢复编辑区状态
  9365. */
  9366. revertContentAreaStatus: function () {
  9367. var holder = this.getEditorHolder(),
  9368. state = this.getContentAreaStatus();
  9369. if (this.supportMin()) {
  9370. delete state.height;
  9371. holder.style.height = null;
  9372. }
  9373. $(holder).css(state);
  9374. },
  9375. /**
  9376. * 恢复页面状态
  9377. */
  9378. revertDocumentStatus: function () {
  9379. var status = this.getDocumentStatus();
  9380. $(this.getEditorDocumentBody()).css({overflowX: status.body.overflowX, overflowY: status.body.overflowY});
  9381. $(this.getEditorDocumentElement()).css({overflowX: status.html.overflowX, overflowY: status.html.overflowY});
  9382. },
  9383. setDocumentStatus: function () {
  9384. $(this.getEditorDocumentBody()).css({
  9385. overflowX: 'hidden',
  9386. overflowY: 'hidden'
  9387. });
  9388. $(this.getEditorDocumentElement()).css({
  9389. overflowX: 'hidden',
  9390. overflowY: 'hidden'
  9391. });
  9392. },
  9393. /**
  9394. * 检测当前编辑器是否处于全屏状态全屏状态
  9395. * @returns {boolean} 是否处于全屏状态
  9396. */
  9397. isFullState: function () {
  9398. return !!this.editor._edui_fullscreen_status;
  9399. },
  9400. /**
  9401. * 获取编辑器状态
  9402. */
  9403. getEditorStatus: function () {
  9404. return STATUS_CACHE[this.editor.uid];
  9405. },
  9406. getContentAreaStatus: function () {
  9407. return CONTENT_AREA_STATUS[this.editor.uid];
  9408. },
  9409. getEditorDocumentElement: function () {
  9410. return this.editor.container.ownerDocument.documentElement;
  9411. },
  9412. getEditorDocumentBody: function () {
  9413. return this.editor.container.ownerDocument.body;
  9414. },
  9415. /**
  9416. * 获取编辑区包裹对象
  9417. */
  9418. getEditorHolder: function () {
  9419. return this.editor.body;
  9420. },
  9421. /**
  9422. * 获取编辑器状态
  9423. * @returns {*}
  9424. */
  9425. getDocumentStatus: function () {
  9426. return {
  9427. 'body': DOCUMENT_STATUS[this.editor.uid],
  9428. 'html': DOCUMENT_ELEMENT_STATUS[this.editor.uid]
  9429. };
  9430. },
  9431. supportMin: function () {
  9432. var node = null;
  9433. if (!this._support) {
  9434. node = document.createElement("div");
  9435. this._support = "minWidth" in node.style;
  9436. node = null;
  9437. }
  9438. return this._support;
  9439. },
  9440. getBound: function () {
  9441. var tags = {
  9442. html: true,
  9443. body: true
  9444. },
  9445. result = {
  9446. top: 0,
  9447. left: 0
  9448. },
  9449. offsetParent = null;
  9450. if (!$.IE6) {
  9451. return result;
  9452. }
  9453. offsetParent = this.editor.container.offsetParent;
  9454. if (offsetParent && !tags[offsetParent.nodeName.toLowerCase()]) {
  9455. tags = offsetParent.getBoundingClientRect();
  9456. result.top = -tags.top;
  9457. result.left = -tags.left;
  9458. }
  9459. return result;
  9460. },
  9461. getStyleValue: function (attr) {
  9462. return parseInt(domUtils.getComputedStyle(this.getEditorHolder(), attr));
  9463. }
  9464. };
  9465. $.extend(Fullscreen, {
  9466. /**
  9467. * 监听resize
  9468. */
  9469. listen: function () {
  9470. var timer = null;
  9471. if (Fullscreen._hasFullscreenListener) {
  9472. return;
  9473. }
  9474. Fullscreen._hasFullscreenListener = true;
  9475. $(window).on('resize', function () {
  9476. if (timer !== null) {
  9477. window.clearTimeout(timer);
  9478. timer = null;
  9479. }
  9480. timer = window.setTimeout(function () {
  9481. for (var key in FULLSCREENS) {
  9482. FULLSCREENS[key].resize();
  9483. }
  9484. timer = null;
  9485. }, 50);
  9486. });
  9487. },
  9488. getInstance: function (editor) {
  9489. if (!FULLSCREENS[editor.uid]) {
  9490. new Fullscreen(editor);
  9491. }
  9492. return FULLSCREENS[editor.uid];
  9493. }
  9494. });
  9495. //开始监听
  9496. Fullscreen.listen();
  9497. })();
  9498. UM.registerUI('link image video map formula', function (name) {
  9499. var me = this, currentRange, $dialog,
  9500. opt = {
  9501. title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9502. url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js'
  9503. };
  9504. var $btn = $.eduibutton({
  9505. icon: name,
  9506. title: this.getLang('labelMap')[name] || ''
  9507. });
  9508. //加载模版数据
  9509. utils.loadFile(document, {
  9510. src: opt.url,
  9511. tag: "script",
  9512. type: "text/javascript",
  9513. defer: "defer"
  9514. }, function () {
  9515. //调整数据
  9516. var data = UM.getWidgetData(name);
  9517. if (!data) return;
  9518. if (data.buttons) {
  9519. var ok = data.buttons.ok;
  9520. if (ok) {
  9521. opt.oklabel = ok.label || me.getLang('ok');
  9522. if (ok.exec) {
  9523. opt.okFn = function () {
  9524. return $.proxy(ok.exec, null, me, $dialog)()
  9525. }
  9526. }
  9527. }
  9528. var cancel = data.buttons.cancel;
  9529. if (cancel) {
  9530. opt.cancellabel = cancel.label || me.getLang('cancel');
  9531. if (cancel.exec) {
  9532. opt.cancelFn = function () {
  9533. return $.proxy(cancel.exec, null, me, $dialog)()
  9534. }
  9535. }
  9536. }
  9537. }
  9538. data.width && (opt.width = data.width);
  9539. data.height && (opt.height = data.height);
  9540. $dialog = $.eduimodal(opt);
  9541. $dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)
  9542. .find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');
  9543. $dialog.edui().on('beforehide', function () {
  9544. var rng = me.selection.getRange();
  9545. if (rng.equals(currentRange)) {
  9546. rng.select()
  9547. }
  9548. }).on('beforeshow', function () {
  9549. var $root = this.root(),
  9550. win = null,
  9551. offset = null;
  9552. currentRange = me.selection.getRange();
  9553. if (!$root.parent()[0]) {
  9554. me.$container.find('.edui-dialog-container').append($root);
  9555. }
  9556. //IE6下 特殊处理, 通过计算进行定位
  9557. if ($.IE6) {
  9558. win = {
  9559. width: $(window).width(),
  9560. height: $(window).height()
  9561. };
  9562. offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();
  9563. $root.css({
  9564. position: 'absolute',
  9565. margin: 0,
  9566. left: (win.width - $root.width()) / 2 - offset.left,
  9567. top: 100 - offset.top
  9568. });
  9569. }
  9570. UM.setWidgetBody(name, $dialog, me);
  9571. UM.setTopEditor(me);
  9572. }).on('afterbackdrop', function () {
  9573. this.$backdrop.css('zIndex', me.getOpt('zIndex') + 1).appendTo(me.$container.find('.edui-dialog-container'))
  9574. $dialog.css('zIndex', me.getOpt('zIndex') + 2)
  9575. }).on('beforeok', function () {
  9576. try {
  9577. currentRange.select()
  9578. } catch (e) {
  9579. }
  9580. }).attachTo($btn)
  9581. });
  9582. me.addListener('selectionchange', function () {
  9583. var state = this.queryCommandState(name);
  9584. $btn.edui().disabled(state == -1).active(state == 1)
  9585. });
  9586. return $btn;
  9587. });
  9588. UM.registerUI('emotion formula', function (name) {
  9589. var me = this,
  9590. url = me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js';
  9591. var $btn = $.eduibutton({
  9592. icon: name,
  9593. title: this.getLang('labelMap')[name] || ''
  9594. });
  9595. //加载模版数据
  9596. utils.loadFile(document, {
  9597. src: url,
  9598. tag: "script",
  9599. type: "text/javascript",
  9600. defer: "defer"
  9601. }, function () {
  9602. var opt = {
  9603. url: url
  9604. };
  9605. //调整数据
  9606. var data = UM.getWidgetData(name);
  9607. data.width && (opt.width = data.width);
  9608. data.height && (opt.height = data.height);
  9609. $.eduipopup(opt).css('zIndex', me.options.zIndex + 1)
  9610. .addClass('edui-popup-' + name)
  9611. .edui()
  9612. .on('beforeshow', function () {
  9613. var $root = this.root();
  9614. if (!$root.parent().length) {
  9615. me.$container.find('.edui-dialog-container').append($root);
  9616. }
  9617. UM.setWidgetBody(name, $root, me);
  9618. UM.setTopEditor(me);
  9619. }).attachTo($btn, {
  9620. offsetTop: -5,
  9621. offsetLeft: 10,
  9622. caretLeft: 11,
  9623. caretTop: -8
  9624. });
  9625. me.addListener('selectionchange', function () {
  9626. var state = this.queryCommandState(name);
  9627. $btn.edui().disabled(state == -1).active(state == 1);
  9628. });
  9629. });
  9630. return $btn;
  9631. });
  9632. UM.registerUI('imagescale', function () {
  9633. var me = this,
  9634. $imagescale;
  9635. me.setOpt('imageScaleEnabled', true);
  9636. if (browser.webkit && me.getOpt('imageScaleEnabled')) {
  9637. me.addListener('click', function (type, e) {
  9638. var range = me.selection.getRange(),
  9639. img = range.getClosedNode(),
  9640. target = e.target;
  9641. /* 点击第一个图片的后面,八个角不消失 fix:3652 */
  9642. if (img && img.tagName == 'IMG' && target == img) {
  9643. if (!$imagescale) {
  9644. $imagescale = $.eduiscale({'$wrap': me.$container}).css('zIndex', me.options.zIndex);
  9645. me.$container.append($imagescale);
  9646. var _keyDownHandler = function () {
  9647. $imagescale.edui().hide();
  9648. }, _mouseDownHandler = function (e) {
  9649. var ele = e.target || e.srcElement;
  9650. if (ele && ele.className.indexOf('edui-scale') == -1) {
  9651. _keyDownHandler(e);
  9652. }
  9653. }, timer;
  9654. $imagescale.edui()
  9655. .on('aftershow', function () {
  9656. $(document).bind('keydown', _keyDownHandler);
  9657. $(document).bind('mousedown', _mouseDownHandler);
  9658. me.selection.getNative().removeAllRanges();
  9659. })
  9660. .on('afterhide', function () {
  9661. $(document).unbind('keydown', _keyDownHandler);
  9662. $(document).unbind('mousedown', _mouseDownHandler);
  9663. var target = $imagescale.edui().getScaleTarget();
  9664. if (target.parentNode) {
  9665. me.selection.getRange().selectNode(target).select();
  9666. }
  9667. })
  9668. .on('mousedown', function (e) {
  9669. me.selection.getNative().removeAllRanges();
  9670. var ele = e.target || e.srcElement;
  9671. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9672. timer = setTimeout(function () {
  9673. $imagescale.edui().hide();
  9674. }, 200);
  9675. }
  9676. })
  9677. .on('mouseup', function (e) {
  9678. var ele = e.target || e.srcElement;
  9679. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9680. clearTimeout(timer);
  9681. }
  9682. });
  9683. }
  9684. $imagescale.edui().show($(img));
  9685. } else {
  9686. if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();
  9687. }
  9688. });
  9689. me.addListener('click', function (type, e) {
  9690. if (e.target.tagName == 'IMG') {
  9691. var range = new dom.Range(me.document, me.body);
  9692. range.selectNode(e.target).select();
  9693. }
  9694. });
  9695. }
  9696. });
  9697. UM.registerUI('autofloat', function () {
  9698. var me = this,
  9699. lang = me.getLang();
  9700. me.setOpt({
  9701. autoFloatEnabled: true,
  9702. topOffset: 0
  9703. });
  9704. var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
  9705. topOffset = me.options.topOffset;
  9706. //如果不固定toolbar的位置,则直接退出
  9707. if (!optsAutoFloatEnabled) {
  9708. return;
  9709. }
  9710. me.ready(function () {
  9711. var LteIE6 = browser.ie && browser.version <= 6,
  9712. quirks = browser.quirks;
  9713. function checkHasUI() {
  9714. if (!UM.ui) {
  9715. alert(lang.autofloatMsg);
  9716. return 0;
  9717. }
  9718. return 1;
  9719. }
  9720. function fixIE6FixedPos() {
  9721. var docStyle = document.body.style;
  9722. docStyle.backgroundImage = 'url("about:blank")';
  9723. docStyle.backgroundAttachment = 'fixed';
  9724. }
  9725. var bakCssText,
  9726. placeHolder = document.createElement('div'),
  9727. toolbarBox, orgTop,
  9728. getPosition = function (element) {
  9729. var bcr;
  9730. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  9731. try {
  9732. bcr = element.getBoundingClientRect();
  9733. } catch (e) {
  9734. bcr = {left: 0, top: 0, height: 0, width: 0}
  9735. }
  9736. var rect = {
  9737. left: Math.round(bcr.left),
  9738. top: Math.round(bcr.top),
  9739. height: Math.round(bcr.bottom - bcr.top),
  9740. width: Math.round(bcr.right - bcr.left)
  9741. };
  9742. var doc;
  9743. while ((doc = element.ownerDocument) !== document &&
  9744. (element = domUtils.getWindow(doc).frameElement)) {
  9745. bcr = element.getBoundingClientRect();
  9746. rect.left += bcr.left;
  9747. rect.top += bcr.top;
  9748. }
  9749. rect.bottom = rect.top + rect.height;
  9750. rect.right = rect.left + rect.width;
  9751. return rect;
  9752. };
  9753. var isFullScreening = false;
  9754. function setFloating() {
  9755. if (isFullScreening) {
  9756. return;
  9757. }
  9758. var toobarBoxPos = domUtils.getXY(toolbarBox),
  9759. origalFloat = domUtils.getComputedStyle(toolbarBox, 'position'),
  9760. origalLeft = domUtils.getComputedStyle(toolbarBox, 'left');
  9761. toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
  9762. toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
  9763. toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
  9764. if (LteIE6 || (quirks && browser.ie)) {
  9765. if (toolbarBox.style.position != 'absolute') {
  9766. toolbarBox.style.position = 'absolute';
  9767. }
  9768. toolbarBox.style.top = (document.body.scrollTop || document.documentElement.scrollTop) - orgTop + topOffset + 'px';
  9769. } else {
  9770. if (toolbarBox.style.position != 'fixed') {
  9771. toolbarBox.style.position = 'fixed';
  9772. toolbarBox.style.top = topOffset + "px";
  9773. ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
  9774. }
  9775. }
  9776. }
  9777. function unsetFloating() {
  9778. if (placeHolder.parentNode) {
  9779. placeHolder.parentNode.removeChild(placeHolder);
  9780. }
  9781. toolbarBox.style.cssText = bakCssText;
  9782. }
  9783. function updateFloating() {
  9784. var rect3 = getPosition(me.container);
  9785. var offset = me.options.toolbarTopOffset || 0;
  9786. if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
  9787. setFloating();
  9788. } else {
  9789. unsetFloating();
  9790. }
  9791. }
  9792. var defer_updateFloating = utils.defer(function () {
  9793. updateFloating();
  9794. }, browser.ie ? 200 : 100, true);
  9795. me.addListener('destroy', function () {
  9796. $(window).off('scroll resize', updateFloating);
  9797. me.removeListener('keydown', defer_updateFloating);
  9798. });
  9799. if (checkHasUI(me)) {
  9800. toolbarBox = $('.edui-toolbar', me.container)[0];
  9801. me.addListener("afteruiready", function () {
  9802. setTimeout(function () {
  9803. orgTop = $(toolbarBox).offset().top;
  9804. }, 100);
  9805. });
  9806. bakCssText = toolbarBox.style.cssText;
  9807. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9808. if (LteIE6) {
  9809. fixIE6FixedPos();
  9810. }
  9811. $(window).on('scroll resize', updateFloating);
  9812. me.addListener('keydown', defer_updateFloating);
  9813. me.addListener('resize', function () {
  9814. unsetFloating();
  9815. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9816. updateFloating();
  9817. });
  9818. me.addListener('beforefullscreenchange', function (t, enabled) {
  9819. if (enabled) {
  9820. unsetFloating();
  9821. isFullScreening = enabled;
  9822. }
  9823. });
  9824. me.addListener('fullscreenchanged', function (t, enabled) {
  9825. if (!enabled) {
  9826. updateFloating();
  9827. }
  9828. isFullScreening = enabled;
  9829. });
  9830. me.addListener('sourcemodechanged', function (t, enabled) {
  9831. setTimeout(function () {
  9832. updateFloating();
  9833. }, 0);
  9834. });
  9835. me.addListener("clearDoc", function () {
  9836. setTimeout(function () {
  9837. updateFloating();
  9838. }, 0);
  9839. })
  9840. }
  9841. })
  9842. });
  9843. UM.registerUI('source', function (name) {
  9844. var me = this;
  9845. me.addListener('fullscreenchanged', function () {
  9846. me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())
  9847. });
  9848. var $btn = $.eduibutton({
  9849. icon: name,
  9850. click: function () {
  9851. me.execCommand(name);
  9852. UM.setTopEditor(me);
  9853. },
  9854. title: this.getLang('labelMap')[name] || ''
  9855. });
  9856. this.addListener('selectionchange', function () {
  9857. var state = this.queryCommandState(name);
  9858. $btn.edui().disabled(state == -1).active(state == 1)
  9859. });
  9860. return $btn;
  9861. });
  9862. UM.registerUI('paragraph fontfamily fontsize', function (name) {
  9863. var me = this,
  9864. label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9865. options = {
  9866. label: label,
  9867. title: label,
  9868. comboboxName: name,
  9869. items: me.options[name] || [],
  9870. itemStyles: [],
  9871. value: [],
  9872. autowidthitem: []
  9873. },
  9874. $combox = null,
  9875. comboboxWidget = null;
  9876. if (options.items.length == 0) {
  9877. return null;
  9878. }
  9879. switch (name) {
  9880. case 'paragraph':
  9881. options = transForParagraph(options);
  9882. break;
  9883. case 'fontfamily':
  9884. options = transForFontfamily(options);
  9885. break;
  9886. case 'fontsize':
  9887. options = transForFontsize(options);
  9888. break;
  9889. }
  9890. //实例化
  9891. $combox = $.eduibuttoncombobox(options).css('zIndex', me.getOpt('zIndex') + 1);
  9892. comboboxWidget = $combox.edui();
  9893. comboboxWidget.on('comboboxselect', function (evt, res) {
  9894. me.execCommand(name, res.value);
  9895. }).on("beforeshow", function () {
  9896. if ($combox.parent().length === 0) {
  9897. $combox.appendTo(me.$container.find('.edui-dialog-container'));
  9898. }
  9899. UM.setTopEditor(me);
  9900. });
  9901. //状态反射
  9902. this.addListener('selectionchange', function (evt) {
  9903. var state = this.queryCommandState(name),
  9904. value = this.queryCommandValue(name);
  9905. //设置按钮状态
  9906. comboboxWidget.button().edui().disabled(state == -1).active(state == 1);
  9907. if (value) {
  9908. //设置label
  9909. value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);
  9910. comboboxWidget.selectItemByLabel(value);
  9911. }
  9912. });
  9913. return comboboxWidget.button().addClass('edui-combobox');
  9914. /**
  9915. * 宽度自适应工具函数
  9916. * @param word 单词内容
  9917. * @param hasSuffix 是否含有后缀
  9918. */
  9919. function wordCountAdaptive(word, hasSuffix) {
  9920. var $tmpNode = $('<span>').html(word).css({
  9921. display: 'inline',
  9922. position: 'absolute',
  9923. top: -10000000,
  9924. left: -100000
  9925. }).appendTo(document.body),
  9926. width = $tmpNode.width();
  9927. $tmpNode.remove();
  9928. $tmpNode = null;
  9929. if (width < 50) {
  9930. return word;
  9931. } else {
  9932. word = word.slice(0, hasSuffix ? -4 : -1);
  9933. if (!word.length) {
  9934. return '...';
  9935. }
  9936. return wordCountAdaptive(word + '...', true);
  9937. }
  9938. }
  9939. //段落参数转换
  9940. function transForParagraph(options) {
  9941. var tempItems = [];
  9942. for (var key in options.items) {
  9943. options.value.push(key);
  9944. tempItems.push(key);
  9945. options.autowidthitem.push(wordCountAdaptive(key));
  9946. }
  9947. options.items = tempItems;
  9948. options.autoRecord = false;
  9949. return options;
  9950. }
  9951. //字体参数转换
  9952. function transForFontfamily(options) {
  9953. var temp = null,
  9954. tempItems = [];
  9955. for (var i = 0, len = options.items.length; i < len; i++) {
  9956. temp = options.items[i].val;
  9957. tempItems.push(temp.split(/\s*,\s*/)[0]);
  9958. options.itemStyles.push('font-family: ' + temp);
  9959. options.value.push(temp);
  9960. options.autowidthitem.push(wordCountAdaptive(tempItems[i]));
  9961. }
  9962. options.items = tempItems;
  9963. return options;
  9964. }
  9965. //字体大小参数转换
  9966. function transForFontsize(options) {
  9967. var temp = null,
  9968. tempItems = [];
  9969. options.itemStyles = [];
  9970. options.value = [];
  9971. for (var i = 0, len = options.items.length; i < len; i++) {
  9972. temp = options.items[i];
  9973. tempItems.push(temp);
  9974. options.itemStyles.push('font-size: ' + temp + 'px');
  9975. }
  9976. options.value = options.items;
  9977. options.items = tempItems;
  9978. options.autoRecord = false;
  9979. return options;
  9980. }
  9981. });
  9982. UM.registerUI('forecolor backcolor', function (name) {
  9983. function getCurrentColor() {
  9984. return domUtils.getComputedStyle($colorLabel[0], 'background-color');
  9985. }
  9986. var me = this,
  9987. $colorPickerWidget = null,
  9988. $colorLabel = null,
  9989. $btn = null;
  9990. //querycommand
  9991. this.addListener('selectionchange', function () {
  9992. var state = this.queryCommandState(name);
  9993. $btn.edui().disabled(state == -1).active(state == 1);
  9994. });
  9995. $btn = $.eduicolorsplitbutton({
  9996. icon: name,
  9997. caret: true,
  9998. name: name,
  9999. title: me.getLang("labelMap")[name],
  10000. click: function () {
  10001. me.execCommand(name, getCurrentColor());
  10002. }
  10003. });
  10004. $colorLabel = $btn.edui().colorLabel();
  10005. $colorPickerWidget = $.eduicolorpicker({
  10006. name: name,
  10007. lang_clearColor: me.getLang('clearColor') || '',
  10008. lang_themeColor: me.getLang('themeColor') || '',
  10009. lang_standardColor: me.getLang('standardColor') || ''
  10010. })
  10011. .on('pickcolor', function (evt, color) {
  10012. window.setTimeout(function () {
  10013. $colorLabel.css("backgroundColor", color);
  10014. me.execCommand(name, color);
  10015. }, 0);
  10016. })
  10017. .on('show', function () {
  10018. UM.setActiveWidget(colorPickerWidget.root());
  10019. }).css('zIndex', me.getOpt('zIndex') + 1);
  10020. $btn.edui().on('arrowclick', function () {
  10021. if (!$colorPickerWidget.parent().length) {
  10022. me.$container.find('.edui-dialog-container').append($colorPickerWidget);
  10023. }
  10024. $colorPickerWidget.edui().show($btn, {
  10025. caretDir: "down",
  10026. offsetTop: -5,
  10027. offsetLeft: 8,
  10028. caretLeft: 11,
  10029. caretTop: -8
  10030. });
  10031. UM.setTopEditor(me);
  10032. }).register('click', $btn, function () {
  10033. $colorPickerWidget.edui().hide()
  10034. });
  10035. return $btn;
  10036. });
  10037. })(jQuery)