ueditor.all.min.js 1.0 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984
  1. /*!
  2. * UEditor
  3. * version: ueditor
  4. * build: Wed Dec 26 2018 17:24:52 GMT+0800 (CST)
  5. */
  6. // const { default: axios } = require('axios')
  7. ;(function () {
  8. // editor.js
  9. UEDITOR_CONFIG = window.UEDITOR_CONFIG || {}
  10. var baidu = window.baidu || {}
  11. window.baidu = baidu
  12. window.UE = baidu.editor = window.UE || {}
  13. UE.plugins = {}
  14. UE.commands = {}
  15. UE.instants = {}
  16. UE.I18N = {}
  17. UE._customizeUI = {}
  18. UE.version = '1.4.3'
  19. var dom = (UE.dom = {})
  20. // core/browser.js
  21. /**
  22. * 浏览器判断模块
  23. * @file
  24. * @module UE.browser
  25. * @since 1.2.6.1
  26. */
  27. /**
  28. * 提供浏览器检测的模块
  29. * @unfile
  30. * @module UE.browser
  31. */
  32. var browser = (UE.browser = (function () {
  33. var agent = navigator.userAgent.toLowerCase(),
  34. opera = window.opera,
  35. browser = {
  36. /**
  37. * @property {boolean} ie 检测当前浏览器是否为IE
  38. * @example
  39. * ```javascript
  40. * if ( UE.browser.ie ) {
  41. * console.log( '当前浏览器是IE' );
  42. * }
  43. * ```
  44. */
  45. ie: /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  46. /**
  47. * @property {boolean} opera 检测当前浏览器是否为Opera
  48. * @example
  49. * ```javascript
  50. * if ( UE.browser.opera ) {
  51. * console.log( '当前浏览器是Opera' );
  52. * }
  53. * ```
  54. */
  55. opera: !!opera && opera.version,
  56. /**
  57. * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
  58. * @example
  59. * ```javascript
  60. * if ( UE.browser.webkit ) {
  61. * console.log( '当前浏览器是webkit内核浏览器' );
  62. * }
  63. * ```
  64. */
  65. webkit: agent.indexOf(' applewebkit/') > -1,
  66. /**
  67. * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
  68. * @example
  69. * ```javascript
  70. * if ( UE.browser.mac ) {
  71. * console.log( '当前浏览器运行在mac平台下' );
  72. * }
  73. * ```
  74. */
  75. mac: agent.indexOf('macintosh') > -1,
  76. /**
  77. * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
  78. * @example
  79. * ```javascript
  80. * if ( UE.browser.quirks ) {
  81. * console.log( '当前浏览器运行处于“怪异模式”' );
  82. * }
  83. * ```
  84. */
  85. quirks: document.compatMode == 'BackCompat'
  86. }
  87. /**
  88. * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
  89. * @example
  90. * ```javascript
  91. * if ( UE.browser.gecko ) {
  92. * console.log( '当前浏览器内核是gecko内核' );
  93. * }
  94. * ```
  95. */
  96. browser.gecko =
  97. navigator.product == 'Gecko' &&
  98. !browser.webkit &&
  99. !browser.opera &&
  100. !browser.ie
  101. var version = 0
  102. // Internet Explorer 6.0+
  103. if (browser.ie) {
  104. var v1 = agent.match(/(?:msie\s([\w.]+))/)
  105. var v2 = agent.match(/(?:trident.*rv:([\w.]+))/)
  106. if (v1 && v2 && v1[1] && v2[1]) {
  107. version = Math.max(v1[1] * 1, v2[1] * 1)
  108. } else if (v1 && v1[1]) {
  109. version = v1[1] * 1
  110. } else if (v2 && v2[1]) {
  111. version = v2[1] * 1
  112. } else {
  113. version = 0
  114. }
  115. browser.ie11Compat = document.documentMode == 11
  116. /**
  117. * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
  118. * @warning 如果浏览器不是IE, 则该值为undefined
  119. * @example
  120. * ```javascript
  121. * if ( UE.browser.ie9Compat ) {
  122. * console.log( '当前浏览器运行在IE9兼容模式下' );
  123. * }
  124. * ```
  125. */
  126. browser.ie9Compat = document.documentMode == 9
  127. /**
  128. * @property { boolean } ie8 检测浏览器是否是IE8浏览器
  129. * @warning 如果浏览器不是IE, 则该值为undefined
  130. * @example
  131. * ```javascript
  132. * if ( UE.browser.ie8 ) {
  133. * console.log( '当前浏览器是IE8浏览器' );
  134. * }
  135. * ```
  136. */
  137. browser.ie8 = !!document.documentMode
  138. /**
  139. * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
  140. * @warning 如果浏览器不是IE, 则该值为undefined
  141. * @example
  142. * ```javascript
  143. * if ( UE.browser.ie8Compat ) {
  144. * console.log( '当前浏览器运行在IE8兼容模式下' );
  145. * }
  146. * ```
  147. */
  148. browser.ie8Compat = document.documentMode == 8
  149. /**
  150. * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
  151. * @warning 如果浏览器不是IE, 则该值为undefined
  152. * @example
  153. * ```javascript
  154. * if ( UE.browser.ie7Compat ) {
  155. * console.log( '当前浏览器运行在IE7兼容模式下' );
  156. * }
  157. * ```
  158. */
  159. browser.ie7Compat =
  160. (version == 7 && !document.documentMode) || document.documentMode == 7
  161. /**
  162. * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
  163. * @warning 如果浏览器不是IE, 则该值为undefined
  164. * @example
  165. * ```javascript
  166. * if ( UE.browser.ie6Compat ) {
  167. * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
  168. * }
  169. * ```
  170. */
  171. browser.ie6Compat = version < 7 || browser.quirks
  172. browser.ie9above = version > 8
  173. browser.ie9below = version < 9
  174. browser.ie11above = version > 10
  175. browser.ie11below = version < 11
  176. }
  177. // Gecko.
  178. if (browser.gecko) {
  179. var geckoRelease = agent.match(/rv:([\d\.]+)/)
  180. if (geckoRelease) {
  181. geckoRelease = geckoRelease[1].split('.')
  182. version =
  183. geckoRelease[0] * 10000 +
  184. (geckoRelease[1] || 0) * 100 +
  185. (geckoRelease[2] || 0) * 1
  186. }
  187. }
  188. /**
  189. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  190. * @warning 如果浏览器不是chrome, 则该值为undefined
  191. * @example
  192. * ```javascript
  193. * if ( UE.browser.chrome ) {
  194. * console.log( '当前浏览器是Chrome' );
  195. * }
  196. * ```
  197. */
  198. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  199. browser.chrome = +RegExp['\x241']
  200. }
  201. /**
  202. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  203. * @warning 如果浏览器不是safari, 则该值为undefined
  204. * @example
  205. * ```javascript
  206. * if ( UE.browser.safari ) {
  207. * console.log( '当前浏览器是Safari' );
  208. * }
  209. * ```
  210. */
  211. if (
  212. /(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) &&
  213. !/chrome/i.test(agent)
  214. ) {
  215. browser.safari = +(RegExp['\x241'] || RegExp['\x242'])
  216. }
  217. // Opera 9.50+
  218. if (browser.opera) version = parseFloat(opera.version())
  219. // WebKit 522+ (Safari 3+)
  220. if (browser.webkit)
  221. version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1])
  222. /**
  223. * @property { Number } version 检测当前浏览器版本号
  224. * @remind
  225. * <ul>
  226. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  227. * <li>gecko系列会返回10900,158900等</li>
  228. * <li>webkit系列会返回其build号 (如 522等)</li>
  229. * </ul>
  230. * @example
  231. * ```javascript
  232. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  233. * ```
  234. */
  235. browser.version = version
  236. /**
  237. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  238. * @example
  239. * ```javascript
  240. * if ( UE.browser.isCompatible ) {
  241. * console.log( '浏览器与UEditor能够良好兼容' );
  242. * }
  243. * ```
  244. */
  245. browser.isCompatible =
  246. !browser.mobile &&
  247. ((browser.ie && version >= 6) ||
  248. (browser.gecko && version >= 10801) ||
  249. (browser.opera && version >= 9.5) ||
  250. (browser.air && version >= 1) ||
  251. (browser.webkit && version >= 522) ||
  252. false)
  253. return browser
  254. })())
  255. //快捷方式
  256. var ie = browser.ie,
  257. webkit = browser.webkit,
  258. gecko = browser.gecko,
  259. opera = browser.opera
  260. // core/utils.js
  261. /**
  262. * 工具函数包
  263. * @file
  264. * @module UE.utils
  265. * @since 1.2.6.1
  266. */
  267. /**
  268. * UEditor封装使用的静态工具函数
  269. * @module UE.utils
  270. * @unfile
  271. */
  272. var utils = (UE.utils = {
  273. /**
  274. * 用给定的迭代器遍历对象
  275. * @method each
  276. * @param { Object } obj 需要遍历的对象
  277. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  278. * @example
  279. * ```javascript
  280. * var demoObj = {
  281. * key1: 1,
  282. * key2: 2
  283. * };
  284. *
  285. * //output: key1: 1, key2: 2
  286. * UE.utils.each( demoObj, funciton ( value, key ) {
  287. *
  288. * console.log( key + ":" + value );
  289. *
  290. * } );
  291. * ```
  292. */
  293. /**
  294. * 用给定的迭代器遍历数组或类数组对象
  295. * @method each
  296. * @param { Array } array 需要遍历的数组或者类数组
  297. * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
  298. * @example
  299. * ```javascript
  300. * var divs = document.getElmentByTagNames( "div" );
  301. *
  302. * //output: 0: DIV, 1: DIV ...
  303. * UE.utils.each( divs, funciton ( value, key ) {
  304. *
  305. * console.log( key + ":" + value.tagName );
  306. *
  307. * } );
  308. * ```
  309. */
  310. each: function (obj, iterator, context) {
  311. if (obj == null) return
  312. if (obj.length === +obj.length) {
  313. for (var i = 0, l = obj.length; i < l; i++) {
  314. if (iterator.call(context, obj[i], i, obj) === false) return false
  315. }
  316. } else {
  317. for (var key in obj) {
  318. if (obj.hasOwnProperty(key)) {
  319. if (iterator.call(context, obj[key], key, obj) === false)
  320. return false
  321. }
  322. }
  323. }
  324. },
  325. /**
  326. * 以给定对象作为原型创建一个新对象
  327. * @method makeInstance
  328. * @param { Object } protoObject 该对象将作为新创建对象的原型
  329. * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
  330. * @example
  331. * ```javascript
  332. *
  333. * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
  334. *
  335. * var newObject = UE.utils.makeInstance( protoObject );
  336. * //output: Hello UEditor!
  337. * newObject.sayHello();
  338. * ```
  339. */
  340. makeInstance: function (obj) {
  341. var noop = new Function()
  342. noop.prototype = obj
  343. obj = new noop()
  344. noop.prototype = null
  345. return obj
  346. },
  347. /**
  348. * 将source对象中的属性扩展到target对象上
  349. * @method extend
  350. * @remind 该方法将强制把source对象上的属性复制到target对象上
  351. * @see UE.utils.extend(Object,Object,Boolean)
  352. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  353. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  354. * @return { Object } 返回target对象
  355. * @example
  356. * ```javascript
  357. *
  358. * var target = { name: 'target', sex: 1 },
  359. * source = { name: 'source', age: 17 };
  360. *
  361. * UE.utils.extend( target, source );
  362. *
  363. * //output: { name: 'source', sex: 1, age: 17 }
  364. * console.log( target );
  365. *
  366. * ```
  367. */
  368. /**
  369. * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
  370. * 源对象属性名相同的属性值。
  371. * @method extend
  372. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  373. * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
  374. * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
  375. * @return { Object } 返回target对象
  376. * @example
  377. * ```javascript
  378. *
  379. * var target = { name: 'target', sex: 1 },
  380. * source = { name: 'source', age: 17 };
  381. *
  382. * UE.utils.extend( target, source, true );
  383. *
  384. * //output: { name: 'target', sex: 1, age: 17 }
  385. * console.log( target );
  386. *
  387. * ```
  388. */
  389. extend: function (t, s, b) {
  390. if (s) {
  391. for (var k in s) {
  392. if (!b || !t.hasOwnProperty(k)) {
  393. t[k] = s[k]
  394. }
  395. }
  396. }
  397. return t
  398. },
  399. /**
  400. * 将给定的多个对象的属性复制到目标对象target上
  401. * @method extend2
  402. * @remind 该方法将强制把源对象上的属性复制到target对象上
  403. * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
  404. * 将会覆盖掉之前的值。
  405. * @param { Object } target 目标对象, 新的属性将附加到该对象上
  406. * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
  407. * @return { Object } 返回target对象
  408. * @example
  409. * ```javascript
  410. *
  411. * var target = {},
  412. * source1 = { name: 'source', age: 17 },
  413. * source2 = { title: 'dev' };
  414. *
  415. * UE.utils.extend2( target, source1, source2 );
  416. *
  417. * //output: { name: 'source', age: 17, title: 'dev' }
  418. * console.log( target );
  419. *
  420. * ```
  421. */
  422. extend2: function (t) {
  423. var a = arguments
  424. for (var i = 1; i < a.length; i++) {
  425. var x = a[i]
  426. for (var k in x) {
  427. if (!t.hasOwnProperty(k)) {
  428. t[k] = x[k]
  429. }
  430. }
  431. }
  432. return t
  433. },
  434. /**
  435. * 模拟继承机制, 使得subClass继承自superClass
  436. * @method inherits
  437. * @param { Object } subClass 子类对象
  438. * @param { Object } superClass 超类对象
  439. * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
  440. * @return { Object } 继承superClass后的子类对象
  441. * @example
  442. * ```javascript
  443. * function SuperClass(){
  444. * this.name = "小李";
  445. * }
  446. *
  447. * SuperClass.prototype = {
  448. * hello:function(str){
  449. * console.log(this.name + str);
  450. * }
  451. * }
  452. *
  453. * function SubClass(){
  454. * this.name = "小张";
  455. * }
  456. *
  457. * UE.utils.inherits(SubClass,SuperClass);
  458. *
  459. * var sub = new SubClass();
  460. * //output: '小张早上好!
  461. * sub.hello("早上好!");
  462. * ```
  463. */
  464. inherits: function (subClass, superClass) {
  465. var oldP = subClass.prototype,
  466. newP = utils.makeInstance(superClass.prototype)
  467. utils.extend(newP, oldP, true)
  468. subClass.prototype = newP
  469. return (newP.constructor = subClass)
  470. },
  471. /**
  472. * 用指定的context对象作为函数fn的上下文
  473. * @method bind
  474. * @param { Function } fn 需要绑定上下文的函数对象
  475. * @param { Object } content 函数fn新的上下文对象
  476. * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
  477. * @example
  478. * ```javascript
  479. *
  480. * var name = 'window',
  481. * newTest = null;
  482. *
  483. * function test () {
  484. * console.log( this.name );
  485. * }
  486. *
  487. * newTest = UE.utils.bind( test, { name: 'object' } );
  488. *
  489. * //output: object
  490. * newTest();
  491. *
  492. * //output: window
  493. * test();
  494. *
  495. * ```
  496. */
  497. bind: function (fn, context) {
  498. return function () {
  499. return fn.apply(context, arguments)
  500. }
  501. },
  502. /**
  503. * 创建延迟指定时间后执行的函数fn
  504. * @method defer
  505. * @param { Function } fn 需要延迟执行的函数对象
  506. * @param { int } delay 延迟的时间, 单位是毫秒
  507. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  508. * 而不能保证刚好到达延迟时间时执行。
  509. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  510. * @example
  511. * ```javascript
  512. * var start = 0;
  513. *
  514. * function test(){
  515. * console.log( new Date() - start );
  516. * }
  517. *
  518. * var testDefer = UE.utils.defer( test, 1000 );
  519. * //
  520. * start = new Date();
  521. * //output: (大约在1000毫秒之后输出) 1000
  522. * testDefer();
  523. * ```
  524. */
  525. /**
  526. * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
  527. * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
  528. * @method defer
  529. * @param { Function } fn 需要延迟执行的函数对象
  530. * @param { int } delay 延迟的时间, 单位是毫秒
  531. * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
  532. * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
  533. * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
  534. * 而不能保证刚好到达延迟时间时执行。
  535. * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
  536. * @example
  537. * ```javascript
  538. *
  539. * function test(){
  540. * console.log(1);
  541. * }
  542. *
  543. * var testDefer = UE.utils.defer( test, 1000, true );
  544. *
  545. * //output: (两次调用仅有一次输出) 1
  546. * testDefer();
  547. * testDefer();
  548. * ```
  549. */
  550. defer: function (fn, delay, exclusion) {
  551. var timerID
  552. return function () {
  553. if (exclusion) {
  554. clearTimeout(timerID)
  555. }
  556. timerID = setTimeout(fn, delay)
  557. }
  558. },
  559. /**
  560. * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
  561. * @method indexOf
  562. * @remind 该方法的匹配过程使用的是恒等“===”
  563. * @param { Array } array 需要查找的数组对象
  564. * @param { * } item 需要在目标数组中查找的值
  565. * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
  566. * @example
  567. * ```javascript
  568. * var item = 1,
  569. * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
  570. *
  571. * //output: 4
  572. * console.log( UE.utils.indexOf( arr, item ) );
  573. * ```
  574. */
  575. /**
  576. * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
  577. * @method indexOf
  578. * @remind 该方法的匹配过程使用的是恒等“===”
  579. * @param { Array } array 需要查找的数组对象
  580. * @param { * } item 需要在目标数组中查找的值
  581. * @param { int } start 搜索的起始位置
  582. * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
  583. * @example
  584. * ```javascript
  585. * var item = 1,
  586. * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
  587. *
  588. * //output: 9
  589. * console.log( UE.utils.indexOf( arr, item, 5 ) );
  590. * ```
  591. */
  592. indexOf: function (array, item, start) {
  593. var index = -1
  594. start = this.isNumber(start) ? start : 0
  595. this.each(array, function (v, i) {
  596. if (i >= start && v === item) {
  597. index = i
  598. return false
  599. }
  600. })
  601. return index
  602. },
  603. /**
  604. * 移除数组array中所有的元素item
  605. * @method removeItem
  606. * @param { Array } array 要移除元素的目标数组
  607. * @param { * } item 将要被移除的元素
  608. * @remind 该方法的匹配过程使用的是恒等“===”
  609. * @example
  610. * ```javascript
  611. * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
  612. *
  613. * UE.utils.removeItem( arr, 4 );
  614. * //output: [ 5, 7, 1, 3, 6 ]
  615. * console.log( arr );
  616. *
  617. * ```
  618. */
  619. removeItem: function (array, item) {
  620. for (var i = 0, l = array.length; i < l; i++) {
  621. if (array[i] === item) {
  622. array.splice(i, 1)
  623. i--
  624. }
  625. }
  626. },
  627. /**
  628. * 删除字符串str的首尾空格
  629. * @method trim
  630. * @param { String } str 需要删除首尾空格的字符串
  631. * @return { String } 删除了首尾的空格后的字符串
  632. * @example
  633. * ```javascript
  634. *
  635. * var str = " UEdtior ";
  636. *
  637. * //output: 9
  638. * console.log( str.length );
  639. *
  640. * //output: 7
  641. * console.log( UE.utils.trim( " UEdtior " ).length );
  642. *
  643. * //output: 9
  644. * console.log( str.length );
  645. *
  646. * ```
  647. */
  648. trim: function (str) {
  649. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '')
  650. },
  651. /**
  652. * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  653. * @method listToMap
  654. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  655. * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
  656. * @return { Object } 转化之后的hash对象
  657. * @example
  658. * ```javascript
  659. *
  660. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  661. * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
  662. *
  663. * ```
  664. */
  665. /**
  666. * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
  667. * @method listToMap
  668. * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
  669. * @param { Array } arr 字符串数组
  670. * @return { Object } 转化之后的hash对象
  671. * @example
  672. * ```javascript
  673. *
  674. * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
  675. * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
  676. *
  677. * ```
  678. */
  679. listToMap: function (list) {
  680. if (!list) return {}
  681. list = utils.isArray(list) ? list : list.split(',')
  682. for (var i = 0, ci, obj = {}; (ci = list[i++]); ) {
  683. obj[ci.toUpperCase()] = obj[ci] = 1
  684. }
  685. return obj
  686. },
  687. /**
  688. * 将str中的html符号转义,将转义“',&,<,",>”五个字符
  689. * @method unhtml
  690. * @param { String } str 需要转义的字符串
  691. * @return { String } 转义后的字符串
  692. * @example
  693. * ```javascript
  694. * var html = '<body>&</body>';
  695. *
  696. * //output: &lt;body&gt;&amp;&lt;/body&gt;
  697. * console.log( UE.utils.unhtml( html ) );
  698. *
  699. * ```
  700. */
  701. unhtml: function (str, reg) {
  702. return str
  703. ? str.replace(
  704. reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g,
  705. function (a, b) {
  706. if (b) {
  707. return a
  708. } else {
  709. return {
  710. '<': '&lt;',
  711. '&': '&amp;',
  712. '"': '&quot;',
  713. '>': '&gt;',
  714. "'": '&#39;'
  715. }[a]
  716. }
  717. }
  718. )
  719. : ''
  720. },
  721. /**
  722. * 将url中的html字符转义, 仅转义 ', ", <, > 四个字符
  723. * @param { String } str 需要转义的字符串
  724. * @param { RegExp } reg 自定义的正则
  725. * @return { String } 转义后的字符串
  726. */
  727. unhtmlForUrl: function (str, reg) {
  728. return str
  729. ? str.replace(reg || /[<">']/g, function (a) {
  730. return {
  731. '<': '&lt;',
  732. '&': '&amp;',
  733. '"': '&quot;',
  734. '>': '&gt;',
  735. "'": '&#39;'
  736. }[a]
  737. })
  738. : ''
  739. },
  740. /**
  741. * 将str中的转义字符还原成html字符
  742. * @see UE.utils.unhtml(String);
  743. * @method html
  744. * @param { String } str 需要逆转义的字符串
  745. * @return { String } 逆转义后的字符串
  746. * @example
  747. * ```javascript
  748. *
  749. * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
  750. *
  751. * //output: <body>&</body>
  752. * console.log( UE.utils.html( str ) );
  753. *
  754. * ```
  755. */
  756. html: function (str) {
  757. return str
  758. ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
  759. return {
  760. '&lt;': '<',
  761. '&amp;': '&',
  762. '&quot;': '"',
  763. '&gt;': '>',
  764. '&#39;': "'",
  765. '&nbsp;': ' '
  766. }[m]
  767. })
  768. : ''
  769. },
  770. /**
  771. * 将css样式转换为驼峰的形式
  772. * @method cssStyleToDomStyle
  773. * @param { String } cssName 需要转换的css样式名
  774. * @return { String } 转换成驼峰形式后的css样式名
  775. * @example
  776. * ```javascript
  777. *
  778. * var str = 'border-top';
  779. *
  780. * //output: borderTop
  781. * console.log( UE.utils.cssStyleToDomStyle( str ) );
  782. *
  783. * ```
  784. */
  785. cssStyleToDomStyle: (function () {
  786. var test = document.createElement('div').style,
  787. cache = {
  788. float:
  789. test.cssFloat != undefined
  790. ? 'cssFloat'
  791. : test.styleFloat != undefined
  792. ? 'styleFloat'
  793. : 'float'
  794. }
  795. return function (cssName) {
  796. return (
  797. cache[cssName] ||
  798. (cache[cssName] = cssName
  799. .toLowerCase()
  800. .replace(/-./g, function (match) {
  801. return match.charAt(1).toUpperCase()
  802. }))
  803. )
  804. }
  805. })(),
  806. /**
  807. * 动态加载文件到doc中
  808. * @method loadFile
  809. * @param { DomDocument } document 需要加载资源文件的文档对象
  810. * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
  811. * @example
  812. * ```javascript
  813. *
  814. * UE.utils.loadFile( document, {
  815. * src:"test.js",
  816. * tag:"script",
  817. * type:"text/javascript",
  818. * defer:"defer"
  819. * } );
  820. *
  821. * ```
  822. */
  823. /**
  824. * 动态加载文件到doc中,加载成功后执行的回调函数fn
  825. * @method loadFile
  826. * @param { DomDocument } document 需要加载资源文件的文档对象
  827. * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
  828. * @param { Function } fn 资源文件加载成功之后执行的回调
  829. * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
  830. * 在此之后的所有同一URL的请求, 将会直接触发回调。
  831. * @example
  832. * ```javascript
  833. *
  834. * UE.utils.loadFile( document, {
  835. * src:"test.js",
  836. * tag:"script",
  837. * type:"text/javascript",
  838. * defer:"defer"
  839. * }, function () {
  840. * console.log('加载成功');
  841. * } );
  842. *
  843. * ```
  844. */
  845. loadFile: (function () {
  846. var tmpList = []
  847. function getItem(doc, obj) {
  848. try {
  849. for (var i = 0, ci; (ci = tmpList[i++]); ) {
  850. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  851. return ci
  852. }
  853. }
  854. } catch (e) {
  855. return null
  856. }
  857. }
  858. return function (doc, obj, fn) {
  859. var item = getItem(doc, obj)
  860. if (item) {
  861. if (item.ready) {
  862. fn && fn()
  863. } else {
  864. item.funs.push(fn)
  865. }
  866. return
  867. }
  868. tmpList.push({
  869. doc: doc,
  870. url: obj.src || obj.href,
  871. funs: [fn]
  872. })
  873. if (!doc.body) {
  874. var html = []
  875. for (var p in obj) {
  876. if (p == 'tag') continue
  877. html.push(p + '="' + obj[p] + '"')
  878. }
  879. doc.write(
  880. '<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>'
  881. )
  882. return
  883. }
  884. if (obj.id && doc.getElementById(obj.id)) {
  885. return
  886. }
  887. var element = doc.createElement(obj.tag)
  888. delete obj.tag
  889. for (var p in obj) {
  890. element.setAttribute(p, obj[p])
  891. }
  892. element.onload = element.onreadystatechange = function () {
  893. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  894. item = getItem(doc, obj)
  895. if (item.funs.length > 0) {
  896. item.ready = 1
  897. for (var fi; (fi = item.funs.pop()); ) {
  898. fi()
  899. }
  900. }
  901. element.onload = element.onreadystatechange = null
  902. }
  903. }
  904. element.onerror = function () {
  905. throw Error(
  906. 'The load ' +
  907. (obj.href || obj.src) +
  908. ' fails,check the url settings of file ueditor.config.js '
  909. )
  910. }
  911. doc.getElementsByTagName('head')[0].appendChild(element)
  912. }
  913. })(),
  914. /**
  915. * 判断obj对象是否为空
  916. * @method isEmptyObject
  917. * @param { * } obj 需要判断的对象
  918. * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
  919. * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
  920. * @return { Boolean } 对象是否为空
  921. * @example
  922. * ```javascript
  923. *
  924. * //output: true
  925. * console.log( UE.utils.isEmptyObject( {} ) );
  926. *
  927. * //output: true
  928. * console.log( UE.utils.isEmptyObject( [] ) );
  929. *
  930. * //output: true
  931. * console.log( UE.utils.isEmptyObject( "" ) );
  932. *
  933. * //output: false
  934. * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
  935. *
  936. * //output: false
  937. * console.log( UE.utils.isEmptyObject( [1] ) );
  938. *
  939. * //output: false
  940. * console.log( UE.utils.isEmptyObject( "1" ) );
  941. *
  942. * ```
  943. */
  944. isEmptyObject: function (obj) {
  945. if (obj == null) return true
  946. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0
  947. for (var key in obj) if (obj.hasOwnProperty(key)) return false
  948. return true
  949. },
  950. /**
  951. * 把rgb格式的颜色值转换成16进制格式
  952. * @method fixColor
  953. * @param { String } rgb格式的颜色值
  954. * @param { String }
  955. * @example
  956. * rgb(255,255,255) => "#ffffff"
  957. */
  958. fixColor: function (name, value) {
  959. if (/color/i.test(name) && /rgba?/.test(value)) {
  960. var array = value.split(',')
  961. if (array.length > 3) return ''
  962. value = '#'
  963. for (var i = 0, color; (color = array[i++]); ) {
  964. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16)
  965. value += color.length == 1 ? '0' + color : color
  966. }
  967. value = value.toUpperCase()
  968. }
  969. return value
  970. },
  971. /**
  972. * 只针对border,padding,margin做了处理,因为性能问题
  973. * @public
  974. * @function
  975. * @param {String} val style字符串
  976. */
  977. optCss: function (val) {
  978. var padding, margin, border
  979. val = val.replace(
  980. /(padding|margin|border)\-([^:]+):([^;]+);?/gi,
  981. function (str, key, name, val) {
  982. if (val.split(' ').length == 1) {
  983. switch (key) {
  984. case 'padding':
  985. !padding && (padding = {})
  986. padding[name] = val
  987. return ''
  988. case 'margin':
  989. !margin && (margin = {})
  990. margin[name] = val
  991. return ''
  992. case 'border':
  993. return val == 'initial' ? '' : str
  994. }
  995. }
  996. return str
  997. }
  998. )
  999. function opt(obj, name) {
  1000. if (!obj) {
  1001. return ''
  1002. }
  1003. var t = obj.top,
  1004. b = obj.bottom,
  1005. l = obj.left,
  1006. r = obj.right,
  1007. val = ''
  1008. if (!t || !l || !b || !r) {
  1009. for (var p in obj) {
  1010. val += ';' + name + '-' + p + ':' + obj[p] + ';'
  1011. }
  1012. } else {
  1013. val +=
  1014. ';' +
  1015. name +
  1016. ':' +
  1017. (t == b && b == l && l == r
  1018. ? t
  1019. : t == b && l == r
  1020. ? t + ' ' + l
  1021. : l == r
  1022. ? t + ' ' + l + ' ' + b
  1023. : t + ' ' + r + ' ' + b + ' ' + l) +
  1024. ';'
  1025. }
  1026. return val
  1027. }
  1028. val += opt(padding, 'padding') + opt(margin, 'margin')
  1029. return val
  1030. .replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '')
  1031. .replace(/;([ \n\r\t]+)|\1;/g, ';')
  1032. .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
  1033. return b ? b + ';;' : ';'
  1034. })
  1035. },
  1036. /**
  1037. * 克隆对象
  1038. * @method clone
  1039. * @param { Object } source 源对象
  1040. * @return { Object } source的一个副本
  1041. */
  1042. /**
  1043. * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
  1044. * @method clone
  1045. * @param { Object } source 源对象
  1046. * @param { Object } target 目标对象
  1047. * @return { Object } 附加了source对象所有属性的target对象
  1048. */
  1049. clone: function (source, target) {
  1050. var tmp
  1051. target = target || {}
  1052. for (var i in source) {
  1053. if (source.hasOwnProperty(i)) {
  1054. tmp = source[i]
  1055. if (typeof tmp == 'object') {
  1056. target[i] = utils.isArray(tmp) ? [] : {}
  1057. utils.clone(source[i], target[i])
  1058. } else {
  1059. target[i] = tmp
  1060. }
  1061. }
  1062. }
  1063. return target
  1064. },
  1065. /**
  1066. * 把cm/pt为单位的值转换为px为单位的值
  1067. * @method transUnitToPx
  1068. * @param { String } 待转换的带单位的字符串
  1069. * @return { String } 转换为px为计量单位的值的字符串
  1070. * @example
  1071. * ```javascript
  1072. *
  1073. * //output: 500px
  1074. * console.log( UE.utils.transUnitToPx( '20cm' ) );
  1075. *
  1076. * //output: 27px
  1077. * console.log( UE.utils.transUnitToPx( '20pt' ) );
  1078. *
  1079. * ```
  1080. */
  1081. transUnitToPx: function (val) {
  1082. if (!/(pt|cm)/.test(val)) {
  1083. return val
  1084. }
  1085. var unit
  1086. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  1087. val = v
  1088. unit = u
  1089. })
  1090. switch (unit) {
  1091. case 'cm':
  1092. val = parseFloat(val) * 25
  1093. break
  1094. case 'pt':
  1095. val = Math.round((parseFloat(val) * 96) / 72)
  1096. }
  1097. return val + (val ? 'px' : '')
  1098. },
  1099. /**
  1100. * 在dom树ready之后执行给定的回调函数
  1101. * @method domReady
  1102. * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
  1103. * @param { Function } fn dom树ready之后的回调函数
  1104. * @example
  1105. * ```javascript
  1106. *
  1107. * UE.utils.domReady( function () {
  1108. *
  1109. * console.log('123');
  1110. *
  1111. * } );
  1112. *
  1113. * ```
  1114. */
  1115. domReady: (function () {
  1116. var fnArr = []
  1117. function doReady(doc) {
  1118. //确保onready只执行一次
  1119. doc.isReady = true
  1120. for (var ci; (ci = fnArr.pop()); ci()) {}
  1121. }
  1122. return function (onready, win) {
  1123. win = win || window
  1124. var doc = win.document
  1125. onready && fnArr.push(onready)
  1126. if (doc.readyState === 'complete') {
  1127. doReady(doc)
  1128. } else {
  1129. doc.isReady && doReady(doc)
  1130. if (browser.ie && browser.version != 11) {
  1131. ;(function () {
  1132. if (doc.isReady) return
  1133. try {
  1134. doc.documentElement.doScroll('left')
  1135. } catch (error) {
  1136. setTimeout(arguments.callee, 0)
  1137. return
  1138. }
  1139. doReady(doc)
  1140. })()
  1141. win.attachEvent('onload', function () {
  1142. doReady(doc)
  1143. })
  1144. } else {
  1145. doc.addEventListener(
  1146. 'DOMContentLoaded',
  1147. function () {
  1148. doc.removeEventListener(
  1149. 'DOMContentLoaded',
  1150. arguments.callee,
  1151. false
  1152. )
  1153. doReady(doc)
  1154. },
  1155. false
  1156. )
  1157. win.addEventListener(
  1158. 'load',
  1159. function () {
  1160. doReady(doc)
  1161. },
  1162. false
  1163. )
  1164. }
  1165. }
  1166. }
  1167. })(),
  1168. /**
  1169. * 动态添加css样式
  1170. * @method cssRule
  1171. * @param { String } 节点名称
  1172. * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  1173. * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  1174. * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  1175. * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
  1176. * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  1177. */
  1178. cssRule:
  1179. browser.ie && browser.version != 11
  1180. ? function (key, style, doc) {
  1181. var indexList, index
  1182. if (
  1183. style === undefined ||
  1184. (style && style.nodeType && style.nodeType == 9)
  1185. ) {
  1186. //获取样式
  1187. doc =
  1188. style && style.nodeType && style.nodeType == 9
  1189. ? style
  1190. : doc || document
  1191. indexList = doc.indexList || (doc.indexList = {})
  1192. index = indexList[key]
  1193. if (index !== undefined) {
  1194. return doc.styleSheets[index].cssText
  1195. }
  1196. return undefined
  1197. }
  1198. doc = doc || document
  1199. indexList = doc.indexList || (doc.indexList = {})
  1200. index = indexList[key]
  1201. //清除样式
  1202. if (style === '') {
  1203. if (index !== undefined) {
  1204. doc.styleSheets[index].cssText = ''
  1205. delete indexList[key]
  1206. return true
  1207. }
  1208. return false
  1209. }
  1210. //添加样式
  1211. if (index !== undefined) {
  1212. sheetStyle = doc.styleSheets[index]
  1213. } else {
  1214. sheetStyle = doc.createStyleSheet(
  1215. '',
  1216. (index = doc.styleSheets.length)
  1217. )
  1218. indexList[key] = index
  1219. }
  1220. sheetStyle.cssText = style
  1221. }
  1222. : function (key, style, doc) {
  1223. var head, node
  1224. if (
  1225. style === undefined ||
  1226. (style && style.nodeType && style.nodeType == 9)
  1227. ) {
  1228. //获取样式
  1229. doc =
  1230. style && style.nodeType && style.nodeType == 9
  1231. ? style
  1232. : doc || document
  1233. node = doc.getElementById(key)
  1234. return node ? node.innerHTML : undefined
  1235. }
  1236. doc = doc || document
  1237. node = doc.getElementById(key)
  1238. //清除样式
  1239. if (style === '') {
  1240. if (node) {
  1241. node.parentNode.removeChild(node)
  1242. return true
  1243. }
  1244. return false
  1245. }
  1246. //添加样式
  1247. if (node) {
  1248. node.innerHTML = style
  1249. } else {
  1250. node = doc.createElement('style')
  1251. node.id = key
  1252. node.innerHTML = style
  1253. doc.getElementsByTagName('head')[0].appendChild(node)
  1254. }
  1255. },
  1256. sort: function (array, compareFn) {
  1257. compareFn =
  1258. compareFn ||
  1259. function (item1, item2) {
  1260. return item1.localeCompare(item2)
  1261. }
  1262. for (var i = 0, len = array.length; i < len; i++) {
  1263. for (var j = i, length = array.length; j < length; j++) {
  1264. if (compareFn(array[i], array[j]) > 0) {
  1265. var t = array[i]
  1266. array[i] = array[j]
  1267. array[j] = t
  1268. }
  1269. }
  1270. }
  1271. return array
  1272. },
  1273. serializeParam: function (json) {
  1274. var strArr = []
  1275. for (var i in json) {
  1276. //忽略默认的几个参数
  1277. if (i == 'method' || i == 'timeout' || i == 'async') continue
  1278. //传递过来的对象和函数不在提交之列
  1279. if (
  1280. !(
  1281. (typeof json[i]).toLowerCase() == 'function' ||
  1282. (typeof json[i]).toLowerCase() == 'object'
  1283. )
  1284. ) {
  1285. strArr.push(encodeURIComponent(i) + '=' + encodeURIComponent(json[i]))
  1286. } else if (utils.isArray(json[i])) {
  1287. //支持传数组内容
  1288. for (var j = 0; j < json[i].length; j++) {
  1289. strArr.push(
  1290. encodeURIComponent(i) + '[]=' + encodeURIComponent(json[i][j])
  1291. )
  1292. }
  1293. }
  1294. }
  1295. return strArr.join('&')
  1296. },
  1297. formatUrl: function (url) {
  1298. var u = url.replace(/&&/g, '&')
  1299. u = u.replace(/\?&/g, '?')
  1300. u = u.replace(/&$/g, '')
  1301. u = u.replace(/&#/g, '#')
  1302. u = u.replace(/&+/g, '&')
  1303. return u
  1304. },
  1305. isCrossDomainUrl: function (url) {
  1306. var a = document.createElement('a')
  1307. a.href = url
  1308. if (browser.ie) {
  1309. a.href = a.href
  1310. }
  1311. return !(
  1312. a.protocol == location.protocol &&
  1313. a.hostname == location.hostname &&
  1314. (a.port == location.port ||
  1315. (a.port == '80' && location.port == '') ||
  1316. (a.port == '' && location.port == '80'))
  1317. )
  1318. },
  1319. clearEmptyAttrs: function (obj) {
  1320. for (var p in obj) {
  1321. if (obj[p] === '') {
  1322. delete obj[p]
  1323. }
  1324. }
  1325. return obj
  1326. },
  1327. str2json: function (s) {
  1328. if (!utils.isString(s)) return null
  1329. if (window.JSON) {
  1330. return JSON.parse(s)
  1331. } else {
  1332. return new Function('return ' + utils.trim(s || ''))()
  1333. }
  1334. },
  1335. json2str: (function () {
  1336. if (window.JSON) {
  1337. return JSON.stringify
  1338. } else {
  1339. var escapeMap = {
  1340. '\b': '\\b',
  1341. '\t': '\\t',
  1342. '\n': '\\n',
  1343. '\f': '\\f',
  1344. '\r': '\\r',
  1345. '"': '\\"',
  1346. '\\': '\\\\'
  1347. }
  1348. function encodeString(source) {
  1349. if (/["\\\x00-\x1f]/.test(source)) {
  1350. source = source.replace(/["\\\x00-\x1f]/g, function (match) {
  1351. var c = escapeMap[match]
  1352. if (c) {
  1353. return c
  1354. }
  1355. c = match.charCodeAt()
  1356. return (
  1357. '\\u00' +
  1358. Math.floor(c / 16).toString(16) +
  1359. (c % 16).toString(16)
  1360. )
  1361. })
  1362. }
  1363. return '"' + source + '"'
  1364. }
  1365. function encodeArray(source) {
  1366. var result = ['['],
  1367. l = source.length,
  1368. preComma,
  1369. i,
  1370. item
  1371. for (i = 0; i < l; i++) {
  1372. item = source[i]
  1373. switch (typeof item) {
  1374. case 'undefined':
  1375. case 'function':
  1376. case 'unknown':
  1377. break
  1378. default:
  1379. if (preComma) {
  1380. result.push(',')
  1381. }
  1382. result.push(utils.json2str(item))
  1383. preComma = 1
  1384. }
  1385. }
  1386. result.push(']')
  1387. return result.join('')
  1388. }
  1389. function pad(source) {
  1390. return source < 10 ? '0' + source : source
  1391. }
  1392. function encodeDate(source) {
  1393. return (
  1394. '"' +
  1395. source.getFullYear() +
  1396. '-' +
  1397. pad(source.getMonth() + 1) +
  1398. '-' +
  1399. pad(source.getDate()) +
  1400. 'T' +
  1401. pad(source.getHours()) +
  1402. ':' +
  1403. pad(source.getMinutes()) +
  1404. ':' +
  1405. pad(source.getSeconds()) +
  1406. '"'
  1407. )
  1408. }
  1409. return function (value) {
  1410. switch (typeof value) {
  1411. case 'undefined':
  1412. return 'undefined'
  1413. case 'number':
  1414. return isFinite(value) ? String(value) : 'null'
  1415. case 'string':
  1416. return encodeString(value)
  1417. case 'boolean':
  1418. return String(value)
  1419. default:
  1420. if (value === null) {
  1421. return 'null'
  1422. } else if (utils.isArray(value)) {
  1423. return encodeArray(value)
  1424. } else if (utils.isDate(value)) {
  1425. return encodeDate(value)
  1426. } else {
  1427. var result = ['{'],
  1428. encode = utils.json2str,
  1429. preComma,
  1430. item
  1431. for (var key in value) {
  1432. if (Object.prototype.hasOwnProperty.call(value, key)) {
  1433. item = value[key]
  1434. switch (typeof item) {
  1435. case 'undefined':
  1436. case 'unknown':
  1437. case 'function':
  1438. break
  1439. default:
  1440. if (preComma) {
  1441. result.push(',')
  1442. }
  1443. preComma = 1
  1444. result.push(encode(key) + ':' + encode(item))
  1445. }
  1446. }
  1447. }
  1448. result.push('}')
  1449. return result.join('')
  1450. }
  1451. }
  1452. }
  1453. }
  1454. })()
  1455. })
  1456. /**
  1457. * 判断给定的对象是否是字符串
  1458. * @method isString
  1459. * @param { * } object 需要判断的对象
  1460. * @return { Boolean } 给定的对象是否是字符串
  1461. */
  1462. /**
  1463. * 判断给定的对象是否是数组
  1464. * @method isArray
  1465. * @param { * } object 需要判断的对象
  1466. * @return { Boolean } 给定的对象是否是数组
  1467. */
  1468. /**
  1469. * 判断给定的对象是否是一个Function
  1470. * @method isFunction
  1471. * @param { * } object 需要判断的对象
  1472. * @return { Boolean } 给定的对象是否是Function
  1473. */
  1474. /**
  1475. * 判断给定的对象是否是Number
  1476. * @method isNumber
  1477. * @param { * } object 需要判断的对象
  1478. * @return { Boolean } 给定的对象是否是Number
  1479. */
  1480. /**
  1481. * 判断给定的对象是否是一个正则表达式
  1482. * @method isRegExp
  1483. * @param { * } object 需要判断的对象
  1484. * @return { Boolean } 给定的对象是否是正则表达式
  1485. */
  1486. /**
  1487. * 判断给定的对象是否是一个普通对象
  1488. * @method isObject
  1489. * @param { * } object 需要判断的对象
  1490. * @return { Boolean } 给定的对象是否是普通对象
  1491. */
  1492. utils.each(
  1493. ['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'],
  1494. function (v) {
  1495. UE.utils['is' + v] = function (obj) {
  1496. return Object.prototype.toString.apply(obj) == '[object ' + v + ']'
  1497. }
  1498. }
  1499. )
  1500. // core/EventBase.js
  1501. /**
  1502. * UE采用的事件基类
  1503. * @file
  1504. * @module UE
  1505. * @class EventBase
  1506. * @since 1.2.6.1
  1507. */
  1508. /**
  1509. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  1510. * @unfile
  1511. * @module UE
  1512. */
  1513. /**
  1514. * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  1515. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  1516. * @unfile
  1517. * @module UE
  1518. * @class EventBase
  1519. */
  1520. /**
  1521. * 通过此构造器,子类可以继承EventBase获取事件监听的方法
  1522. * @constructor
  1523. * @example
  1524. * ```javascript
  1525. * UE.EventBase.call(editor);
  1526. * ```
  1527. */
  1528. var EventBase = (UE.EventBase = function () {})
  1529. EventBase.prototype = {
  1530. /**
  1531. * 注册事件监听器
  1532. * @method addListener
  1533. * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
  1534. * @param { Function } fn 监听的事件被触发时,会执行该回调函数
  1535. * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
  1536. * @example
  1537. * ```javascript
  1538. * editor.addListener('selectionchange',function(){
  1539. * console.log("选区已经变化!");
  1540. * })
  1541. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  1542. * if(type == 'beforegetcontent'){
  1543. * //do something
  1544. * }else{
  1545. * //do something
  1546. * }
  1547. * console.log(this.getContent) // this是注册的事件的编辑器实例
  1548. * })
  1549. * ```
  1550. * @see UE.EventBase:fireEvent(String)
  1551. */
  1552. addListener: function (types, listener) {
  1553. types = utils.trim(types).split(/\s+/)
  1554. for (var i = 0, ti; (ti = types[i++]); ) {
  1555. getListener(this, ti, true).push(listener)
  1556. }
  1557. },
  1558. on: function (types, listener) {
  1559. return this.addListener(types, listener)
  1560. },
  1561. off: function (types, listener) {
  1562. return this.removeListener(types, listener)
  1563. },
  1564. trigger: function () {
  1565. return this.fireEvent.apply(this, arguments)
  1566. },
  1567. /**
  1568. * 移除事件监听器
  1569. * @method removeListener
  1570. * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
  1571. * @param { Function } fn 移除监听事件的函数引用
  1572. * @example
  1573. * ```javascript
  1574. * //changeCallback为方法体
  1575. * editor.removeListener("selectionchange",changeCallback);
  1576. * ```
  1577. */
  1578. removeListener: function (types, listener) {
  1579. types = utils.trim(types).split(/\s+/)
  1580. for (var i = 0, ti; (ti = types[i++]); ) {
  1581. utils.removeItem(getListener(this, ti) || [], listener)
  1582. }
  1583. },
  1584. /**
  1585. * 触发事件
  1586. * @method fireEvent
  1587. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1588. * @remind 该方法会触发addListener
  1589. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1590. * @example
  1591. * ```javascript
  1592. * editor.fireEvent("selectionchange");
  1593. * ```
  1594. */
  1595. /**
  1596. * 触发事件
  1597. * @method fireEvent
  1598. * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
  1599. * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
  1600. * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
  1601. * @example
  1602. * ```javascript
  1603. *
  1604. * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
  1605. *
  1606. * console.log( arg1 + " " + arg2 );
  1607. *
  1608. * } );
  1609. *
  1610. * //触发selectionchange事件, 会执行上面的事件监听器
  1611. * //output: Hello World
  1612. * editor.fireEvent("selectionchange", "Hello", "World");
  1613. * ```
  1614. */
  1615. fireEvent: function () {
  1616. var types = arguments[0]
  1617. types = utils.trim(types).split(' ')
  1618. for (var i = 0, ti; (ti = types[i++]); ) {
  1619. var listeners = getListener(this, ti),
  1620. r,
  1621. t,
  1622. k
  1623. if (listeners) {
  1624. k = listeners.length
  1625. while (k--) {
  1626. if (!listeners[k]) continue
  1627. t = listeners[k].apply(this, arguments)
  1628. if (t === true) {
  1629. return t
  1630. }
  1631. if (t !== undefined) {
  1632. r = t
  1633. }
  1634. }
  1635. }
  1636. if ((t = this['on' + ti.toLowerCase()])) {
  1637. r = t.apply(this, arguments)
  1638. }
  1639. }
  1640. return r
  1641. }
  1642. }
  1643. /**
  1644. * 获得对象所拥有监听类型的所有监听器
  1645. * @unfile
  1646. * @module UE
  1647. * @since 1.2.6.1
  1648. * @method getListener
  1649. * @public
  1650. * @param { Object } obj 查询监听器的对象
  1651. * @param { String } type 事件类型
  1652. * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  1653. * @return { Array } 监听器数组
  1654. */
  1655. function getListener(obj, type, force) {
  1656. var allListeners
  1657. type = type.toLowerCase()
  1658. return (
  1659. (allListeners =
  1660. obj.__allListeners || (force && (obj.__allListeners = {}))) &&
  1661. (allListeners[type] || (force && (allListeners[type] = [])))
  1662. )
  1663. }
  1664. // core/dtd.js
  1665. ///import editor.js
  1666. ///import core/dom/dom.js
  1667. ///import core/utils.js
  1668. /**
  1669. * dtd html语义化的体现类
  1670. * @constructor
  1671. * @namespace dtd
  1672. */
  1673. var dtd = (dom.dtd = (function () {
  1674. function _(s) {
  1675. for (var k in s) {
  1676. s[k.toUpperCase()] = s[k]
  1677. }
  1678. return s
  1679. }
  1680. var X = utils.extend2
  1681. var A = _({ isindex: 1, fieldset: 1 }),
  1682. B = _({ input: 1, button: 1, select: 1, textarea: 1, label: 1 }),
  1683. C = X(_({ a: 1 }), B),
  1684. D = X({ iframe: 1 }, C),
  1685. E = _({
  1686. hr: 1,
  1687. ul: 1,
  1688. menu: 1,
  1689. div: 1,
  1690. blockquote: 1,
  1691. noscript: 1,
  1692. table: 1,
  1693. center: 1,
  1694. address: 1,
  1695. dir: 1,
  1696. pre: 1,
  1697. h5: 1,
  1698. dl: 1,
  1699. h4: 1,
  1700. noframes: 1,
  1701. h6: 1,
  1702. ol: 1,
  1703. h1: 1,
  1704. h3: 1,
  1705. h2: 1
  1706. }),
  1707. F = _({ ins: 1, del: 1, script: 1, style: 1 }),
  1708. G = X(
  1709. _({
  1710. b: 1,
  1711. acronym: 1,
  1712. bdo: 1,
  1713. var: 1,
  1714. '#': 1,
  1715. abbr: 1,
  1716. code: 1,
  1717. br: 1,
  1718. i: 1,
  1719. cite: 1,
  1720. kbd: 1,
  1721. u: 1,
  1722. strike: 1,
  1723. s: 1,
  1724. tt: 1,
  1725. strong: 1,
  1726. q: 1,
  1727. samp: 1,
  1728. em: 1,
  1729. dfn: 1,
  1730. span: 1
  1731. }),
  1732. F
  1733. ),
  1734. H = X(
  1735. _({
  1736. sub: 1,
  1737. img: 1,
  1738. embed: 1,
  1739. object: 1,
  1740. sup: 1,
  1741. basefont: 1,
  1742. map: 1,
  1743. applet: 1,
  1744. font: 1,
  1745. big: 1,
  1746. small: 1
  1747. }),
  1748. G
  1749. ),
  1750. I = X(_({ p: 1 }), H),
  1751. J = X(_({ iframe: 1 }), H, B),
  1752. K = _({
  1753. img: 1,
  1754. embed: 1,
  1755. noscript: 1,
  1756. br: 1,
  1757. kbd: 1,
  1758. center: 1,
  1759. button: 1,
  1760. basefont: 1,
  1761. h5: 1,
  1762. h4: 1,
  1763. samp: 1,
  1764. h6: 1,
  1765. ol: 1,
  1766. h1: 1,
  1767. h3: 1,
  1768. h2: 1,
  1769. form: 1,
  1770. font: 1,
  1771. '#': 1,
  1772. select: 1,
  1773. menu: 1,
  1774. ins: 1,
  1775. abbr: 1,
  1776. label: 1,
  1777. code: 1,
  1778. table: 1,
  1779. script: 1,
  1780. cite: 1,
  1781. input: 1,
  1782. iframe: 1,
  1783. strong: 1,
  1784. textarea: 1,
  1785. noframes: 1,
  1786. big: 1,
  1787. small: 1,
  1788. span: 1,
  1789. hr: 1,
  1790. sub: 1,
  1791. bdo: 1,
  1792. var: 1,
  1793. div: 1,
  1794. object: 1,
  1795. sup: 1,
  1796. strike: 1,
  1797. dir: 1,
  1798. map: 1,
  1799. dl: 1,
  1800. applet: 1,
  1801. del: 1,
  1802. isindex: 1,
  1803. fieldset: 1,
  1804. ul: 1,
  1805. b: 1,
  1806. acronym: 1,
  1807. a: 1,
  1808. blockquote: 1,
  1809. i: 1,
  1810. u: 1,
  1811. s: 1,
  1812. tt: 1,
  1813. address: 1,
  1814. q: 1,
  1815. pre: 1,
  1816. p: 1,
  1817. em: 1,
  1818. dfn: 1
  1819. }),
  1820. L = X(_({ a: 0 }), J), //a不能被切开,所以把他
  1821. M = _({ tr: 1 }),
  1822. N = _({ '#': 1 }),
  1823. O = X(_({ param: 1 }), K),
  1824. P = X(_({ form: 1 }), A, D, E, I),
  1825. Q = _({ li: 1, ol: 1, ul: 1 }),
  1826. R = _({ style: 1, script: 1 }),
  1827. S = _({ base: 1, link: 1, meta: 1, title: 1 }),
  1828. T = X(S, R),
  1829. U = _({ head: 1, body: 1 }),
  1830. V = _({ html: 1 })
  1831. var block = _({
  1832. address: 1,
  1833. blockquote: 1,
  1834. center: 1,
  1835. dir: 1,
  1836. div: 1,
  1837. dl: 1,
  1838. fieldset: 1,
  1839. form: 1,
  1840. h1: 1,
  1841. h2: 1,
  1842. h3: 1,
  1843. h4: 1,
  1844. h5: 1,
  1845. h6: 1,
  1846. hr: 1,
  1847. isindex: 1,
  1848. menu: 1,
  1849. noframes: 1,
  1850. ol: 1,
  1851. p: 1,
  1852. pre: 1,
  1853. table: 1,
  1854. ul: 1
  1855. }),
  1856. empty = _({
  1857. area: 1,
  1858. base: 1,
  1859. basefont: 1,
  1860. br: 1,
  1861. col: 1,
  1862. command: 1,
  1863. dialog: 1,
  1864. embed: 1,
  1865. hr: 1,
  1866. img: 1,
  1867. input: 1,
  1868. isindex: 1,
  1869. keygen: 1,
  1870. link: 1,
  1871. meta: 1,
  1872. param: 1,
  1873. source: 1,
  1874. track: 1,
  1875. wbr: 1
  1876. })
  1877. return _({
  1878. // $ 表示自定的属性
  1879. // body外的元素列表.
  1880. $nonBodyContent: X(V, U, S),
  1881. //块结构元素列表
  1882. $block: block,
  1883. //内联元素列表
  1884. $inline: L,
  1885. $inlineWithA: X(_({ a: 1 }), L),
  1886. $body: X(_({ script: 1, style: 1 }), block),
  1887. $cdata: _({ script: 1, style: 1 }),
  1888. //自闭和元素
  1889. $empty: empty,
  1890. //不是自闭合,但不能让range选中里边
  1891. $nonChild: _({ iframe: 1, textarea: 1 }),
  1892. //列表元素列表
  1893. $listItem: _({ dd: 1, dt: 1, li: 1 }),
  1894. //列表根元素列表
  1895. $list: _({ ul: 1, ol: 1, dl: 1 }),
  1896. //不能认为是空的元素
  1897. $isNotEmpty: _({
  1898. table: 1,
  1899. ul: 1,
  1900. ol: 1,
  1901. dl: 1,
  1902. iframe: 1,
  1903. area: 1,
  1904. base: 1,
  1905. col: 1,
  1906. hr: 1,
  1907. img: 1,
  1908. embed: 1,
  1909. input: 1,
  1910. link: 1,
  1911. meta: 1,
  1912. param: 1,
  1913. h1: 1,
  1914. h2: 1,
  1915. h3: 1,
  1916. h4: 1,
  1917. h5: 1,
  1918. h6: 1
  1919. }),
  1920. //如果没有子节点就可以删除的元素列表,像span,a
  1921. $removeEmpty: _({
  1922. a: 1,
  1923. abbr: 1,
  1924. acronym: 1,
  1925. address: 1,
  1926. b: 1,
  1927. bdo: 1,
  1928. big: 1,
  1929. cite: 1,
  1930. code: 1,
  1931. del: 1,
  1932. dfn: 1,
  1933. em: 1,
  1934. font: 1,
  1935. i: 1,
  1936. ins: 1,
  1937. label: 1,
  1938. kbd: 1,
  1939. q: 1,
  1940. s: 1,
  1941. samp: 1,
  1942. small: 1,
  1943. span: 1,
  1944. strike: 1,
  1945. strong: 1,
  1946. sub: 1,
  1947. sup: 1,
  1948. tt: 1,
  1949. u: 1,
  1950. var: 1
  1951. }),
  1952. $removeEmptyBlock: _({ p: 1, div: 1 }),
  1953. //在table元素里的元素列表
  1954. $tableContent: _({
  1955. caption: 1,
  1956. col: 1,
  1957. colgroup: 1,
  1958. tbody: 1,
  1959. td: 1,
  1960. tfoot: 1,
  1961. th: 1,
  1962. thead: 1,
  1963. tr: 1,
  1964. table: 1
  1965. }),
  1966. //不转换的标签
  1967. $notTransContent: _({ pre: 1, script: 1, style: 1, textarea: 1 }),
  1968. html: U,
  1969. head: T,
  1970. style: N,
  1971. script: N,
  1972. body: P,
  1973. base: {},
  1974. link: {},
  1975. meta: {},
  1976. title: N,
  1977. col: {},
  1978. tr: _({ td: 1, th: 1 }),
  1979. img: {},
  1980. embed: {},
  1981. colgroup: _({ thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1 }),
  1982. noscript: P,
  1983. td: P,
  1984. br: {},
  1985. th: P,
  1986. center: P,
  1987. kbd: L,
  1988. button: X(I, E),
  1989. basefont: {},
  1990. h5: L,
  1991. h4: L,
  1992. samp: L,
  1993. h6: L,
  1994. ol: Q,
  1995. h1: L,
  1996. h3: L,
  1997. option: N,
  1998. h2: L,
  1999. form: X(A, D, E, I),
  2000. select: _({ optgroup: 1, option: 1 }),
  2001. font: L,
  2002. ins: L,
  2003. menu: Q,
  2004. abbr: L,
  2005. label: L,
  2006. table: _({
  2007. thead: 1,
  2008. col: 1,
  2009. tbody: 1,
  2010. tr: 1,
  2011. colgroup: 1,
  2012. caption: 1,
  2013. tfoot: 1
  2014. }),
  2015. code: L,
  2016. tfoot: M,
  2017. cite: L,
  2018. li: P,
  2019. input: {},
  2020. iframe: P,
  2021. strong: L,
  2022. textarea: N,
  2023. noframes: P,
  2024. big: L,
  2025. small: L,
  2026. //trace:
  2027. span: _({
  2028. '#': 1,
  2029. br: 1,
  2030. b: 1,
  2031. strong: 1,
  2032. u: 1,
  2033. i: 1,
  2034. em: 1,
  2035. sub: 1,
  2036. sup: 1,
  2037. strike: 1,
  2038. span: 1
  2039. }),
  2040. hr: L,
  2041. dt: L,
  2042. sub: L,
  2043. optgroup: _({ option: 1 }),
  2044. param: {},
  2045. bdo: L,
  2046. var: L,
  2047. div: P,
  2048. object: O,
  2049. sup: L,
  2050. dd: P,
  2051. strike: L,
  2052. area: {},
  2053. dir: Q,
  2054. map: X(_({ area: 1, form: 1, p: 1 }), A, F, E),
  2055. applet: O,
  2056. dl: _({ dt: 1, dd: 1 }),
  2057. del: L,
  2058. isindex: {},
  2059. fieldset: X(_({ legend: 1 }), K),
  2060. thead: M,
  2061. ul: Q,
  2062. acronym: L,
  2063. b: L,
  2064. a: X(_({ a: 1 }), J),
  2065. blockquote: X(_({ td: 1, tr: 1, tbody: 1, li: 1 }), P),
  2066. caption: L,
  2067. i: L,
  2068. u: L,
  2069. tbody: M,
  2070. s: L,
  2071. address: X(D, I),
  2072. tt: L,
  2073. legend: L,
  2074. q: L,
  2075. pre: X(G, C),
  2076. p: X(_({ a: 1 }), L),
  2077. em: L,
  2078. dfn: L
  2079. })
  2080. })())
  2081. // core/domUtils.js
  2082. /**
  2083. * Dom操作工具包
  2084. * @file
  2085. * @module UE.dom.domUtils
  2086. * @since 1.2.6.1
  2087. */
  2088. /**
  2089. * Dom操作工具包
  2090. * @unfile
  2091. * @module UE.dom.domUtils
  2092. */
  2093. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  2094. var tmpNode = startFromChild && node[start],
  2095. parent
  2096. !tmpNode && (tmpNode = node[ltr])
  2097. while (!tmpNode && (parent = (parent || node).parentNode)) {
  2098. if (parent.tagName == 'BODY' || (guard && !guard(parent))) {
  2099. return null
  2100. }
  2101. tmpNode = parent[ltr]
  2102. }
  2103. if (tmpNode && fn && !fn(tmpNode)) {
  2104. return getDomNode(tmpNode, start, ltr, false, fn)
  2105. }
  2106. return tmpNode
  2107. }
  2108. var attrFix =
  2109. ie && browser.version < 9
  2110. ? {
  2111. tabindex: 'tabIndex',
  2112. readonly: 'readOnly',
  2113. for: 'htmlFor',
  2114. class: 'className',
  2115. maxlength: 'maxLength',
  2116. cellspacing: 'cellSpacing',
  2117. cellpadding: 'cellPadding',
  2118. rowspan: 'rowSpan',
  2119. colspan: 'colSpan',
  2120. usemap: 'useMap',
  2121. frameborder: 'frameBorder'
  2122. }
  2123. : {
  2124. tabindex: 'tabIndex',
  2125. readonly: 'readOnly'
  2126. },
  2127. styleBlock = utils.listToMap([
  2128. '-webkit-box',
  2129. '-moz-box',
  2130. 'block',
  2131. 'list-item',
  2132. 'table',
  2133. 'table-row-group',
  2134. 'table-header-group',
  2135. 'table-footer-group',
  2136. 'table-row',
  2137. 'table-column-group',
  2138. 'table-column',
  2139. 'table-cell',
  2140. 'table-caption'
  2141. ])
  2142. var domUtils = (dom.domUtils = {
  2143. //节点常量
  2144. NODE_ELEMENT: 1,
  2145. NODE_DOCUMENT: 9,
  2146. NODE_TEXT: 3,
  2147. NODE_COMMENT: 8,
  2148. NODE_DOCUMENT_FRAGMENT: 11,
  2149. //位置关系
  2150. POSITION_IDENTICAL: 0,
  2151. POSITION_DISCONNECTED: 1,
  2152. POSITION_FOLLOWING: 2,
  2153. POSITION_PRECEDING: 4,
  2154. POSITION_IS_CONTAINED: 8,
  2155. POSITION_CONTAINS: 16,
  2156. //ie6使用其他的会有一段空白出现
  2157. fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
  2158. //-------------------------Node部分--------------------------------
  2159. keys: {
  2160. /*Backspace*/ 8: 1,
  2161. /*Delete*/ 46: 1,
  2162. /*Shift*/ 16: 1,
  2163. /*Ctrl*/ 17: 1,
  2164. /*Alt*/ 18: 1,
  2165. 37: 1,
  2166. 38: 1,
  2167. 39: 1,
  2168. 40: 1,
  2169. 13: 1 /*enter*/
  2170. },
  2171. /**
  2172. * 获取节点A相对于节点B的位置关系
  2173. * @method getPosition
  2174. * @param { Node } nodeA 需要查询位置关系的节点A
  2175. * @param { Node } nodeB 需要查询位置关系的节点B
  2176. * @return { Number } 节点A与节点B的关系
  2177. * @example
  2178. * ```javascript
  2179. * //output: 20
  2180. * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
  2181. *
  2182. * switch ( position ) {
  2183. *
  2184. * //0
  2185. * case UE.dom.domUtils.POSITION_IDENTICAL:
  2186. * console.log('元素相同');
  2187. * break;
  2188. * //1
  2189. * case UE.dom.domUtils.POSITION_DISCONNECTED:
  2190. * console.log('两个节点在不同的文档中');
  2191. * break;
  2192. * //2
  2193. * case UE.dom.domUtils.POSITION_FOLLOWING:
  2194. * console.log('节点A在节点B之后');
  2195. * break;
  2196. * //4
  2197. * case UE.dom.domUtils.POSITION_PRECEDING;
  2198. * console.log('节点A在节点B之前');
  2199. * break;
  2200. * //8
  2201. * case UE.dom.domUtils.POSITION_IS_CONTAINED:
  2202. * console.log('节点A被节点B包含');
  2203. * break;
  2204. * case 10:
  2205. * console.log('节点A被节点B包含且节点A在节点B之后');
  2206. * break;
  2207. * //16
  2208. * case UE.dom.domUtils.POSITION_CONTAINS:
  2209. * console.log('节点A包含节点B');
  2210. * break;
  2211. * case 20:
  2212. * console.log('节点A包含节点B且节点A在节点B之前');
  2213. * break;
  2214. *
  2215. * }
  2216. * ```
  2217. */
  2218. getPosition: function (nodeA, nodeB) {
  2219. // 如果两个节点是同一个节点
  2220. if (nodeA === nodeB) {
  2221. // domUtils.POSITION_IDENTICAL
  2222. return 0
  2223. }
  2224. var node,
  2225. parentsA = [nodeA],
  2226. parentsB = [nodeB]
  2227. node = nodeA
  2228. while ((node = node.parentNode)) {
  2229. // 如果nodeB是nodeA的祖先节点
  2230. if (node === nodeB) {
  2231. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  2232. return 10
  2233. }
  2234. parentsA.push(node)
  2235. }
  2236. node = nodeB
  2237. while ((node = node.parentNode)) {
  2238. // 如果nodeA是nodeB的祖先节点
  2239. if (node === nodeA) {
  2240. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  2241. return 20
  2242. }
  2243. parentsB.push(node)
  2244. }
  2245. parentsA.reverse()
  2246. parentsB.reverse()
  2247. if (parentsA[0] !== parentsB[0]) {
  2248. // domUtils.POSITION_DISCONNECTED
  2249. return 1
  2250. }
  2251. var i = -1
  2252. while ((i++, parentsA[i] === parentsB[i])) {}
  2253. nodeA = parentsA[i]
  2254. nodeB = parentsB[i]
  2255. while ((nodeA = nodeA.nextSibling)) {
  2256. if (nodeA === nodeB) {
  2257. // domUtils.POSITION_PRECEDING
  2258. return 4
  2259. }
  2260. }
  2261. // domUtils.POSITION_FOLLOWING
  2262. return 2
  2263. },
  2264. /**
  2265. * 检测节点node在父节点中的索引位置
  2266. * @method getNodeIndex
  2267. * @param { Node } node 需要检测的节点对象
  2268. * @return { Number } 该节点在父节点中的位置
  2269. * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
  2270. */
  2271. /**
  2272. * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
  2273. * @method getNodeIndex
  2274. * @param { Node } node 需要检测的节点对象
  2275. * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
  2276. * @return { Number } 该节点在父节点中的位置
  2277. * @example
  2278. * ```javascript
  2279. *
  2280. * var node = document.createElement("div");
  2281. *
  2282. * node.appendChild( document.createTextNode( "hello" ) );
  2283. * node.appendChild( document.createTextNode( "world" ) );
  2284. * node.appendChild( node = document.createElement( "div" ) );
  2285. *
  2286. * //output: 2
  2287. * console.log( UE.dom.domUtils.getNodeIndex( node ) );
  2288. *
  2289. * //output: 1
  2290. * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
  2291. *
  2292. * ```
  2293. */
  2294. getNodeIndex: function (node, ignoreTextNode) {
  2295. var preNode = node,
  2296. i = 0
  2297. while ((preNode = preNode.previousSibling)) {
  2298. if (ignoreTextNode && preNode.nodeType == 3) {
  2299. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  2300. i++
  2301. }
  2302. continue
  2303. }
  2304. i++
  2305. }
  2306. return i
  2307. },
  2308. /**
  2309. * 检测节点node是否在给定的document对象上
  2310. * @method inDoc
  2311. * @param { Node } node 需要检测的节点对象
  2312. * @param { DomDocument } doc 需要检测的document对象
  2313. * @return { Boolean } 该节点node是否在给定的document的dom树上
  2314. * @example
  2315. * ```javascript
  2316. *
  2317. * var node = document.createElement("div");
  2318. *
  2319. * //output: false
  2320. * console.log( UE.do.domUtils.inDoc( node, document ) );
  2321. *
  2322. * document.body.appendChild( node );
  2323. *
  2324. * //output: true
  2325. * console.log( UE.do.domUtils.inDoc( node, document ) );
  2326. *
  2327. * ```
  2328. */
  2329. inDoc: function (node, doc) {
  2330. return domUtils.getPosition(node, doc) == 10
  2331. },
  2332. /**
  2333. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  2334. * 查找的起点是给定node节点的父节点。
  2335. * @method findParent
  2336. * @param { Node } node 需要查找的节点
  2337. * @param { Function } filterFn 自定义的过滤方法。
  2338. * @warning 查找的终点是到body节点为止
  2339. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  2340. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  2341. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  2342. * @example
  2343. * ```javascript
  2344. * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
  2345. *
  2346. * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
  2347. * return node.tagName === "HTML";
  2348. *
  2349. * } );
  2350. *
  2351. * //output: true
  2352. * console.log( filterNode === null );
  2353. * ```
  2354. */
  2355. /**
  2356. * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
  2357. * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
  2358. * @method findParent
  2359. * @param { Node } node 需要查找的节点
  2360. * @param { Function } filterFn 自定义的过滤方法。
  2361. * @param { Boolean } includeSelf 查找过程是否包含自身
  2362. * @warning 查找的终点是到body节点为止
  2363. * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
  2364. * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
  2365. * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
  2366. * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
  2367. * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
  2368. * @example
  2369. * ```html
  2370. * <body>
  2371. *
  2372. * <div id="test">
  2373. * </div>
  2374. *
  2375. * <script type="text/javascript">
  2376. *
  2377. * //output: DIV, BODY
  2378. * var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
  2379. *
  2380. * console.log( node.tagName );
  2381. * return false;
  2382. *
  2383. * }, true );
  2384. *
  2385. * </script>
  2386. * </body>
  2387. * ```
  2388. */
  2389. findParent: function (node, filterFn, includeSelf) {
  2390. if (node && !domUtils.isBody(node)) {
  2391. node = includeSelf ? node : node.parentNode
  2392. while (node) {
  2393. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  2394. return filterFn && !filterFn(node) && domUtils.isBody(node)
  2395. ? null
  2396. : node
  2397. }
  2398. node = node.parentNode
  2399. }
  2400. }
  2401. return null
  2402. },
  2403. /**
  2404. * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
  2405. * @method findParentByTagName
  2406. * @param { Node } node 需要查找的节点对象
  2407. * @param { Array } tagNames 需要查找的父节点的名称数组
  2408. * @warning 查找的终点是到body节点为止
  2409. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2410. * @example
  2411. * ```javascript
  2412. * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
  2413. * //output: BODY
  2414. * console.log( node.tagName );
  2415. * ```
  2416. */
  2417. /**
  2418. * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
  2419. * 否则, 起点是node的父节点。
  2420. * @method findParentByTagName
  2421. * @param { Node } node 需要查找的节点对象
  2422. * @param { Array } tagNames 需要查找的父节点的名称数组
  2423. * @param { Boolean } includeSelf 查找过程是否包含node节点自身
  2424. * @warning 查找的终点是到body节点为止
  2425. * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
  2426. * @example
  2427. * ```javascript
  2428. * var queryTarget = document.getElementsByTagName("div")[0];
  2429. * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
  2430. * //output: true
  2431. * console.log( queryTarget === node );
  2432. * ```
  2433. */
  2434. findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
  2435. tagNames = utils.listToMap(
  2436. utils.isArray(tagNames) ? tagNames : [tagNames]
  2437. )
  2438. return domUtils.findParent(
  2439. node,
  2440. function (node) {
  2441. return tagNames[node.tagName] && !(excludeFn && excludeFn(node))
  2442. },
  2443. includeSelf
  2444. )
  2445. },
  2446. /**
  2447. * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
  2448. * @method findParents
  2449. * @param { Node } node 需要查找的节点对象
  2450. * @return { Array } 给定节点的祖先节点数组
  2451. * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  2452. * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  2453. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  2454. * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  2455. */
  2456. /**
  2457. * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
  2458. * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
  2459. * @method findParents
  2460. * @param { Node } node 需要查找的节点对象
  2461. * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
  2462. * @return { Array } 给定节点的祖先节点数组
  2463. */
  2464. findParents: function (node, includeSelf, filterFn, closerFirst) {
  2465. var parents =
  2466. includeSelf && ((filterFn && filterFn(node)) || !filterFn) ? [node] : []
  2467. while ((node = domUtils.findParent(node, filterFn))) {
  2468. parents.push(node)
  2469. }
  2470. return closerFirst ? parents : parents.reverse()
  2471. },
  2472. /**
  2473. * 在节点node后面插入新节点newNode
  2474. * @method insertAfter
  2475. * @param { Node } node 目标节点
  2476. * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
  2477. * @return { Node } 新插入的节点
  2478. */
  2479. insertAfter: function (node, newNode) {
  2480. return node.nextSibling
  2481. ? node.parentNode.insertBefore(newNode, node.nextSibling)
  2482. : node.parentNode.appendChild(newNode)
  2483. },
  2484. /**
  2485. * 删除节点node及其下属的所有节点
  2486. * @method remove
  2487. * @param { Node } node 需要删除的节点对象
  2488. * @return { Node } 返回刚删除的节点对象
  2489. * @example
  2490. * ```html
  2491. * <div id="test">
  2492. * <div id="child">你好</div>
  2493. * </div>
  2494. * <script>
  2495. * UE.dom.domUtils.remove( document.body, false );
  2496. * //output: false
  2497. * console.log( document.getElementById( "child" ) !== null );
  2498. * </script>
  2499. * ```
  2500. */
  2501. /**
  2502. * 删除节点node,并根据keepChildren的值决定是否保留子节点
  2503. * @method remove
  2504. * @param { Node } node 需要删除的节点对象
  2505. * @param { Boolean } keepChildren 是否需要保留子节点
  2506. * @return { Node } 返回刚删除的节点对象
  2507. * @example
  2508. * ```html
  2509. * <div id="test">
  2510. * <div id="child">你好</div>
  2511. * </div>
  2512. * <script>
  2513. * UE.dom.domUtils.remove( document.body, true );
  2514. * //output: true
  2515. * console.log( document.getElementById( "child" ) !== null );
  2516. * </script>
  2517. * ```
  2518. */
  2519. remove: function (node, keepChildren) {
  2520. var parent = node.parentNode,
  2521. child
  2522. if (parent) {
  2523. if (keepChildren && node.hasChildNodes()) {
  2524. while ((child = node.firstChild)) {
  2525. parent.insertBefore(child, node)
  2526. }
  2527. }
  2528. parent.removeChild(node)
  2529. }
  2530. return node
  2531. },
  2532. /**
  2533. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  2534. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  2535. * @method getNextDomNode
  2536. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2537. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2538. * @example
  2539. * ```html
  2540. * <body>
  2541. * <div id="test">
  2542. * <span></span>
  2543. * </div>
  2544. * <i>xxx</i>
  2545. * </body>
  2546. * <script>
  2547. *
  2548. * //output: i节点
  2549. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2550. *
  2551. * </script>
  2552. * ```
  2553. * @example
  2554. * ```html
  2555. * <body>
  2556. * <div>
  2557. * <span></span>
  2558. * <i id="test">xxx</i>
  2559. * </div>
  2560. * <b>xxx</b>
  2561. * </body>
  2562. * <script>
  2563. *
  2564. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  2565. * //output: b节点
  2566. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  2567. *
  2568. * </script>
  2569. * ```
  2570. */
  2571. /**
  2572. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  2573. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  2574. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  2575. * @method getNextDomNode
  2576. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  2577. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  2578. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  2579. * @see UE.dom.domUtils.getNextDomNode(Node)
  2580. */
  2581. getNextDomNode: function (node, startFromChild, filterFn, guard) {
  2582. return getDomNode(
  2583. node,
  2584. 'firstChild',
  2585. 'nextSibling',
  2586. startFromChild,
  2587. filterFn,
  2588. guard
  2589. )
  2590. },
  2591. getPreDomNode: function (node, startFromChild, filterFn, guard) {
  2592. return getDomNode(
  2593. node,
  2594. 'lastChild',
  2595. 'previousSibling',
  2596. startFromChild,
  2597. filterFn,
  2598. guard
  2599. )
  2600. },
  2601. /**
  2602. * 检测节点node是否属是UEditor定义的bookmark节点
  2603. * @method isBookmarkNode
  2604. * @private
  2605. * @param { Node } node 需要检测的节点对象
  2606. * @return { Boolean } 是否是bookmark节点
  2607. * @example
  2608. * ```html
  2609. * <span id="_baidu_bookmark_1"></span>
  2610. * <script>
  2611. * var bookmarkNode = document.getElementById("_baidu_bookmark_1");
  2612. * //output: true
  2613. * console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
  2614. * </script>
  2615. * ```
  2616. */
  2617. isBookmarkNode: function (node) {
  2618. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id)
  2619. },
  2620. /**
  2621. * 获取节点node所属的window对象
  2622. * @method getWindow
  2623. * @param { Node } node 节点对象
  2624. * @return { Window } 当前节点所属的window对象
  2625. * @example
  2626. * ```javascript
  2627. * //output: true
  2628. * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
  2629. * ```
  2630. */
  2631. getWindow: function (node) {
  2632. var doc = node.ownerDocument || node
  2633. return doc.defaultView || doc.parentWindow
  2634. },
  2635. /**
  2636. * 获取离nodeA与nodeB最近的公共的祖先节点
  2637. * @method getCommonAncestor
  2638. * @param { Node } nodeA 第一个节点
  2639. * @param { Node } nodeB 第二个节点
  2640. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  2641. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  2642. * @example
  2643. * ```javascript
  2644. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  2645. * //output: true
  2646. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  2647. * ```
  2648. */
  2649. getCommonAncestor: function (nodeA, nodeB) {
  2650. if (nodeA === nodeB) return nodeA
  2651. var parentsA = [nodeA],
  2652. parentsB = [nodeB],
  2653. parent = nodeA,
  2654. i = -1
  2655. while ((parent = parent.parentNode)) {
  2656. if (parent === nodeB) {
  2657. return parent
  2658. }
  2659. parentsA.push(parent)
  2660. }
  2661. parent = nodeB
  2662. while ((parent = parent.parentNode)) {
  2663. if (parent === nodeA) return parent
  2664. parentsB.push(parent)
  2665. }
  2666. parentsA.reverse()
  2667. parentsB.reverse()
  2668. while ((i++, parentsA[i] === parentsB[i])) {}
  2669. return i == 0 ? null : parentsA[i - 1]
  2670. },
  2671. /**
  2672. * 清除node节点左右连续为空的兄弟inline节点
  2673. * @method clearEmptySibling
  2674. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2675. * 则这些兄弟节点将被删除
  2676. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  2677. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  2678. * @example
  2679. * ```html
  2680. * <body>
  2681. * <div></div>
  2682. * <span id="test"></span>
  2683. * <i></i>
  2684. * <b></b>
  2685. * <em>xxx</em>
  2686. * <span></span>
  2687. * </body>
  2688. * <script>
  2689. *
  2690. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  2691. *
  2692. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  2693. * console.log( document.body.innerHTML );
  2694. *
  2695. * </script>
  2696. * ```
  2697. */
  2698. /**
  2699. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2700. * 则忽略对右边兄弟节点的操作。
  2701. * @method clearEmptySibling
  2702. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2703. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2704. * 则这些兄弟节点将被删除
  2705. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2706. */
  2707. /**
  2708. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  2709. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  2710. * @method clearEmptySibling
  2711. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  2712. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  2713. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  2714. * 则这些兄弟节点将被删除
  2715. * @see UE.dom.domUtils.clearEmptySibling(Node)
  2716. */
  2717. clearEmptySibling: function (node, ignoreNext, ignorePre) {
  2718. function clear(next, dir) {
  2719. var tmpNode
  2720. while (
  2721. next &&
  2722. !domUtils.isBookmarkNode(next) &&
  2723. (domUtils.isEmptyInlineElement(next) ||
  2724. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  2725. !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(
  2726. next.nodeValue
  2727. ))
  2728. ) {
  2729. tmpNode = next[dir]
  2730. domUtils.remove(next)
  2731. next = tmpNode
  2732. }
  2733. }
  2734. !ignoreNext && clear(node.nextSibling, 'nextSibling')
  2735. !ignorePre && clear(node.previousSibling, 'previousSibling')
  2736. },
  2737. /**
  2738. * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
  2739. * @method split
  2740. * @param { Node } textNode 需要拆分的文本节点对象
  2741. * @param { int } offset 需要拆分的位置, 位置计算从0开始
  2742. * @return { Node } 拆分后形成的新节点
  2743. * @example
  2744. * ```html
  2745. * <div id="test">abcdef</div>
  2746. * <script>
  2747. * var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
  2748. * //output: def
  2749. * console.log( newNode.nodeValue );
  2750. * </script>
  2751. * ```
  2752. */
  2753. split: function (node, offset) {
  2754. var doc = node.ownerDocument
  2755. if (browser.ie && offset == node.nodeValue.length) {
  2756. var next = doc.createTextNode('')
  2757. return domUtils.insertAfter(node, next)
  2758. }
  2759. var retval = node.splitText(offset)
  2760. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  2761. if (browser.ie8) {
  2762. var tmpNode = doc.createTextNode('')
  2763. domUtils.insertAfter(retval, tmpNode)
  2764. domUtils.remove(tmpNode)
  2765. }
  2766. return retval
  2767. },
  2768. /**
  2769. * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
  2770. * @method isWhitespace
  2771. * @param { Node } node 需要检测的节点对象
  2772. * @return { Boolean } 检测的节点是否为空
  2773. * @example
  2774. * ```html
  2775. * <div id="test">
  2776. *
  2777. * </div>
  2778. * <script>
  2779. * //output: true
  2780. * console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
  2781. * </script>
  2782. * ```
  2783. */
  2784. isWhitespace: function (node) {
  2785. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(
  2786. node.nodeValue
  2787. )
  2788. },
  2789. /**
  2790. * 获取元素element相对于viewport的位置坐标
  2791. * @method getXY
  2792. * @param { Node } element 需要计算位置的节点对象
  2793. * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
  2794. * y代表垂直偏移距离。
  2795. *
  2796. * @example
  2797. * ```javascript
  2798. * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
  2799. * //output: test的坐标为: 12, 24
  2800. * console.log( 'test的坐标为: ', location.x, ',', location.y );
  2801. * ```
  2802. */
  2803. getXY: function (element) {
  2804. var x = 0,
  2805. y = 0
  2806. while (element.offsetParent) {
  2807. y += element.offsetTop
  2808. x += element.offsetLeft
  2809. element = element.offsetParent
  2810. }
  2811. return { x: x, y: y }
  2812. },
  2813. /**
  2814. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2815. * @method on
  2816. * @param { Node } element 需要绑定事件的节点对象
  2817. * @param { String } type 绑定的事件类型
  2818. * @param { Function } handler 事件处理器
  2819. * @example
  2820. * ```javascript
  2821. * UE.dom.domUtils.on(document.body,"click",function(e){
  2822. * //e为事件对象,this为被点击元素对戏那个
  2823. * });
  2824. * ```
  2825. */
  2826. /**
  2827. * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
  2828. * @method on
  2829. * @param { Node } element 需要绑定事件的节点对象
  2830. * @param { Array } type 绑定的事件类型数组
  2831. * @param { Function } handler 事件处理器
  2832. * @example
  2833. * ```javascript
  2834. * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
  2835. * //evt为事件对象,this为被点击元素对象
  2836. * });
  2837. * ```
  2838. */
  2839. on: function (element, type, handler) {
  2840. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2841. k = types.length
  2842. if (k)
  2843. while (k--) {
  2844. type = types[k]
  2845. if (element.addEventListener) {
  2846. element.addEventListener(type, handler, false)
  2847. } else {
  2848. if (!handler._d) {
  2849. handler._d = {
  2850. els: []
  2851. }
  2852. }
  2853. var key = type + handler.toString(),
  2854. index = utils.indexOf(handler._d.els, element)
  2855. if (!handler._d[key] || index == -1) {
  2856. if (index == -1) {
  2857. handler._d.els.push(element)
  2858. }
  2859. if (!handler._d[key]) {
  2860. handler._d[key] = function (evt) {
  2861. return handler.call(evt.srcElement, evt || window.event)
  2862. }
  2863. }
  2864. element.attachEvent('on' + type, handler._d[key])
  2865. }
  2866. }
  2867. }
  2868. element = null
  2869. },
  2870. /**
  2871. * 解除DOM事件绑定
  2872. * @method un
  2873. * @param { Node } element 需要解除事件绑定的节点对象
  2874. * @param { String } type 需要接触绑定的事件类型
  2875. * @param { Function } handler 对应的事件处理器
  2876. * @example
  2877. * ```javascript
  2878. * UE.dom.domUtils.un(document.body,"click",function(evt){
  2879. * //evt为事件对象,this为被点击元素对象
  2880. * });
  2881. * ```
  2882. */
  2883. /**
  2884. * 解除DOM事件绑定
  2885. * @method un
  2886. * @param { Node } element 需要解除事件绑定的节点对象
  2887. * @param { Array } type 需要接触绑定的事件类型数组
  2888. * @param { Function } handler 对应的事件处理器
  2889. * @example
  2890. * ```javascript
  2891. * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
  2892. * //evt为事件对象,this为被点击元素对象
  2893. * });
  2894. * ```
  2895. */
  2896. un: function (element, type, handler) {
  2897. var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
  2898. k = types.length
  2899. if (k)
  2900. while (k--) {
  2901. type = types[k]
  2902. if (element.removeEventListener) {
  2903. element.removeEventListener(type, handler, false)
  2904. } else {
  2905. var key = type + handler.toString()
  2906. try {
  2907. element.detachEvent(
  2908. 'on' + type,
  2909. handler._d ? handler._d[key] : handler
  2910. )
  2911. } catch (e) {}
  2912. if (handler._d && handler._d[key]) {
  2913. var index = utils.indexOf(handler._d.els, element)
  2914. if (index != -1) {
  2915. handler._d.els.splice(index, 1)
  2916. }
  2917. handler._d.els.length == 0 && delete handler._d[key]
  2918. }
  2919. }
  2920. }
  2921. },
  2922. /**
  2923. * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
  2924. * @method isSameElement
  2925. * @param { Node } nodeA 需要比较的节点
  2926. * @param { Node } nodeB 需要比较的节点
  2927. * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
  2928. * @example
  2929. * ```html
  2930. * <span style="font-size:12px">ssss</span>
  2931. * <span style="font-size:12px">bbbbb</span>
  2932. * <span style="font-size:13px">ssss</span>
  2933. * <span style="font-size:14px">bbbbb</span>
  2934. *
  2935. * <script>
  2936. *
  2937. * var nodes = document.getElementsByTagName( "span" );
  2938. *
  2939. * //output: true
  2940. * console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
  2941. *
  2942. * //output: false
  2943. * console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
  2944. *
  2945. * </script>
  2946. * ```
  2947. */
  2948. isSameElement: function (nodeA, nodeB) {
  2949. if (nodeA.tagName != nodeB.tagName) {
  2950. return false
  2951. }
  2952. var thisAttrs = nodeA.attributes,
  2953. otherAttrs = nodeB.attributes
  2954. if (!ie && thisAttrs.length != otherAttrs.length) {
  2955. return false
  2956. }
  2957. var attrA,
  2958. attrB,
  2959. al = 0,
  2960. bl = 0
  2961. for (var i = 0; (attrA = thisAttrs[i++]); ) {
  2962. if (attrA.nodeName == 'style') {
  2963. if (attrA.specified) {
  2964. al++
  2965. }
  2966. if (domUtils.isSameStyle(nodeA, nodeB)) {
  2967. continue
  2968. } else {
  2969. return false
  2970. }
  2971. }
  2972. if (ie) {
  2973. if (attrA.specified) {
  2974. al++
  2975. attrB = otherAttrs.getNamedItem(attrA.nodeName)
  2976. } else {
  2977. continue
  2978. }
  2979. } else {
  2980. attrB = nodeB.attributes[attrA.nodeName]
  2981. }
  2982. if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
  2983. return false
  2984. }
  2985. }
  2986. // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
  2987. if (ie) {
  2988. for (i = 0; (attrB = otherAttrs[i++]); ) {
  2989. if (attrB.specified) {
  2990. bl++
  2991. }
  2992. }
  2993. if (al != bl) {
  2994. return false
  2995. }
  2996. }
  2997. return true
  2998. },
  2999. /**
  3000. * 判断节点nodeA与节点nodeB的元素的style属性是否一致
  3001. * @method isSameStyle
  3002. * @param { Node } nodeA 需要比较的节点
  3003. * @param { Node } nodeB 需要比较的节点
  3004. * @return { Boolean } 两个节点是否具有相同的style属性值
  3005. * @example
  3006. * ```html
  3007. * <span style="font-size:12px">ssss</span>
  3008. * <span style="font-size:12px">bbbbb</span>
  3009. * <span style="font-size:13px">ssss</span>
  3010. * <span style="font-size:14px">bbbbb</span>
  3011. *
  3012. * <script>
  3013. *
  3014. * var nodes = document.getElementsByTagName( "span" );
  3015. *
  3016. * //output: true
  3017. * console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
  3018. *
  3019. * //output: false
  3020. * console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
  3021. *
  3022. * </script>
  3023. * ```
  3024. */
  3025. isSameStyle: function (nodeA, nodeB) {
  3026. var styleA = nodeA.style.cssText
  3027. .replace(/( ?; ?)/g, ';')
  3028. .replace(/( ?: ?)/g, ':'),
  3029. styleB = nodeB.style.cssText
  3030. .replace(/( ?; ?)/g, ';')
  3031. .replace(/( ?: ?)/g, ':')
  3032. if (browser.opera) {
  3033. styleA = nodeA.style
  3034. styleB = nodeB.style
  3035. if (styleA.length != styleB.length) return false
  3036. for (var p in styleA) {
  3037. if (/^(\d+|csstext)$/i.test(p)) {
  3038. continue
  3039. }
  3040. if (styleA[p] != styleB[p]) {
  3041. return false
  3042. }
  3043. }
  3044. return true
  3045. }
  3046. if (!styleA || !styleB) {
  3047. return styleA == styleB
  3048. }
  3049. styleA = styleA.split(';')
  3050. styleB = styleB.split(';')
  3051. if (styleA.length != styleB.length) {
  3052. return false
  3053. }
  3054. for (var i = 0, ci; (ci = styleA[i++]); ) {
  3055. if (utils.indexOf(styleB, ci) == -1) {
  3056. return false
  3057. }
  3058. }
  3059. return true
  3060. },
  3061. /**
  3062. * 检查节点node是否为block元素
  3063. * @method isBlockElm
  3064. * @param { Node } node 需要检测的节点对象
  3065. * @return { Boolean } 是否是block元素节点
  3066. * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
  3067. * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
  3068. * @example
  3069. * ```html
  3070. * <span id="test1" style="display: block"></span>
  3071. * <span id="test2"></span>
  3072. * <div id="test3" style="display: inline"></div>
  3073. *
  3074. * <script>
  3075. *
  3076. * //output: true
  3077. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
  3078. *
  3079. * //output: false
  3080. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
  3081. *
  3082. * //output: true
  3083. * console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
  3084. *
  3085. * </script>
  3086. * ```
  3087. */
  3088. isBlockElm: function (node) {
  3089. return (
  3090. node.nodeType == 1 &&
  3091. (dtd.$block[node.tagName] ||
  3092. styleBlock[domUtils.getComputedStyle(node, 'display')]) &&
  3093. !dtd.$nonChild[node.tagName]
  3094. )
  3095. },
  3096. /**
  3097. * 检测node节点是否为body节点
  3098. * @method isBody
  3099. * @param { Element } node 需要检测的dom元素
  3100. * @return { Boolean } 给定的元素是否是body元素
  3101. * @example
  3102. * ```javascript
  3103. * //output: true
  3104. * console.log( UE.dom.domUtils.isBody( document.body ) );
  3105. * ```
  3106. */
  3107. isBody: function (node) {
  3108. return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body'
  3109. },
  3110. /**
  3111. * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
  3112. * 拆分形成的两个节点之间是node节点
  3113. * @method breakParent
  3114. * @param { Node } node 作为分界的节点对象
  3115. * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
  3116. * @return { Node } 给定的node分界节点
  3117. * @example
  3118. * ```javascript
  3119. *
  3120. * var node = document.createElement("span"),
  3121. * wrapNode = document.createElement( "div" ),
  3122. * parent = document.createElement("p");
  3123. *
  3124. * parent.appendChild( node );
  3125. * wrapNode.appendChild( parent );
  3126. *
  3127. * //拆分前
  3128. * //output: <p><span></span></p>
  3129. * console.log( wrapNode.innerHTML );
  3130. *
  3131. *
  3132. * UE.dom.domUtils.breakParent( node, parent );
  3133. * //拆分后
  3134. * //output: <p></p><span></span><p></p>
  3135. * console.log( wrapNode.innerHTML );
  3136. *
  3137. * ```
  3138. */
  3139. breakParent: function (node, parent) {
  3140. var tmpNode,
  3141. parentClone = node,
  3142. clone = node,
  3143. leftNodes,
  3144. rightNodes
  3145. do {
  3146. parentClone = parentClone.parentNode
  3147. if (leftNodes) {
  3148. tmpNode = parentClone.cloneNode(false)
  3149. tmpNode.appendChild(leftNodes)
  3150. leftNodes = tmpNode
  3151. tmpNode = parentClone.cloneNode(false)
  3152. tmpNode.appendChild(rightNodes)
  3153. rightNodes = tmpNode
  3154. } else {
  3155. leftNodes = parentClone.cloneNode(false)
  3156. rightNodes = leftNodes.cloneNode(false)
  3157. }
  3158. while ((tmpNode = clone.previousSibling)) {
  3159. leftNodes.insertBefore(tmpNode, leftNodes.firstChild)
  3160. }
  3161. while ((tmpNode = clone.nextSibling)) {
  3162. rightNodes.appendChild(tmpNode)
  3163. }
  3164. clone = parentClone
  3165. } while (parent !== parentClone)
  3166. tmpNode = parent.parentNode
  3167. tmpNode.insertBefore(leftNodes, parent)
  3168. tmpNode.insertBefore(rightNodes, parent)
  3169. tmpNode.insertBefore(node, rightNodes)
  3170. domUtils.remove(parent)
  3171. return node
  3172. },
  3173. /**
  3174. * 检查节点node是否是空inline节点
  3175. * @method isEmptyInlineElement
  3176. * @param { Node } node 需要检测的节点对象
  3177. * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
  3178. * @example
  3179. * ```html
  3180. * <b><i></i></b> => 1
  3181. * <b><i></i><u></u></b> => 1
  3182. * <b></b> => 1
  3183. * <b>xx<i></i></b> => 0
  3184. * ```
  3185. */
  3186. isEmptyInlineElement: function (node) {
  3187. if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
  3188. return 0
  3189. }
  3190. node = node.firstChild
  3191. while (node) {
  3192. //如果是创建的bookmark就跳过
  3193. if (domUtils.isBookmarkNode(node)) {
  3194. return 0
  3195. }
  3196. if (
  3197. (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) ||
  3198. (node.nodeType == 3 && !domUtils.isWhitespace(node))
  3199. ) {
  3200. return 0
  3201. }
  3202. node = node.nextSibling
  3203. }
  3204. return 1
  3205. },
  3206. /**
  3207. * 删除node节点下首尾两端的空白文本子节点
  3208. * @method trimWhiteTextNode
  3209. * @param { Element } node 需要执行删除操作的元素对象
  3210. * @example
  3211. * ```javascript
  3212. * var node = document.createElement("div");
  3213. *
  3214. * node.appendChild( document.createTextNode( "" ) );
  3215. *
  3216. * node.appendChild( document.createElement("div") );
  3217. *
  3218. * node.appendChild( document.createTextNode( "" ) );
  3219. *
  3220. * //3
  3221. * console.log( node.childNodes.length );
  3222. *
  3223. * UE.dom.domUtils.trimWhiteTextNode( node );
  3224. *
  3225. * //1
  3226. * console.log( node.childNodes.length );
  3227. * ```
  3228. */
  3229. trimWhiteTextNode: function (node) {
  3230. function remove(dir) {
  3231. var child
  3232. while (
  3233. (child = node[dir]) &&
  3234. child.nodeType == 3 &&
  3235. domUtils.isWhitespace(child)
  3236. ) {
  3237. node.removeChild(child)
  3238. }
  3239. }
  3240. remove('firstChild')
  3241. remove('lastChild')
  3242. },
  3243. /**
  3244. * 合并node节点下相同的子节点
  3245. * @name mergeChild
  3246. * @desc
  3247. * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
  3248. * @example
  3249. * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
  3250. * ==> UE.dom.domUtils.mergeChild(node,'span')
  3251. * <p><span style="font-size:12px;">xxaaxx</span></p>
  3252. */
  3253. mergeChild: function (node, tagName, attrs) {
  3254. var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase())
  3255. for (var i = 0, ci; (ci = list[i++]); ) {
  3256. if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
  3257. continue
  3258. }
  3259. //span单独处理
  3260. if (ci.tagName.toLowerCase() == 'span') {
  3261. if (node === ci.parentNode) {
  3262. domUtils.trimWhiteTextNode(node)
  3263. if (node.childNodes.length == 1) {
  3264. node.style.cssText = ci.style.cssText + ';' + node.style.cssText
  3265. domUtils.remove(ci, true)
  3266. continue
  3267. }
  3268. }
  3269. ci.style.cssText = node.style.cssText + ';' + ci.style.cssText
  3270. if (attrs) {
  3271. var style = attrs.style
  3272. if (style) {
  3273. style = style.split(';')
  3274. for (var j = 0, s; (s = style[j++]); ) {
  3275. ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] =
  3276. s.split(':')[1]
  3277. }
  3278. }
  3279. }
  3280. if (domUtils.isSameStyle(ci, node)) {
  3281. domUtils.remove(ci, true)
  3282. }
  3283. continue
  3284. }
  3285. if (domUtils.isSameElement(node, ci)) {
  3286. domUtils.remove(ci, true)
  3287. }
  3288. }
  3289. },
  3290. /**
  3291. * 原生方法getElementsByTagName的封装
  3292. * @method getElementsByTagName
  3293. * @param { Node } node 目标节点对象
  3294. * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
  3295. * @return { Array } 符合条件的节点集合
  3296. */
  3297. getElementsByTagName: function (node, name, filter) {
  3298. if (filter && utils.isString(filter)) {
  3299. var className = filter
  3300. filter = function (node) {
  3301. return domUtils.hasClass(node, className)
  3302. }
  3303. }
  3304. name = utils
  3305. .trim(name)
  3306. .replace(/[ ]{2,}/g, ' ')
  3307. .split(' ')
  3308. var arr = []
  3309. for (var n = 0, ni; (ni = name[n++]); ) {
  3310. var list = node.getElementsByTagName(ni)
  3311. for (var i = 0, ci; (ci = list[i++]); ) {
  3312. if (!filter || filter(ci)) arr.push(ci)
  3313. }
  3314. }
  3315. return arr
  3316. },
  3317. /**
  3318. * 将节点node提取到父节点上
  3319. * @method mergeToParent
  3320. * @param { Element } node 需要提取的元素对象
  3321. * @example
  3322. * ```html
  3323. * <div id="parent">
  3324. * <div id="sub">
  3325. * <span id="child"></span>
  3326. * </div>
  3327. * </div>
  3328. *
  3329. * <script>
  3330. *
  3331. * var child = document.getElementById( "child" );
  3332. *
  3333. * //output: sub
  3334. * console.log( child.parentNode.id );
  3335. *
  3336. * UE.dom.domUtils.mergeToParent( child );
  3337. *
  3338. * //output: parent
  3339. * console.log( child.parentNode.id );
  3340. *
  3341. * </script>
  3342. * ```
  3343. */
  3344. mergeToParent: function (node) {
  3345. var parent = node.parentNode
  3346. while (parent && dtd.$removeEmpty[parent.tagName]) {
  3347. if (parent.tagName == node.tagName || parent.tagName == 'A') {
  3348. //针对a标签单独处理
  3349. domUtils.trimWhiteTextNode(parent)
  3350. //span需要特殊处理 不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
  3351. if (
  3352. (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)) ||
  3353. (parent.tagName == 'A' && node.tagName == 'SPAN')
  3354. ) {
  3355. if (parent.childNodes.length > 1 || parent !== node.parentNode) {
  3356. node.style.cssText =
  3357. parent.style.cssText + ';' + node.style.cssText
  3358. parent = parent.parentNode
  3359. continue
  3360. } else {
  3361. parent.style.cssText += ';' + node.style.cssText
  3362. //trace:952 a标签要保持下划线
  3363. if (parent.tagName == 'A') {
  3364. parent.style.textDecoration = 'underline'
  3365. }
  3366. }
  3367. }
  3368. if (parent.tagName != 'A') {
  3369. parent === node.parentNode && domUtils.remove(node, true)
  3370. break
  3371. }
  3372. }
  3373. parent = parent.parentNode
  3374. }
  3375. },
  3376. /**
  3377. * 合并节点node的左右兄弟节点
  3378. * @method mergeSibling
  3379. * @param { Element } node 需要合并的目标节点
  3380. * @example
  3381. * ```html
  3382. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3383. *
  3384. * <script>
  3385. * var demoNode = document.getElementById("test");
  3386. * UE.dom.domUtils.mergeSibling( demoNode );
  3387. * //output: xxxxoooxxxx
  3388. * console.log( demoNode.innerHTML );
  3389. * </script>
  3390. * ```
  3391. */
  3392. /**
  3393. * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
  3394. * @method mergeSibling
  3395. * @param { Element } node 需要合并的目标节点
  3396. * @param { Boolean } ignorePre 是否忽略合并左节点
  3397. * @example
  3398. * ```html
  3399. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3400. *
  3401. * <script>
  3402. * var demoNode = document.getElementById("test");
  3403. * UE.dom.domUtils.mergeSibling( demoNode, true );
  3404. * //output: oooxxxx
  3405. * console.log( demoNode.innerHTML );
  3406. * </script>
  3407. * ```
  3408. */
  3409. /**
  3410. * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
  3411. * @method mergeSibling
  3412. * @param { Element } node 需要合并的目标节点
  3413. * @param { Boolean } ignorePre 是否忽略合并左节点
  3414. * @param { Boolean } ignoreNext 是否忽略合并右节点
  3415. * @remind 如果同时忽略左右节点, 则该操作什么也不会做
  3416. * @example
  3417. * ```html
  3418. * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
  3419. *
  3420. * <script>
  3421. * var demoNode = document.getElementById("test");
  3422. * UE.dom.domUtils.mergeSibling( demoNode, false, true );
  3423. * //output: xxxxooo
  3424. * console.log( demoNode.innerHTML );
  3425. * </script>
  3426. * ```
  3427. */
  3428. mergeSibling: function (node, ignorePre, ignoreNext) {
  3429. function merge(rtl, start, node) {
  3430. var next
  3431. if (
  3432. (next = node[rtl]) &&
  3433. !domUtils.isBookmarkNode(next) &&
  3434. next.nodeType == 1 &&
  3435. domUtils.isSameElement(node, next)
  3436. ) {
  3437. while (next.firstChild) {
  3438. if (start == 'firstChild') {
  3439. node.insertBefore(next.lastChild, node.firstChild)
  3440. } else {
  3441. node.appendChild(next.firstChild)
  3442. }
  3443. }
  3444. domUtils.remove(next)
  3445. }
  3446. }
  3447. !ignorePre && merge('previousSibling', 'firstChild', node)
  3448. !ignoreNext && merge('nextSibling', 'lastChild', node)
  3449. },
  3450. /**
  3451. * 设置节点node及其子节点不会被选中
  3452. * @method unSelectable
  3453. * @param { Element } node 需要执行操作的dom元素
  3454. * @remind 执行该操作后的节点, 将不能被鼠标选中
  3455. * @example
  3456. * ```javascript
  3457. * UE.dom.domUtils.unSelectable( document.body );
  3458. * ```
  3459. */
  3460. unSelectable:
  3461. (ie && browser.ie9below) || browser.opera
  3462. ? function (node) {
  3463. //for ie9
  3464. node.onselectstart = function () {
  3465. return false
  3466. }
  3467. node.onclick =
  3468. node.onkeyup =
  3469. node.onkeydown =
  3470. function () {
  3471. return false
  3472. }
  3473. node.unselectable = 'on'
  3474. node.setAttribute('unselectable', 'on')
  3475. for (var i = 0, ci; (ci = node.all[i++]); ) {
  3476. switch (ci.tagName.toLowerCase()) {
  3477. case 'iframe':
  3478. case 'textarea':
  3479. case 'input':
  3480. case 'select':
  3481. break
  3482. default:
  3483. ci.unselectable = 'on'
  3484. node.setAttribute('unselectable', 'on')
  3485. }
  3486. }
  3487. }
  3488. : function (node) {
  3489. node.style.MozUserSelect =
  3490. node.style.webkitUserSelect =
  3491. node.style.msUserSelect =
  3492. node.style.KhtmlUserSelect =
  3493. 'none'
  3494. },
  3495. /**
  3496. * 删除节点node上的指定属性名称的属性
  3497. * @method removeAttributes
  3498. * @param { Node } node 需要删除属性的节点对象
  3499. * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
  3500. * @example
  3501. * ```html
  3502. * <div id="wrap">
  3503. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3504. * </div>
  3505. *
  3506. * <script>
  3507. *
  3508. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
  3509. *
  3510. * //output: <span style="font-size:14px;">xxxxx</span>
  3511. * console.log( document.getElementById("wrap").innerHTML );
  3512. *
  3513. * </script>
  3514. * ```
  3515. */
  3516. /**
  3517. * 删除节点node上的指定属性名称的属性
  3518. * @method removeAttributes
  3519. * @param { Node } node 需要删除属性的节点对象
  3520. * @param { Array } attrNames 需要删除的属性名数组
  3521. * @example
  3522. * ```html
  3523. * <div id="wrap">
  3524. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  3525. * </div>
  3526. *
  3527. * <script>
  3528. *
  3529. * UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
  3530. *
  3531. * //output: <span style="font-size:14px;">xxxxx</span>
  3532. * console.log( document.getElementById("wrap").innerHTML );
  3533. *
  3534. * </script>
  3535. * ```
  3536. */
  3537. removeAttributes: function (node, attrNames) {
  3538. attrNames = utils.isArray(attrNames)
  3539. ? attrNames
  3540. : utils
  3541. .trim(attrNames)
  3542. .replace(/[ ]{2,}/g, ' ')
  3543. .split(' ')
  3544. for (var i = 0, ci; (ci = attrNames[i++]); ) {
  3545. ci = attrFix[ci] || ci
  3546. switch (ci) {
  3547. case 'className':
  3548. node[ci] = ''
  3549. break
  3550. case 'style':
  3551. node.style.cssText = ''
  3552. var val = node.getAttributeNode('style')
  3553. !browser.ie && val && node.removeAttributeNode(val)
  3554. }
  3555. node.removeAttribute(ci)
  3556. }
  3557. },
  3558. /**
  3559. * 在doc下创建一个标签名为tag,属性为attrs的元素
  3560. * @method createElement
  3561. * @param { DomDocument } doc 新创建的元素属于该document节点创建
  3562. * @param { String } tagName 需要创建的元素的标签名
  3563. * @param { Object } attrs 新创建的元素的属性key-value集合
  3564. * @return { Element } 新创建的元素对象
  3565. * @example
  3566. * ```javascript
  3567. * var ele = UE.dom.domUtils.createElement( document, 'div', {
  3568. * id: 'test'
  3569. * } );
  3570. *
  3571. * //output: DIV
  3572. * console.log( ele.tagName );
  3573. *
  3574. * //output: test
  3575. * console.log( ele.id );
  3576. *
  3577. * ```
  3578. */
  3579. createElement: function (doc, tag, attrs) {
  3580. return domUtils.setAttributes(doc.createElement(tag), attrs)
  3581. },
  3582. /**
  3583. * 为节点node添加属性attrs,attrs为属性键值对
  3584. * @method setAttributes
  3585. * @param { Element } node 需要设置属性的元素对象
  3586. * @param { Object } attrs 需要设置的属性名-值对
  3587. * @return { Element } 设置属性的元素对象
  3588. * @example
  3589. * ```html
  3590. * <span id="test"></span>
  3591. *
  3592. * <script>
  3593. *
  3594. * var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
  3595. * id: 'demo'
  3596. * } );
  3597. *
  3598. * //output: demo
  3599. * console.log( testNode.id );
  3600. *
  3601. * </script>
  3602. *
  3603. */
  3604. setAttributes: function (node, attrs) {
  3605. for (var attr in attrs) {
  3606. if (attrs.hasOwnProperty(attr)) {
  3607. var value = attrs[attr]
  3608. switch (attr) {
  3609. case 'class':
  3610. //ie下要这样赋值,setAttribute不起作用
  3611. node.className = value
  3612. break
  3613. case 'style':
  3614. node.style.cssText = node.style.cssText + ';' + value
  3615. break
  3616. case 'innerHTML':
  3617. node[attr] = value
  3618. break
  3619. case 'value':
  3620. node.value = value
  3621. break
  3622. default:
  3623. node.setAttribute(attrFix[attr] || attr, value)
  3624. }
  3625. }
  3626. }
  3627. return node
  3628. },
  3629. /**
  3630. * 获取元素element经过计算后的样式值
  3631. * @method getComputedStyle
  3632. * @param { Element } element 需要获取样式的元素对象
  3633. * @param { String } styleName 需要获取的样式名
  3634. * @return { String } 获取到的样式值
  3635. * @example
  3636. * ```html
  3637. * <style type="text/css">
  3638. * #test {
  3639. * font-size: 15px;
  3640. * }
  3641. * </style>
  3642. *
  3643. * <span id="test"></span>
  3644. *
  3645. * <script>
  3646. * //output: 15px
  3647. * console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
  3648. * </script>
  3649. * ```
  3650. */
  3651. getComputedStyle: function (element, styleName) {
  3652. //一下的属性单独处理
  3653. var pros = 'width height top left'
  3654. if (pros.indexOf(styleName) > -1) {
  3655. return (
  3656. element[
  3657. 'offset' +
  3658. styleName.replace(/^\w/, function (s) {
  3659. return s.toUpperCase()
  3660. })
  3661. ] + 'px'
  3662. )
  3663. }
  3664. //忽略文本节点
  3665. if (element.nodeType == 3) {
  3666. element = element.parentNode
  3667. }
  3668. //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
  3669. if (
  3670. browser.ie &&
  3671. browser.version < 9 &&
  3672. styleName == 'font-size' &&
  3673. !element.style.fontSize &&
  3674. !dtd.$empty[element.tagName] &&
  3675. !dtd.$nonChild[element.tagName]
  3676. ) {
  3677. var span = element.ownerDocument.createElement('span')
  3678. span.style.cssText = 'padding:0;border:0;font-family:simsun;'
  3679. span.innerHTML = '.'
  3680. element.appendChild(span)
  3681. var result = span.offsetHeight
  3682. element.removeChild(span)
  3683. span = null
  3684. return result + 'px'
  3685. }
  3686. try {
  3687. var value =
  3688. domUtils.getStyle(element, styleName) ||
  3689. (window.getComputedStyle
  3690. ? domUtils
  3691. .getWindow(element)
  3692. .getComputedStyle(element, '')
  3693. .getPropertyValue(styleName)
  3694. : (element.currentStyle || element.style)[
  3695. utils.cssStyleToDomStyle(styleName)
  3696. ])
  3697. } catch (e) {
  3698. return ''
  3699. }
  3700. return utils.transUnitToPx(utils.fixColor(styleName, value))
  3701. },
  3702. /**
  3703. * 删除元素element指定的className
  3704. * @method removeClasses
  3705. * @param { Element } ele 需要删除class的元素节点
  3706. * @param { String } classNames 需要删除的className, 多个className之间以空格分开
  3707. * @example
  3708. * ```html
  3709. * <span id="test" class="test1 test2 test3">xxx</span>
  3710. *
  3711. * <script>
  3712. *
  3713. * var testNode = document.getElementById( "test" );
  3714. * UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
  3715. *
  3716. * //output: test3
  3717. * console.log( testNode.className );
  3718. *
  3719. * </script>
  3720. * ```
  3721. */
  3722. /**
  3723. * 删除元素element指定的className
  3724. * @method removeClasses
  3725. * @param { Element } ele 需要删除class的元素节点
  3726. * @param { Array } classNames 需要删除的className数组
  3727. * @example
  3728. * ```html
  3729. * <span id="test" class="test1 test2 test3">xxx</span>
  3730. *
  3731. * <script>
  3732. *
  3733. * var testNode = document.getElementById( "test" );
  3734. * UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
  3735. *
  3736. * //output: test3
  3737. * console.log( testNode.className );
  3738. *
  3739. * </script>
  3740. * ```
  3741. */
  3742. removeClasses: function (elm, classNames) {
  3743. classNames = utils.isArray(classNames)
  3744. ? classNames
  3745. : utils
  3746. .trim(classNames)
  3747. .replace(/[ ]{2,}/g, ' ')
  3748. .split(' ')
  3749. for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]); ) {
  3750. cls = cls.replace(new RegExp('\\b' + ci + '\\b'), '')
  3751. }
  3752. cls = utils.trim(cls).replace(/[ ]{2,}/g, ' ')
  3753. if (cls) {
  3754. elm.className = cls
  3755. } else {
  3756. domUtils.removeAttributes(elm, ['class'])
  3757. }
  3758. },
  3759. /**
  3760. * 给元素element添加className
  3761. * @method addClass
  3762. * @param { Node } ele 需要增加className的元素
  3763. * @param { String } classNames 需要添加的className, 多个className之间以空格分割
  3764. * @remind 相同的类名不会被重复添加
  3765. * @example
  3766. * ```html
  3767. * <span id="test" class="cls1 cls2"></span>
  3768. *
  3769. * <script>
  3770. * var testNode = document.getElementById("test");
  3771. *
  3772. * UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
  3773. *
  3774. * //output: cl1 cls2 cls3 cls4
  3775. * console.log( testNode.className );
  3776. *
  3777. * <script>
  3778. * ```
  3779. */
  3780. /**
  3781. * 给元素element添加className
  3782. * @method addClass
  3783. * @param { Node } ele 需要增加className的元素
  3784. * @param { Array } classNames 需要添加的className的数组
  3785. * @remind 相同的类名不会被重复添加
  3786. * @example
  3787. * ```html
  3788. * <span id="test" class="cls1 cls2"></span>
  3789. *
  3790. * <script>
  3791. * var testNode = document.getElementById("test");
  3792. *
  3793. * UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
  3794. *
  3795. * //output: cl1 cls2 cls3 cls4
  3796. * console.log( testNode.className );
  3797. *
  3798. * <script>
  3799. * ```
  3800. */
  3801. addClass: function (elm, classNames) {
  3802. if (!elm) return
  3803. classNames = utils
  3804. .trim(classNames)
  3805. .replace(/[ ]{2,}/g, ' ')
  3806. .split(' ')
  3807. for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]); ) {
  3808. if (!new RegExp('\\b' + ci + '\\b').test(cls)) {
  3809. cls += ' ' + ci
  3810. }
  3811. }
  3812. elm.className = utils.trim(cls)
  3813. },
  3814. /**
  3815. * 判断元素element是否包含给定的样式类名className
  3816. * @method hasClass
  3817. * @param { Node } ele 需要检测的元素
  3818. * @param { String } classNames 需要检测的className, 多个className之间用空格分割
  3819. * @return { Boolean } 元素是否包含所有给定的className
  3820. * @example
  3821. * ```html
  3822. * <span id="test1" class="cls1 cls2"></span>
  3823. *
  3824. * <script>
  3825. * var test1 = document.getElementById("test1");
  3826. *
  3827. * //output: false
  3828. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
  3829. *
  3830. * //output: true
  3831. * console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
  3832. * </script>
  3833. * ```
  3834. */
  3835. /**
  3836. * 判断元素element是否包含给定的样式类名className
  3837. * @method hasClass
  3838. * @param { Node } ele 需要检测的元素
  3839. * @param { Array } classNames 需要检测的className数组
  3840. * @return { Boolean } 元素是否包含所有给定的className
  3841. * @example
  3842. * ```html
  3843. * <span id="test1" class="cls1 cls2"></span>
  3844. *
  3845. * <script>
  3846. * var test1 = document.getElementById("test1");
  3847. *
  3848. * //output: false
  3849. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
  3850. *
  3851. * //output: true
  3852. * console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
  3853. * </script>
  3854. * ```
  3855. */
  3856. hasClass: function (element, className) {
  3857. if (utils.isRegExp(className)) {
  3858. return className.test(element.className)
  3859. }
  3860. className = utils
  3861. .trim(className)
  3862. .replace(/[ ]{2,}/g, ' ')
  3863. .split(' ')
  3864. for (var i = 0, ci, cls = element.className; (ci = className[i++]); ) {
  3865. if (!new RegExp('\\b' + ci + '\\b', 'i').test(cls)) {
  3866. return false
  3867. }
  3868. }
  3869. return i - 1 == className.length
  3870. },
  3871. /**
  3872. * 阻止事件默认行为
  3873. * @method preventDefault
  3874. * @param { Event } evt 需要阻止默认行为的事件对象
  3875. * @example
  3876. * ```javascript
  3877. * UE.dom.domUtils.preventDefault( evt );
  3878. * ```
  3879. */
  3880. preventDefault: function (evt) {
  3881. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
  3882. },
  3883. /**
  3884. * 删除元素element指定的样式
  3885. * @method removeStyle
  3886. * @param { Element } element 需要删除样式的元素
  3887. * @param { String } styleName 需要删除的样式名
  3888. * @example
  3889. * ```html
  3890. * <span id="test" style="color: red; background: blue;"></span>
  3891. *
  3892. * <script>
  3893. *
  3894. * var testNode = document.getElementById("test");
  3895. *
  3896. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  3897. *
  3898. * //output: background: blue;
  3899. * console.log( testNode.style.cssText );
  3900. *
  3901. * </script>
  3902. * ```
  3903. */
  3904. removeStyle: function (element, name) {
  3905. if (browser.ie) {
  3906. //针对color先单独处理一下
  3907. if (name == 'color') {
  3908. name = '(^|;)' + name
  3909. }
  3910. element.style.cssText = element.style.cssText.replace(
  3911. new RegExp(name + '[^:]*:[^;]+;?', 'ig'),
  3912. ''
  3913. )
  3914. } else {
  3915. if (element.style.removeProperty) {
  3916. element.style.removeProperty(name)
  3917. } else {
  3918. element.style.removeAttribute(utils.cssStyleToDomStyle(name))
  3919. }
  3920. }
  3921. if (!element.style.cssText) {
  3922. domUtils.removeAttributes(element, ['style'])
  3923. }
  3924. },
  3925. /**
  3926. * 获取元素element的style属性的指定值
  3927. * @method getStyle
  3928. * @param { Element } element 需要获取属性值的元素
  3929. * @param { String } styleName 需要获取的style的名称
  3930. * @warning 该方法仅获取元素style属性中所标明的值
  3931. * @return { String } 该元素包含指定的style属性值
  3932. * @example
  3933. * ```html
  3934. * <div id="test" style="color: red;"></div>
  3935. *
  3936. * <script>
  3937. *
  3938. * var testNode = document.getElementById( "test" );
  3939. *
  3940. * //output: red
  3941. * console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
  3942. *
  3943. * //output: ""
  3944. * console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
  3945. *
  3946. * </script>
  3947. * ```
  3948. */
  3949. getStyle: function (element, name) {
  3950. var value = element.style[utils.cssStyleToDomStyle(name)]
  3951. return utils.fixColor(name, value)
  3952. },
  3953. /**
  3954. * 为元素element设置样式属性值
  3955. * @method setStyle
  3956. * @param { Element } element 需要设置样式的元素
  3957. * @param { String } styleName 样式名
  3958. * @param { String } styleValue 样式值
  3959. * @example
  3960. * ```html
  3961. * <div id="test"></div>
  3962. *
  3963. * <script>
  3964. *
  3965. * var testNode = document.getElementById( "test" );
  3966. *
  3967. * //output: ""
  3968. * console.log( testNode.style.color );
  3969. *
  3970. * UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
  3971. * //output: "red"
  3972. * console.log( testNode.style.color );
  3973. *
  3974. * </script>
  3975. * ```
  3976. */
  3977. setStyle: function (element, name, value) {
  3978. element.style[utils.cssStyleToDomStyle(name)] = value
  3979. if (!utils.trim(element.style.cssText)) {
  3980. this.removeAttributes(element, 'style')
  3981. }
  3982. },
  3983. /**
  3984. * 为元素element设置多个样式属性值
  3985. * @method setStyles
  3986. * @param { Element } element 需要设置样式的元素
  3987. * @param { Object } styles 样式名值对
  3988. * @example
  3989. * ```html
  3990. * <div id="test"></div>
  3991. *
  3992. * <script>
  3993. *
  3994. * var testNode = document.getElementById( "test" );
  3995. *
  3996. * //output: ""
  3997. * console.log( testNode.style.color );
  3998. *
  3999. * UE.dom.domUtils.setStyles( testNode, {
  4000. * 'color': 'red'
  4001. * } );
  4002. * //output: "red"
  4003. * console.log( testNode.style.color );
  4004. *
  4005. * </script>
  4006. * ```
  4007. */
  4008. setStyles: function (element, styles) {
  4009. for (var name in styles) {
  4010. if (styles.hasOwnProperty(name)) {
  4011. domUtils.setStyle(element, name, styles[name])
  4012. }
  4013. }
  4014. },
  4015. /**
  4016. * 删除_moz_dirty属性
  4017. * @private
  4018. * @method removeDirtyAttr
  4019. */
  4020. removeDirtyAttr: function (node) {
  4021. for (
  4022. var i = 0, ci, nodes = node.getElementsByTagName('*');
  4023. (ci = nodes[i++]);
  4024. ) {
  4025. ci.removeAttribute('_moz_dirty')
  4026. }
  4027. node.removeAttribute('_moz_dirty')
  4028. },
  4029. /**
  4030. * 获取子节点的数量
  4031. * @method getChildCount
  4032. * @param { Element } node 需要检测的元素
  4033. * @return { Number } 给定的node元素的子节点数量
  4034. * @example
  4035. * ```html
  4036. * <div id="test">
  4037. * <span></span>
  4038. * </div>
  4039. *
  4040. * <script>
  4041. *
  4042. * //output: 3
  4043. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
  4044. *
  4045. * </script>
  4046. * ```
  4047. */
  4048. /**
  4049. * 根据给定的过滤规则, 获取符合条件的子节点的数量
  4050. * @method getChildCount
  4051. * @param { Element } node 需要检测的元素
  4052. * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
  4053. * @return { Number } 符合过滤条件的node元素的子节点数量
  4054. * @example
  4055. * ```html
  4056. * <div id="test">
  4057. * <span></span>
  4058. * </div>
  4059. *
  4060. * <script>
  4061. *
  4062. * //output: 1
  4063. * console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
  4064. *
  4065. * return node.nodeType === 1;
  4066. *
  4067. * } ) );
  4068. *
  4069. * </script>
  4070. * ```
  4071. */
  4072. getChildCount: function (node, fn) {
  4073. var count = 0,
  4074. first = node.firstChild
  4075. fn =
  4076. fn ||
  4077. function () {
  4078. return 1
  4079. }
  4080. while (first) {
  4081. if (fn(first)) {
  4082. count++
  4083. }
  4084. first = first.nextSibling
  4085. }
  4086. return count
  4087. },
  4088. /**
  4089. * 判断给定节点是否为空节点
  4090. * @method isEmptyNode
  4091. * @param { Node } node 需要检测的节点对象
  4092. * @return { Boolean } 节点是否为空
  4093. * @example
  4094. * ```javascript
  4095. * UE.dom.domUtils.isEmptyNode( document.body );
  4096. * ```
  4097. */
  4098. isEmptyNode: function (node) {
  4099. return (
  4100. !node.firstChild ||
  4101. domUtils.getChildCount(node, function (node) {
  4102. return (
  4103. !domUtils.isBr(node) &&
  4104. !domUtils.isBookmarkNode(node) &&
  4105. !domUtils.isWhitespace(node)
  4106. )
  4107. }) == 0
  4108. )
  4109. },
  4110. clearSelectedArr: function (nodes) {
  4111. var node
  4112. while ((node = nodes.pop())) {
  4113. domUtils.removeAttributes(node, ['class'])
  4114. }
  4115. },
  4116. /**
  4117. * 将显示区域滚动到指定节点的位置
  4118. * @method scrollToView
  4119. * @param {Node} node 节点
  4120. * @param {window} win window对象
  4121. * @param {Number} offsetTop 距离上方的偏移量
  4122. */
  4123. scrollToView: function (node, win, offsetTop) {
  4124. var getViewPaneSize = function () {
  4125. var doc = win.document,
  4126. mode = doc.compatMode == 'CSS1Compat'
  4127. return {
  4128. width:
  4129. (mode ? doc.documentElement.clientWidth : doc.body.clientWidth) ||
  4130. 0,
  4131. height:
  4132. (mode
  4133. ? doc.documentElement.clientHeight
  4134. : doc.body.clientHeight) || 0
  4135. }
  4136. },
  4137. getScrollPosition = function (win) {
  4138. if ('pageXOffset' in win) {
  4139. return {
  4140. x: win.pageXOffset || 0,
  4141. y: win.pageYOffset || 0
  4142. }
  4143. } else {
  4144. var doc = win.document
  4145. return {
  4146. x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
  4147. y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
  4148. }
  4149. }
  4150. }
  4151. var winHeight = getViewPaneSize().height,
  4152. offset = winHeight * -1 + offsetTop
  4153. offset += node.offsetHeight || 0
  4154. var elementPosition = domUtils.getXY(node)
  4155. offset += elementPosition.y
  4156. var currentScroll = getScrollPosition(win).y
  4157. // offset += 50;
  4158. if (offset > currentScroll || offset < currentScroll - winHeight) {
  4159. win.scrollTo(0, offset + (offset < 0 ? -20 : 20))
  4160. }
  4161. },
  4162. /**
  4163. * 判断给定节点是否为br
  4164. * @method isBr
  4165. * @param { Node } node 需要判断的节点对象
  4166. * @return { Boolean } 给定的节点是否是br节点
  4167. */
  4168. isBr: function (node) {
  4169. return node.nodeType == 1 && node.tagName == 'BR'
  4170. },
  4171. /**
  4172. * 判断给定的节点是否是一个“填充”节点
  4173. * @private
  4174. * @method isFillChar
  4175. * @param { Node } node 需要判断的节点
  4176. * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
  4177. * @returns { Boolean } 节点是否是填充节点
  4178. */
  4179. isFillChar: function (node, isInStart) {
  4180. if (node.nodeType != 3) return false
  4181. var text = node.nodeValue
  4182. if (isInStart) {
  4183. return new RegExp('^' + domUtils.fillChar).test(text)
  4184. }
  4185. return !text.replace(new RegExp(domUtils.fillChar, 'g'), '').length
  4186. },
  4187. isStartInblock: function (range) {
  4188. var tmpRange = range.cloneRange(),
  4189. flag = 0,
  4190. start = tmpRange.startContainer,
  4191. tmp
  4192. if (start.nodeType == 1 && start.childNodes[tmpRange.startOffset]) {
  4193. start = start.childNodes[tmpRange.startOffset]
  4194. var pre = start.previousSibling
  4195. while (pre && domUtils.isFillChar(pre)) {
  4196. start = pre
  4197. pre = pre.previousSibling
  4198. }
  4199. }
  4200. if (this.isFillChar(start, true) && tmpRange.startOffset == 1) {
  4201. tmpRange.setStartBefore(start)
  4202. start = tmpRange.startContainer
  4203. }
  4204. while (start && domUtils.isFillChar(start)) {
  4205. tmp = start
  4206. start = start.previousSibling
  4207. }
  4208. if (tmp) {
  4209. tmpRange.setStartBefore(tmp)
  4210. start = tmpRange.startContainer
  4211. }
  4212. if (
  4213. start.nodeType == 1 &&
  4214. domUtils.isEmptyNode(start) &&
  4215. tmpRange.startOffset == 1
  4216. ) {
  4217. tmpRange.setStart(start, 0).collapse(true)
  4218. }
  4219. while (!tmpRange.startOffset) {
  4220. start = tmpRange.startContainer
  4221. if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
  4222. flag = 1
  4223. break
  4224. }
  4225. var pre = tmpRange.startContainer.previousSibling,
  4226. tmpNode
  4227. if (!pre) {
  4228. tmpRange.setStartBefore(tmpRange.startContainer)
  4229. } else {
  4230. while (pre && domUtils.isFillChar(pre)) {
  4231. tmpNode = pre
  4232. pre = pre.previousSibling
  4233. }
  4234. if (tmpNode) {
  4235. tmpRange.setStartBefore(tmpNode)
  4236. } else {
  4237. tmpRange.setStartBefore(tmpRange.startContainer)
  4238. }
  4239. }
  4240. }
  4241. return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0
  4242. },
  4243. /**
  4244. * 判断给定的元素是否是一个空元素
  4245. * @method isEmptyBlock
  4246. * @param { Element } node 需要判断的元素
  4247. * @return { Boolean } 是否是空元素
  4248. * @example
  4249. * ```html
  4250. * <div id="test"></div>
  4251. *
  4252. * <script>
  4253. * //output: true
  4254. * console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
  4255. * </script>
  4256. * ```
  4257. */
  4258. /**
  4259. * 根据指定的判断规则判断给定的元素是否是一个空元素
  4260. * @method isEmptyBlock
  4261. * @param { Element } node 需要判断的元素
  4262. * @param { RegExp } reg 对内容执行判断的正则表达式对象
  4263. * @return { Boolean } 是否是空元素
  4264. */
  4265. isEmptyBlock: function (node, reg) {
  4266. // HaoChuan9421
  4267. if (!node) {
  4268. return
  4269. }
  4270. if (node.nodeType != 1) return 0
  4271. reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g')
  4272. if (
  4273. node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length >
  4274. 0
  4275. ) {
  4276. return 0
  4277. }
  4278. for (var n in dtd.$isNotEmpty) {
  4279. if (node.getElementsByTagName(n).length) {
  4280. return 0
  4281. }
  4282. }
  4283. return 1
  4284. },
  4285. /**
  4286. * 移动元素使得该元素的位置移动指定的偏移量的距离
  4287. * @method setViewportOffset
  4288. * @param { Element } element 需要设置偏移量的元素
  4289. * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
  4290. * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
  4291. * offset.top的距离
  4292. * @example
  4293. * ```html
  4294. * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
  4295. *
  4296. * <script>
  4297. *
  4298. * var testNode = document.getElementById("test");
  4299. *
  4300. * UE.dom.domUtils.setViewportOffset( testNode, {
  4301. * left: 200,
  4302. * top: 50
  4303. * } );
  4304. *
  4305. * //output: top: 300px; left: 100px; position: absolute;
  4306. * console.log( testNode.style.cssText );
  4307. *
  4308. * </script>
  4309. * ```
  4310. */
  4311. setViewportOffset: function (element, offset) {
  4312. var left = parseInt(element.style.left) | 0
  4313. var top = parseInt(element.style.top) | 0
  4314. var rect = element.getBoundingClientRect()
  4315. var offsetLeft = offset.left - rect.left
  4316. var offsetTop = offset.top - rect.top
  4317. if (offsetLeft) {
  4318. element.style.left = left + offsetLeft + 'px'
  4319. }
  4320. if (offsetTop) {
  4321. element.style.top = top + offsetTop + 'px'
  4322. }
  4323. },
  4324. /**
  4325. * 用“填充字符”填充节点
  4326. * @method fillNode
  4327. * @private
  4328. * @param { DomDocument } doc 填充的节点所在的docment对象
  4329. * @param { Node } node 需要填充的节点对象
  4330. * @example
  4331. * ```html
  4332. * <div id="test"></div>
  4333. *
  4334. * <script>
  4335. * var testNode = document.getElementById("test");
  4336. *
  4337. * //output: 0
  4338. * console.log( testNode.childNodes.length );
  4339. *
  4340. * UE.dom.domUtils.fillNode( document, testNode );
  4341. *
  4342. * //output: 1
  4343. * console.log( testNode.childNodes.length );
  4344. *
  4345. * </script>
  4346. * ```
  4347. */
  4348. fillNode: function (doc, node) {
  4349. var tmpNode = browser.ie
  4350. ? doc.createTextNode(domUtils.fillChar)
  4351. : doc.createElement('br')
  4352. node.innerHTML = ''
  4353. node.appendChild(tmpNode)
  4354. },
  4355. /**
  4356. * 把节点src的所有子节点追加到另一个节点tag上去
  4357. * @method moveChild
  4358. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  4359. * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
  4360. * @example
  4361. * ```html
  4362. * <div id="test1">
  4363. * <span></span>
  4364. * </div>
  4365. * <div id="test2">
  4366. * <div></div>
  4367. * </div>
  4368. *
  4369. * <script>
  4370. *
  4371. * var test1 = document.getElementById("test1"),
  4372. * test2 = document.getElementById("test2");
  4373. *
  4374. * UE.dom.domUtils.moveChild( test1, test2 );
  4375. *
  4376. * //output: ""(空字符串)
  4377. * console.log( test1.innerHTML );
  4378. *
  4379. * //output: "<div></div><span></span>"
  4380. * console.log( test2.innerHTML );
  4381. *
  4382. * </script>
  4383. * ```
  4384. */
  4385. /**
  4386. * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
  4387. * @method moveChild
  4388. * @param { Node } src 源节点, 该节点下的所有子节点将被移除
  4389. * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
  4390. * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
  4391. * @example
  4392. * ```html
  4393. * <div id="test1">
  4394. * <span></span>
  4395. * </div>
  4396. * <div id="test2">
  4397. * <div></div>
  4398. * </div>
  4399. *
  4400. * <script>
  4401. *
  4402. * var test1 = document.getElementById("test1"),
  4403. * test2 = document.getElementById("test2");
  4404. *
  4405. * UE.dom.domUtils.moveChild( test1, test2, true );
  4406. *
  4407. * //output: ""(空字符串)
  4408. * console.log( test1.innerHTML );
  4409. *
  4410. * //output: "<span></span><div></div>"
  4411. * console.log( test2.innerHTML );
  4412. *
  4413. * </script>
  4414. * ```
  4415. */
  4416. moveChild: function (src, tag, dir) {
  4417. while (src.firstChild) {
  4418. if (dir && tag.firstChild) {
  4419. tag.insertBefore(src.lastChild, tag.firstChild)
  4420. } else {
  4421. tag.appendChild(src.firstChild)
  4422. }
  4423. }
  4424. },
  4425. /**
  4426. * 判断节点的标签上是否不存在任何属性
  4427. * @method hasNoAttributes
  4428. * @private
  4429. * @param { Node } node 需要检测的节点对象
  4430. * @return { Boolean } 节点是否不包含任何属性
  4431. * @example
  4432. * ```html
  4433. * <div id="test"><span>xxxx</span></div>
  4434. *
  4435. * <script>
  4436. *
  4437. * //output: false
  4438. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
  4439. *
  4440. * //output: true
  4441. * console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
  4442. *
  4443. * </script>
  4444. * ```
  4445. */
  4446. hasNoAttributes: function (node) {
  4447. return browser.ie
  4448. ? /^<\w+\s*?>/.test(node.outerHTML)
  4449. : node.attributes.length == 0
  4450. },
  4451. /**
  4452. * 检测节点是否是UEditor所使用的辅助节点
  4453. * @method isCustomeNode
  4454. * @private
  4455. * @param { Node } node 需要检测的节点
  4456. * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
  4457. * @return { Boolean } 给定的节点是否是一个辅助节点
  4458. */
  4459. isCustomeNode: function (node) {
  4460. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_')
  4461. },
  4462. /**
  4463. * 检测节点的标签是否是给定的标签
  4464. * @method isTagNode
  4465. * @param { Node } node 需要检测的节点对象
  4466. * @param { String } tagName 标签
  4467. * @return { Boolean } 节点的标签是否是给定的标签
  4468. * @example
  4469. * ```html
  4470. * <div id="test"></div>
  4471. *
  4472. * <script>
  4473. *
  4474. * //output: true
  4475. * console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
  4476. *
  4477. * </script>
  4478. * ```
  4479. */
  4480. isTagNode: function (node, tagNames) {
  4481. return (
  4482. node.nodeType == 1 &&
  4483. new RegExp('\\b' + node.tagName + '\\b', 'i').test(tagNames)
  4484. )
  4485. },
  4486. /**
  4487. * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
  4488. * @method filterNodeList
  4489. * @param { Array } nodeList 需要过滤的节点数组
  4490. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  4491. * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
  4492. * @example
  4493. * ```javascript
  4494. * var divNodes = document.getElementsByTagName("div");
  4495. * divNodes = [].slice.call( divNodes, 0 );
  4496. *
  4497. * //output: null
  4498. * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4499. * return node.tagName.toLowerCase() !== 'div';
  4500. * } ) );
  4501. * ```
  4502. */
  4503. /**
  4504. * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
  4505. * @method filterNodeList
  4506. * @param { Array } nodeList 需要过滤的节点数组
  4507. * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
  4508. * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
  4509. * @example
  4510. * ```javascript
  4511. * var divNodes = document.getElementsByTagName("div");
  4512. * divNodes = [].slice.call( divNodes, 0 );
  4513. *
  4514. * //output: null
  4515. * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
  4516. * ```
  4517. */
  4518. /**
  4519. * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
  4520. * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
  4521. * @method filterNodeList
  4522. * @param { Array } nodeList 需要过滤的节点数组
  4523. * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
  4524. * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
  4525. * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
  4526. * 过滤条件的节点数组或第一个节点, 否则返回NULL
  4527. * @example
  4528. * ```javascript
  4529. * var divNodes = document.getElementsByTagName("div");
  4530. * divNodes = [].slice.call( divNodes, 0 );
  4531. *
  4532. * //output: 3(假定有3个div)
  4533. * console.log( divNodes.length );
  4534. *
  4535. * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4536. * return node.tagName.toLowerCase() === 'div';
  4537. * }, true );
  4538. *
  4539. * //output: 3
  4540. * console.log( nodes.length );
  4541. *
  4542. * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
  4543. * return node.tagName.toLowerCase() === 'div';
  4544. * }, false );
  4545. *
  4546. * //output: div
  4547. * console.log( node.nodeName );
  4548. * ```
  4549. */
  4550. filterNodeList: function (nodelist, filter, forAll) {
  4551. var results = []
  4552. if (!utils.isFunction(filter)) {
  4553. var str = filter
  4554. filter = function (n) {
  4555. return (
  4556. utils.indexOf(
  4557. utils.isArray(str) ? str : str.split(' '),
  4558. n.tagName.toLowerCase()
  4559. ) != -1
  4560. )
  4561. }
  4562. }
  4563. utils.each(nodelist, function (n) {
  4564. filter(n) && results.push(n)
  4565. })
  4566. return results.length == 0
  4567. ? null
  4568. : results.length == 1 || !forAll
  4569. ? results[0]
  4570. : results
  4571. },
  4572. /**
  4573. * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
  4574. * @method isInNodeEndBoundary
  4575. * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
  4576. * @param node 需要检测的节点对象
  4577. * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
  4578. */
  4579. isInNodeEndBoundary: function (rng, node) {
  4580. var start = rng.startContainer
  4581. if (start.nodeType == 3 && rng.startOffset != start.nodeValue.length) {
  4582. return 0
  4583. }
  4584. if (start.nodeType == 1 && rng.startOffset != start.childNodes.length) {
  4585. return 0
  4586. }
  4587. while (start !== node) {
  4588. if (start.nextSibling) {
  4589. return 0
  4590. }
  4591. start = start.parentNode
  4592. }
  4593. return 1
  4594. },
  4595. isBoundaryNode: function (node, dir) {
  4596. var tmp
  4597. while (!domUtils.isBody(node)) {
  4598. tmp = node
  4599. node = node.parentNode
  4600. if (tmp !== node[dir]) {
  4601. return false
  4602. }
  4603. }
  4604. return true
  4605. },
  4606. fillHtml: browser.ie11below ? '&nbsp;' : '<br/>'
  4607. })
  4608. var fillCharReg = new RegExp(domUtils.fillChar, 'g')
  4609. // core/Range.js
  4610. /**
  4611. * Range封装
  4612. * @file
  4613. * @module UE.dom
  4614. * @class Range
  4615. * @since 1.2.6.1
  4616. */
  4617. /**
  4618. * dom操作封装
  4619. * @unfile
  4620. * @module UE.dom
  4621. */
  4622. /**
  4623. * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
  4624. * @unfile
  4625. * @module UE.dom
  4626. * @class Range
  4627. */
  4628. ;(function () {
  4629. var guid = 0,
  4630. fillChar = domUtils.fillChar,
  4631. fillData
  4632. /**
  4633. * 更新range的collapse状态
  4634. * @param {Range} range range对象
  4635. */
  4636. function updateCollapse(range) {
  4637. range.collapsed =
  4638. range.startContainer &&
  4639. range.endContainer &&
  4640. range.startContainer === range.endContainer &&
  4641. range.startOffset == range.endOffset
  4642. }
  4643. function selectOneNode(rng) {
  4644. return (
  4645. !rng.collapsed &&
  4646. rng.startContainer.nodeType == 1 &&
  4647. rng.startContainer === rng.endContainer &&
  4648. rng.endOffset - rng.startOffset == 1
  4649. )
  4650. }
  4651. function setEndPoint(toStart, node, offset, range) {
  4652. //如果node是自闭合标签要处理
  4653. if (
  4654. node.nodeType == 1 &&
  4655. (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])
  4656. ) {
  4657. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1)
  4658. node = node.parentNode
  4659. }
  4660. if (toStart) {
  4661. range.startContainer = node
  4662. range.startOffset = offset
  4663. if (!range.endContainer) {
  4664. range.collapse(true)
  4665. }
  4666. } else {
  4667. range.endContainer = node
  4668. range.endOffset = offset
  4669. if (!range.startContainer) {
  4670. range.collapse(false)
  4671. }
  4672. }
  4673. updateCollapse(range)
  4674. return range
  4675. }
  4676. function execContentsAction(range, action) {
  4677. //调整边界
  4678. //range.includeBookmark();
  4679. var start = range.startContainer,
  4680. end = range.endContainer,
  4681. startOffset = range.startOffset,
  4682. endOffset = range.endOffset,
  4683. doc = range.document,
  4684. frag = doc.createDocumentFragment(),
  4685. tmpStart,
  4686. tmpEnd
  4687. if (start.nodeType == 1) {
  4688. start =
  4689. start.childNodes[startOffset] ||
  4690. (tmpStart = start.appendChild(doc.createTextNode('')))
  4691. }
  4692. if (end.nodeType == 1) {
  4693. end =
  4694. end.childNodes[endOffset] ||
  4695. (tmpEnd = end.appendChild(doc.createTextNode('')))
  4696. }
  4697. if (start === end && start.nodeType == 3) {
  4698. frag.appendChild(
  4699. doc.createTextNode(
  4700. start.substringData(startOffset, endOffset - startOffset)
  4701. )
  4702. )
  4703. //is not clone
  4704. if (action) {
  4705. start.deleteData(startOffset, endOffset - startOffset)
  4706. range.collapse(true)
  4707. }
  4708. return frag
  4709. }
  4710. var current,
  4711. currentLevel,
  4712. clone = frag,
  4713. startParents = domUtils.findParents(start, true),
  4714. endParents = domUtils.findParents(end, true)
  4715. for (var i = 0; startParents[i] == endParents[i]; ) {
  4716. i++
  4717. }
  4718. for (var j = i, si; (si = startParents[j]); j++) {
  4719. current = si.nextSibling
  4720. if (si == start) {
  4721. if (!tmpStart) {
  4722. if (range.startContainer.nodeType == 3) {
  4723. clone.appendChild(
  4724. doc.createTextNode(start.nodeValue.slice(startOffset))
  4725. )
  4726. //is not clone
  4727. if (action) {
  4728. start.deleteData(
  4729. startOffset,
  4730. start.nodeValue.length - startOffset
  4731. )
  4732. }
  4733. } else {
  4734. clone.appendChild(!action ? start.cloneNode(true) : start)
  4735. }
  4736. }
  4737. } else {
  4738. currentLevel = si.cloneNode(false)
  4739. clone.appendChild(currentLevel)
  4740. }
  4741. while (current) {
  4742. if (current === end || current === endParents[j]) {
  4743. break
  4744. }
  4745. si = current.nextSibling
  4746. clone.appendChild(!action ? current.cloneNode(true) : current)
  4747. current = si
  4748. }
  4749. clone = currentLevel
  4750. }
  4751. clone = frag
  4752. if (!startParents[i]) {
  4753. clone.appendChild(startParents[i - 1].cloneNode(false))
  4754. clone = clone.firstChild
  4755. }
  4756. for (var j = i, ei; (ei = endParents[j]); j++) {
  4757. current = ei.previousSibling
  4758. if (ei == end) {
  4759. if (!tmpEnd && range.endContainer.nodeType == 3) {
  4760. clone.appendChild(
  4761. doc.createTextNode(end.substringData(0, endOffset))
  4762. )
  4763. //is not clone
  4764. if (action) {
  4765. end.deleteData(0, endOffset)
  4766. }
  4767. }
  4768. } else {
  4769. currentLevel = ei.cloneNode(false)
  4770. clone.appendChild(currentLevel)
  4771. }
  4772. //如果两端同级,右边第一次已经被开始做了
  4773. if (j != i || !startParents[i]) {
  4774. while (current) {
  4775. if (current === start) {
  4776. break
  4777. }
  4778. ei = current.previousSibling
  4779. clone.insertBefore(
  4780. !action ? current.cloneNode(true) : current,
  4781. clone.firstChild
  4782. )
  4783. current = ei
  4784. }
  4785. }
  4786. clone = currentLevel
  4787. }
  4788. if (action) {
  4789. range
  4790. .setStartBefore(
  4791. !endParents[i]
  4792. ? endParents[i - 1]
  4793. : !startParents[i]
  4794. ? startParents[i - 1]
  4795. : endParents[i]
  4796. )
  4797. .collapse(true)
  4798. }
  4799. tmpStart && domUtils.remove(tmpStart)
  4800. tmpEnd && domUtils.remove(tmpEnd)
  4801. return frag
  4802. }
  4803. /**
  4804. * 创建一个跟document绑定的空的Range实例
  4805. * @constructor
  4806. * @param { Document } document 新建的选区所属的文档对象
  4807. */
  4808. /**
  4809. * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
  4810. */
  4811. /**
  4812. * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
  4813. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4814. */
  4815. /**
  4816. * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
  4817. */
  4818. /**
  4819. * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
  4820. * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
  4821. */
  4822. /**
  4823. * @property { Boolean } collapsed 当前Range是否闭合
  4824. * @default true
  4825. * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
  4826. */
  4827. /**
  4828. * @property { Document } document 当前Range所属的Document对象
  4829. * @remind 不同range的的document属性可以是不同的
  4830. */
  4831. var Range = (dom.Range = function (document) {
  4832. var me = this
  4833. me.startContainer = me.startOffset = me.endContainer = me.endOffset = null
  4834. me.document = document
  4835. me.collapsed = true
  4836. })
  4837. /**
  4838. * 删除fillData
  4839. * @param doc
  4840. * @param excludeNode
  4841. */
  4842. function removeFillData(doc, excludeNode) {
  4843. try {
  4844. if (fillData && domUtils.inDoc(fillData, doc)) {
  4845. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  4846. var tmpNode = fillData.parentNode
  4847. domUtils.remove(fillData)
  4848. while (
  4849. tmpNode &&
  4850. domUtils.isEmptyInlineElement(tmpNode) &&
  4851. //safari的contains有bug
  4852. (browser.safari
  4853. ? !(
  4854. domUtils.getPosition(tmpNode, excludeNode) &
  4855. domUtils.POSITION_CONTAINS
  4856. )
  4857. : !tmpNode.contains(excludeNode))
  4858. ) {
  4859. fillData = tmpNode.parentNode
  4860. domUtils.remove(tmpNode)
  4861. tmpNode = fillData
  4862. }
  4863. } else {
  4864. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '')
  4865. }
  4866. }
  4867. } catch (e) {}
  4868. }
  4869. /**
  4870. * @param node
  4871. * @param dir
  4872. */
  4873. function mergeSibling(node, dir) {
  4874. var tmpNode
  4875. node = node[dir]
  4876. while (node && domUtils.isFillChar(node)) {
  4877. tmpNode = node[dir]
  4878. domUtils.remove(node)
  4879. node = tmpNode
  4880. }
  4881. }
  4882. Range.prototype = {
  4883. /**
  4884. * 克隆选区的内容到一个DocumentFragment里
  4885. * @method cloneContents
  4886. * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
  4887. * @example
  4888. * ```html
  4889. * <body>
  4890. * <!-- 中括号表示选区 -->
  4891. * <b>x<i>x[x</i>xx]x</b>
  4892. *
  4893. * <script>
  4894. * //range是已选中的选区
  4895. * var fragment = range.cloneContents(),
  4896. * node = document.createElement("div");
  4897. *
  4898. * node.appendChild( fragment );
  4899. *
  4900. * //output: <i>x</i>xx
  4901. * console.log( node.innerHTML );
  4902. *
  4903. * </script>
  4904. * </body>
  4905. * ```
  4906. */
  4907. cloneContents: function () {
  4908. return this.collapsed ? null : execContentsAction(this, 0)
  4909. },
  4910. /**
  4911. * 删除当前选区范围中的所有内容
  4912. * @method deleteContents
  4913. * @remind 执行完该操作后, 当前Range对象变成了闭合状态
  4914. * @return { UE.dom.Range } 当前操作的Range对象
  4915. * @example
  4916. * ```html
  4917. * <body>
  4918. * <!-- 中括号表示选区 -->
  4919. * <b>x<i>x[x</i>xx]x</b>
  4920. *
  4921. * <script>
  4922. * //range是已选中的选区
  4923. * range.deleteContents();
  4924. *
  4925. * //竖线表示闭合后的选区位置
  4926. * //output: <b>x<i>x</i>|x</b>
  4927. * console.log( document.body.innerHTML );
  4928. *
  4929. * //此时, range的各项属性为
  4930. * //output: B
  4931. * console.log( range.startContainer.tagName );
  4932. * //output: 2
  4933. * console.log( range.startOffset );
  4934. * //output: B
  4935. * console.log( range.endContainer.tagName );
  4936. * //output: 2
  4937. * console.log( range.endOffset );
  4938. * //output: true
  4939. * console.log( range.collapsed );
  4940. *
  4941. * </script>
  4942. * </body>
  4943. * ```
  4944. */
  4945. deleteContents: function () {
  4946. var txt
  4947. if (!this.collapsed) {
  4948. execContentsAction(this, 1)
  4949. }
  4950. if (browser.webkit) {
  4951. txt = this.startContainer
  4952. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  4953. this.setStartBefore(txt).collapse(true)
  4954. domUtils.remove(txt)
  4955. }
  4956. }
  4957. return this
  4958. },
  4959. /**
  4960. * 将当前选区的内容提取到一个DocumentFragment里
  4961. * @method extractContents
  4962. * @remind 执行该操作后, 选区将变成闭合状态
  4963. * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
  4964. * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
  4965. * @example
  4966. * ```html
  4967. * <body>
  4968. * <!-- 中括号表示选区 -->
  4969. * <b>x<i>x[x</i>xx]x</b>
  4970. *
  4971. * <script>
  4972. * //range是已选中的选区
  4973. * var fragment = range.extractContents(),
  4974. * node = document.createElement( "div" );
  4975. *
  4976. * node.appendChild( fragment );
  4977. *
  4978. * //竖线表示闭合后的选区位置
  4979. *
  4980. * //output: <b>x<i>x</i>|x</b>
  4981. * console.log( document.body.innerHTML );
  4982. * //output: <i>x</i>xx
  4983. * console.log( node.innerHTML );
  4984. *
  4985. * //此时, range的各项属性为
  4986. * //output: B
  4987. * console.log( range.startContainer.tagName );
  4988. * //output: 2
  4989. * console.log( range.startOffset );
  4990. * //output: B
  4991. * console.log( range.endContainer.tagName );
  4992. * //output: 2
  4993. * console.log( range.endOffset );
  4994. * //output: true
  4995. * console.log( range.collapsed );
  4996. *
  4997. * </script>
  4998. * </body>
  4999. */
  5000. extractContents: function () {
  5001. return this.collapsed ? null : execContentsAction(this, 2)
  5002. },
  5003. /**
  5004. * 设置Range的开始容器节点和偏移量
  5005. * @method setStart
  5006. * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
  5007. * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
  5008. * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
  5009. * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
  5010. * 中的索引
  5011. * @param { Node } node 将被设为当前选区开始边界容器的节点对象
  5012. * @param { int } offset 选区的开始位置偏移量
  5013. * @return { UE.dom.Range } 当前range对象
  5014. * @example
  5015. * ```html
  5016. * <!-- 选区 -->
  5017. * <b>xxx<i>x<span>xx</span>xx<em>xx</em>xxx</i>[xxx]</b>
  5018. *
  5019. * <script>
  5020. *
  5021. * //执行操作
  5022. * range.setStart( document.getElementsByTagName("i")[0], 1 );
  5023. *
  5024. * //此时, 选区变成了
  5025. * //<b>xxx<i>x[<span>xx</span>xx<em>xx</em>xxx</i>xxx]</b>
  5026. *
  5027. * </script>
  5028. * ```
  5029. * @example
  5030. * ```html
  5031. * <!-- 选区 -->
  5032. * <b>xxx<img>[xx]x</b>
  5033. *
  5034. * <script>
  5035. *
  5036. * //执行操作
  5037. * range.setStart( document.getElementsByTagName("img")[0], 3 );
  5038. *
  5039. * //此时, 选区变成了
  5040. * //<b>xxx[<img>xx]x</b>
  5041. *
  5042. * </script>
  5043. * ```
  5044. */
  5045. setStart: function (node, offset) {
  5046. return setEndPoint(true, node, offset, this)
  5047. },
  5048. /**
  5049. * 设置Range的结束容器和偏移量
  5050. * @method setEnd
  5051. * @param { Node } node 作为当前选区结束边界容器的节点对象
  5052. * @param { int } offset 结束边界的偏移量
  5053. * @see UE.dom.Range:setStart(Node,int)
  5054. * @return { UE.dom.Range } 当前range对象
  5055. */
  5056. setEnd: function (node, offset) {
  5057. return setEndPoint(false, node, offset, this)
  5058. },
  5059. /**
  5060. * 将Range开始位置设置到node节点之后
  5061. * @method setStartAfter
  5062. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
  5063. * @param { Node } node 选区的开始边界将紧接着该节点之后
  5064. * @return { UE.dom.Range } 当前range对象
  5065. * @example
  5066. * ```html
  5067. * <!-- 选区示例 -->
  5068. * <b>xx<i>xxx</i><span>xx[x</span>xxx]</b>
  5069. *
  5070. * <script>
  5071. *
  5072. * //执行操作
  5073. * range.setStartAfter( document.getElementsByTagName("i")[0] );
  5074. *
  5075. * //结果选区
  5076. * //<b>xx<i>xxx</i>[<span>xxx</span>xxx]</b>
  5077. *
  5078. * </script>
  5079. * ```
  5080. */
  5081. setStartAfter: function (node) {
  5082. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1)
  5083. },
  5084. /**
  5085. * 将Range开始位置设置到node节点之前
  5086. * @method setStartBefore
  5087. * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
  5088. * @param { Node } node 新的选区开始位置在该节点之前
  5089. * @see UE.dom.Range:setStartAfter(Node)
  5090. * @return { UE.dom.Range } 当前range对象
  5091. */
  5092. setStartBefore: function (node) {
  5093. return this.setStart(node.parentNode, domUtils.getNodeIndex(node))
  5094. },
  5095. /**
  5096. * 将Range结束位置设置到node节点之后
  5097. * @method setEndAfter
  5098. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
  5099. * @param { Node } node 目标节点
  5100. * @see UE.dom.Range:setStartAfter(Node)
  5101. * @return { UE.dom.Range } 当前range对象
  5102. * @example
  5103. * ```html
  5104. * <!-- 选区示例 -->
  5105. * <b>[xx<i>xxx</i><span>xx]x</span>xxx</b>
  5106. *
  5107. * <script>
  5108. *
  5109. * //执行操作
  5110. * range.setStartAfter( document.getElementsByTagName("span")[0] );
  5111. *
  5112. * //结果选区
  5113. * //<b>[xx<i>xxx</i><span>xxx</span>]xxx</b>
  5114. *
  5115. * </script>
  5116. * ```
  5117. */
  5118. setEndAfter: function (node) {
  5119. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1)
  5120. },
  5121. /**
  5122. * 将Range结束位置设置到node节点之前
  5123. * @method setEndBefore
  5124. * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
  5125. * @param { Node } node 目标节点
  5126. * @see UE.dom.Range:setEndAfter(Node)
  5127. * @return { UE.dom.Range } 当前range对象
  5128. */
  5129. setEndBefore: function (node) {
  5130. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node))
  5131. },
  5132. /**
  5133. * 设置Range的开始位置到node节点内的第一个子节点之前
  5134. * @method setStartAtFirst
  5135. * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
  5136. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  5137. * @param { Node } node 目标节点
  5138. * @see UE.dom.Range:setStartBefore(Node)
  5139. * @return { UE.dom.Range } 当前range对象
  5140. * @example
  5141. * ```html
  5142. * <!-- 选区示例 -->
  5143. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  5144. *
  5145. * <script>
  5146. *
  5147. * //执行操作
  5148. * range.setStartAtFirst( document.getElementsByTagName("i")[0] );
  5149. *
  5150. * //结果选区
  5151. * //<b>xx<i>[xxx</i><span>xx]x</span>xxx</b>
  5152. *
  5153. * </script>
  5154. * ```
  5155. */
  5156. setStartAtFirst: function (node) {
  5157. return this.setStart(node, 0)
  5158. },
  5159. /**
  5160. * 设置Range的开始位置到node节点内的最后一个节点之后
  5161. * @method setStartAtLast
  5162. * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
  5163. * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
  5164. * @param { Node } node 目标节点
  5165. * @see UE.dom.Range:setStartAtFirst(Node)
  5166. * @return { UE.dom.Range } 当前range对象
  5167. */
  5168. setStartAtLast: function (node) {
  5169. return this.setStart(
  5170. node,
  5171. node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
  5172. )
  5173. },
  5174. /**
  5175. * 设置Range的结束位置到node节点内的第一个节点之前
  5176. * @method setEndAtFirst
  5177. * @param { Node } node 目标节点
  5178. * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
  5179. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  5180. * @see UE.dom.Range:setStartAtFirst(Node)
  5181. * @return { UE.dom.Range } 当前range对象
  5182. */
  5183. setEndAtFirst: function (node) {
  5184. return this.setEnd(node, 0)
  5185. },
  5186. /**
  5187. * 设置Range的结束位置到node节点内的最后一个节点之后
  5188. * @method setEndAtLast
  5189. * @param { Node } node 目标节点
  5190. * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
  5191. * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
  5192. * @see UE.dom.Range:setStartAtFirst(Node)
  5193. * @return { UE.dom.Range } 当前range对象
  5194. */
  5195. setEndAtLast: function (node) {
  5196. return this.setEnd(
  5197. node,
  5198. node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
  5199. )
  5200. },
  5201. /**
  5202. * 选中给定节点
  5203. * @method selectNode
  5204. * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
  5205. * 而endOffset为startOffset+1
  5206. * @param { Node } node 需要选中的节点
  5207. * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
  5208. * @example
  5209. * ```html
  5210. * <!-- 选区示例 -->
  5211. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  5212. *
  5213. * <script>
  5214. *
  5215. * //执行操作
  5216. * range.selectNode( document.getElementsByTagName("i")[0] );
  5217. *
  5218. * //结果选区
  5219. * //<b>xx[<i>xxx</i>]<span>xxx</span>xxx</b>
  5220. *
  5221. * </script>
  5222. * ```
  5223. */
  5224. selectNode: function (node) {
  5225. return this.setStartBefore(node).setEndAfter(node)
  5226. },
  5227. /**
  5228. * 选中给定节点内部的所有节点
  5229. * @method selectNodeContents
  5230. * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
  5231. * 而endOffset是该节点的子节点数。
  5232. * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
  5233. * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
  5234. * @example
  5235. * ```html
  5236. * <!-- 选区示例 -->
  5237. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  5238. *
  5239. * <script>
  5240. *
  5241. * //执行操作
  5242. * range.selectNode( document.getElementsByTagName("b")[0] );
  5243. *
  5244. * //结果选区
  5245. * //<b>[xx<i>xxx</i><span>xxx</span>xxx]</b>
  5246. *
  5247. * </script>
  5248. * ```
  5249. */
  5250. selectNodeContents: function (node) {
  5251. return this.setStart(node, 0).setEndAtLast(node)
  5252. },
  5253. /**
  5254. * clone当前Range对象
  5255. * @method cloneRange
  5256. * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
  5257. * @return { UE.dom.Range } 当前range对象的一个副本
  5258. */
  5259. cloneRange: function () {
  5260. var me = this
  5261. return new Range(me.document)
  5262. .setStart(me.startContainer, me.startOffset)
  5263. .setEnd(me.endContainer, me.endOffset)
  5264. },
  5265. /**
  5266. * 向当前选区的结束处闭合选区
  5267. * @method collapse
  5268. * @return { UE.dom.Range } 当前range对象
  5269. * @example
  5270. * ```html
  5271. * <!-- 选区示例 -->
  5272. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  5273. *
  5274. * <script>
  5275. *
  5276. * //执行操作
  5277. * range.collapse();
  5278. *
  5279. * //结果选区
  5280. * //“|”表示选区已闭合
  5281. * //<b>xx<i>xxx</i><span>xx|x</span>xxx</b>
  5282. *
  5283. * </script>
  5284. * ```
  5285. */
  5286. /**
  5287. * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
  5288. * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
  5289. * @method collapse
  5290. * @param { Boolean } toStart 是否向选区开始处闭合
  5291. * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
  5292. * @see UE.dom.Range:collapse()
  5293. * @example
  5294. * ```html
  5295. * <!-- 选区示例 -->
  5296. * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
  5297. *
  5298. * <script>
  5299. *
  5300. * //执行操作
  5301. * range.collapse( true );
  5302. *
  5303. * //结果选区
  5304. * //“|”表示选区已闭合
  5305. * //<b>xx<i>xxx</i><span>|xxx</span>xxx</b>
  5306. *
  5307. * </script>
  5308. * ```
  5309. */
  5310. collapse: function (toStart) {
  5311. var me = this
  5312. if (toStart) {
  5313. me.endContainer = me.startContainer
  5314. me.endOffset = me.startOffset
  5315. } else {
  5316. me.startContainer = me.endContainer
  5317. me.startOffset = me.endOffset
  5318. }
  5319. me.collapsed = true
  5320. return me
  5321. },
  5322. /**
  5323. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
  5324. * @method shrinkBoundary
  5325. * @return { UE.dom.Range } 当前range对象
  5326. * @example
  5327. * ```html
  5328. * <span>xx<b>xx[</b>xxxxx]</span> => <span>xx<b>xx</b>[xxxxx]</span>
  5329. * ```
  5330. *
  5331. * @example
  5332. * ```html
  5333. * <!-- 选区示例 -->
  5334. * <b>x[xx</b><i>]xxx</i>
  5335. *
  5336. * <script>
  5337. *
  5338. * //执行收缩
  5339. * range.shrinkBoundary();
  5340. *
  5341. * //结果选区
  5342. * //<b>x[xx]</b><i>xxx</i>
  5343. * </script>
  5344. * ```
  5345. *
  5346. * @example
  5347. * ```html
  5348. * [<b><i>xxxx</i>xxxxxxx</b>] => <b><i>[xxxx</i>xxxxxxx]</b>
  5349. * ```
  5350. */
  5351. /**
  5352. * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
  5353. * 如果ignoreEnd的值为true,则忽略对结束位置的调整
  5354. * @method shrinkBoundary
  5355. * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
  5356. * @return { UE.dom.Range } 当前range对象
  5357. * @see UE.dom.domUtils.Range:shrinkBoundary()
  5358. */
  5359. shrinkBoundary: function (ignoreEnd) {
  5360. var me = this,
  5361. child,
  5362. collapsed = me.collapsed
  5363. function check(node) {
  5364. return (
  5365. node.nodeType == 1 &&
  5366. !domUtils.isBookmarkNode(node) &&
  5367. !dtd.$empty[node.tagName] &&
  5368. !dtd.$nonChild[node.tagName]
  5369. )
  5370. }
  5371. while (
  5372. me.startContainer.nodeType == 1 && //是element
  5373. (child = me.startContainer.childNodes[me.startOffset]) && //子节点也是element
  5374. check(child)
  5375. ) {
  5376. me.setStart(child, 0)
  5377. }
  5378. if (collapsed) {
  5379. return me.collapse(true)
  5380. }
  5381. if (!ignoreEnd) {
  5382. while (
  5383. me.endContainer.nodeType == 1 && //是element
  5384. me.endOffset > 0 && //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  5385. (child = me.endContainer.childNodes[me.endOffset - 1]) && //子节点也是element
  5386. check(child)
  5387. ) {
  5388. me.setEnd(child, child.childNodes.length)
  5389. }
  5390. }
  5391. return me
  5392. },
  5393. /**
  5394. * 获取离当前选区内包含的所有节点最近的公共祖先节点,
  5395. * @method getCommonAncestor
  5396. * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
  5397. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5398. * @example
  5399. * ```html
  5400. * //选区示例
  5401. * <span>xxx<b>x[x<em>xx]x</em>xxx</b>xx</span>
  5402. * <script>
  5403. *
  5404. * var node = range.getCommonAncestor();
  5405. *
  5406. * //公共祖先节点是: b节点
  5407. * //输出: B
  5408. * console.log(node.tagName);
  5409. *
  5410. * </script>
  5411. * ```
  5412. */
  5413. /**
  5414. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  5415. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  5416. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
  5417. * @method getCommonAncestor
  5418. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  5419. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5420. * @see UE.dom.Range:getCommonAncestor()
  5421. * @example
  5422. * ```html
  5423. * <body>
  5424. *
  5425. * <!-- 选区示例 -->
  5426. * <b>xxx<i>xxxx<span>xx[x</span>xx]x</i>xxxxxxx</b>
  5427. *
  5428. * <script>
  5429. *
  5430. * var node = range.getCommonAncestor( false );
  5431. *
  5432. * //这里的公共祖先节点是B而不是I, 是因为参数限制了获取到的节点不能是容器节点
  5433. * //output: B
  5434. * console.log( node.tagName );
  5435. *
  5436. * </script>
  5437. *
  5438. * </body>
  5439. * ```
  5440. */
  5441. /**
  5442. * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
  5443. * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
  5444. * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
  5445. * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
  5446. * @method getCommonAncestor
  5447. * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
  5448. * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
  5449. * @return { Node } 当前range对象内所有节点的公共祖先节点
  5450. * @see UE.dom.Range:getCommonAncestor()
  5451. * @see UE.dom.Range:getCommonAncestor(Boolean)
  5452. * @example
  5453. * ```html
  5454. * <body>
  5455. *
  5456. * <!-- 选区示例 -->
  5457. * <b>xxx<i>xxxx<span>x[x]x</span>xxx</i>xxxxxxx</b>
  5458. *
  5459. * <script>
  5460. *
  5461. * var node = range.getCommonAncestor( true, false );
  5462. *
  5463. * //output: SPAN
  5464. * console.log( node.tagName );
  5465. *
  5466. * </script>
  5467. *
  5468. * </body>
  5469. * ```
  5470. */
  5471. getCommonAncestor: function (includeSelf, ignoreTextNode) {
  5472. var me = this,
  5473. start = me.startContainer,
  5474. end = me.endContainer
  5475. if (start === end) {
  5476. if (includeSelf && selectOneNode(this)) {
  5477. start = start.childNodes[me.startOffset]
  5478. if (start.nodeType == 1) return start
  5479. }
  5480. //只有在上来就相等的情况下才会出现是文本的情况
  5481. return ignoreTextNode && start.nodeType == 3
  5482. ? start.parentNode
  5483. : start
  5484. }
  5485. return domUtils.getCommonAncestor(start, end)
  5486. },
  5487. /**
  5488. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
  5489. * @method trimBoundary
  5490. * @remind 该操作有可能会引起文本节点被切开
  5491. * @return { UE.dom.Range } 当前range对象
  5492. * @example
  5493. * ```html
  5494. *
  5495. * //选区示例
  5496. * <b>xxx<i>[xxxxx]</i>xxx</b>
  5497. *
  5498. * <script>
  5499. * //未调整前, 选区的开始容器和结束都是文本节点
  5500. * //执行调整
  5501. * range.trimBoundary();
  5502. *
  5503. * //调整之后, 容器节点变成了i节点
  5504. * //<b>xxx[<i>xxxxx</i>]xxx</b>
  5505. * </script>
  5506. * ```
  5507. */
  5508. /**
  5509. * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
  5510. * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
  5511. * @method trimBoundary
  5512. * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
  5513. * @return { UE.dom.Range } 当前range对象
  5514. * @example
  5515. * ```html
  5516. *
  5517. * //选区示例
  5518. * <b>xxx<i>[xxxxx]</i>xxx</b>
  5519. *
  5520. * <script>
  5521. * //未调整前, 选区的开始容器和结束都是文本节点
  5522. * //执行调整
  5523. * range.trimBoundary( true );
  5524. *
  5525. * //调整之后, 开始容器节点变成了i节点
  5526. * //但是, 结束容器没有发生变化
  5527. * //<b>xxx[<i>xxxxx]</i>xxx</b>
  5528. * </script>
  5529. * ```
  5530. */
  5531. trimBoundary: function (ignoreEnd) {
  5532. this.txtToElmBoundary()
  5533. var start = this.startContainer,
  5534. offset = this.startOffset,
  5535. collapsed = this.collapsed,
  5536. end = this.endContainer
  5537. if (start.nodeType == 3) {
  5538. if (offset == 0) {
  5539. this.setStartBefore(start)
  5540. } else {
  5541. if (offset >= start.nodeValue.length) {
  5542. this.setStartAfter(start)
  5543. } else {
  5544. var textNode = domUtils.split(start, offset)
  5545. //跟新结束边界
  5546. if (start === end) {
  5547. this.setEnd(textNode, this.endOffset - offset)
  5548. } else if (start.parentNode === end) {
  5549. this.endOffset += 1
  5550. }
  5551. this.setStartBefore(textNode)
  5552. }
  5553. }
  5554. if (collapsed) {
  5555. return this.collapse(true)
  5556. }
  5557. }
  5558. if (!ignoreEnd) {
  5559. offset = this.endOffset
  5560. end = this.endContainer
  5561. if (end.nodeType == 3) {
  5562. if (offset == 0) {
  5563. this.setEndBefore(end)
  5564. } else {
  5565. offset < end.nodeValue.length && domUtils.split(end, offset)
  5566. this.setEndAfter(end)
  5567. }
  5568. }
  5569. }
  5570. return this
  5571. },
  5572. /**
  5573. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
  5574. * @method txtToElmBoundary
  5575. * @remind 该操作不会修改dom节点
  5576. * @return { UE.dom.Range } 当前range对象
  5577. */
  5578. /**
  5579. * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
  5580. * ignoreCollapsed 的值决定是否执行该调整
  5581. * @method txtToElmBoundary
  5582. * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
  5583. * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
  5584. * @return { UE.dom.Range } 当前range对象
  5585. */
  5586. txtToElmBoundary: function (ignoreCollapsed) {
  5587. function adjust(r, c) {
  5588. var container = r[c + 'Container'],
  5589. offset = r[c + 'Offset']
  5590. if (container.nodeType == 3) {
  5591. if (!offset) {
  5592. r[
  5593. 'set' +
  5594. c.replace(/(\w)/, function (a) {
  5595. return a.toUpperCase()
  5596. }) +
  5597. 'Before'
  5598. ](container)
  5599. } else if (offset >= container.nodeValue.length) {
  5600. r[
  5601. 'set' +
  5602. c.replace(/(\w)/, function (a) {
  5603. return a.toUpperCase()
  5604. }) +
  5605. 'After'
  5606. ](container)
  5607. }
  5608. }
  5609. }
  5610. if (ignoreCollapsed || !this.collapsed) {
  5611. adjust(this, 'start')
  5612. adjust(this, 'end')
  5613. }
  5614. return this
  5615. },
  5616. /**
  5617. * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
  5618. * @method insertNode
  5619. * @param { Node } node 需要插入的节点
  5620. * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
  5621. * @return { UE.dom.Range } 当前range对象
  5622. */
  5623. insertNode: function (node) {
  5624. var first = node,
  5625. length = 1
  5626. if (node.nodeType == 11) {
  5627. first = node.firstChild
  5628. length = node.childNodes.length
  5629. }
  5630. this.trimBoundary(true)
  5631. var start = this.startContainer,
  5632. offset = this.startOffset
  5633. var nextNode = start.childNodes[offset]
  5634. if (nextNode) {
  5635. start.insertBefore(node, nextNode)
  5636. } else {
  5637. start.appendChild(node)
  5638. }
  5639. if (first.parentNode === this.endContainer) {
  5640. this.endOffset = this.endOffset + length
  5641. }
  5642. return this.setStartBefore(first)
  5643. },
  5644. /**
  5645. * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
  5646. * @method setCursor
  5647. * @return { UE.dom.Range } 当前range对象
  5648. * @see UE.dom.Range:collapse()
  5649. */
  5650. /**
  5651. * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
  5652. * @method setCursor
  5653. * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
  5654. * 反之,则向开始容器方向闭合
  5655. * @return { UE.dom.Range } 当前range对象
  5656. * @see UE.dom.Range:collapse(Boolean)
  5657. */
  5658. setCursor: function (toEnd, noFillData) {
  5659. return this.collapse(!toEnd).select(noFillData)
  5660. },
  5661. /**
  5662. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  5663. * @method createBookmark
  5664. * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
  5665. * 返回标记位置的ID, 反之则返回标记位置节点的引用
  5666. * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
  5667. * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
  5668. * 返回的记录的类型为ID, 反之则为引用
  5669. */
  5670. createBookmark: function (serialize, same) {
  5671. var endNode,
  5672. startNode = this.document.createElement('span')
  5673. startNode.style.cssText = 'display:none;line-height:0px;'
  5674. startNode.appendChild(this.document.createTextNode('\u200D'))
  5675. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++)
  5676. if (!this.collapsed) {
  5677. endNode = startNode.cloneNode(true)
  5678. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++)
  5679. }
  5680. this.insertNode(startNode)
  5681. if (endNode) {
  5682. this.collapse().insertNode(endNode).setEndBefore(endNode)
  5683. }
  5684. this.setStartAfter(startNode)
  5685. return {
  5686. start: serialize ? startNode.id : startNode,
  5687. end: endNode ? (serialize ? endNode.id : endNode) : null,
  5688. id: serialize
  5689. }
  5690. },
  5691. /**
  5692. * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
  5693. * @method moveToBookmark
  5694. * @param { BookMark } bookmark createBookmark所创建的标签对象
  5695. * @return { UE.dom.Range } 当前range对象
  5696. * @see UE.dom.Range:createBookmark(Boolean)
  5697. */
  5698. moveToBookmark: function (bookmark) {
  5699. var start = bookmark.id
  5700. ? this.document.getElementById(bookmark.start)
  5701. : bookmark.start,
  5702. end =
  5703. bookmark.end && bookmark.id
  5704. ? this.document.getElementById(bookmark.end)
  5705. : bookmark.end
  5706. this.setStartBefore(start)
  5707. domUtils.remove(start)
  5708. if (end) {
  5709. this.setEndBefore(end)
  5710. domUtils.remove(end)
  5711. } else {
  5712. this.collapse(true)
  5713. }
  5714. return this
  5715. },
  5716. /**
  5717. * 调整range的边界,使其"放大"到最近的父节点
  5718. * @method enlarge
  5719. * @remind 会引起选区的变化
  5720. * @return { UE.dom.Range } 当前range对象
  5721. */
  5722. /**
  5723. * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
  5724. * 要求扩大之后的父节点是block节点
  5725. * @method enlarge
  5726. * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
  5727. * @return { UE.dom.Range } 当前range对象
  5728. */
  5729. enlarge: function (toBlock, stopFn) {
  5730. var isBody = domUtils.isBody,
  5731. pre,
  5732. node,
  5733. tmp = this.document.createTextNode('')
  5734. if (toBlock) {
  5735. node = this.startContainer
  5736. if (node.nodeType == 1) {
  5737. if (node.childNodes[this.startOffset]) {
  5738. pre = node = node.childNodes[this.startOffset]
  5739. } else {
  5740. node.appendChild(tmp)
  5741. pre = node = tmp
  5742. }
  5743. } else {
  5744. pre = node
  5745. }
  5746. while (1) {
  5747. if (domUtils.isBlockElm(node)) {
  5748. node = pre
  5749. while (
  5750. (pre = node.previousSibling) &&
  5751. !domUtils.isBlockElm(pre)
  5752. ) {
  5753. node = pre
  5754. }
  5755. this.setStartBefore(node)
  5756. break
  5757. }
  5758. pre = node
  5759. node = node.parentNode
  5760. }
  5761. node = this.endContainer
  5762. if (node.nodeType == 1) {
  5763. if ((pre = node.childNodes[this.endOffset])) {
  5764. node.insertBefore(tmp, pre)
  5765. } else {
  5766. node.appendChild(tmp)
  5767. }
  5768. pre = node = tmp
  5769. } else {
  5770. pre = node
  5771. }
  5772. while (1) {
  5773. if (domUtils.isBlockElm(node)) {
  5774. node = pre
  5775. while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
  5776. node = pre
  5777. }
  5778. this.setEndAfter(node)
  5779. break
  5780. }
  5781. pre = node
  5782. node = node.parentNode
  5783. }
  5784. if (tmp.parentNode === this.endContainer) {
  5785. this.endOffset--
  5786. }
  5787. domUtils.remove(tmp)
  5788. }
  5789. // 扩展边界到最大
  5790. if (!this.collapsed) {
  5791. while (this.startOffset == 0) {
  5792. if (stopFn && stopFn(this.startContainer)) {
  5793. break
  5794. }
  5795. if (isBody(this.startContainer)) {
  5796. break
  5797. }
  5798. this.setStartBefore(this.startContainer)
  5799. }
  5800. while (
  5801. this.endOffset ==
  5802. (this.endContainer.nodeType == 1
  5803. ? this.endContainer.childNodes.length
  5804. : this.endContainer.nodeValue.length)
  5805. ) {
  5806. if (stopFn && stopFn(this.endContainer)) {
  5807. break
  5808. }
  5809. if (isBody(this.endContainer)) {
  5810. break
  5811. }
  5812. this.setEndAfter(this.endContainer)
  5813. }
  5814. }
  5815. return this
  5816. },
  5817. enlargeToBlockElm: function (ignoreEnd) {
  5818. while (!domUtils.isBlockElm(this.startContainer)) {
  5819. this.setStartBefore(this.startContainer)
  5820. }
  5821. if (!ignoreEnd) {
  5822. while (!domUtils.isBlockElm(this.endContainer)) {
  5823. this.setEndAfter(this.endContainer)
  5824. }
  5825. }
  5826. return this
  5827. },
  5828. /**
  5829. * 调整Range的边界,使其"缩小"到最合适的位置
  5830. * @method adjustmentBoundary
  5831. * @return { UE.dom.Range } 当前range对象
  5832. * @see UE.dom.Range:shrinkBoundary()
  5833. */
  5834. adjustmentBoundary: function () {
  5835. if (!this.collapsed) {
  5836. while (
  5837. !domUtils.isBody(this.startContainer) &&
  5838. this.startOffset ==
  5839. this.startContainer[
  5840. this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'
  5841. ].length &&
  5842. this.startContainer[
  5843. this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'
  5844. ].length
  5845. ) {
  5846. this.setStartAfter(this.startContainer)
  5847. }
  5848. while (
  5849. !domUtils.isBody(this.endContainer) &&
  5850. !this.endOffset &&
  5851. this.endContainer[
  5852. this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'
  5853. ].length
  5854. ) {
  5855. this.setEndBefore(this.endContainer)
  5856. }
  5857. }
  5858. return this
  5859. },
  5860. /**
  5861. * 给range选区中的内容添加给定的inline标签
  5862. * @method applyInlineStyle
  5863. * @param { String } tagName 需要添加的标签名
  5864. * @example
  5865. * ```html
  5866. * <p>xxxx[xxxx]x</p> ==> range.applyInlineStyle("strong") ==> <p>xxxx[<strong>xxxx</strong>]x</p>
  5867. * ```
  5868. */
  5869. /**
  5870. * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
  5871. * @method applyInlineStyle
  5872. * @param { String } tagName 需要添加的标签名
  5873. * @param { Object } attrs 跟随新添加的标签的属性
  5874. * @return { UE.dom.Range } 当前选区
  5875. * @example
  5876. * ```html
  5877. * <p>xxxx[xxxx]x</p>
  5878. *
  5879. * ==>
  5880. *
  5881. * <!-- 执行操作 -->
  5882. * range.applyInlineStyle("strong",{"style":"font-size:12px"})
  5883. *
  5884. * ==>
  5885. *
  5886. * <p>xxxx[<strong style="font-size:12px">xxxx</strong>]x</p>
  5887. * ```
  5888. */
  5889. applyInlineStyle: function (tagName, attrs, list) {
  5890. if (this.collapsed) return this
  5891. this.trimBoundary()
  5892. .enlarge(false, function (node) {
  5893. return node.nodeType == 1 && domUtils.isBlockElm(node)
  5894. })
  5895. .adjustmentBoundary()
  5896. var bookmark = this.createBookmark(),
  5897. end = bookmark.end,
  5898. filterFn = function (node) {
  5899. return node.nodeType == 1
  5900. ? node.tagName.toLowerCase() != 'br'
  5901. : !domUtils.isWhitespace(node)
  5902. },
  5903. current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
  5904. node,
  5905. pre,
  5906. range = this.cloneRange()
  5907. while (
  5908. current &&
  5909. domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING
  5910. ) {
  5911. if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
  5912. range.setStartBefore(current)
  5913. node = current
  5914. while (
  5915. node &&
  5916. (node.nodeType == 3 || dtd[tagName][node.tagName]) &&
  5917. node !== end
  5918. ) {
  5919. pre = node
  5920. node = domUtils.getNextDomNode(
  5921. node,
  5922. node.nodeType == 1,
  5923. null,
  5924. function (parent) {
  5925. return dtd[tagName][parent.tagName]
  5926. }
  5927. )
  5928. }
  5929. var frag = range.setEndAfter(pre).extractContents(),
  5930. elm
  5931. if (list && list.length > 0) {
  5932. var level, top
  5933. top = level = list[0].cloneNode(false)
  5934. for (var i = 1, ci; (ci = list[i++]); ) {
  5935. level.appendChild(ci.cloneNode(false))
  5936. level = level.firstChild
  5937. }
  5938. elm = level
  5939. } else {
  5940. elm = range.document.createElement(tagName)
  5941. }
  5942. if (attrs) {
  5943. domUtils.setAttributes(elm, attrs)
  5944. }
  5945. elm.appendChild(frag)
  5946. range.insertNode(list ? top : elm)
  5947. //处理下滑线在a上的情况
  5948. var aNode
  5949. if (
  5950. tagName == 'span' &&
  5951. attrs.style &&
  5952. /text\-decoration/.test(attrs.style) &&
  5953. (aNode = domUtils.findParentByTagName(elm, 'a', true))
  5954. ) {
  5955. domUtils.setAttributes(aNode, attrs)
  5956. domUtils.remove(elm, true)
  5957. elm = aNode
  5958. } else {
  5959. domUtils.mergeSibling(elm)
  5960. domUtils.clearEmptySibling(elm)
  5961. }
  5962. //去除子节点相同的
  5963. domUtils.mergeChild(elm, attrs)
  5964. current = domUtils.getNextDomNode(elm, false, filterFn)
  5965. domUtils.mergeToParent(elm)
  5966. if (node === end) {
  5967. break
  5968. }
  5969. } else {
  5970. current = domUtils.getNextDomNode(current, true, filterFn)
  5971. }
  5972. }
  5973. return this.moveToBookmark(bookmark)
  5974. },
  5975. /**
  5976. * 移除当前选区内指定的inline标签,但保留其中的内容
  5977. * @method removeInlineStyle
  5978. * @param { String } tagName 需要移除的标签名
  5979. * @return { UE.dom.Range } 当前的range对象
  5980. * @example
  5981. * ```html
  5982. * xx[x<span>xxx<em>yyy</em>zz]z</span> => range.removeInlineStyle(["em"]) => xx[x<span>xxxyyyzz]z</span>
  5983. * ```
  5984. */
  5985. /**
  5986. * 移除当前选区内指定的一组inline标签,但保留其中的内容
  5987. * @method removeInlineStyle
  5988. * @param { Array } tagNameArr 需要移除的标签名的数组
  5989. * @return { UE.dom.Range } 当前的range对象
  5990. * @see UE.dom.Range:removeInlineStyle(String)
  5991. */
  5992. removeInlineStyle: function (tagNames) {
  5993. if (this.collapsed) return this
  5994. tagNames = utils.isArray(tagNames) ? tagNames : [tagNames]
  5995. this.shrinkBoundary().adjustmentBoundary()
  5996. var start = this.startContainer,
  5997. end = this.endContainer
  5998. while (1) {
  5999. if (start.nodeType == 1) {
  6000. if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
  6001. break
  6002. }
  6003. if (start.tagName.toLowerCase() == 'body') {
  6004. start = null
  6005. break
  6006. }
  6007. }
  6008. start = start.parentNode
  6009. }
  6010. while (1) {
  6011. if (end.nodeType == 1) {
  6012. if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
  6013. break
  6014. }
  6015. if (end.tagName.toLowerCase() == 'body') {
  6016. end = null
  6017. break
  6018. }
  6019. }
  6020. end = end.parentNode
  6021. }
  6022. var bookmark = this.createBookmark(),
  6023. frag,
  6024. tmpRange
  6025. if (start) {
  6026. tmpRange = this.cloneRange()
  6027. .setEndBefore(bookmark.start)
  6028. .setStartBefore(start)
  6029. frag = tmpRange.extractContents()
  6030. tmpRange.insertNode(frag)
  6031. domUtils.clearEmptySibling(start, true)
  6032. start.parentNode.insertBefore(bookmark.start, start)
  6033. }
  6034. if (end) {
  6035. tmpRange = this.cloneRange()
  6036. .setStartAfter(bookmark.end)
  6037. .setEndAfter(end)
  6038. frag = tmpRange.extractContents()
  6039. tmpRange.insertNode(frag)
  6040. domUtils.clearEmptySibling(end, false, true)
  6041. end.parentNode.insertBefore(bookmark.end, end.nextSibling)
  6042. }
  6043. var current = domUtils.getNextDomNode(
  6044. bookmark.start,
  6045. false,
  6046. function (node) {
  6047. return node.nodeType == 1
  6048. }
  6049. ),
  6050. next
  6051. while (current && current !== bookmark.end) {
  6052. next = domUtils.getNextDomNode(current, true, function (node) {
  6053. return node.nodeType == 1
  6054. })
  6055. if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
  6056. domUtils.remove(current, true)
  6057. }
  6058. current = next
  6059. }
  6060. return this.moveToBookmark(bookmark)
  6061. },
  6062. /**
  6063. * 获取当前选中的自闭合的节点
  6064. * @method getClosedNode
  6065. * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
  6066. */
  6067. getClosedNode: function () {
  6068. var node
  6069. if (!this.collapsed) {
  6070. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary()
  6071. if (selectOneNode(range)) {
  6072. var child = range.startContainer.childNodes[range.startOffset]
  6073. if (
  6074. child &&
  6075. child.nodeType == 1 &&
  6076. (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])
  6077. ) {
  6078. node = child
  6079. }
  6080. }
  6081. }
  6082. return node
  6083. },
  6084. /**
  6085. * 在页面上高亮range所表示的选区
  6086. * @method select
  6087. * @return { UE.dom.Range } 返回当前Range对象
  6088. */
  6089. //这里不区分ie9以上,trace:3824
  6090. select: browser.ie
  6091. ? function (noFillData, textRange) {
  6092. var nativeRange
  6093. if (!this.collapsed) this.shrinkBoundary()
  6094. var node = this.getClosedNode()
  6095. if (node && !textRange) {
  6096. try {
  6097. nativeRange = this.document.body.createControlRange()
  6098. nativeRange.addElement(node)
  6099. nativeRange.select()
  6100. } catch (e) {}
  6101. return this
  6102. }
  6103. var bookmark = this.createBookmark(),
  6104. start = bookmark.start,
  6105. end
  6106. nativeRange = this.document.body.createTextRange()
  6107. nativeRange.moveToElementText(start)
  6108. nativeRange.moveStart('character', 1)
  6109. if (!this.collapsed) {
  6110. var nativeRangeEnd = this.document.body.createTextRange()
  6111. end = bookmark.end
  6112. nativeRangeEnd.moveToElementText(end)
  6113. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd)
  6114. } else {
  6115. if (!noFillData && this.startContainer.nodeType != 3) {
  6116. //使用<span>|x<span>固定住光标
  6117. var tmpText = this.document.createTextNode(fillChar),
  6118. tmp = this.document.createElement('span')
  6119. tmp.appendChild(this.document.createTextNode(fillChar))
  6120. start.parentNode.insertBefore(tmp, start)
  6121. start.parentNode.insertBefore(tmpText, start)
  6122. //当点b,i,u时,不能清除i上边的b
  6123. removeFillData(this.document, tmpText)
  6124. fillData = tmpText
  6125. mergeSibling(tmp, 'previousSibling')
  6126. mergeSibling(start, 'nextSibling')
  6127. nativeRange.moveStart('character', -1)
  6128. nativeRange.collapse(true)
  6129. }
  6130. }
  6131. this.moveToBookmark(bookmark)
  6132. tmp && domUtils.remove(tmp)
  6133. //IE在隐藏状态下不支持range操作,catch一下
  6134. try {
  6135. nativeRange.select()
  6136. } catch (e) {}
  6137. return this
  6138. }
  6139. : function (notInsertFillData) {
  6140. function checkOffset(rng) {
  6141. function check(node, offset, dir) {
  6142. if (node.nodeType == 3 && node.nodeValue.length < offset) {
  6143. rng[dir + 'Offset'] = node.nodeValue.length
  6144. }
  6145. }
  6146. check(rng.startContainer, rng.startOffset, 'start')
  6147. check(rng.endContainer, rng.endOffset, 'end')
  6148. }
  6149. var win = domUtils.getWindow(this.document),
  6150. sel = win.getSelection(),
  6151. txtNode
  6152. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  6153. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  6154. browser.gecko ? this.document.body.focus() : win.focus()
  6155. if (sel) {
  6156. sel.removeAllRanges()
  6157. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  6158. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  6159. if (this.collapsed && !notInsertFillData) {
  6160. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  6161. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  6162. // var tmp = this.document.createTextNode('');
  6163. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  6164. // }
  6165. //
  6166. //处理光标落在文本节点的情况
  6167. //处理以下的情况
  6168. //<b>|xxxx</b>
  6169. //<b>xxxx</b>|xxxx
  6170. //xxxx<b>|</b>
  6171. var start = this.startContainer,
  6172. child = start
  6173. if (start.nodeType == 1) {
  6174. child = start.childNodes[this.startOffset]
  6175. }
  6176. if (
  6177. !(start.nodeType == 3 && this.startOffset) &&
  6178. (child
  6179. ? !child.previousSibling ||
  6180. child.previousSibling.nodeType != 3
  6181. : !start.lastChild || start.lastChild.nodeType != 3)
  6182. ) {
  6183. txtNode = this.document.createTextNode(fillChar)
  6184. //跟着前边走
  6185. this.insertNode(txtNode)
  6186. removeFillData(this.document, txtNode)
  6187. mergeSibling(txtNode, 'previousSibling')
  6188. mergeSibling(txtNode, 'nextSibling')
  6189. fillData = txtNode
  6190. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true)
  6191. }
  6192. }
  6193. var nativeRange = this.document.createRange()
  6194. if (
  6195. this.collapsed &&
  6196. browser.opera &&
  6197. this.startContainer.nodeType == 1
  6198. ) {
  6199. var child = this.startContainer.childNodes[this.startOffset]
  6200. if (!child) {
  6201. //往前靠拢
  6202. child = this.startContainer.lastChild
  6203. if (child && domUtils.isBr(child)) {
  6204. this.setStartBefore(child).collapse(true)
  6205. }
  6206. } else {
  6207. //向后靠拢
  6208. while (child && domUtils.isBlockElm(child)) {
  6209. if (child.nodeType == 1 && child.childNodes[0]) {
  6210. child = child.childNodes[0]
  6211. } else {
  6212. break
  6213. }
  6214. }
  6215. child && this.setStartBefore(child).collapse(true)
  6216. }
  6217. }
  6218. //是createAddress最后一位算的不准,现在这里进行微调
  6219. checkOffset(this)
  6220. nativeRange.setStart(this.startContainer, this.startOffset)
  6221. nativeRange.setEnd(this.endContainer, this.endOffset)
  6222. sel.addRange(nativeRange)
  6223. }
  6224. return this
  6225. },
  6226. /**
  6227. * 滚动到当前range开始的位置
  6228. * @method scrollToView
  6229. * @param { Window } win 当前range对象所属的window对象
  6230. * @return { UE.dom.Range } 当前Range对象
  6231. */
  6232. /**
  6233. * 滚动到距离当前range开始位置 offset 的位置处
  6234. * @method scrollToView
  6235. * @param { Window } win 当前range对象所属的window对象
  6236. * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
  6237. * @return { UE.dom.Range } 当前Range对象
  6238. */
  6239. scrollToView: function (win, offset) {
  6240. win = win ? window : domUtils.getWindow(this.document)
  6241. var me = this,
  6242. span = me.document.createElement('span')
  6243. //trace:717
  6244. span.innerHTML = '&nbsp;'
  6245. me.cloneRange().insertNode(span)
  6246. domUtils.scrollToView(span, win, offset)
  6247. domUtils.remove(span)
  6248. return me
  6249. },
  6250. /**
  6251. * 判断当前选区内容是否占位符
  6252. * @private
  6253. * @method inFillChar
  6254. * @return { Boolean } 如果是占位符返回true,否则返回false
  6255. */
  6256. inFillChar: function () {
  6257. var start = this.startContainer
  6258. if (
  6259. this.collapsed &&
  6260. start.nodeType == 3 &&
  6261. start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '')
  6262. .length +
  6263. 1 ==
  6264. start.nodeValue.length
  6265. ) {
  6266. return true
  6267. }
  6268. return false
  6269. },
  6270. /**
  6271. * 保存
  6272. * @method createAddress
  6273. * @private
  6274. * @return { Boolean } 返回开始和结束的位置
  6275. * @example
  6276. * ```html
  6277. * <body>
  6278. * <p>
  6279. * aaaa
  6280. * <em>
  6281. * <!-- 选区开始 -->
  6282. * bbbb
  6283. * <!-- 选区结束 -->
  6284. * </em>
  6285. * </p>
  6286. *
  6287. * <script>
  6288. * //output: {startAddress:[0,1,0,0],endAddress:[0,1,0,4]}
  6289. * console.log( range.createAddress() );
  6290. * </script>
  6291. * </body>
  6292. * ```
  6293. */
  6294. createAddress: function (ignoreEnd, ignoreTxt) {
  6295. var addr = {},
  6296. me = this
  6297. function getAddress(isStart) {
  6298. var node = isStart ? me.startContainer : me.endContainer
  6299. var parents = domUtils.findParents(node, true, function (node) {
  6300. return !domUtils.isBody(node)
  6301. }),
  6302. addrs = []
  6303. for (var i = 0, ci; (ci = parents[i++]); ) {
  6304. addrs.push(domUtils.getNodeIndex(ci, ignoreTxt))
  6305. }
  6306. var firstIndex = 0
  6307. if (ignoreTxt) {
  6308. if (node.nodeType == 3) {
  6309. var tmpNode = node.previousSibling
  6310. while (tmpNode && tmpNode.nodeType == 3) {
  6311. firstIndex += tmpNode.nodeValue.replace(fillCharReg, '').length
  6312. tmpNode = tmpNode.previousSibling
  6313. }
  6314. firstIndex += isStart ? me.startOffset : me.endOffset // - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  6315. } else {
  6316. node = node.childNodes[isStart ? me.startOffset : me.endOffset]
  6317. if (node) {
  6318. firstIndex = domUtils.getNodeIndex(node, ignoreTxt)
  6319. } else {
  6320. node = isStart ? me.startContainer : me.endContainer
  6321. var first = node.firstChild
  6322. while (first) {
  6323. if (domUtils.isFillChar(first)) {
  6324. first = first.nextSibling
  6325. continue
  6326. }
  6327. firstIndex++
  6328. if (first.nodeType == 3) {
  6329. while (first && first.nodeType == 3) {
  6330. first = first.nextSibling
  6331. }
  6332. } else {
  6333. first = first.nextSibling
  6334. }
  6335. }
  6336. }
  6337. }
  6338. } else {
  6339. firstIndex = isStart
  6340. ? domUtils.isFillChar(node)
  6341. ? 0
  6342. : me.startOffset
  6343. : me.endOffset
  6344. }
  6345. if (firstIndex < 0) {
  6346. firstIndex = 0
  6347. }
  6348. addrs.push(firstIndex)
  6349. return addrs
  6350. }
  6351. addr.startAddress = getAddress(true)
  6352. if (!ignoreEnd) {
  6353. addr.endAddress = me.collapsed
  6354. ? [].concat(addr.startAddress)
  6355. : getAddress()
  6356. }
  6357. return addr
  6358. },
  6359. /**
  6360. * 保存
  6361. * @method createAddress
  6362. * @private
  6363. * @return { Boolean } 返回开始和结束的位置
  6364. * @example
  6365. * ```html
  6366. * <body>
  6367. * <p>
  6368. * aaaa
  6369. * <em>
  6370. * <!-- 选区开始 -->
  6371. * bbbb
  6372. * <!-- 选区结束 -->
  6373. * </em>
  6374. * </p>
  6375. *
  6376. * <script>
  6377. * var range = editor.selection.getRange();
  6378. * range.moveToAddress({startAddress:[0,1,0,0],endAddress:[0,1,0,4]});
  6379. * range.select();
  6380. * //output: 'bbbb'
  6381. * console.log(editor.selection.getText());
  6382. * </script>
  6383. * </body>
  6384. * ```
  6385. */
  6386. moveToAddress: function (addr, ignoreEnd) {
  6387. var me = this
  6388. function getNode(address, isStart) {
  6389. var tmpNode = me.document.body,
  6390. parentNode,
  6391. offset
  6392. for (var i = 0, ci, l = address.length; i < l; i++) {
  6393. ci = address[i]
  6394. parentNode = tmpNode
  6395. tmpNode = tmpNode.childNodes[ci]
  6396. if (!tmpNode) {
  6397. offset = ci
  6398. break
  6399. }
  6400. }
  6401. if (isStart) {
  6402. if (tmpNode) {
  6403. me.setStartBefore(tmpNode)
  6404. } else {
  6405. me.setStart(parentNode, offset)
  6406. }
  6407. } else {
  6408. if (tmpNode) {
  6409. me.setEndBefore(tmpNode)
  6410. } else {
  6411. me.setEnd(parentNode, offset)
  6412. }
  6413. }
  6414. }
  6415. getNode(addr.startAddress, true)
  6416. !ignoreEnd && addr.endAddress && getNode(addr.endAddress)
  6417. return me
  6418. },
  6419. /**
  6420. * 判断给定的Range对象是否和当前Range对象表示的是同一个选区
  6421. * @method equals
  6422. * @param { UE.dom.Range } 需要判断的Range对象
  6423. * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false
  6424. */
  6425. equals: function (rng) {
  6426. for (var p in this) {
  6427. if (this.hasOwnProperty(p)) {
  6428. if (this[p] !== rng[p]) return false
  6429. }
  6430. }
  6431. return true
  6432. },
  6433. /**
  6434. * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  6435. * 作为其参数。
  6436. * @method traversal
  6437. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  6438. * @return { UE.dom.Range } 当前range对象
  6439. * @example
  6440. * ```html
  6441. *
  6442. * <body>
  6443. *
  6444. * <!-- 选区开始 -->
  6445. * <span></span>
  6446. * <a></a>
  6447. * <!-- 选区结束 -->
  6448. * </body>
  6449. *
  6450. * <script>
  6451. *
  6452. * //output: <span></span><a></a>
  6453. * console.log( range.cloneContents() );
  6454. *
  6455. * range.traversal( function ( node ) {
  6456. *
  6457. * if ( node.nodeType === 1 ) {
  6458. * node.className = "test";
  6459. * }
  6460. *
  6461. * } );
  6462. *
  6463. * //output: <span class="test"></span><a class="test"></a>
  6464. * console.log( range.cloneContents() );
  6465. *
  6466. * </script>
  6467. * ```
  6468. */
  6469. /**
  6470. * 遍历range内的节点。
  6471. * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
  6472. * 作为其参数。
  6473. * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
  6474. * 发doFn函数的执行
  6475. * @method traversal
  6476. * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
  6477. * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
  6478. * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
  6479. * 会触发doFn。
  6480. * @return { UE.dom.Range } 当前range对象
  6481. * @see UE.dom.Range:traversal(Function)
  6482. * @example
  6483. * ```html
  6484. *
  6485. * <body>
  6486. *
  6487. * <!-- 选区开始 -->
  6488. * <span></span>
  6489. * <a></a>
  6490. * <!-- 选区结束 -->
  6491. * </body>
  6492. *
  6493. * <script>
  6494. *
  6495. * //output: <span></span><a></a>
  6496. * console.log( range.cloneContents() );
  6497. *
  6498. * range.traversal( function ( node ) {
  6499. *
  6500. * node.className = "test";
  6501. *
  6502. * }, function ( node ) {
  6503. * return node.nodeType === 1;
  6504. * } );
  6505. *
  6506. * //output: <span class="test"></span><a class="test"></a>
  6507. * console.log( range.cloneContents() );
  6508. *
  6509. * </script>
  6510. * ```
  6511. */
  6512. traversal: function (doFn, filterFn) {
  6513. if (this.collapsed) return this
  6514. var bookmark = this.createBookmark(),
  6515. end = bookmark.end,
  6516. current = domUtils.getNextDomNode(bookmark.start, false, filterFn)
  6517. while (
  6518. current &&
  6519. current !== end &&
  6520. domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING
  6521. ) {
  6522. var tmpNode = domUtils.getNextDomNode(current, false, filterFn)
  6523. doFn(current)
  6524. current = tmpNode
  6525. }
  6526. return this.moveToBookmark(bookmark)
  6527. }
  6528. }
  6529. })()
  6530. // core/Selection.js
  6531. /**
  6532. * 选集
  6533. * @file
  6534. * @module UE.dom
  6535. * @class Selection
  6536. * @since 1.2.6.1
  6537. */
  6538. /**
  6539. * 选区集合
  6540. * @unfile
  6541. * @module UE.dom
  6542. * @class Selection
  6543. */
  6544. ;(function () {
  6545. function getBoundaryInformation(range, start) {
  6546. var getIndex = domUtils.getNodeIndex
  6547. range = range.duplicate()
  6548. range.collapse(start)
  6549. var parent = range.parentElement()
  6550. //如果节点里没有子节点,直接退出
  6551. if (!parent.hasChildNodes()) {
  6552. return { container: parent, offset: 0 }
  6553. }
  6554. var siblings = parent.children,
  6555. child,
  6556. testRange = range.duplicate(),
  6557. startIndex = 0,
  6558. endIndex = siblings.length - 1,
  6559. index = -1,
  6560. distance
  6561. while (startIndex <= endIndex) {
  6562. index = Math.floor((startIndex + endIndex) / 2)
  6563. child = siblings[index]
  6564. testRange.moveToElementText(child)
  6565. var position = testRange.compareEndPoints('StartToStart', range)
  6566. if (position > 0) {
  6567. endIndex = index - 1
  6568. } else if (position < 0) {
  6569. startIndex = index + 1
  6570. } else {
  6571. //trace:1043
  6572. return { container: parent, offset: getIndex(child) }
  6573. }
  6574. }
  6575. if (index == -1) {
  6576. testRange.moveToElementText(parent)
  6577. testRange.setEndPoint('StartToStart', range)
  6578. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length
  6579. siblings = parent.childNodes
  6580. if (!distance) {
  6581. child = siblings[siblings.length - 1]
  6582. return { container: child, offset: child.nodeValue.length }
  6583. }
  6584. var i = siblings.length
  6585. while (distance > 0) {
  6586. distance -= siblings[--i].nodeValue.length
  6587. }
  6588. return { container: siblings[i], offset: -distance }
  6589. }
  6590. testRange.collapse(position > 0)
  6591. testRange.setEndPoint(position > 0 ? 'StartToStart' : 'EndToStart', range)
  6592. distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length
  6593. if (!distance) {
  6594. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName]
  6595. ? {
  6596. container: parent,
  6597. offset: getIndex(child) + (position > 0 ? 0 : 1)
  6598. }
  6599. : {
  6600. container: child,
  6601. offset: position > 0 ? 0 : child.childNodes.length
  6602. }
  6603. }
  6604. while (distance > 0) {
  6605. try {
  6606. var pre = child
  6607. child = child[position > 0 ? 'previousSibling' : 'nextSibling']
  6608. distance -= child.nodeValue.length
  6609. } catch (e) {
  6610. return { container: parent, offset: getIndex(pre) }
  6611. }
  6612. }
  6613. return {
  6614. container: child,
  6615. offset: position > 0 ? -distance : child.nodeValue.length + distance
  6616. }
  6617. }
  6618. /**
  6619. * 将ieRange转换为Range对象
  6620. * @param {Range} ieRange ieRange对象
  6621. * @param {Range} range Range对象
  6622. * @return {Range} range 返回转换后的Range对象
  6623. */
  6624. function transformIERangeToRange(ieRange, range) {
  6625. if (ieRange.item) {
  6626. range.selectNode(ieRange.item(0))
  6627. } else {
  6628. var bi = getBoundaryInformation(ieRange, true)
  6629. range.setStart(bi.container, bi.offset)
  6630. if (ieRange.compareEndPoints('StartToEnd', ieRange) != 0) {
  6631. bi = getBoundaryInformation(ieRange, false)
  6632. range.setEnd(bi.container, bi.offset)
  6633. }
  6634. }
  6635. return range
  6636. }
  6637. /**
  6638. * 获得ieRange
  6639. * @param {Selection} sel Selection对象
  6640. * @return {ieRange} 得到ieRange
  6641. */
  6642. function _getIERange(sel) {
  6643. var ieRange
  6644. //ie下有可能报错
  6645. try {
  6646. ieRange = sel.getNative().createRange()
  6647. } catch (e) {
  6648. return null
  6649. }
  6650. var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement()
  6651. if ((el.ownerDocument || el) === sel.document) {
  6652. return ieRange
  6653. }
  6654. return null
  6655. }
  6656. var Selection = (dom.Selection = function (doc) {
  6657. var me = this,
  6658. iframe
  6659. me.document = doc
  6660. if (browser.ie9below) {
  6661. iframe = domUtils.getWindow(doc).frameElement
  6662. domUtils.on(iframe, 'beforedeactivate', function () {
  6663. me._bakIERange = me.getIERange()
  6664. })
  6665. domUtils.on(iframe, 'activate', function () {
  6666. try {
  6667. if (!_getIERange(me) && me._bakIERange) {
  6668. me._bakIERange.select()
  6669. }
  6670. } catch (ex) {}
  6671. me._bakIERange = null
  6672. })
  6673. }
  6674. iframe = doc = null
  6675. })
  6676. Selection.prototype = {
  6677. rangeInBody: function (rng, txtRange) {
  6678. var node =
  6679. browser.ie9below || txtRange
  6680. ? rng.item
  6681. ? rng.item()
  6682. : rng.parentElement()
  6683. : rng.startContainer
  6684. return (
  6685. node === this.document.body || domUtils.inDoc(node, this.document)
  6686. )
  6687. },
  6688. /**
  6689. * 获取原生seleciton对象
  6690. * @method getNative
  6691. * @return { Object } 获得selection对象
  6692. * @example
  6693. * ```javascript
  6694. * editor.selection.getNative();
  6695. * ```
  6696. */
  6697. getNative: function () {
  6698. var doc = this.document
  6699. try {
  6700. return !doc
  6701. ? null
  6702. : browser.ie9below
  6703. ? doc.selection
  6704. : domUtils.getWindow(doc).getSelection()
  6705. } catch (e) {
  6706. return null
  6707. }
  6708. },
  6709. /**
  6710. * 获得ieRange
  6711. * @method getIERange
  6712. * @return { Object } 返回ie原生的Range
  6713. * @example
  6714. * ```javascript
  6715. * editor.selection.getIERange();
  6716. * ```
  6717. */
  6718. getIERange: function () {
  6719. var ieRange = _getIERange(this)
  6720. if (!ieRange) {
  6721. if (this._bakIERange) {
  6722. return this._bakIERange
  6723. }
  6724. }
  6725. return ieRange
  6726. },
  6727. /**
  6728. * 缓存当前选区的range和选区的开始节点
  6729. * @method cache
  6730. */
  6731. cache: function () {
  6732. this.clear()
  6733. this._cachedRange = this.getRange()
  6734. this._cachedStartElement = this.getStart()
  6735. this._cachedStartElementPath = this.getStartElementPath()
  6736. },
  6737. /**
  6738. * 获取选区开始位置的父节点到body
  6739. * @method getStartElementPath
  6740. * @return { Array } 返回父节点集合
  6741. * @example
  6742. * ```javascript
  6743. * editor.selection.getStartElementPath();
  6744. * ```
  6745. */
  6746. getStartElementPath: function () {
  6747. if (this._cachedStartElementPath) {
  6748. return this._cachedStartElementPath
  6749. }
  6750. var start = this.getStart()
  6751. if (start) {
  6752. return domUtils.findParents(start, true, null, true)
  6753. }
  6754. return []
  6755. },
  6756. /**
  6757. * 清空缓存
  6758. * @method clear
  6759. */
  6760. clear: function () {
  6761. this._cachedStartElementPath =
  6762. this._cachedRange =
  6763. this._cachedStartElement =
  6764. null
  6765. },
  6766. /**
  6767. * 编辑器是否得到了选区
  6768. * @method isFocus
  6769. */
  6770. isFocus: function () {
  6771. try {
  6772. if (browser.ie9below) {
  6773. var nativeRange = _getIERange(this)
  6774. return !!(nativeRange && this.rangeInBody(nativeRange))
  6775. } else {
  6776. return !!this.getNative().rangeCount
  6777. }
  6778. } catch (e) {
  6779. return false
  6780. }
  6781. },
  6782. /**
  6783. * 获取选区对应的Range
  6784. * @method getRange
  6785. * @return { Object } 得到Range对象
  6786. * @example
  6787. * ```javascript
  6788. * editor.selection.getRange();
  6789. * ```
  6790. */
  6791. getRange: function () {
  6792. var me = this
  6793. function optimze(range) {
  6794. var child = me.document.body.firstChild,
  6795. collapsed = range.collapsed
  6796. while (child && child.firstChild) {
  6797. range.setStart(child, 0)
  6798. child = child.firstChild
  6799. }
  6800. if (!range.startContainer) {
  6801. range.setStart(me.document.body, 0)
  6802. }
  6803. if (collapsed) {
  6804. range.collapse(true)
  6805. }
  6806. }
  6807. if (me._cachedRange != null) {
  6808. return this._cachedRange
  6809. }
  6810. var range = new baidu.editor.dom.Range(me.document)
  6811. if (browser.ie9below) {
  6812. var nativeRange = me.getIERange()
  6813. if (nativeRange) {
  6814. //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
  6815. try {
  6816. transformIERangeToRange(nativeRange, range)
  6817. } catch (e) {
  6818. optimze(range)
  6819. }
  6820. } else {
  6821. optimze(range)
  6822. }
  6823. } else {
  6824. var sel = me.getNative()
  6825. if (sel && sel.rangeCount) {
  6826. var firstRange = sel.getRangeAt(0)
  6827. var lastRange = sel.getRangeAt(sel.rangeCount - 1)
  6828. range
  6829. .setStart(firstRange.startContainer, firstRange.startOffset)
  6830. .setEnd(lastRange.endContainer, lastRange.endOffset)
  6831. if (
  6832. range.collapsed &&
  6833. domUtils.isBody(range.startContainer) &&
  6834. !range.startOffset
  6835. ) {
  6836. optimze(range)
  6837. }
  6838. } else {
  6839. //trace:1734 有可能已经不在dom树上了,标识的节点
  6840. if (
  6841. this._bakRange &&
  6842. domUtils.inDoc(this._bakRange.startContainer, this.document)
  6843. ) {
  6844. return this._bakRange
  6845. }
  6846. optimze(range)
  6847. }
  6848. }
  6849. return (this._bakRange = range)
  6850. },
  6851. /**
  6852. * 获取开始元素,用于状态反射
  6853. * @method getStart
  6854. * @return { Element } 获得开始元素
  6855. * @example
  6856. * ```javascript
  6857. * editor.selection.getStart();
  6858. * ```
  6859. */
  6860. getStart: function () {
  6861. if (this._cachedStartElement) {
  6862. return this._cachedStartElement
  6863. }
  6864. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  6865. tmpRange,
  6866. start,
  6867. tmp,
  6868. parent
  6869. if (browser.ie9below) {
  6870. if (!range) {
  6871. //todo 给第一个值可能会有问题
  6872. return this.document.body.firstChild
  6873. }
  6874. //control元素
  6875. if (range.item) {
  6876. return range.item(0)
  6877. }
  6878. tmpRange = range.duplicate()
  6879. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  6880. tmpRange.text.length > 0 && tmpRange.moveStart('character', 1)
  6881. tmpRange.collapse(1)
  6882. start = tmpRange.parentElement()
  6883. parent = tmp = range.parentElement()
  6884. while ((tmp = tmp.parentNode)) {
  6885. if (tmp == start) {
  6886. start = parent
  6887. break
  6888. }
  6889. }
  6890. } else {
  6891. range.shrinkBoundary()
  6892. start = range.startContainer
  6893. if (start.nodeType == 1 && start.hasChildNodes()) {
  6894. start =
  6895. start.childNodes[
  6896. Math.min(start.childNodes.length - 1, range.startOffset)
  6897. ]
  6898. }
  6899. if (start.nodeType == 3) {
  6900. return start.parentNode
  6901. }
  6902. }
  6903. return start
  6904. },
  6905. /**
  6906. * 得到选区中的文本
  6907. * @method getText
  6908. * @return { String } 选区中包含的文本
  6909. * @example
  6910. * ```javascript
  6911. * editor.selection.getText();
  6912. * ```
  6913. */
  6914. getText: function () {
  6915. var nativeSel, nativeRange
  6916. if (this.isFocus() && (nativeSel = this.getNative())) {
  6917. nativeRange = browser.ie9below
  6918. ? nativeSel.createRange()
  6919. : nativeSel.getRangeAt(0)
  6920. return browser.ie9below ? nativeRange.text : nativeRange.toString()
  6921. }
  6922. return ''
  6923. },
  6924. /**
  6925. * 清除选区
  6926. * @method clearRange
  6927. * @example
  6928. * ```javascript
  6929. * editor.selection.clearRange();
  6930. * ```
  6931. */
  6932. clearRange: function () {
  6933. this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
  6934. }
  6935. }
  6936. })()
  6937. // core/Editor.js
  6938. /**
  6939. * 编辑器主类,包含编辑器提供的大部分公用接口
  6940. * @file
  6941. * @module UE
  6942. * @class Editor
  6943. * @since 1.2.6.1
  6944. */
  6945. /**
  6946. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  6947. * @unfile
  6948. * @module UE
  6949. */
  6950. /**
  6951. * UEditor的核心类,为用户提供与编辑器交互的接口。
  6952. * @unfile
  6953. * @module UE
  6954. * @class Editor
  6955. */
  6956. ;(function () {
  6957. var uid = 0,
  6958. _selectionChangeTimer
  6959. /**
  6960. * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
  6961. * @private
  6962. * @method setValue
  6963. * @param { UE.Editor } editor 编辑器事例
  6964. */
  6965. function setValue(form, editor) {
  6966. var textarea
  6967. if (editor.textarea) {
  6968. if (utils.isString(editor.textarea)) {
  6969. for (
  6970. var i = 0,
  6971. ti,
  6972. tis = domUtils.getElementsByTagName(form, 'textarea');
  6973. (ti = tis[i++]);
  6974. ) {
  6975. if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
  6976. textarea = ti
  6977. break
  6978. }
  6979. }
  6980. } else {
  6981. textarea = editor.textarea
  6982. }
  6983. }
  6984. if (!textarea) {
  6985. form.appendChild(
  6986. (textarea = domUtils.createElement(document, 'textarea', {
  6987. name: editor.options.textarea,
  6988. id: 'ueditor_textarea_' + editor.options.textarea,
  6989. style: 'display:none'
  6990. }))
  6991. )
  6992. //不要产生多个textarea
  6993. editor.textarea = textarea
  6994. }
  6995. !textarea.getAttribute('name') &&
  6996. textarea.setAttribute('name', editor.options.textarea)
  6997. textarea.value = editor.hasContents()
  6998. ? editor.options.allHtmlEnabled
  6999. ? editor.getAllHtml()
  7000. : editor.getContent(null, null, true)
  7001. : ''
  7002. }
  7003. function loadPlugins(me) {
  7004. //初始化插件
  7005. for (var pi in UE.plugins) {
  7006. UE.plugins[pi].call(me)
  7007. }
  7008. }
  7009. function checkCurLang(I18N) {
  7010. for (var lang in I18N) {
  7011. return lang
  7012. }
  7013. }
  7014. function langReadied(me) {
  7015. me.langIsReady = true
  7016. me.fireEvent('langReady')
  7017. }
  7018. /**
  7019. * 编辑器准备就绪后会触发该事件
  7020. * @module UE
  7021. * @class Editor
  7022. * @event ready
  7023. * @remind render方法执行完成之后,会触发该事件
  7024. * @remind
  7025. * @example
  7026. * ```javascript
  7027. * editor.addListener( 'ready', function( editor ) {
  7028. * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
  7029. * } );
  7030. * ```
  7031. */
  7032. /**
  7033. * 执行destroy方法,会触发该事件
  7034. * @module UE
  7035. * @class Editor
  7036. * @event destroy
  7037. * @see UE.Editor:destroy()
  7038. */
  7039. /**
  7040. * 执行reset方法,会触发该事件
  7041. * @module UE
  7042. * @class Editor
  7043. * @event reset
  7044. * @see UE.Editor:reset()
  7045. */
  7046. /**
  7047. * 执行focus方法,会触发该事件
  7048. * @module UE
  7049. * @class Editor
  7050. * @event focus
  7051. * @see UE.Editor:focus(Boolean)
  7052. */
  7053. /**
  7054. * 语言加载完成会触发该事件
  7055. * @module UE
  7056. * @class Editor
  7057. * @event langReady
  7058. */
  7059. /**
  7060. * 运行命令之后会触发该命令
  7061. * @module UE
  7062. * @class Editor
  7063. * @event beforeExecCommand
  7064. */
  7065. /**
  7066. * 运行命令之后会触发该命令
  7067. * @module UE
  7068. * @class Editor
  7069. * @event afterExecCommand
  7070. */
  7071. /**
  7072. * 运行命令之前会触发该命令
  7073. * @module UE
  7074. * @class Editor
  7075. * @event firstBeforeExecCommand
  7076. */
  7077. /**
  7078. * 在getContent方法执行之前会触发该事件
  7079. * @module UE
  7080. * @class Editor
  7081. * @event beforeGetContent
  7082. * @see UE.Editor:getContent()
  7083. */
  7084. /**
  7085. * 在getContent方法执行之后会触发该事件
  7086. * @module UE
  7087. * @class Editor
  7088. * @event afterGetContent
  7089. * @see UE.Editor:getContent()
  7090. */
  7091. /**
  7092. * 在getAllHtml方法执行时会触发该事件
  7093. * @module UE
  7094. * @class Editor
  7095. * @event getAllHtml
  7096. * @see UE.Editor:getAllHtml()
  7097. */
  7098. /**
  7099. * 在setContent方法执行之前会触发该事件
  7100. * @module UE
  7101. * @class Editor
  7102. * @event beforeSetContent
  7103. * @see UE.Editor:setContent(String)
  7104. */
  7105. /**
  7106. * 在setContent方法执行之后会触发该事件
  7107. * @module UE
  7108. * @class Editor
  7109. * @event afterSetContent
  7110. * @see UE.Editor:setContent(String)
  7111. */
  7112. /**
  7113. * 每当编辑器内部选区发生改变时,将触发该事件
  7114. * @event selectionchange
  7115. * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
  7116. * @example
  7117. * ```javascript
  7118. * editor.addListener( 'selectionchange', function( editor ) {
  7119. * console.log('选区发生改变');
  7120. * }
  7121. */
  7122. /**
  7123. * 在所有selectionchange的监听函数执行之前,会触发该事件
  7124. * @module UE
  7125. * @class Editor
  7126. * @event beforeSelectionChange
  7127. * @see UE.Editor:selectionchange
  7128. */
  7129. /**
  7130. * 在所有selectionchange的监听函数执行完之后,会触发该事件
  7131. * @module UE
  7132. * @class Editor
  7133. * @event afterSelectionChange
  7134. * @see UE.Editor:selectionchange
  7135. */
  7136. /**
  7137. * 编辑器内容发生改变时会触发该事件
  7138. * @module UE
  7139. * @class Editor
  7140. * @event contentChange
  7141. */
  7142. /**
  7143. * 以默认参数构建一个编辑器实例
  7144. * @constructor
  7145. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  7146. * @example
  7147. * ```javascript
  7148. * var editor = new UE.Editor();
  7149. * editor.execCommand('blod');
  7150. * ```
  7151. * @see UE.Config
  7152. */
  7153. /**
  7154. * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
  7155. * @constructor
  7156. * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
  7157. * @param { Object } setting 创建编辑器的参数
  7158. * @example
  7159. * ```javascript
  7160. * var editor = new UE.Editor();
  7161. * editor.execCommand('blod');
  7162. * ```
  7163. * @see UE.Config
  7164. */
  7165. var Editor = (UE.Editor = function (options) {
  7166. var me = this
  7167. me.uid = uid++
  7168. EventBase.call(me)
  7169. me.commands = {}
  7170. me.options = utils.extend(
  7171. utils.clone(options || {}),
  7172. UEDITOR_CONFIG,
  7173. true
  7174. )
  7175. me.shortcutkeys = {}
  7176. me.inputRules = []
  7177. me.outputRules = []
  7178. //设置默认的常用属性
  7179. me.setOpt(Editor.defaultOptions(me))
  7180. /* 尝试异步加载后台配置 */
  7181. me.loadServerConfig()
  7182. if (!utils.isEmptyObject(UE.I18N)) {
  7183. //修改默认的语言类型
  7184. me.options.lang = checkCurLang(UE.I18N)
  7185. UE.plugin.load(me)
  7186. langReadied(me)
  7187. } else {
  7188. utils.loadFile(
  7189. document,
  7190. {
  7191. src:
  7192. me.options.langPath +
  7193. me.options.lang +
  7194. '/' +
  7195. me.options.lang +
  7196. '.js',
  7197. tag: 'script',
  7198. type: 'text/javascript',
  7199. defer: 'defer'
  7200. },
  7201. function () {
  7202. UE.plugin.load(me)
  7203. langReadied(me)
  7204. }
  7205. )
  7206. }
  7207. UE.instants['ueditorInstant' + me.uid] = me
  7208. })
  7209. Editor.prototype = {
  7210. registerCommand: function (name, obj) {
  7211. this.commands[name] = obj
  7212. },
  7213. /**
  7214. * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
  7215. * @method ready
  7216. * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
  7217. * 立即触发该回调。
  7218. * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
  7219. * @example
  7220. * ```javascript
  7221. * editor.ready( function( editor ) {
  7222. * editor.setContent('初始化完毕');
  7223. * } );
  7224. * ```
  7225. * @see UE.Editor.event:ready
  7226. */
  7227. ready: function (fn) {
  7228. var me = this
  7229. if (fn) {
  7230. me.isReady ? fn.apply(me) : me.addListener('ready', fn)
  7231. }
  7232. },
  7233. /**
  7234. * 该方法是提供给插件里面使用,设置配置项默认值
  7235. * @method setOpt
  7236. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  7237. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  7238. * @param { String } key 编辑器的可接受的选项名称
  7239. * @param { * } val 该选项可接受的值
  7240. * @example
  7241. * ```javascript
  7242. * editor.setOpt( 'initContent', '欢迎使用编辑器' );
  7243. * ```
  7244. */
  7245. /**
  7246. * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
  7247. * @method setOpt
  7248. * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
  7249. * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
  7250. * @param { Object } options 将要设置的选项的键值对对象
  7251. * @example
  7252. * ```javascript
  7253. * editor.setOpt( {
  7254. * 'initContent': '欢迎使用编辑器'
  7255. * } );
  7256. * ```
  7257. */
  7258. setOpt: function (key, val) {
  7259. var obj = {}
  7260. if (utils.isString(key)) {
  7261. obj[key] = val
  7262. } else {
  7263. obj = key
  7264. }
  7265. utils.extend(this.options, obj, true)
  7266. },
  7267. getOpt: function (key) {
  7268. return this.options[key]
  7269. },
  7270. /**
  7271. * 销毁编辑器实例,使用textarea代替
  7272. * @method destroy
  7273. * @example
  7274. * ```javascript
  7275. * editor.destroy();
  7276. * ```
  7277. */
  7278. destroy: function () {
  7279. var me = this
  7280. me.fireEvent('destroy')
  7281. var container = me.container.parentNode
  7282. var textarea = me.textarea
  7283. if (!textarea) {
  7284. textarea = document.createElement('textarea')
  7285. container.parentNode.insertBefore(textarea, container)
  7286. } else {
  7287. textarea.style.display = ''
  7288. }
  7289. textarea.style.width = me.iframe.offsetWidth + 'px'
  7290. textarea.style.height = me.iframe.offsetHeight + 'px'
  7291. textarea.value = me.getContent()
  7292. textarea.id = me.key
  7293. container.innerHTML = ''
  7294. domUtils.remove(container)
  7295. var key = me.key
  7296. //trace:2004
  7297. for (var p in me) {
  7298. if (me.hasOwnProperty(p)) {
  7299. delete this[p]
  7300. }
  7301. }
  7302. UE.delEditor(key)
  7303. },
  7304. /**
  7305. * 渲染编辑器的DOM到指定容器
  7306. * @method render
  7307. * @param { String } containerId 指定一个容器ID
  7308. * @remind 执行该方法,会触发ready事件
  7309. * @warning 必须且只能调用一次
  7310. */
  7311. /**
  7312. * 渲染编辑器的DOM到指定容器
  7313. * @method render
  7314. * @param { Element } containerDom 直接指定容器对象
  7315. * @remind 执行该方法,会触发ready事件
  7316. * @warning 必须且只能调用一次
  7317. */
  7318. render: function (container) {
  7319. var me = this,
  7320. options = me.options,
  7321. getStyleValue = function (attr) {
  7322. return parseInt(domUtils.getComputedStyle(container, attr))
  7323. }
  7324. if (utils.isString(container)) {
  7325. container = document.getElementById(container)
  7326. }
  7327. if (container) {
  7328. if (options.initialFrameWidth) {
  7329. options.minFrameWidth = options.initialFrameWidth
  7330. } else {
  7331. options.minFrameWidth = options.initialFrameWidth =
  7332. container.offsetWidth
  7333. }
  7334. if (options.initialFrameHeight) {
  7335. options.minFrameHeight = options.initialFrameHeight
  7336. } else {
  7337. options.initialFrameHeight = options.minFrameHeight =
  7338. container.offsetHeight
  7339. }
  7340. container.style.width = /%$/.test(options.initialFrameWidth)
  7341. ? '100%'
  7342. : options.initialFrameWidth -
  7343. getStyleValue('padding-left') -
  7344. getStyleValue('padding-right') +
  7345. 'px'
  7346. container.style.height = /%$/.test(options.initialFrameHeight)
  7347. ? '100%'
  7348. : options.initialFrameHeight -
  7349. getStyleValue('padding-top') -
  7350. getStyleValue('padding-bottom') +
  7351. 'px'
  7352. container.style.zIndex = options.zIndex
  7353. var html =
  7354. (ie && browser.version < 9 ? '' : '<!DOCTYPE html>') +
  7355. "<html xmlns='http://www.w3.org/1999/xhtml' class='view' ><head>" +
  7356. "<style type='text/css'>" +
  7357. //设置四周的留边
  7358. '.view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n' +
  7359. //设置默认字体和字号
  7360. //font-family不能呢随便改,在safari下fillchar会有解析问题
  7361. 'body{margin:8px;font-family:sans-serif;font-size:16px;}' +
  7362. //设置段落间距
  7363. 'p{margin:5px 0;}</style>' +
  7364. (options.iframeCssUrl
  7365. ? "<link rel='stylesheet' type='text/css' href='" +
  7366. utils.unhtml(options.iframeCssUrl) +
  7367. "'/>"
  7368. : '') +
  7369. (options.initialStyle
  7370. ? '<style>' + options.initialStyle + '</style>'
  7371. : '') +
  7372. "</head><body class='view' ></body>" +
  7373. "<script type='text/javascript' " +
  7374. (ie ? "defer='defer'" : '') +
  7375. " id='_initialScript'>" +
  7376. "setTimeout(function(){editor = window.parent.UE.instants['ueditorInstant" +
  7377. me.uid +
  7378. "'];editor._setup(document);},0);" +
  7379. "var _tmpScript = document.getElementById('_initialScript');_tmpScript.parentNode.removeChild(_tmpScript);</script></html>"
  7380. container.appendChild(
  7381. domUtils.createElement(document, 'iframe', {
  7382. id: 'ueditor_' + me.uid,
  7383. width: '100%',
  7384. height: '100%',
  7385. frameborder: '0',
  7386. //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
  7387. // scrolling :'no',
  7388. src:
  7389. 'javascript:void(function(){document.open();' +
  7390. (options.customDomain && document.domain != location.hostname
  7391. ? 'document.domain="' + document.domain + '";'
  7392. : '') +
  7393. 'document.write("' +
  7394. html +
  7395. '");document.close();}())'
  7396. })
  7397. )
  7398. container.style.overflow = 'hidden'
  7399. //解决如果是给定的百分比,会导致高度算不对的问题
  7400. setTimeout(function () {
  7401. if (/%$/.test(options.initialFrameWidth)) {
  7402. options.minFrameWidth = options.initialFrameWidth =
  7403. container.offsetWidth
  7404. //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
  7405. // container.style.width = options.initialFrameWidth + 'px';
  7406. }
  7407. if (/%$/.test(options.initialFrameHeight)) {
  7408. options.minFrameHeight = options.initialFrameHeight =
  7409. container.offsetHeight
  7410. container.style.height = options.initialFrameHeight + 'px'
  7411. }
  7412. })
  7413. }
  7414. },
  7415. /**
  7416. * 编辑器初始化
  7417. * @method _setup
  7418. * @private
  7419. * @param { Element } doc 编辑器Iframe中的文档对象
  7420. */
  7421. _setup: function (doc) {
  7422. var me = this,
  7423. options = me.options
  7424. if (ie) {
  7425. doc.body.disabled = true
  7426. doc.body.contentEditable = true
  7427. doc.body.disabled = false
  7428. } else {
  7429. doc.body.contentEditable = true
  7430. }
  7431. doc.body.spellcheck = false
  7432. me.document = doc
  7433. me.window = doc.defaultView || doc.parentWindow
  7434. me.iframe = me.window.frameElement
  7435. me.body = doc.body
  7436. me.selection = new dom.Selection(doc)
  7437. //gecko初始化就能得到range,无法判断isFocus了
  7438. var geckoSel
  7439. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  7440. geckoSel.removeAllRanges()
  7441. }
  7442. this._initEvents()
  7443. //为form提交提供一个隐藏的textarea
  7444. for (
  7445. var form = this.iframe.parentNode;
  7446. !domUtils.isBody(form);
  7447. form = form.parentNode
  7448. ) {
  7449. if (form.tagName == 'FORM') {
  7450. me.form = form
  7451. if (me.options.autoSyncData) {
  7452. domUtils.on(me.window, 'blur', function () {
  7453. setValue(form, me)
  7454. })
  7455. } else {
  7456. domUtils.on(form, 'submit', function () {
  7457. setValue(this, me)
  7458. })
  7459. }
  7460. break
  7461. }
  7462. }
  7463. if (options.initialContent) {
  7464. if (options.autoClearinitialContent) {
  7465. var oldExecCommand = me.execCommand
  7466. me.execCommand = function () {
  7467. me.fireEvent('firstBeforeExecCommand')
  7468. return oldExecCommand.apply(me, arguments)
  7469. }
  7470. this._setDefaultContent(options.initialContent)
  7471. } else this.setContent(options.initialContent, false, true)
  7472. }
  7473. //编辑器不能为空内容
  7474. if (domUtils.isEmptyNode(me.body)) {
  7475. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  7476. }
  7477. //如果要求focus, 就把光标定位到内容开始
  7478. if (options.focus) {
  7479. setTimeout(function () {
  7480. me.focus(me.options.focusInEnd)
  7481. //如果自动清除开着,就不需要做selectionchange;
  7482. !me.options.autoClearinitialContent && me._selectionChange()
  7483. }, 0)
  7484. }
  7485. if (!me.container) {
  7486. me.container = this.iframe.parentNode
  7487. }
  7488. if (options.fullscreen && me.ui) {
  7489. me.ui.setFullScreen(true)
  7490. }
  7491. try {
  7492. me.document.execCommand('2D-position', false, false)
  7493. } catch (e) {}
  7494. try {
  7495. me.document.execCommand('enableInlineTableEditing', false, false)
  7496. } catch (e) {}
  7497. try {
  7498. me.document.execCommand('enableObjectResizing', false, false)
  7499. } catch (e) {}
  7500. //挂接快捷键
  7501. me._bindshortcutKeys()
  7502. me.isReady = 1
  7503. me.fireEvent('ready')
  7504. options.onready && options.onready.call(me)
  7505. if (!browser.ie9below) {
  7506. domUtils.on(me.window, ['blur', 'focus'], function (e) {
  7507. //chrome下会出现alt+tab切换时,导致选区位置不对
  7508. if (e.type == 'blur') {
  7509. me._bakRange = me.selection.getRange()
  7510. try {
  7511. me._bakNativeRange = me.selection.getNative().getRangeAt(0)
  7512. me.selection.getNative().removeAllRanges()
  7513. } catch (e) {
  7514. me._bakNativeRange = null
  7515. }
  7516. } else {
  7517. try {
  7518. me._bakRange && me._bakRange.select()
  7519. } catch (e) {}
  7520. }
  7521. })
  7522. }
  7523. //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
  7524. if (browser.gecko && browser.version <= 10902) {
  7525. //修复ff3.6初始化进来,不能点击获得焦点
  7526. me.body.contentEditable = false
  7527. setTimeout(function () {
  7528. me.body.contentEditable = true
  7529. }, 100)
  7530. setInterval(function () {
  7531. me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
  7532. }, 100)
  7533. }
  7534. !options.isShow && me.setHide()
  7535. options.readonly && me.setDisabled()
  7536. },
  7537. /**
  7538. * 同步数据到编辑器所在的form
  7539. * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
  7540. * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
  7541. * @method sync
  7542. * @example
  7543. * ```javascript
  7544. * editor.sync();
  7545. * form.sumbit(); //form变量已经指向了form元素
  7546. * ```
  7547. */
  7548. /**
  7549. * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
  7550. * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
  7551. * @method sync
  7552. * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  7553. */
  7554. sync: function (formId) {
  7555. var me = this,
  7556. form = formId
  7557. ? document.getElementById(formId)
  7558. : domUtils.findParent(
  7559. me.iframe.parentNode,
  7560. function (node) {
  7561. return node.tagName == 'FORM'
  7562. },
  7563. true
  7564. )
  7565. form && setValue(form, me)
  7566. },
  7567. /**
  7568. * 设置编辑器高度
  7569. * @method setHeight
  7570. * @remind 当配置项autoHeightEnabled为真时,该方法无效
  7571. * @param { Number } number 设置的高度值,纯数值,不带单位
  7572. * @example
  7573. * ```javascript
  7574. * editor.setHeight(number);
  7575. * ```
  7576. */
  7577. setHeight: function (height, notSetHeight) {
  7578. if (height !== parseInt(this.iframe.parentNode.style.height)) {
  7579. this.iframe.parentNode.style.height = height + 'px'
  7580. }
  7581. !notSetHeight &&
  7582. (this.options.minFrameHeight = this.options.initialFrameHeight =
  7583. height)
  7584. this.body.style.height = height + 'px'
  7585. !notSetHeight && this.trigger('setHeight')
  7586. },
  7587. /**
  7588. * 为编辑器的编辑命令提供快捷键
  7589. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  7590. * @method addshortcutkey
  7591. * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
  7592. * @example
  7593. * ```javascript
  7594. * editor.addshortcutkey({
  7595. * "Bold" : "ctrl+66",//^B
  7596. * "Italic" : "ctrl+73", //^I
  7597. * });
  7598. * ```
  7599. */
  7600. /**
  7601. * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
  7602. * @method addshortcutkey
  7603. * @param { String } cmd 触发快捷键时,响应的命令
  7604. * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
  7605. * @example
  7606. * ```javascript
  7607. * editor.addshortcutkey("Underline", "ctrl+85"); //^U
  7608. * ```
  7609. */
  7610. addshortcutkey: function (cmd, keys) {
  7611. var obj = {}
  7612. if (keys) {
  7613. obj[cmd] = keys
  7614. } else {
  7615. obj = cmd
  7616. }
  7617. utils.extend(this.shortcutkeys, obj)
  7618. },
  7619. /**
  7620. * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
  7621. * @method _bindshortcutKeys
  7622. * @private
  7623. */
  7624. _bindshortcutKeys: function () {
  7625. var me = this,
  7626. shortcutkeys = this.shortcutkeys
  7627. me.addListener('keydown', function (type, e) {
  7628. var keyCode = e.keyCode || e.which
  7629. for (var i in shortcutkeys) {
  7630. var tmp = shortcutkeys[i].split(',')
  7631. for (var t = 0, ti; (ti = tmp[t++]); ) {
  7632. ti = ti.split(':')
  7633. var key = ti[0],
  7634. param = ti[1]
  7635. if (
  7636. /^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) ||
  7637. /^(\d+)$/.test(key)
  7638. ) {
  7639. if (
  7640. ((RegExp.$1 == 'ctrl' ? e.ctrlKey || e.metaKey : 0) &&
  7641. (RegExp.$2 != '' ? e[RegExp.$2.slice(1) + 'Key'] : 1) &&
  7642. keyCode == RegExp.$3) ||
  7643. keyCode == RegExp.$1
  7644. ) {
  7645. if (me.queryCommandState(i, param) != -1)
  7646. me.execCommand(i, param)
  7647. domUtils.preventDefault(e)
  7648. }
  7649. }
  7650. }
  7651. }
  7652. })
  7653. },
  7654. /**
  7655. * 获取编辑器的内容
  7656. * @method getContent
  7657. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  7658. * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”&lt;p&gt;&lt;br/&gt;&lt;/p&gt;“), 则返回空字符串
  7659. * @example
  7660. * ```javascript
  7661. * //编辑器html内容:<p>1<strong>2<em>34</em>5</strong>6</p>
  7662. * var content = editor.getContent(); //返回值:<p>1<strong>2<em>34</em>5</strong>6</p>
  7663. * ```
  7664. */
  7665. /**
  7666. * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
  7667. * @method getContent
  7668. * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
  7669. * 代表当前编辑器的内容是否空,
  7670. * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
  7671. * 经过内置过滤规则处理后的内容。
  7672. * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
  7673. * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
  7674. * @return { String } 编辑器的内容字符串
  7675. * @example
  7676. * ```javascript
  7677. * // editor 是一个编辑器的实例
  7678. * var content = editor.getContent( function ( editor ) {
  7679. * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
  7680. * } );
  7681. * ```
  7682. */
  7683. getContent: function (cmd, fn, notSetCursor, ignoreBlank, formatter) {
  7684. var me = this
  7685. if (cmd && utils.isFunction(cmd)) {
  7686. fn = cmd
  7687. cmd = ''
  7688. }
  7689. if (fn ? !fn() : !this.hasContents()) {
  7690. return ''
  7691. }
  7692. me.fireEvent('beforegetcontent')
  7693. var root = UE.htmlparser(me.body.innerHTML, ignoreBlank)
  7694. me.filterOutputRule(root)
  7695. me.fireEvent('aftergetcontent', cmd, root)
  7696. return root.toHtml(formatter)
  7697. },
  7698. /**
  7699. * 取得完整的html代码,可以直接显示成完整的html文档
  7700. * @method getAllHtml
  7701. * @return { String } 编辑器的内容html文档字符串
  7702. * @eaxmple
  7703. * ```javascript
  7704. * editor.getAllHtml(); //返回格式大致是: <html><head>...</head><body>...</body></html>
  7705. * ```
  7706. */
  7707. getAllHtml: function () {
  7708. var me = this,
  7709. headHtml = [],
  7710. html = ''
  7711. me.fireEvent('getAllHtml', headHtml)
  7712. if (browser.ie && browser.version > 8) {
  7713. var headHtmlForIE9 = ''
  7714. utils.each(me.document.styleSheets, function (si) {
  7715. headHtmlForIE9 += si.href
  7716. ? '<link rel="stylesheet" type="text/css" href="' +
  7717. si.href +
  7718. '" />'
  7719. : '<style>' + si.cssText + '</style>'
  7720. })
  7721. utils.each(me.document.getElementsByTagName('script'), function (si) {
  7722. headHtmlForIE9 += si.outerHTML
  7723. })
  7724. }
  7725. return (
  7726. '<html><head>' +
  7727. (me.options.charset
  7728. ? '<meta http-equiv="Content-Type" content="text/html; charset=' +
  7729. me.options.charset +
  7730. '"/>'
  7731. : '') +
  7732. (headHtmlForIE9 ||
  7733. me.document.getElementsByTagName('head')[0].innerHTML) +
  7734. headHtml.join('\n') +
  7735. '</head>' +
  7736. '<body ' +
  7737. (ie && browser.version < 9 ? 'class="view"' : '') +
  7738. '>' +
  7739. me.getContent(null, null, true) +
  7740. '</body></html>'
  7741. )
  7742. },
  7743. /**
  7744. * 得到编辑器的纯文本内容,但会保留段落格式
  7745. * @method getPlainTxt
  7746. * @return { String } 编辑器带段落格式的纯文本内容字符串
  7747. * @example
  7748. * ```javascript
  7749. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  7750. * console.log(editor.getPlainTxt()); //输出:"1\n2\n
  7751. * ```
  7752. */
  7753. getPlainTxt: function () {
  7754. var reg = new RegExp(domUtils.fillChar, 'g'),
  7755. html = this.body.innerHTML.replace(/[\n\r]/g, '') //ie要先去了\n在处理
  7756. html = html
  7757. .replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  7758. .replace(/<br\/?>/gi, '\n')
  7759. .replace(/<[^>/]+>/g, '')
  7760. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  7761. return dtd.$block[c] ? '\n' : b ? b : ''
  7762. })
  7763. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  7764. return html
  7765. .replace(reg, '')
  7766. .replace(/\u00a0/g, ' ')
  7767. .replace(/&nbsp;/g, ' ')
  7768. },
  7769. /**
  7770. * 获取编辑器中的纯文本内容,没有段落格式
  7771. * @method getContentTxt
  7772. * @return { String } 编辑器不带段落格式的纯文本内容字符串
  7773. * @example
  7774. * ```javascript
  7775. * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
  7776. * console.log(editor.getPlainTxt()); //输出:"12
  7777. * ```
  7778. */
  7779. getContentTxt: function () {
  7780. var reg = new RegExp(domUtils.fillChar, 'g')
  7781. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  7782. return this.body[browser.ie ? 'innerText' : 'textContent']
  7783. .replace(reg, '')
  7784. .replace(/\u00a0/g, ' ')
  7785. },
  7786. /**
  7787. * 设置编辑器的内容,可修改编辑器当前的html内容
  7788. * @method setContent
  7789. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  7790. * @warning 该方法会触发selectionchange事件
  7791. * @param { String } html 要插入的html内容
  7792. * @example
  7793. * ```javascript
  7794. * editor.getContent('<p>test</p>');
  7795. * ```
  7796. */
  7797. /**
  7798. * 设置编辑器的内容,可修改编辑器当前的html内容
  7799. * @method setContent
  7800. * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
  7801. * @warning 该方法会触发selectionchange事件
  7802. * @param { String } html 要插入的html内容
  7803. * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
  7804. * @example
  7805. * ```javascript
  7806. * //假设设置前的编辑器内容是 <p>old text</p>
  7807. * editor.setContent('<p>new text</p>', true); //插入的结果是<p>old text</p><p>new text</p>
  7808. * ```
  7809. */
  7810. setContent: function (html, isAppendTo, notFireSelectionchange) {
  7811. var me = this
  7812. me.fireEvent('beforesetcontent', html)
  7813. var root = UE.htmlparser(html)
  7814. me.filterInputRule(root)
  7815. html = root.toHtml()
  7816. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html
  7817. function isCdataDiv(node) {
  7818. return node.tagName == 'DIV' && node.getAttribute('cdata_tag')
  7819. }
  7820. //给文本或者inline节点套p标签
  7821. if (me.options.enterTag == 'p') {
  7822. var child = this.body.firstChild,
  7823. tmpNode
  7824. if (
  7825. !child ||
  7826. (child.nodeType == 1 &&
  7827. (dtd.$cdata[child.tagName] ||
  7828. isCdataDiv(child) ||
  7829. domUtils.isCustomeNode(child)) &&
  7830. child === this.body.lastChild)
  7831. ) {
  7832. this.body.innerHTML =
  7833. '<p>' +
  7834. (browser.ie ? '&nbsp;' : '<br/>') +
  7835. '</p>' +
  7836. this.body.innerHTML
  7837. } else {
  7838. var p = me.document.createElement('p')
  7839. while (child) {
  7840. while (
  7841. child &&
  7842. (child.nodeType == 3 ||
  7843. (child.nodeType == 1 &&
  7844. dtd.p[child.tagName] &&
  7845. !dtd.$cdata[child.tagName]))
  7846. ) {
  7847. tmpNode = child.nextSibling
  7848. p.appendChild(child)
  7849. child = tmpNode
  7850. }
  7851. if (p.firstChild) {
  7852. if (!child) {
  7853. me.body.appendChild(p)
  7854. break
  7855. } else {
  7856. child.parentNode.insertBefore(p, child)
  7857. p = me.document.createElement('p')
  7858. }
  7859. }
  7860. child = child.nextSibling
  7861. }
  7862. }
  7863. }
  7864. me.fireEvent('aftersetcontent')
  7865. me.fireEvent('contentchange')
  7866. !notFireSelectionchange && me._selectionChange()
  7867. //清除保存的选区
  7868. me._bakRange = me._bakIERange = me._bakNativeRange = null
  7869. //trace:1742 setContent后gecko能得到焦点问题
  7870. var geckoSel
  7871. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  7872. geckoSel.removeAllRanges()
  7873. }
  7874. if (me.options.autoSyncData) {
  7875. me.form && setValue(me.form, me)
  7876. }
  7877. },
  7878. /**
  7879. * 让编辑器获得焦点,默认focus到编辑器头部
  7880. * @method focus
  7881. * @example
  7882. * ```javascript
  7883. * editor.focus()
  7884. * ```
  7885. */
  7886. /**
  7887. * 让编辑器获得焦点,toEnd确定focus位置
  7888. * @method focus
  7889. * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
  7890. * @example
  7891. * ```javascript
  7892. * editor.focus(true)
  7893. * ```
  7894. */
  7895. focus: function (toEnd) {
  7896. try {
  7897. var me = this,
  7898. rng = me.selection.getRange()
  7899. if (toEnd) {
  7900. var node = me.body.lastChild
  7901. if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
  7902. if (domUtils.isEmptyBlock(node)) {
  7903. rng.setStartAtFirst(node)
  7904. } else {
  7905. rng.setStartAtLast(node)
  7906. }
  7907. rng.collapse(true)
  7908. }
  7909. rng.setCursor(true)
  7910. } else {
  7911. if (
  7912. !rng.collapsed &&
  7913. domUtils.isBody(rng.startContainer) &&
  7914. rng.startOffset == 0
  7915. ) {
  7916. var node = me.body.firstChild
  7917. if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
  7918. rng.setStartAtFirst(node).collapse(true)
  7919. }
  7920. }
  7921. rng.select(true)
  7922. }
  7923. this.fireEvent('focus selectionchange')
  7924. } catch (e) {}
  7925. },
  7926. isFocus: function () {
  7927. return this.selection.isFocus()
  7928. },
  7929. blur: function () {
  7930. var sel = this.selection.getNative()
  7931. if (sel.empty && browser.ie) {
  7932. var nativeRng = document.body.createTextRange()
  7933. nativeRng.moveToElementText(document.body)
  7934. nativeRng.collapse(true)
  7935. nativeRng.select()
  7936. sel.empty()
  7937. } else {
  7938. sel.removeAllRanges()
  7939. }
  7940. //this.fireEvent('blur selectionchange');
  7941. },
  7942. /**
  7943. * 初始化UE事件及部分事件代理
  7944. * @method _initEvents
  7945. * @private
  7946. */
  7947. _initEvents: function () {
  7948. var me = this,
  7949. doc = me.document,
  7950. win = me.window
  7951. me._proxyDomEvent = utils.bind(me._proxyDomEvent, me)
  7952. domUtils.on(
  7953. doc,
  7954. [
  7955. 'click',
  7956. 'contextmenu',
  7957. 'mousedown',
  7958. 'keydown',
  7959. 'keyup',
  7960. 'keypress',
  7961. 'mouseup',
  7962. 'mouseover',
  7963. 'mouseout',
  7964. 'selectstart'
  7965. ],
  7966. me._proxyDomEvent
  7967. )
  7968. domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent)
  7969. domUtils.on(me.body, 'drop', function (e) {
  7970. //阻止ff下默认的弹出新页面打开图片
  7971. if (browser.gecko && e.stopPropagation) {
  7972. e.stopPropagation()
  7973. }
  7974. me.fireEvent('contentchange')
  7975. })
  7976. domUtils.on(doc, ['mouseup', 'keydown'], function (evt) {
  7977. //特殊键不触发selectionchange
  7978. if (
  7979. evt.type == 'keydown' &&
  7980. (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)
  7981. ) {
  7982. return
  7983. }
  7984. if (evt.button == 2) return
  7985. me._selectionChange(250, evt)
  7986. })
  7987. },
  7988. /**
  7989. * 触发事件代理
  7990. * @method _proxyDomEvent
  7991. * @private
  7992. * @return { * } fireEvent的返回值
  7993. * @see UE.EventBase:fireEvent(String)
  7994. */
  7995. _proxyDomEvent: function (evt) {
  7996. if (
  7997. this.fireEvent(
  7998. 'before' + evt.type.replace(/^on/, '').toLowerCase()
  7999. ) === false
  8000. ) {
  8001. return false
  8002. }
  8003. if (this.fireEvent(evt.type.replace(/^on/, ''), evt) === false) {
  8004. return false
  8005. }
  8006. return this.fireEvent(
  8007. 'after' + evt.type.replace(/^on/, '').toLowerCase()
  8008. )
  8009. },
  8010. /**
  8011. * 变化选区
  8012. * @method _selectionChange
  8013. * @private
  8014. */
  8015. _selectionChange: function (delay, evt) {
  8016. var me = this
  8017. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  8018. // if ( !me.selection.isFocus() ){
  8019. // return;
  8020. // }
  8021. var hackForMouseUp = false
  8022. var mouseX, mouseY
  8023. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  8024. var range = this.selection.getRange()
  8025. if (!range.collapsed) {
  8026. hackForMouseUp = true
  8027. mouseX = evt.clientX
  8028. mouseY = evt.clientY
  8029. }
  8030. }
  8031. clearTimeout(_selectionChangeTimer)
  8032. _selectionChangeTimer = setTimeout(function () {
  8033. if (!me.selection || !me.selection.getNative()) {
  8034. return
  8035. }
  8036. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  8037. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  8038. var ieRange
  8039. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  8040. ieRange = me.document.body.createTextRange()
  8041. try {
  8042. ieRange.moveToPoint(mouseX, mouseY)
  8043. } catch (ex) {
  8044. ieRange = null
  8045. }
  8046. }
  8047. var bakGetIERange
  8048. if (ieRange) {
  8049. bakGetIERange = me.selection.getIERange
  8050. me.selection.getIERange = function () {
  8051. return ieRange
  8052. }
  8053. }
  8054. me.selection.cache()
  8055. if (bakGetIERange) {
  8056. me.selection.getIERange = bakGetIERange
  8057. }
  8058. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  8059. me.fireEvent('beforeselectionchange')
  8060. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  8061. me.fireEvent('selectionchange', !!evt)
  8062. me.fireEvent('afterselectionchange')
  8063. me.selection.clear()
  8064. }
  8065. }, delay || 50)
  8066. },
  8067. /**
  8068. * 执行编辑命令
  8069. * @method _callCmdFn
  8070. * @private
  8071. * @param { String } fnName 函数名称
  8072. * @param { * } args 传给命令函数的参数
  8073. * @return { * } 返回命令函数运行的返回值
  8074. */
  8075. _callCmdFn: function (fnName, args) {
  8076. var cmdName = args[0].toLowerCase(),
  8077. cmd,
  8078. cmdFn
  8079. cmd = this.commands[cmdName] || UE.commands[cmdName]
  8080. cmdFn = cmd && cmd[fnName]
  8081. //没有querycommandstate或者没有command的都默认返回0
  8082. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  8083. return 0
  8084. } else if (cmdFn) {
  8085. return cmdFn.apply(this, args)
  8086. }
  8087. },
  8088. /**
  8089. * 执行编辑命令cmdName,完成富文本编辑效果
  8090. * @method execCommand
  8091. * @param { String } cmdName 需要执行的命令
  8092. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  8093. * @return { * } 返回命令函数运行的返回值
  8094. * @example
  8095. * ```javascript
  8096. * editor.execCommand(cmdName);
  8097. * ```
  8098. */
  8099. execCommand: function (cmdName) {
  8100. cmdName = cmdName.toLowerCase()
  8101. var me = this,
  8102. result,
  8103. cmd = me.commands[cmdName] || UE.commands[cmdName]
  8104. if (!cmd || !cmd.execCommand) {
  8105. return null
  8106. }
  8107. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  8108. me.__hasEnterExecCommand = true
  8109. if (me.queryCommandState.apply(me, arguments) != -1) {
  8110. me.fireEvent('saveScene')
  8111. me.fireEvent.apply(
  8112. me,
  8113. ['beforeexeccommand', cmdName].concat(arguments)
  8114. )
  8115. result = this._callCmdFn('execCommand', arguments)
  8116. //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
  8117. // (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  8118. me.fireEvent.apply(
  8119. me,
  8120. ['afterexeccommand', cmdName].concat(arguments)
  8121. )
  8122. me.fireEvent('saveScene')
  8123. }
  8124. me.__hasEnterExecCommand = false
  8125. } else {
  8126. result = this._callCmdFn('execCommand', arguments)
  8127. !me.__hasEnterExecCommand &&
  8128. !cmd.ignoreContentChange &&
  8129. !me._ignoreContentChange &&
  8130. me.fireEvent('contentchange')
  8131. }
  8132. !me.__hasEnterExecCommand &&
  8133. !cmd.ignoreContentChange &&
  8134. !me._ignoreContentChange &&
  8135. me._selectionChange()
  8136. return result
  8137. },
  8138. /**
  8139. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  8140. * @method queryCommandState
  8141. * @param { String } cmdName 需要查询的命令名称
  8142. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  8143. * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
  8144. * @example
  8145. * ```javascript
  8146. * editor.queryCommandState(cmdName) => (-1|0|1)
  8147. * ```
  8148. * @see COMMAND.LIST
  8149. */
  8150. queryCommandState: function (cmdName) {
  8151. return this._callCmdFn('queryCommandState', arguments)
  8152. },
  8153. /**
  8154. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  8155. * @method queryCommandValue
  8156. * @param { String } cmdName 需要查询的命令名称
  8157. * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
  8158. * @remind 只有部分插件有此方法
  8159. * @return { * } 返回每个命令特定的当前状态值
  8160. * @grammar editor.queryCommandValue(cmdName) => {*}
  8161. * @see COMMAND.LIST
  8162. */
  8163. queryCommandValue: function (cmdName) {
  8164. return this._callCmdFn('queryCommandValue', arguments)
  8165. },
  8166. /**
  8167. * 检查编辑区域中是否有内容
  8168. * @method hasContents
  8169. * @remind 默认有文本内容,或者有以下节点都不认为是空
  8170. * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
  8171. * @return { Boolean } 检查有内容返回true,否则返回false
  8172. * @example
  8173. * ```javascript
  8174. * editor.hasContents()
  8175. * ```
  8176. */
  8177. /**
  8178. * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
  8179. * @method hasContents
  8180. * @param { Array } tags 传入数组判断时用到的节点类型
  8181. * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
  8182. * @example
  8183. * ```javascript
  8184. * editor.hasContents(['span']);
  8185. * ```
  8186. */
  8187. hasContents: function (tags) {
  8188. if (tags) {
  8189. for (var i = 0, ci; (ci = tags[i++]); ) {
  8190. if (this.document.getElementsByTagName(ci).length > 0) {
  8191. return true
  8192. }
  8193. }
  8194. }
  8195. if (!domUtils.isEmptyBlock(this.body)) {
  8196. return true
  8197. }
  8198. //随时添加,定义的特殊标签如果存在,不能认为是空
  8199. tags = ['div']
  8200. for (i = 0; (ci = tags[i++]); ) {
  8201. var nodes = domUtils.getElementsByTagName(this.document, ci)
  8202. for (var n = 0, cn; (cn = nodes[n++]); ) {
  8203. if (domUtils.isCustomeNode(cn)) {
  8204. return true
  8205. }
  8206. }
  8207. }
  8208. return false
  8209. },
  8210. /**
  8211. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  8212. * @method reset
  8213. * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
  8214. * @example
  8215. * ```javascript
  8216. * editor.reset()
  8217. * ```
  8218. */
  8219. reset: function () {
  8220. this.fireEvent('reset')
  8221. },
  8222. /**
  8223. * 设置当前编辑区域可以编辑
  8224. * @method setEnabled
  8225. * @example
  8226. * ```javascript
  8227. * editor.setEnabled()
  8228. * ```
  8229. */
  8230. setEnabled: function () {
  8231. var me = this,
  8232. range
  8233. if (me.body.contentEditable == 'false') {
  8234. me.body.contentEditable = true
  8235. range = me.selection.getRange()
  8236. //有可能内容丢失了
  8237. try {
  8238. range.moveToBookmark(me.lastBk)
  8239. delete me.lastBk
  8240. } catch (e) {
  8241. range.setStartAtFirst(me.body).collapse(true)
  8242. }
  8243. range.select(true)
  8244. if (me.bkqueryCommandState) {
  8245. me.queryCommandState = me.bkqueryCommandState
  8246. delete me.bkqueryCommandState
  8247. }
  8248. if (me.bkqueryCommandValue) {
  8249. me.queryCommandValue = me.bkqueryCommandValue
  8250. delete me.bkqueryCommandValue
  8251. }
  8252. me.fireEvent('selectionchange')
  8253. }
  8254. },
  8255. enable: function () {
  8256. return this.setEnabled()
  8257. },
  8258. /** 设置当前编辑区域不可编辑
  8259. * @method setDisabled
  8260. */
  8261. /** 设置当前编辑区域不可编辑,except中的命令除外
  8262. * @method setDisabled
  8263. * @param { String } except 例外命令的字符串
  8264. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  8265. * @example
  8266. * ```javascript
  8267. * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
  8268. * ```
  8269. */
  8270. /** 设置当前编辑区域不可编辑,except中的命令除外
  8271. * @method setDisabled
  8272. * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
  8273. * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
  8274. * @example
  8275. * ```javascript
  8276. * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
  8277. * ```
  8278. */
  8279. setDisabled: function (except) {
  8280. var me = this
  8281. except = except ? (utils.isArray(except) ? except : [except]) : []
  8282. if (me.body.contentEditable == 'true') {
  8283. if (!me.lastBk) {
  8284. me.lastBk = me.selection.getRange().createBookmark(true)
  8285. }
  8286. me.body.contentEditable = false
  8287. me.bkqueryCommandState = me.queryCommandState
  8288. me.bkqueryCommandValue = me.queryCommandValue
  8289. me.queryCommandState = function (type) {
  8290. if (utils.indexOf(except, type) != -1) {
  8291. return me.bkqueryCommandState.apply(me, arguments)
  8292. }
  8293. return -1
  8294. }
  8295. me.queryCommandValue = function (type) {
  8296. if (utils.indexOf(except, type) != -1) {
  8297. return me.bkqueryCommandValue.apply(me, arguments)
  8298. }
  8299. return null
  8300. }
  8301. me.fireEvent('selectionchange')
  8302. }
  8303. },
  8304. disable: function (except) {
  8305. return this.setDisabled(except)
  8306. },
  8307. /**
  8308. * 设置默认内容
  8309. * @method _setDefaultContent
  8310. * @private
  8311. * @param { String } cont 要存入的内容
  8312. */
  8313. _setDefaultContent: (function () {
  8314. function clear() {
  8315. var me = this
  8316. if (me.document.getElementById('initContent')) {
  8317. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>'
  8318. me.removeListener('firstBeforeExecCommand focus', clear)
  8319. setTimeout(function () {
  8320. me.focus()
  8321. me._selectionChange()
  8322. }, 0)
  8323. }
  8324. }
  8325. return function (cont) {
  8326. var me = this
  8327. me.body.innerHTML = '<p id="initContent">' + cont + '</p>'
  8328. me.addListener('firstBeforeExecCommand focus', clear)
  8329. }
  8330. })(),
  8331. /**
  8332. * 显示编辑器
  8333. * @method setShow
  8334. * @example
  8335. * ```javascript
  8336. * editor.setShow()
  8337. * ```
  8338. */
  8339. setShow: function () {
  8340. var me = this,
  8341. range = me.selection.getRange()
  8342. if (me.container.style.display == 'none') {
  8343. //有可能内容丢失了
  8344. try {
  8345. range.moveToBookmark(me.lastBk)
  8346. delete me.lastBk
  8347. } catch (e) {
  8348. range.setStartAtFirst(me.body).collapse(true)
  8349. }
  8350. //ie下focus实效,所以做了个延迟
  8351. setTimeout(function () {
  8352. range.select(true)
  8353. }, 100)
  8354. me.container.style.display = ''
  8355. }
  8356. },
  8357. show: function () {
  8358. return this.setShow()
  8359. },
  8360. /**
  8361. * 隐藏编辑器
  8362. * @method setHide
  8363. * @example
  8364. * ```javascript
  8365. * editor.setHide()
  8366. * ```
  8367. */
  8368. setHide: function () {
  8369. var me = this
  8370. if (!me.lastBk) {
  8371. me.lastBk = me.selection.getRange().createBookmark(true)
  8372. }
  8373. me.container.style.display = 'none'
  8374. },
  8375. hide: function () {
  8376. return this.setHide()
  8377. },
  8378. /**
  8379. * 根据指定的路径,获取对应的语言资源
  8380. * @method getLang
  8381. * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
  8382. * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
  8383. * @example
  8384. * ```javascript
  8385. * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
  8386. * ```
  8387. */
  8388. getLang: function (path) {
  8389. // HaoChuan9421
  8390. if (!this.options) {
  8391. return ''
  8392. }
  8393. var lang = UE.I18N[this.options.lang]
  8394. if (!lang) {
  8395. throw Error('not import language file')
  8396. }
  8397. path = (path || '').split('.')
  8398. for (var i = 0, ci; (ci = path[i++]); ) {
  8399. lang = lang[ci]
  8400. if (!lang) break
  8401. }
  8402. return lang
  8403. },
  8404. /**
  8405. * 计算编辑器html内容字符串的长度
  8406. * @method getContentLength
  8407. * @return { Number } 返回计算的长度
  8408. * @example
  8409. * ```javascript
  8410. * //编辑器html内容<p><strong>132</strong></p>
  8411. * editor.getContentLength() //返回27
  8412. * ```
  8413. */
  8414. /**
  8415. * 计算编辑器当前纯文本内容的长度
  8416. * @method getContentLength
  8417. * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
  8418. * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
  8419. * @example
  8420. * ```javascript
  8421. * //编辑器html内容<p><strong>132</strong></p>
  8422. * editor.getContentLength() //返回3
  8423. * ```
  8424. */
  8425. getContentLength: function (ingoneHtml, tagNames) {
  8426. var count = this.getContent(false, false, true).length
  8427. if (ingoneHtml) {
  8428. tagNames = (tagNames || []).concat(['hr', 'img', 'iframe'])
  8429. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length
  8430. for (var i = 0, ci; (ci = tagNames[i++]); ) {
  8431. count += this.document.getElementsByTagName(ci).length
  8432. }
  8433. }
  8434. return count
  8435. },
  8436. /**
  8437. * 注册输入过滤规则
  8438. * @method addInputRule
  8439. * @param { Function } rule 要添加的过滤规则
  8440. * @example
  8441. * ```javascript
  8442. * editor.addInputRule(function(root){
  8443. * $.each(root.getNodesByTagName('div'),function(i,node){
  8444. * node.tagName="p";
  8445. * });
  8446. * });
  8447. * ```
  8448. */
  8449. addInputRule: function (rule) {
  8450. this.inputRules.push(rule)
  8451. },
  8452. /**
  8453. * 执行注册的过滤规则
  8454. * @method filterInputRule
  8455. * @param { UE.uNode } root 要过滤的uNode节点
  8456. * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
  8457. * @example
  8458. * ```javascript
  8459. * editor.filterInputRule(editor.body);
  8460. * ```
  8461. * @see UE.Editor:addInputRule
  8462. */
  8463. filterInputRule: function (root) {
  8464. for (var i = 0, ci; (ci = this.inputRules[i++]); ) {
  8465. ci.call(this, root)
  8466. }
  8467. },
  8468. /**
  8469. * 注册输出过滤规则
  8470. * @method addOutputRule
  8471. * @param { Function } rule 要添加的过滤规则
  8472. * @example
  8473. * ```javascript
  8474. * editor.addOutputRule(function(root){
  8475. * $.each(root.getNodesByTagName('p'),function(i,node){
  8476. * node.tagName="div";
  8477. * });
  8478. * });
  8479. * ```
  8480. */
  8481. addOutputRule: function (rule) {
  8482. this.outputRules.push(rule)
  8483. },
  8484. /**
  8485. * 根据输出过滤规则,过滤编辑器内容
  8486. * @method filterOutputRule
  8487. * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
  8488. * @param { UE.uNode } root 要过滤的uNode节点
  8489. * @example
  8490. * ```javascript
  8491. * editor.filterOutputRule(editor.body);
  8492. * ```
  8493. * @see UE.Editor:addOutputRule
  8494. */
  8495. filterOutputRule: function (root) {
  8496. for (var i = 0, ci; (ci = this.outputRules[i++]); ) {
  8497. ci.call(this, root)
  8498. }
  8499. },
  8500. /**
  8501. * 根据action名称获取请求的路径
  8502. * @method getActionUrl
  8503. * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
  8504. * @param { String } action action名称
  8505. * @example
  8506. * ```javascript
  8507. * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
  8508. * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
  8509. * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
  8510. * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
  8511. * ```
  8512. */
  8513. getActionUrl: function (action) {
  8514. var actionName = this.getOpt(action) || action,
  8515. imageUrl = this.getOpt('imageUrl'),
  8516. serverUrl = this.getOpt('serverUrl')
  8517. if (!serverUrl && imageUrl) {
  8518. serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2')
  8519. }
  8520. if (serverUrl) {
  8521. serverUrl =
  8522. serverUrl +
  8523. (serverUrl.indexOf('?') == -1 ? '?' : '&') +
  8524. 'action=' +
  8525. (actionName || '')
  8526. return utils.formatUrl(serverUrl)
  8527. } else {
  8528. return ''
  8529. }
  8530. }
  8531. }
  8532. utils.inherits(Editor, EventBase)
  8533. })()
  8534. // core/Editor.defaultoptions.js
  8535. //维护编辑器一下默认的不在插件中的配置项
  8536. UE.Editor.defaultOptions = function (editor) {
  8537. var _url = editor.options.UEDITOR_HOME_URL
  8538. return {
  8539. isShow: true,
  8540. initialContent: '',
  8541. initialStyle: '',
  8542. autoClearinitialContent: false,
  8543. iframeCssUrl: _url + 'themes/iframe.css',
  8544. textarea: 'editorValue',
  8545. focus: false,
  8546. focusInEnd: true,
  8547. autoClearEmptyNode: true,
  8548. fullscreen: false,
  8549. readonly: false,
  8550. zIndex: 999,
  8551. imagePopup: true,
  8552. enterTag: 'p',
  8553. customDomain: false,
  8554. lang: 'zh-cn',
  8555. langPath: _url + 'lang/',
  8556. theme: 'default',
  8557. themePath: _url + 'themes/',
  8558. allHtmlEnabled: false,
  8559. scaleEnabled: false,
  8560. tableNativeEditInFF: false,
  8561. autoSyncData: true,
  8562. fileNameFormat: '{time}{rand:6}'
  8563. }
  8564. }
  8565. // core/loadconfig.js
  8566. ;(function () {
  8567. UE.Editor.prototype.loadServerConfig = function () {
  8568. var me = this
  8569. setTimeout(function () {
  8570. try {
  8571. me.options.imageUrl &&
  8572. me.setOpt(
  8573. 'serverUrl',
  8574. me.options.imageUrl.replace(
  8575. /^(.*[\/]).+([\.].+)$/,
  8576. '$1controller$2'
  8577. )
  8578. )
  8579. var configUrl = me.getActionUrl('config'),
  8580. isJsonp = utils.isCrossDomainUrl(configUrl)
  8581. /* 发出ajax请求 */
  8582. me._serverConfigLoaded = false
  8583. configUrl &&
  8584. UE.ajax.request(configUrl, {
  8585. method: 'GET',
  8586. dataType: isJsonp ? 'jsonp' : '',
  8587. onsuccess: function (r) {
  8588. try {
  8589. var config = isJsonp ? r : eval('(' + r.responseText + ')')
  8590. utils.extend(me.options, config)
  8591. me.fireEvent('serverConfigLoaded')
  8592. me._serverConfigLoaded = true
  8593. } catch (e) {
  8594. showErrorMsg(me.getLang('loadconfigFormatError'))
  8595. }
  8596. },
  8597. onerror: function () {
  8598. showErrorMsg(me.getLang('loadconfigHttpError'))
  8599. }
  8600. })
  8601. } catch (e) {
  8602. showErrorMsg(me.getLang('loadconfigError'))
  8603. }
  8604. })
  8605. function showErrorMsg(msg) {
  8606. console && console.error(msg)
  8607. //me.fireEvent('showMessage', {
  8608. // 'title': msg,
  8609. // 'type': 'error'
  8610. //});
  8611. }
  8612. }
  8613. UE.Editor.prototype.isServerConfigLoaded = function () {
  8614. var me = this
  8615. return me._serverConfigLoaded || false
  8616. }
  8617. UE.Editor.prototype.afterConfigReady = function (handler) {
  8618. if (!handler || !utils.isFunction(handler)) return
  8619. var me = this
  8620. var readyHandler = function () {
  8621. handler.apply(me, arguments)
  8622. me.removeListener('serverConfigLoaded', readyHandler)
  8623. }
  8624. if (me.isServerConfigLoaded()) {
  8625. handler.call(me, 'serverConfigLoaded')
  8626. } else {
  8627. me.addListener('serverConfigLoaded', readyHandler)
  8628. }
  8629. }
  8630. })()
  8631. // core/ajax.js
  8632. /**
  8633. * @file
  8634. * @module UE.ajax
  8635. * @since 1.2.6.1
  8636. */
  8637. /**
  8638. * 提供对ajax请求的支持
  8639. * @module UE.ajax
  8640. */
  8641. UE.ajax = (function () {
  8642. //创建一个ajaxRequest对象
  8643. var fnStr = 'XMLHttpRequest()'
  8644. try {
  8645. new ActiveXObject('Msxml2.XMLHTTP')
  8646. fnStr = "ActiveXObject('Msxml2.XMLHTTP')"
  8647. } catch (e) {
  8648. try {
  8649. new ActiveXObject('Microsoft.XMLHTTP')
  8650. fnStr = "ActiveXObject('Microsoft.XMLHTTP')"
  8651. } catch (e) {}
  8652. }
  8653. var creatAjaxRequest = new Function('return new ' + fnStr)
  8654. /**
  8655. * 将json参数转化成适合ajax提交的参数列表
  8656. * @param json
  8657. */
  8658. function json2str(json) {
  8659. var strArr = []
  8660. for (var i in json) {
  8661. //忽略默认的几个参数
  8662. if (
  8663. i == 'method' ||
  8664. i == 'timeout' ||
  8665. i == 'async' ||
  8666. i == 'dataType' ||
  8667. i == 'callback'
  8668. )
  8669. continue
  8670. //忽略控制
  8671. if (json[i] == undefined || json[i] == null) continue
  8672. //传递过来的对象和函数不在提交之列
  8673. if (
  8674. !(
  8675. (typeof json[i]).toLowerCase() == 'function' ||
  8676. (typeof json[i]).toLowerCase() == 'object'
  8677. )
  8678. ) {
  8679. strArr.push(encodeURIComponent(i) + '=' + encodeURIComponent(json[i]))
  8680. } else if (utils.isArray(json[i])) {
  8681. //支持传数组内容
  8682. for (var j = 0; j < json[i].length; j++) {
  8683. strArr.push(
  8684. encodeURIComponent(i) + '[]=' + encodeURIComponent(json[i][j])
  8685. )
  8686. }
  8687. }
  8688. }
  8689. return strArr.join('&')
  8690. }
  8691. function doAjax(url, ajaxOptions) {
  8692. var xhr = creatAjaxRequest(),
  8693. //是否超时
  8694. timeIsOut = false,
  8695. //默认参数
  8696. defaultAjaxOptions = {
  8697. method: 'POST',
  8698. timeout: 5000,
  8699. async: true,
  8700. data: {}, //需要传递对象的话只能覆盖
  8701. onsuccess: function () {},
  8702. onerror: function () {}
  8703. }
  8704. if (typeof url === 'object') {
  8705. ajaxOptions = url
  8706. url = ajaxOptions.url
  8707. }
  8708. if (!xhr || !url) return
  8709. var ajaxOpts = ajaxOptions
  8710. ? utils.extend(defaultAjaxOptions, ajaxOptions)
  8711. : defaultAjaxOptions
  8712. var submitStr = json2str(ajaxOpts) // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  8713. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  8714. if (!utils.isEmptyObject(ajaxOpts.data)) {
  8715. submitStr += (submitStr ? '&' : '') + json2str(ajaxOpts.data)
  8716. }
  8717. //超时检测
  8718. var timerID = setTimeout(function () {
  8719. if (xhr.readyState != 4) {
  8720. timeIsOut = true
  8721. xhr.abort()
  8722. clearTimeout(timerID)
  8723. }
  8724. }, ajaxOpts.timeout)
  8725. var method = ajaxOpts.method.toUpperCase()
  8726. var str =
  8727. url +
  8728. (url.indexOf('?') == -1 ? '?' : '&') +
  8729. (method == 'POST' ? '' : submitStr + '&noCache=' + +new Date())
  8730. xhr.open(method, str, ajaxOpts.async)
  8731. xhr.onreadystatechange = function () {
  8732. if (xhr.readyState == 4) {
  8733. if (!timeIsOut && xhr.status == 200) {
  8734. ajaxOpts.onsuccess(xhr)
  8735. } else {
  8736. ajaxOpts.onerror(xhr)
  8737. }
  8738. }
  8739. }
  8740. if (method == 'POST') {
  8741. xhr.setRequestHeader(
  8742. 'Content-Type',
  8743. 'application/x-www-form-urlencoded'
  8744. )
  8745. xhr.send(submitStr)
  8746. } else {
  8747. xhr.send(null)
  8748. }
  8749. }
  8750. function doJsonp(url, opts) {
  8751. var successhandler = opts.onsuccess || function () {},
  8752. scr = document.createElement('SCRIPT'),
  8753. options = opts || {},
  8754. charset = options['charset'],
  8755. callbackField = options['jsonp'] || 'callback',
  8756. callbackFnName,
  8757. timeOut = options['timeOut'] || 0,
  8758. timer,
  8759. reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)'),
  8760. matches
  8761. if (utils.isFunction(successhandler)) {
  8762. callbackFnName =
  8763. 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36)
  8764. window[callbackFnName] = getCallBack(0)
  8765. } else if (utils.isString(successhandler)) {
  8766. callbackFnName = successhandler
  8767. } else {
  8768. if ((matches = reg.exec(url))) {
  8769. callbackFnName = matches[2]
  8770. }
  8771. }
  8772. url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName)
  8773. if (url.search(reg) < 0) {
  8774. url +=
  8775. (url.indexOf('?') < 0 ? '?' : '&') +
  8776. callbackField +
  8777. '=' +
  8778. callbackFnName
  8779. }
  8780. var queryStr = json2str(opts) // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
  8781. //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
  8782. if (!utils.isEmptyObject(opts.data)) {
  8783. queryStr += (queryStr ? '&' : '') + json2str(opts.data)
  8784. }
  8785. if (queryStr) {
  8786. url = url.replace(/\?/, '?' + queryStr + '&')
  8787. }
  8788. scr.onerror = getCallBack(1)
  8789. if (timeOut) {
  8790. timer = setTimeout(getCallBack(1), timeOut)
  8791. }
  8792. createScriptTag(scr, url, charset)
  8793. function createScriptTag(scr, url, charset) {
  8794. scr.setAttribute('type', 'text/javascript')
  8795. scr.setAttribute('defer', 'defer')
  8796. charset && scr.setAttribute('charset', charset)
  8797. scr.setAttribute('src', url)
  8798. document.getElementsByTagName('head')[0].appendChild(scr)
  8799. }
  8800. function getCallBack(onTimeOut) {
  8801. return function () {
  8802. try {
  8803. if (onTimeOut) {
  8804. options.onerror && options.onerror()
  8805. } else {
  8806. try {
  8807. clearTimeout(timer)
  8808. successhandler.apply(window, arguments)
  8809. } catch (e) {}
  8810. }
  8811. } catch (exception) {
  8812. options.onerror && options.onerror.call(window, exception)
  8813. } finally {
  8814. options.oncomplete && options.oncomplete.apply(window, arguments)
  8815. scr.parentNode && scr.parentNode.removeChild(scr)
  8816. window[callbackFnName] = null
  8817. try {
  8818. delete window[callbackFnName]
  8819. } catch (e) {}
  8820. }
  8821. }
  8822. }
  8823. }
  8824. return {
  8825. /**
  8826. * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  8827. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
  8828. * @method request
  8829. * @param { URLString } url ajax请求的url地址
  8830. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  8831. * @example
  8832. * ```javascript
  8833. * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
  8834. * UE.ajax.requeset( 'sayhello.php', {
  8835. *
  8836. * //请求方法。可选值: 'GET', 'POST',默认值是'POST'
  8837. * method: 'GET',
  8838. *
  8839. * //超时时间。 默认为5000, 单位是ms
  8840. * timeout: 10000,
  8841. *
  8842. * //是否是异步请求。 true为异步请求, false为同步请求
  8843. * async: true,
  8844. *
  8845. * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
  8846. * data: {
  8847. * name: 'ueditor'
  8848. * },
  8849. *
  8850. * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
  8851. * onsuccess: function ( xhr ) {
  8852. * console.log( xhr.responseText );
  8853. * },
  8854. *
  8855. * //请求失败或者超时后的回调。
  8856. * onerror: function ( xhr ) {
  8857. * alert( 'Ajax请求失败' );
  8858. * }
  8859. *
  8860. * } );
  8861. * ```
  8862. */
  8863. /**
  8864. * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
  8865. * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
  8866. * @method request
  8867. * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
  8868. * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
  8869. * @example
  8870. * ```javascript
  8871. *
  8872. * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
  8873. * UE.ajax.requeset( 'sayhello.php', {
  8874. *
  8875. * //请求的地址, 该项是必须的。
  8876. * url: 'sayhello.php'
  8877. *
  8878. * } );
  8879. * ```
  8880. */
  8881. request: function (url, opts) {
  8882. if (opts && opts.dataType == 'jsonp') {
  8883. doJsonp(url, opts)
  8884. } else {
  8885. doAjax(url, opts)
  8886. }
  8887. },
  8888. getJSONP: function (url, data, fn) {
  8889. var opts = {
  8890. data: data,
  8891. oncomplete: fn
  8892. }
  8893. doJsonp(url, opts)
  8894. }
  8895. }
  8896. })()
  8897. // core/filterword.js
  8898. /**
  8899. * UE过滤word的静态方法
  8900. * @file
  8901. */
  8902. /**
  8903. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  8904. * @module UE
  8905. */
  8906. /**
  8907. * 根据传入html字符串过滤word
  8908. * @module UE
  8909. * @since 1.2.6.1
  8910. * @method filterWord
  8911. * @param { String } html html字符串
  8912. * @return { String } 已过滤后的结果字符串
  8913. * @example
  8914. * ```javascript
  8915. * UE.filterWord(html);
  8916. * ```
  8917. */
  8918. var filterWord = (UE.filterWord = (function () {
  8919. //是否是word过来的内容
  8920. function isWordDocument(str) {
  8921. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/gi.test(
  8922. str
  8923. )
  8924. }
  8925. //去掉小数
  8926. function transUnit(v) {
  8927. v = v.replace(/[\d.]+\w+/g, function (m) {
  8928. return utils.transUnitToPx(m)
  8929. })
  8930. return v
  8931. }
  8932. function filterPasteWord(str) {
  8933. return (
  8934. str
  8935. .replace(/[\t\r\n]+/g, ' ')
  8936. .replace(/<!--[\s\S]*?-->/gi, '')
  8937. //转换图片
  8938. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi, function (str) {
  8939. //opera能自己解析出image所这里直接返回空
  8940. if (browser.opera) {
  8941. return ''
  8942. }
  8943. try {
  8944. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  8945. if (/Bitmap/i.test(str)) {
  8946. return ''
  8947. }
  8948. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  8949. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  8950. src = str.match(/src=\s*"([^"]*)"/i)[1]
  8951. return (
  8952. '<img width="' +
  8953. transUnit(width) +
  8954. '" height="' +
  8955. transUnit(height) +
  8956. '" src="' +
  8957. src +
  8958. '" />'
  8959. )
  8960. } catch (e) {
  8961. return ''
  8962. }
  8963. })
  8964. //针对wps添加的多余标签处理
  8965. .replace(/<\/?div[^>]*>/g, '')
  8966. //去掉多余的属性
  8967. .replace(/v:\w+=(["']?)[^'"]+\1/g, '')
  8968. .replace(
  8969. /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi,
  8970. ''
  8971. )
  8972. .replace(
  8973. /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,
  8974. '<p><strong>$1</strong></p>'
  8975. )
  8976. //去掉多余的属性
  8977. .replace(
  8978. /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/gi,
  8979. function (str, name, marks, val) {
  8980. //保留list的标示
  8981. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  8982. }
  8983. )
  8984. //清除多余的font/span不能匹配&nbsp;有可能是空格
  8985. .replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function (a, b, c) {
  8986. return c.replace(/[\t\r\n ]+/g, ' ')
  8987. })
  8988. //处理style的问题
  8989. .replace(
  8990. /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi,
  8991. function (str, tag, tmp, style) {
  8992. var n = [],
  8993. s = style
  8994. .replace(/^\s+|\s+$/, '')
  8995. .replace(/&#39;/g, "'")
  8996. .replace(/&quot;/gi, "'")
  8997. .replace(/[\d.]+(cm|pt)/g, function (str) {
  8998. return utils.transUnitToPx(str)
  8999. })
  9000. .split(/;\s*/g)
  9001. for (var i = 0, v; (v = s[i]); i++) {
  9002. var name,
  9003. value,
  9004. parts = v.split(':')
  9005. if (parts.length == 2) {
  9006. name = parts[0].toLowerCase()
  9007. value = parts[1].toLowerCase()
  9008. if (
  9009. (/^(background)\w*/.test(name) &&
  9010. value.replace(/(initial|\s)/g, '').length == 0) ||
  9011. (/^(margin)\w*/.test(name) && /^0\w+$/.test(value))
  9012. ) {
  9013. continue
  9014. }
  9015. switch (name) {
  9016. case 'mso-padding-alt':
  9017. case 'mso-padding-top-alt':
  9018. case 'mso-padding-right-alt':
  9019. case 'mso-padding-bottom-alt':
  9020. case 'mso-padding-left-alt':
  9021. case 'mso-margin-alt':
  9022. case 'mso-margin-top-alt':
  9023. case 'mso-margin-right-alt':
  9024. case 'mso-margin-bottom-alt':
  9025. case 'mso-margin-left-alt':
  9026. //ie下会出现挤到一起的情况
  9027. //case "mso-table-layout-alt":
  9028. case 'mso-height':
  9029. case 'mso-width':
  9030. case 'mso-vertical-align-alt':
  9031. //trace:1819 ff下会解析出padding在table上
  9032. if (!/<table/.test(tag))
  9033. n[i] =
  9034. name.replace(/^mso-|-alt$/g, '') +
  9035. ':' +
  9036. transUnit(value)
  9037. continue
  9038. case 'horiz-align':
  9039. n[i] = 'text-align:' + value
  9040. continue
  9041. case 'vert-align':
  9042. n[i] = 'vertical-align:' + value
  9043. continue
  9044. case 'font-color':
  9045. case 'mso-foreground':
  9046. n[i] = 'color:' + value
  9047. continue
  9048. case 'mso-background':
  9049. case 'mso-highlight':
  9050. n[i] = 'background:' + value
  9051. continue
  9052. case 'mso-default-height':
  9053. n[i] = 'min-height:' + transUnit(value)
  9054. continue
  9055. case 'mso-default-width':
  9056. n[i] = 'min-width:' + transUnit(value)
  9057. continue
  9058. case 'mso-padding-between-alt':
  9059. n[i] =
  9060. 'border-collapse:separate;border-spacing:' +
  9061. transUnit(value)
  9062. continue
  9063. case 'text-line-through':
  9064. if (value == 'single' || value == 'double') {
  9065. n[i] = 'text-decoration:line-through'
  9066. }
  9067. continue
  9068. case 'mso-zero-height':
  9069. if (value == 'yes') {
  9070. n[i] = 'display:none'
  9071. }
  9072. continue
  9073. // case 'background':
  9074. // break;
  9075. case 'margin':
  9076. if (!/[1-9]/.test(value)) {
  9077. continue
  9078. }
  9079. }
  9080. if (
  9081. /^(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(
  9082. name
  9083. ) ||
  9084. (/text\-indent|padding|margin/.test(name) &&
  9085. /\-[\d.]+/.test(value))
  9086. ) {
  9087. continue
  9088. }
  9089. n[i] = name + ':' + parts[1]
  9090. }
  9091. }
  9092. return (
  9093. tag +
  9094. (n.length
  9095. ? ' style="' + n.join(';').replace(/;{2,}/g, ';') + '"'
  9096. : '')
  9097. )
  9098. }
  9099. )
  9100. )
  9101. }
  9102. return function (html) {
  9103. return isWordDocument(html) ? filterPasteWord(html) : html
  9104. }
  9105. })())
  9106. // core/node.js
  9107. /**
  9108. * 编辑器模拟的节点类
  9109. * @file
  9110. * @module UE
  9111. * @class uNode
  9112. * @since 1.2.6.1
  9113. */
  9114. /**
  9115. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  9116. * @unfile
  9117. * @module UE
  9118. */
  9119. ;(function () {
  9120. /**
  9121. * 编辑器模拟的节点类
  9122. * @unfile
  9123. * @module UE
  9124. * @class uNode
  9125. */
  9126. /**
  9127. * 通过一个键值对,创建一个uNode对象
  9128. * @constructor
  9129. * @param { Object } attr 传入要创建的uNode的初始属性
  9130. * @example
  9131. * ```javascript
  9132. * var node = new uNode({
  9133. * type:'element',
  9134. * tagName:'span',
  9135. * attrs:{style:'font-size:14px;'}
  9136. * }
  9137. * ```
  9138. */
  9139. var uNode = (UE.uNode = function (obj) {
  9140. this.type = obj.type
  9141. this.data = obj.data
  9142. this.tagName = obj.tagName
  9143. this.parentNode = obj.parentNode
  9144. this.attrs = obj.attrs || {}
  9145. this.children = obj.children
  9146. })
  9147. var notTransAttrs = {
  9148. href: 1,
  9149. src: 1,
  9150. _src: 1,
  9151. _href: 1,
  9152. cdata_data: 1
  9153. }
  9154. var notTransTagName = {
  9155. style: 1,
  9156. script: 1
  9157. }
  9158. var indentChar = ' ',
  9159. breakChar = '\n'
  9160. function insertLine(arr, current, begin) {
  9161. arr.push(breakChar)
  9162. return current + (begin ? 1 : -1)
  9163. }
  9164. function insertIndent(arr, current) {
  9165. //插入缩进
  9166. for (var i = 0; i < current; i++) {
  9167. arr.push(indentChar)
  9168. }
  9169. }
  9170. //创建uNode的静态方法
  9171. //支持标签和html
  9172. uNode.createElement = function (html) {
  9173. if (/[<>]/.test(html)) {
  9174. return UE.htmlparser(html).children[0]
  9175. } else {
  9176. return new uNode({
  9177. type: 'element',
  9178. children: [],
  9179. tagName: html
  9180. })
  9181. }
  9182. }
  9183. uNode.createText = function (data, noTrans) {
  9184. return new UE.uNode({
  9185. type: 'text',
  9186. data: noTrans ? data : utils.unhtml(data || '')
  9187. })
  9188. }
  9189. function nodeToHtml(node, arr, formatter, current) {
  9190. switch (node.type) {
  9191. case 'root':
  9192. for (var i = 0, ci; (ci = node.children[i++]); ) {
  9193. //插入新行
  9194. if (
  9195. formatter &&
  9196. ci.type == 'element' &&
  9197. !dtd.$inlineWithA[ci.tagName] &&
  9198. i > 1
  9199. ) {
  9200. insertLine(arr, current, true)
  9201. insertIndent(arr, current)
  9202. }
  9203. nodeToHtml(ci, arr, formatter, current)
  9204. }
  9205. break
  9206. case 'text':
  9207. isText(node, arr)
  9208. break
  9209. case 'element':
  9210. isElement(node, arr, formatter, current)
  9211. break
  9212. case 'comment':
  9213. isComment(node, arr, formatter)
  9214. }
  9215. return arr
  9216. }
  9217. function isText(node, arr) {
  9218. if (node.parentNode.tagName == 'pre') {
  9219. //源码模式下输入html标签,不能做转换处理,直接输出
  9220. arr.push(node.data)
  9221. } else {
  9222. arr.push(
  9223. notTransTagName[node.parentNode.tagName]
  9224. ? utils.html(node.data)
  9225. : node.data.replace(/[ ]{2}/g, ' &nbsp;')
  9226. )
  9227. }
  9228. }
  9229. function isElement(node, arr, formatter, current) {
  9230. var attrhtml = ''
  9231. if (node.attrs) {
  9232. attrhtml = []
  9233. var attrs = node.attrs
  9234. for (var a in attrs) {
  9235. //这里就针对
  9236. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  9237. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  9238. //有可能做的不够
  9239. attrhtml.push(
  9240. a +
  9241. (attrs[a] !== undefined
  9242. ? '="' +
  9243. (notTransAttrs[a]
  9244. ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  9245. return '&quot;'
  9246. })
  9247. : utils.unhtml(attrs[a])) +
  9248. '"'
  9249. : '')
  9250. )
  9251. }
  9252. attrhtml = attrhtml.join(' ')
  9253. }
  9254. arr.push(
  9255. '<' +
  9256. node.tagName +
  9257. (attrhtml ? ' ' + attrhtml : '') +
  9258. (dtd.$empty[node.tagName] ? '/' : '') +
  9259. '>'
  9260. )
  9261. //插入新行
  9262. if (
  9263. formatter &&
  9264. !dtd.$inlineWithA[node.tagName] &&
  9265. node.tagName != 'pre'
  9266. ) {
  9267. if (node.children && node.children.length) {
  9268. current = insertLine(arr, current, true)
  9269. insertIndent(arr, current)
  9270. }
  9271. }
  9272. if (node.children && node.children.length) {
  9273. for (var i = 0, ci; (ci = node.children[i++]); ) {
  9274. if (
  9275. formatter &&
  9276. ci.type == 'element' &&
  9277. !dtd.$inlineWithA[ci.tagName] &&
  9278. i > 1
  9279. ) {
  9280. insertLine(arr, current)
  9281. insertIndent(arr, current)
  9282. }
  9283. nodeToHtml(ci, arr, formatter, current)
  9284. }
  9285. }
  9286. if (!dtd.$empty[node.tagName]) {
  9287. if (
  9288. formatter &&
  9289. !dtd.$inlineWithA[node.tagName] &&
  9290. node.tagName != 'pre'
  9291. ) {
  9292. if (node.children && node.children.length) {
  9293. current = insertLine(arr, current)
  9294. insertIndent(arr, current)
  9295. }
  9296. }
  9297. arr.push('</' + node.tagName + '>')
  9298. }
  9299. }
  9300. function isComment(node, arr) {
  9301. arr.push('<!--' + node.data + '-->')
  9302. }
  9303. function getNodeById(root, id) {
  9304. var node
  9305. if (root.type == 'element' && root.getAttr('id') == id) {
  9306. return root
  9307. }
  9308. if (root.children && root.children.length) {
  9309. for (var i = 0, ci; (ci = root.children[i++]); ) {
  9310. if ((node = getNodeById(ci, id))) {
  9311. return node
  9312. }
  9313. }
  9314. }
  9315. }
  9316. function getNodesByTagName(node, tagName, arr) {
  9317. if (node.type == 'element' && node.tagName == tagName) {
  9318. arr.push(node)
  9319. }
  9320. if (node.children && node.children.length) {
  9321. for (var i = 0, ci; (ci = node.children[i++]); ) {
  9322. getNodesByTagName(ci, tagName, arr)
  9323. }
  9324. }
  9325. }
  9326. function nodeTraversal(root, fn) {
  9327. if (root.children && root.children.length) {
  9328. for (var i = 0, ci; (ci = root.children[i]); ) {
  9329. nodeTraversal(ci, fn)
  9330. //ci被替换的情况,这里就不再走 fn了
  9331. if (ci.parentNode) {
  9332. if (ci.children && ci.children.length) {
  9333. fn(ci)
  9334. }
  9335. if (ci.parentNode) i++
  9336. }
  9337. }
  9338. } else {
  9339. fn(root)
  9340. }
  9341. }
  9342. uNode.prototype = {
  9343. /**
  9344. * 当前节点对象,转换成html文本
  9345. * @method toHtml
  9346. * @return { String } 返回转换后的html字符串
  9347. * @example
  9348. * ```javascript
  9349. * node.toHtml();
  9350. * ```
  9351. */
  9352. /**
  9353. * 当前节点对象,转换成html文本
  9354. * @method toHtml
  9355. * @param { Boolean } formatter 是否格式化返回值
  9356. * @return { String } 返回转换后的html字符串
  9357. * @example
  9358. * ```javascript
  9359. * node.toHtml( true );
  9360. * ```
  9361. */
  9362. toHtml: function (formatter) {
  9363. var arr = []
  9364. nodeToHtml(this, arr, formatter, 0)
  9365. return arr.join('')
  9366. },
  9367. /**
  9368. * 获取节点的html内容
  9369. * @method innerHTML
  9370. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  9371. * @return { String } 返回节点的html内容
  9372. * @example
  9373. * ```javascript
  9374. * var htmlstr = node.innerHTML();
  9375. * ```
  9376. */
  9377. /**
  9378. * 设置节点的html内容
  9379. * @method innerHTML
  9380. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  9381. * @param { String } htmlstr 传入要设置的html内容
  9382. * @return { UE.uNode } 返回节点本身
  9383. * @example
  9384. * ```javascript
  9385. * node.innerHTML('<span>text</span>');
  9386. * ```
  9387. */
  9388. innerHTML: function (htmlstr) {
  9389. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  9390. return this
  9391. }
  9392. if (utils.isString(htmlstr)) {
  9393. if (this.children) {
  9394. for (var i = 0, ci; (ci = this.children[i++]); ) {
  9395. ci.parentNode = null
  9396. }
  9397. }
  9398. this.children = []
  9399. var tmpRoot = UE.htmlparser(htmlstr)
  9400. for (var i = 0, ci; (ci = tmpRoot.children[i++]); ) {
  9401. this.children.push(ci)
  9402. ci.parentNode = this
  9403. }
  9404. return this
  9405. } else {
  9406. var tmpRoot = new UE.uNode({
  9407. type: 'root',
  9408. children: this.children
  9409. })
  9410. return tmpRoot.toHtml()
  9411. }
  9412. },
  9413. /**
  9414. * 获取节点的纯文本内容
  9415. * @method innerText
  9416. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  9417. * @return { String } 返回节点的存文本内容
  9418. * @example
  9419. * ```javascript
  9420. * var textStr = node.innerText();
  9421. * ```
  9422. */
  9423. /**
  9424. * 设置节点的纯文本内容
  9425. * @method innerText
  9426. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  9427. * @param { String } textStr 传入要设置的文本内容
  9428. * @return { UE.uNode } 返回节点本身
  9429. * @example
  9430. * ```javascript
  9431. * node.innerText('<span>text</span>');
  9432. * ```
  9433. */
  9434. innerText: function (textStr, noTrans) {
  9435. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  9436. return this
  9437. }
  9438. if (textStr) {
  9439. if (this.children) {
  9440. for (var i = 0, ci; (ci = this.children[i++]); ) {
  9441. ci.parentNode = null
  9442. }
  9443. }
  9444. this.children = []
  9445. this.appendChild(uNode.createText(textStr, noTrans))
  9446. return this
  9447. } else {
  9448. return this.toHtml().replace(/<[^>]+>/g, '')
  9449. }
  9450. },
  9451. /**
  9452. * 获取当前对象的data属性
  9453. * @method getData
  9454. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  9455. * @example
  9456. * ```javascript
  9457. * node.getData();
  9458. * ```
  9459. */
  9460. getData: function () {
  9461. if (this.type == 'element') return ''
  9462. return this.data
  9463. },
  9464. /**
  9465. * 获取当前节点下的第一个子节点
  9466. * @method firstChild
  9467. * @return { UE.uNode } 返回第一个子节点
  9468. * @example
  9469. * ```javascript
  9470. * node.firstChild(); //返回第一个子节点
  9471. * ```
  9472. */
  9473. firstChild: function () {
  9474. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  9475. // return this;
  9476. // }
  9477. return this.children ? this.children[0] : null
  9478. },
  9479. /**
  9480. * 获取当前节点下的最后一个子节点
  9481. * @method lastChild
  9482. * @return { UE.uNode } 返回最后一个子节点
  9483. * @example
  9484. * ```javascript
  9485. * node.lastChild(); //返回最后一个子节点
  9486. * ```
  9487. */
  9488. lastChild: function () {
  9489. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  9490. // return this;
  9491. // }
  9492. return this.children ? this.children[this.children.length - 1] : null
  9493. },
  9494. /**
  9495. * 获取和当前节点有相同父亲节点的前一个节点
  9496. * @method previousSibling
  9497. * @return { UE.uNode } 返回前一个节点
  9498. * @example
  9499. * ```javascript
  9500. * node.children[2].previousSibling(); //返回子节点node.children[1]
  9501. * ```
  9502. */
  9503. previousSibling: function () {
  9504. var parent = this.parentNode
  9505. for (var i = 0, ci; (ci = parent.children[i]); i++) {
  9506. if (ci === this) {
  9507. return i == 0 ? null : parent.children[i - 1]
  9508. }
  9509. }
  9510. },
  9511. /**
  9512. * 获取和当前节点有相同父亲节点的后一个节点
  9513. * @method nextSibling
  9514. * @return { UE.uNode } 返回后一个节点,找不到返回null
  9515. * @example
  9516. * ```javascript
  9517. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  9518. * ```
  9519. */
  9520. nextSibling: function () {
  9521. var parent = this.parentNode
  9522. for (var i = 0, ci; (ci = parent.children[i++]); ) {
  9523. if (ci === this) {
  9524. return parent.children[i]
  9525. }
  9526. }
  9527. },
  9528. /**
  9529. * 用新的节点替换当前节点
  9530. * @method replaceChild
  9531. * @param { UE.uNode } target 要替换成该节点参数
  9532. * @param { UE.uNode } source 要被替换掉的节点
  9533. * @return { UE.uNode } 返回替换之后的节点对象
  9534. * @example
  9535. * ```javascript
  9536. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  9537. * ```
  9538. */
  9539. replaceChild: function (target, source) {
  9540. if (this.children) {
  9541. if (target.parentNode) {
  9542. target.parentNode.removeChild(target)
  9543. }
  9544. for (var i = 0, ci; (ci = this.children[i]); i++) {
  9545. if (ci === source) {
  9546. this.children.splice(i, 1, target)
  9547. source.parentNode = null
  9548. target.parentNode = this
  9549. return target
  9550. }
  9551. }
  9552. }
  9553. },
  9554. /**
  9555. * 在节点的子节点列表最后位置插入一个节点
  9556. * @method appendChild
  9557. * @param { UE.uNode } node 要插入的节点
  9558. * @return { UE.uNode } 返回刚插入的子节点
  9559. * @example
  9560. * ```javascript
  9561. * node.appendChild( newNode ); //在node内插入子节点newNode
  9562. * ```
  9563. */
  9564. appendChild: function (node) {
  9565. if (
  9566. this.type == 'root' ||
  9567. (this.type == 'element' && !dtd.$empty[this.tagName])
  9568. ) {
  9569. if (!this.children) {
  9570. this.children = []
  9571. }
  9572. if (node.parentNode) {
  9573. node.parentNode.removeChild(node)
  9574. }
  9575. for (var i = 0, ci; (ci = this.children[i]); i++) {
  9576. if (ci === node) {
  9577. this.children.splice(i, 1)
  9578. break
  9579. }
  9580. }
  9581. this.children.push(node)
  9582. node.parentNode = this
  9583. return node
  9584. }
  9585. },
  9586. /**
  9587. * 在传入节点的前面插入一个节点
  9588. * @method insertBefore
  9589. * @param { UE.uNode } target 要插入的节点
  9590. * @param { UE.uNode } source 在该参数节点前面插入
  9591. * @return { UE.uNode } 返回刚插入的子节点
  9592. * @example
  9593. * ```javascript
  9594. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  9595. * ```
  9596. */
  9597. insertBefore: function (target, source) {
  9598. if (this.children) {
  9599. if (target.parentNode) {
  9600. target.parentNode.removeChild(target)
  9601. }
  9602. for (var i = 0, ci; (ci = this.children[i]); i++) {
  9603. if (ci === source) {
  9604. this.children.splice(i, 0, target)
  9605. target.parentNode = this
  9606. return target
  9607. }
  9608. }
  9609. }
  9610. },
  9611. /**
  9612. * 在传入节点的后面插入一个节点
  9613. * @method insertAfter
  9614. * @param { UE.uNode } target 要插入的节点
  9615. * @param { UE.uNode } source 在该参数节点后面插入
  9616. * @return { UE.uNode } 返回刚插入的子节点
  9617. * @example
  9618. * ```javascript
  9619. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  9620. * ```
  9621. */
  9622. insertAfter: function (target, source) {
  9623. if (this.children) {
  9624. if (target.parentNode) {
  9625. target.parentNode.removeChild(target)
  9626. }
  9627. for (var i = 0, ci; (ci = this.children[i]); i++) {
  9628. if (ci === source) {
  9629. this.children.splice(i + 1, 0, target)
  9630. target.parentNode = this
  9631. return target
  9632. }
  9633. }
  9634. }
  9635. },
  9636. /**
  9637. * 从当前节点的子节点列表中,移除节点
  9638. * @method removeChild
  9639. * @param { UE.uNode } node 要移除的节点引用
  9640. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  9641. * @return { * } 返回刚移除的子节点
  9642. * @example
  9643. * ```javascript
  9644. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  9645. * ```
  9646. */
  9647. removeChild: function (node, keepChildren) {
  9648. if (this.children) {
  9649. for (var i = 0, ci; (ci = this.children[i]); i++) {
  9650. if (ci === node) {
  9651. this.children.splice(i, 1)
  9652. ci.parentNode = null
  9653. if (keepChildren && ci.children && ci.children.length) {
  9654. for (var j = 0, cj; (cj = ci.children[j]); j++) {
  9655. this.children.splice(i + j, 0, cj)
  9656. cj.parentNode = this
  9657. }
  9658. }
  9659. return ci
  9660. }
  9661. }
  9662. }
  9663. },
  9664. /**
  9665. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  9666. * @method getAttr
  9667. * @param { String } attrName 要获取的属性名称
  9668. * @return { * } 返回attrs对象下的属性值
  9669. * @example
  9670. * ```javascript
  9671. * node.getAttr('title');
  9672. * ```
  9673. */
  9674. getAttr: function (attrName) {
  9675. return this.attrs && this.attrs[attrName.toLowerCase()]
  9676. },
  9677. /**
  9678. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  9679. * @method setAttr
  9680. * @param { String } attrName 要设置的属性名称
  9681. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  9682. * @return { * } 返回attrs对象下的属性值
  9683. * @example
  9684. * ```javascript
  9685. * node.setAttr('title','标题');
  9686. * ```
  9687. */
  9688. setAttr: function (attrName, attrVal) {
  9689. if (!attrName) {
  9690. delete this.attrs
  9691. return
  9692. }
  9693. if (!this.attrs) {
  9694. this.attrs = {}
  9695. }
  9696. if (utils.isObject(attrName)) {
  9697. for (var a in attrName) {
  9698. if (!attrName[a]) {
  9699. delete this.attrs[a]
  9700. } else {
  9701. this.attrs[a.toLowerCase()] = attrName[a]
  9702. }
  9703. }
  9704. } else {
  9705. if (!attrVal) {
  9706. delete this.attrs[attrName]
  9707. } else {
  9708. this.attrs[attrName.toLowerCase()] = attrVal
  9709. }
  9710. }
  9711. },
  9712. /**
  9713. * 获取当前节点在父节点下的位置索引
  9714. * @method getIndex
  9715. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  9716. * @example
  9717. * ```javascript
  9718. * node.getIndex();
  9719. * ```
  9720. */
  9721. getIndex: function () {
  9722. var parent = this.parentNode
  9723. for (var i = 0, ci; (ci = parent.children[i]); i++) {
  9724. if (ci === this) {
  9725. return i
  9726. }
  9727. }
  9728. return -1
  9729. },
  9730. /**
  9731. * 在当前节点下,根据id查找节点
  9732. * @method getNodeById
  9733. * @param { String } id 要查找的id
  9734. * @return { UE.uNode } 返回找到的节点
  9735. * @example
  9736. * ```javascript
  9737. * node.getNodeById('textId');
  9738. * ```
  9739. */
  9740. getNodeById: function (id) {
  9741. var node
  9742. if (this.children && this.children.length) {
  9743. for (var i = 0, ci; (ci = this.children[i++]); ) {
  9744. if ((node = getNodeById(ci, id))) {
  9745. return node
  9746. }
  9747. }
  9748. }
  9749. },
  9750. /**
  9751. * 在当前节点下,根据元素名称查找节点列表
  9752. * @method getNodesByTagName
  9753. * @param { String } tagNames 要查找的元素名称
  9754. * @return { Array } 返回找到的节点列表
  9755. * @example
  9756. * ```javascript
  9757. * node.getNodesByTagName('span');
  9758. * ```
  9759. */
  9760. getNodesByTagName: function (tagNames) {
  9761. tagNames = utils
  9762. .trim(tagNames)
  9763. .replace(/[ ]{2,}/g, ' ')
  9764. .split(' ')
  9765. var arr = [],
  9766. me = this
  9767. utils.each(tagNames, function (tagName) {
  9768. if (me.children && me.children.length) {
  9769. for (var i = 0, ci; (ci = me.children[i++]); ) {
  9770. getNodesByTagName(ci, tagName, arr)
  9771. }
  9772. }
  9773. })
  9774. return arr
  9775. },
  9776. /**
  9777. * 根据样式名称,获取节点的样式值
  9778. * @method getStyle
  9779. * @param { String } name 要获取的样式名称
  9780. * @return { String } 返回样式值
  9781. * @example
  9782. * ```javascript
  9783. * node.getStyle('font-size');
  9784. * ```
  9785. */
  9786. getStyle: function (name) {
  9787. var cssStyle = this.getAttr('style')
  9788. if (!cssStyle) {
  9789. return ''
  9790. }
  9791. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)', 'i')
  9792. var match = cssStyle.match(reg)
  9793. if (match && match[0]) {
  9794. return match[2]
  9795. }
  9796. return ''
  9797. },
  9798. /**
  9799. * 给节点设置样式
  9800. * @method setStyle
  9801. * @param { String } name 要设置的的样式名称
  9802. * @param { String } val 要设置的的样值
  9803. * @example
  9804. * ```javascript
  9805. * node.setStyle('font-size', '12px');
  9806. * ```
  9807. */
  9808. setStyle: function (name, val) {
  9809. function exec(name, val) {
  9810. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi')
  9811. cssStyle = cssStyle.replace(reg, '$1')
  9812. if (val) {
  9813. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  9814. }
  9815. }
  9816. var cssStyle = this.getAttr('style')
  9817. if (!cssStyle) {
  9818. cssStyle = ''
  9819. }
  9820. if (utils.isObject(name)) {
  9821. for (var a in name) {
  9822. exec(a, name[a])
  9823. }
  9824. } else {
  9825. exec(name, val)
  9826. }
  9827. this.setAttr('style', utils.trim(cssStyle))
  9828. },
  9829. /**
  9830. * 传入一个函数,递归遍历当前节点下的所有节点
  9831. * @method traversal
  9832. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  9833. * @example
  9834. * ```javascript
  9835. * traversal(node, function(){
  9836. * console.log(node.type);
  9837. * });
  9838. * ```
  9839. */
  9840. traversal: function (fn) {
  9841. if (this.children && this.children.length) {
  9842. nodeTraversal(this, fn)
  9843. }
  9844. return this
  9845. }
  9846. }
  9847. })()
  9848. // core/htmlparser.js
  9849. /**
  9850. * html字符串转换成uNode节点
  9851. * @file
  9852. * @module UE
  9853. * @since 1.2.6.1
  9854. */
  9855. /**
  9856. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  9857. * @unfile
  9858. * @module UE
  9859. */
  9860. /**
  9861. * html字符串转换成uNode节点的静态方法
  9862. * @method htmlparser
  9863. * @param { String } htmlstr 要转换的html代码
  9864. * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
  9865. * @return { uNode } 给定的html片段转换形成的uNode对象
  9866. * @example
  9867. * ```javascript
  9868. * var root = UE.htmlparser('<p><b>htmlparser</b></p>', true);
  9869. * ```
  9870. */
  9871. var htmlparser = (UE.htmlparser = function (htmlstr, ignoreBlank) {
  9872. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  9873. //先去掉了,加上的原因忘了,这里先记录
  9874. var re_tag =
  9875. /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  9876. re_attr =
  9877. /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g
  9878. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  9879. var allowEmptyTags = {
  9880. b: 1,
  9881. code: 1,
  9882. i: 1,
  9883. u: 1,
  9884. strike: 1,
  9885. s: 1,
  9886. tt: 1,
  9887. strong: 1,
  9888. q: 1,
  9889. samp: 1,
  9890. em: 1,
  9891. span: 1,
  9892. sub: 1,
  9893. img: 1,
  9894. sup: 1,
  9895. font: 1,
  9896. big: 1,
  9897. small: 1,
  9898. iframe: 1,
  9899. a: 1,
  9900. br: 1,
  9901. pre: 1
  9902. }
  9903. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '')
  9904. if (!ignoreBlank) {
  9905. htmlstr = htmlstr.replace(
  9906. new RegExp(
  9907. '[\\r\\t\\n' +
  9908. (ignoreBlank ? '' : ' ') +
  9909. ']*</?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n' +
  9910. (ignoreBlank ? '' : ' ') +
  9911. ']*',
  9912. 'g'
  9913. ),
  9914. function (a, b) {
  9915. //br暂时单独处理
  9916. if (b && allowEmptyTags[b.toLowerCase()]) {
  9917. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, '')
  9918. }
  9919. return a
  9920. .replace(
  9921. new RegExp('^[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+'),
  9922. ''
  9923. )
  9924. .replace(
  9925. new RegExp('[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+$'),
  9926. ''
  9927. )
  9928. }
  9929. )
  9930. }
  9931. var notTransAttrs = {
  9932. href: 1,
  9933. src: 1
  9934. }
  9935. var uNode = UE.uNode,
  9936. needParentNode = {
  9937. td: 'tr',
  9938. tr: ['tbody', 'thead', 'tfoot'],
  9939. tbody: 'table',
  9940. th: 'tr',
  9941. thead: 'table',
  9942. tfoot: 'table',
  9943. caption: 'table',
  9944. li: ['ul', 'ol'],
  9945. dt: 'dl',
  9946. dd: 'dl',
  9947. option: 'select'
  9948. },
  9949. needChild = {
  9950. ol: 'li',
  9951. ul: 'li'
  9952. }
  9953. function text(parent, data) {
  9954. if (needChild[parent.tagName]) {
  9955. var tmpNode = uNode.createElement(needChild[parent.tagName])
  9956. parent.appendChild(tmpNode)
  9957. tmpNode.appendChild(uNode.createText(data))
  9958. parent = tmpNode
  9959. } else {
  9960. parent.appendChild(uNode.createText(data))
  9961. }
  9962. }
  9963. function element(parent, tagName, htmlattr) {
  9964. var needParentTag
  9965. if ((needParentTag = needParentNode[tagName])) {
  9966. var tmpParent = parent,
  9967. hasParent
  9968. while (tmpParent.type != 'root') {
  9969. if (
  9970. utils.isArray(needParentTag)
  9971. ? utils.indexOf(needParentTag, tmpParent.tagName) != -1
  9972. : needParentTag == tmpParent.tagName
  9973. ) {
  9974. parent = tmpParent
  9975. hasParent = true
  9976. break
  9977. }
  9978. tmpParent = tmpParent.parentNode
  9979. }
  9980. if (!hasParent) {
  9981. parent = element(
  9982. parent,
  9983. utils.isArray(needParentTag) ? needParentTag[0] : needParentTag
  9984. )
  9985. }
  9986. }
  9987. //按dtd处理嵌套
  9988. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  9989. // parent = parent.parentNode;
  9990. var elm = new uNode({
  9991. parentNode: parent,
  9992. type: 'element',
  9993. tagName: tagName.toLowerCase(),
  9994. //是自闭合的处理一下
  9995. children: dtd.$empty[tagName] ? null : []
  9996. })
  9997. //如果属性存在,处理属性
  9998. if (htmlattr) {
  9999. var attrs = {},
  10000. match
  10001. while ((match = re_attr.exec(htmlattr))) {
  10002. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()]
  10003. ? match[2] || match[3] || match[4]
  10004. : utils.unhtml(match[2] || match[3] || match[4])
  10005. }
  10006. elm.attrs = attrs
  10007. }
  10008. //trace:3970
  10009. // //如果parent下不能放elm
  10010. // if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
  10011. // parent = parent.parentNode;
  10012. // elm.parentNode = parent;
  10013. // }
  10014. parent.children.push(elm)
  10015. //如果是自闭合节点返回父亲节点
  10016. return dtd.$empty[tagName] ? parent : elm
  10017. }
  10018. function comment(parent, data) {
  10019. parent.children.push(
  10020. new uNode({
  10021. type: 'comment',
  10022. data: data,
  10023. parentNode: parent
  10024. })
  10025. )
  10026. }
  10027. var match,
  10028. currentIndex = 0,
  10029. nextIndex = 0
  10030. //设置根节点
  10031. var root = new uNode({
  10032. type: 'root',
  10033. children: []
  10034. })
  10035. var currentParent = root
  10036. while ((match = re_tag.exec(htmlstr))) {
  10037. currentIndex = match.index
  10038. try {
  10039. if (currentIndex > nextIndex) {
  10040. //text node
  10041. text(currentParent, htmlstr.slice(nextIndex, currentIndex))
  10042. }
  10043. if (match[3]) {
  10044. if (dtd.$cdata[currentParent.tagName]) {
  10045. text(currentParent, match[0])
  10046. } else {
  10047. //start tag
  10048. currentParent = element(
  10049. currentParent,
  10050. match[3].toLowerCase(),
  10051. match[4]
  10052. )
  10053. }
  10054. } else if (match[1]) {
  10055. if (currentParent.type != 'root') {
  10056. if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
  10057. text(currentParent, match[0])
  10058. } else {
  10059. var tmpParent = currentParent
  10060. while (
  10061. currentParent.type == 'element' &&
  10062. currentParent.tagName != match[1].toLowerCase()
  10063. ) {
  10064. currentParent = currentParent.parentNode
  10065. if (currentParent.type == 'root') {
  10066. currentParent = tmpParent
  10067. throw 'break'
  10068. }
  10069. }
  10070. //end tag
  10071. currentParent = currentParent.parentNode
  10072. }
  10073. }
  10074. } else if (match[2]) {
  10075. //comment
  10076. comment(currentParent, match[2])
  10077. }
  10078. } catch (e) {}
  10079. nextIndex = re_tag.lastIndex
  10080. }
  10081. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  10082. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  10083. if (nextIndex < htmlstr.length) {
  10084. text(currentParent, htmlstr.slice(nextIndex))
  10085. }
  10086. return root
  10087. })
  10088. // core/filternode.js
  10089. /**
  10090. * UE过滤节点的静态方法
  10091. * @file
  10092. */
  10093. /**
  10094. * UEditor公用空间,UEditor所有的功能都挂载在该空间下
  10095. * @module UE
  10096. */
  10097. /**
  10098. * 根据传入节点和过滤规则过滤相应节点
  10099. * @module UE
  10100. * @since 1.2.6.1
  10101. * @method filterNode
  10102. * @param { Object } root 指定root节点
  10103. * @param { Object } rules 过滤规则json对象
  10104. * @example
  10105. * ```javascript
  10106. * UE.filterNode(root,editor.options.filterRules);
  10107. * ```
  10108. */
  10109. var filterNode = (UE.filterNode = (function () {
  10110. function filterNode(node, rules) {
  10111. switch (node.type) {
  10112. case 'text':
  10113. break
  10114. case 'element':
  10115. var val
  10116. if ((val = rules[node.tagName])) {
  10117. if (val === '-') {
  10118. node.parentNode.removeChild(node)
  10119. } else if (utils.isFunction(val)) {
  10120. var parentNode = node.parentNode,
  10121. index = node.getIndex()
  10122. val(node)
  10123. if (node.parentNode) {
  10124. if (node.children) {
  10125. for (var i = 0, ci; (ci = node.children[i]); ) {
  10126. filterNode(ci, rules)
  10127. if (ci.parentNode) {
  10128. i++
  10129. }
  10130. }
  10131. }
  10132. } else {
  10133. for (var i = index, ci; (ci = parentNode.children[i]); ) {
  10134. filterNode(ci, rules)
  10135. if (ci.parentNode) {
  10136. i++
  10137. }
  10138. }
  10139. }
  10140. } else {
  10141. var attrs = val['$']
  10142. if (attrs && node.attrs) {
  10143. var tmpAttrs = {},
  10144. tmpVal
  10145. for (var a in attrs) {
  10146. tmpVal = node.getAttr(a)
  10147. //todo 只先对style单独处理
  10148. if (a == 'style' && utils.isArray(attrs[a])) {
  10149. var tmpCssStyle = []
  10150. utils.each(attrs[a], function (v) {
  10151. var tmp
  10152. if ((tmp = node.getStyle(v))) {
  10153. tmpCssStyle.push(v + ':' + tmp)
  10154. }
  10155. })
  10156. tmpVal = tmpCssStyle.join(';')
  10157. }
  10158. if (tmpVal) {
  10159. tmpAttrs[a] = tmpVal
  10160. }
  10161. }
  10162. node.attrs = tmpAttrs
  10163. }
  10164. if (node.children) {
  10165. for (var i = 0, ci; (ci = node.children[i]); ) {
  10166. filterNode(ci, rules)
  10167. if (ci.parentNode) {
  10168. i++
  10169. }
  10170. }
  10171. }
  10172. }
  10173. } else {
  10174. //如果不在名单里扣出子节点并删除该节点,cdata除外
  10175. if (dtd.$cdata[node.tagName]) {
  10176. node.parentNode.removeChild(node)
  10177. } else {
  10178. var parentNode = node.parentNode,
  10179. index = node.getIndex()
  10180. node.parentNode.removeChild(node, true)
  10181. for (var i = index, ci; (ci = parentNode.children[i]); ) {
  10182. filterNode(ci, rules)
  10183. if (ci.parentNode) {
  10184. i++
  10185. }
  10186. }
  10187. }
  10188. }
  10189. break
  10190. case 'comment':
  10191. node.parentNode.removeChild(node)
  10192. }
  10193. }
  10194. return function (root, rules) {
  10195. if (utils.isEmptyObject(rules)) {
  10196. return root
  10197. }
  10198. var val
  10199. if ((val = rules['-'])) {
  10200. utils.each(val.split(' '), function (k) {
  10201. rules[k] = '-'
  10202. })
  10203. }
  10204. for (var i = 0, ci; (ci = root.children[i]); ) {
  10205. filterNode(ci, rules)
  10206. if (ci.parentNode) {
  10207. i++
  10208. }
  10209. }
  10210. return root
  10211. }
  10212. })())
  10213. // core/plugin.js
  10214. /**
  10215. * Created with JetBrains PhpStorm.
  10216. * User: campaign
  10217. * Date: 10/8/13
  10218. * Time: 6:15 PM
  10219. * To change this template use File | Settings | File Templates.
  10220. */
  10221. UE.plugin = (function () {
  10222. var _plugins = {}
  10223. return {
  10224. register: function (pluginName, fn, oldOptionName, afterDisabled) {
  10225. if (oldOptionName && utils.isFunction(oldOptionName)) {
  10226. afterDisabled = oldOptionName
  10227. oldOptionName = null
  10228. }
  10229. _plugins[pluginName] = {
  10230. optionName: oldOptionName || pluginName,
  10231. execFn: fn,
  10232. //当插件被禁用时执行
  10233. afterDisabled: afterDisabled
  10234. }
  10235. },
  10236. load: function (editor) {
  10237. utils.each(_plugins, function (plugin) {
  10238. var _export = plugin.execFn.call(editor)
  10239. if (editor.options[plugin.optionName] !== false) {
  10240. if (_export) {
  10241. //后边需要再做扩展
  10242. utils.each(_export, function (v, k) {
  10243. switch (k.toLowerCase()) {
  10244. case 'shortcutkey':
  10245. editor.addshortcutkey(v)
  10246. break
  10247. case 'bindevents':
  10248. utils.each(v, function (fn, eventName) {
  10249. editor.addListener(eventName, fn)
  10250. })
  10251. break
  10252. case 'bindmultievents':
  10253. utils.each(utils.isArray(v) ? v : [v], function (event) {
  10254. var types = utils.trim(event.type).split(/\s+/)
  10255. utils.each(types, function (eventName) {
  10256. editor.addListener(eventName, event.handler)
  10257. })
  10258. })
  10259. break
  10260. case 'commands':
  10261. utils.each(v, function (execFn, execName) {
  10262. editor.commands[execName] = execFn
  10263. })
  10264. break
  10265. case 'outputrule':
  10266. editor.addOutputRule(v)
  10267. break
  10268. case 'inputrule':
  10269. editor.addInputRule(v)
  10270. break
  10271. case 'defaultoptions':
  10272. editor.setOpt(v)
  10273. }
  10274. })
  10275. }
  10276. } else if (plugin.afterDisabled) {
  10277. plugin.afterDisabled.call(editor)
  10278. }
  10279. })
  10280. //向下兼容
  10281. utils.each(UE.plugins, function (plugin) {
  10282. plugin.call(editor)
  10283. })
  10284. },
  10285. run: function (pluginName, editor) {
  10286. var plugin = _plugins[pluginName]
  10287. if (plugin) {
  10288. plugin.exeFn.call(editor)
  10289. }
  10290. }
  10291. }
  10292. })()
  10293. // core/keymap.js
  10294. var keymap = (UE.keymap = {
  10295. Backspace: 8,
  10296. Tab: 9,
  10297. Enter: 13,
  10298. Shift: 16,
  10299. Control: 17,
  10300. Alt: 18,
  10301. CapsLock: 20,
  10302. Esc: 27,
  10303. Spacebar: 32,
  10304. PageUp: 33,
  10305. PageDown: 34,
  10306. End: 35,
  10307. Home: 36,
  10308. Left: 37,
  10309. Up: 38,
  10310. Right: 39,
  10311. Down: 40,
  10312. Insert: 45,
  10313. Del: 46,
  10314. NumLock: 144,
  10315. Cmd: 91,
  10316. '=': 187,
  10317. '-': 189,
  10318. b: 66,
  10319. i: 73,
  10320. //回退
  10321. z: 90,
  10322. y: 89,
  10323. //粘贴
  10324. v: 86,
  10325. x: 88,
  10326. s: 83,
  10327. n: 78
  10328. })
  10329. // core/localstorage.js
  10330. //存储媒介封装
  10331. var LocalStorage = (UE.LocalStorage = (function () {
  10332. var storage = window.localStorage || getUserData() || null,
  10333. LOCAL_FILE = 'localStorage'
  10334. return {
  10335. saveLocalData: function (key, data) {
  10336. if (storage && data) {
  10337. storage.setItem(key, data)
  10338. return true
  10339. }
  10340. return false
  10341. },
  10342. getLocalData: function (key) {
  10343. if (storage) {
  10344. return storage.getItem(key)
  10345. }
  10346. return null
  10347. },
  10348. removeItem: function (key) {
  10349. storage && storage.removeItem(key)
  10350. }
  10351. }
  10352. function getUserData() {
  10353. var container = document.createElement('div')
  10354. container.style.display = 'none'
  10355. if (!container.addBehavior) {
  10356. return null
  10357. }
  10358. container.addBehavior('#default#userdata')
  10359. return {
  10360. getItem: function (key) {
  10361. var result = null
  10362. try {
  10363. document.body.appendChild(container)
  10364. container.load(LOCAL_FILE)
  10365. result = container.getAttribute(key)
  10366. document.body.removeChild(container)
  10367. } catch (e) {}
  10368. return result
  10369. },
  10370. setItem: function (key, value) {
  10371. document.body.appendChild(container)
  10372. container.setAttribute(key, value)
  10373. container.save(LOCAL_FILE)
  10374. document.body.removeChild(container)
  10375. },
  10376. //// 暂时没有用到
  10377. //clear: function () {
  10378. //
  10379. // var expiresTime = new Date();
  10380. // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
  10381. // document.body.appendChild(container);
  10382. // container.expires = expiresTime.toUTCString();
  10383. // container.save(LOCAL_FILE);
  10384. // document.body.removeChild(container);
  10385. //
  10386. //},
  10387. removeItem: function (key) {
  10388. document.body.appendChild(container)
  10389. container.removeAttribute(key)
  10390. container.save(LOCAL_FILE)
  10391. document.body.removeChild(container)
  10392. }
  10393. }
  10394. }
  10395. })())
  10396. ;(function () {
  10397. var ROOTKEY = 'ueditor_preference'
  10398. UE.Editor.prototype.setPreferences = function (key, value) {
  10399. var obj = {}
  10400. if (utils.isString(key)) {
  10401. obj[key] = value
  10402. } else {
  10403. obj = key
  10404. }
  10405. var data = LocalStorage.getLocalData(ROOTKEY)
  10406. if (data && (data = utils.str2json(data))) {
  10407. utils.extend(data, obj)
  10408. } else {
  10409. data = obj
  10410. }
  10411. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
  10412. }
  10413. UE.Editor.prototype.getPreferences = function (key) {
  10414. var data = LocalStorage.getLocalData(ROOTKEY)
  10415. if (data && (data = utils.str2json(data))) {
  10416. return key ? data[key] : data
  10417. }
  10418. return null
  10419. }
  10420. UE.Editor.prototype.removePreferences = function (key) {
  10421. var data = LocalStorage.getLocalData(ROOTKEY)
  10422. if (data && (data = utils.str2json(data))) {
  10423. data[key] = undefined
  10424. delete data[key]
  10425. }
  10426. data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
  10427. }
  10428. })()
  10429. // plugins/defaultfilter.js
  10430. ///import core
  10431. ///plugin 编辑器默认的过滤转换机制
  10432. UE.plugins['defaultfilter'] = function () {
  10433. var me = this
  10434. me.setOpt({
  10435. allowDivTransToP: true,
  10436. disabledTableInTable: true
  10437. })
  10438. //默认的过滤处理
  10439. //进入编辑器的内容处理
  10440. me.addInputRule(function (root) {
  10441. var allowDivTransToP = this.options.allowDivTransToP
  10442. var val
  10443. function tdParent(node) {
  10444. while (node && node.type == 'element') {
  10445. if (node.tagName == 'td') {
  10446. return true
  10447. }
  10448. node = node.parentNode
  10449. }
  10450. return false
  10451. }
  10452. //进行默认的处理
  10453. root.traversal(function (node) {
  10454. if (node.type == 'element') {
  10455. if (
  10456. !dtd.$cdata[node.tagName] &&
  10457. me.options.autoClearEmptyNode &&
  10458. dtd.$inline[node.tagName] &&
  10459. !dtd.$empty[node.tagName] &&
  10460. (!node.attrs || utils.isEmptyObject(node.attrs))
  10461. ) {
  10462. if (!node.firstChild()) node.parentNode.removeChild(node)
  10463. else if (
  10464. node.tagName == 'span' &&
  10465. (!node.attrs || utils.isEmptyObject(node.attrs))
  10466. ) {
  10467. node.parentNode.removeChild(node, true)
  10468. }
  10469. return
  10470. }
  10471. switch (node.tagName) {
  10472. case 'style':
  10473. case 'script':
  10474. node.setAttr({
  10475. cdata_tag: node.tagName,
  10476. cdata_data: node.innerHTML() || '',
  10477. _ue_custom_node_: 'true'
  10478. })
  10479. node.tagName = 'div'
  10480. node.innerHTML('')
  10481. break
  10482. case 'a':
  10483. if ((val = node.getAttr('href'))) {
  10484. node.setAttr('_href', val)
  10485. }
  10486. break
  10487. case 'img':
  10488. //todo base64暂时去掉,后边做远程图片上传后,干掉这个
  10489. if ((val = node.getAttr('src'))) {
  10490. if (/^data:/.test(val)) {
  10491. node.parentNode.removeChild(node)
  10492. break
  10493. }
  10494. }
  10495. node.setAttr('_src', node.getAttr('src'))
  10496. break
  10497. case 'span':
  10498. if (browser.webkit && (val = node.getStyle('white-space'))) {
  10499. if (/nowrap|normal/.test(val)) {
  10500. node.setStyle('white-space', '')
  10501. if (
  10502. me.options.autoClearEmptyNode &&
  10503. utils.isEmptyObject(node.attrs)
  10504. ) {
  10505. node.parentNode.removeChild(node, true)
  10506. }
  10507. }
  10508. }
  10509. val = node.getAttr('id')
  10510. if (val && /^_baidu_bookmark_/i.test(val)) {
  10511. node.parentNode.removeChild(node)
  10512. }
  10513. break
  10514. case 'p':
  10515. if ((val = node.getAttr('align'))) {
  10516. node.setAttr('align')
  10517. node.setStyle('text-align', val)
  10518. }
  10519. //trace:3431
  10520. // var cssStyle = node.getAttr('style');
  10521. // if (cssStyle) {
  10522. // cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
  10523. // node.setAttr('style', cssStyle)
  10524. //
  10525. // }
  10526. //p标签不允许嵌套
  10527. utils.each(node.children, function (n) {
  10528. if (n.type == 'element' && n.tagName == 'p') {
  10529. var next = n.nextSibling()
  10530. node.parentNode.insertAfter(n, node)
  10531. var last = n
  10532. while (next) {
  10533. var tmp = next.nextSibling()
  10534. node.parentNode.insertAfter(next, last)
  10535. last = next
  10536. next = tmp
  10537. }
  10538. return false
  10539. }
  10540. })
  10541. if (!node.firstChild()) {
  10542. node.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
  10543. }
  10544. break
  10545. case 'div':
  10546. if (node.getAttr('cdata_tag')) {
  10547. break
  10548. }
  10549. //针对代码这里不处理插入代码的div
  10550. val = node.getAttr('class')
  10551. if (val && /^line number\d+/.test(val)) {
  10552. break
  10553. }
  10554. if (!allowDivTransToP) {
  10555. break
  10556. }
  10557. var tmpNode,
  10558. p = UE.uNode.createElement('p')
  10559. while ((tmpNode = node.firstChild())) {
  10560. if (
  10561. tmpNode.type == 'text' ||
  10562. !UE.dom.dtd.$block[tmpNode.tagName]
  10563. ) {
  10564. p.appendChild(tmpNode)
  10565. } else {
  10566. if (p.firstChild()) {
  10567. node.parentNode.insertBefore(p, node)
  10568. p = UE.uNode.createElement('p')
  10569. } else {
  10570. node.parentNode.insertBefore(tmpNode, node)
  10571. }
  10572. }
  10573. }
  10574. if (p.firstChild()) {
  10575. node.parentNode.insertBefore(p, node)
  10576. }
  10577. node.parentNode.removeChild(node)
  10578. break
  10579. case 'dl':
  10580. node.tagName = 'ul'
  10581. break
  10582. case 'dt':
  10583. case 'dd':
  10584. node.tagName = 'li'
  10585. break
  10586. case 'li':
  10587. var className = node.getAttr('class')
  10588. if (!className || !/list\-/.test(className)) {
  10589. node.setAttr()
  10590. }
  10591. var tmpNodes = node.getNodesByTagName('ol ul')
  10592. UE.utils.each(tmpNodes, function (n) {
  10593. node.parentNode.insertAfter(n, node)
  10594. })
  10595. break
  10596. case 'td':
  10597. case 'th':
  10598. case 'caption':
  10599. if (!node.children || !node.children.length) {
  10600. node.appendChild(
  10601. browser.ie11below
  10602. ? UE.uNode.createText(' ')
  10603. : UE.uNode.createElement('br')
  10604. )
  10605. }
  10606. break
  10607. case 'table':
  10608. if (me.options.disabledTableInTable && tdParent(node)) {
  10609. node.parentNode.insertBefore(
  10610. UE.uNode.createText(node.innerText()),
  10611. node
  10612. )
  10613. node.parentNode.removeChild(node)
  10614. }
  10615. }
  10616. }
  10617. // if(node.type == 'comment'){
  10618. // node.parentNode.removeChild(node);
  10619. // }
  10620. })
  10621. })
  10622. //从编辑器出去的内容处理
  10623. me.addOutputRule(function (root) {
  10624. var val
  10625. root.traversal(function (node) {
  10626. if (node.type == 'element') {
  10627. if (
  10628. me.options.autoClearEmptyNode &&
  10629. dtd.$inline[node.tagName] &&
  10630. !dtd.$empty[node.tagName] &&
  10631. (!node.attrs || utils.isEmptyObject(node.attrs))
  10632. ) {
  10633. if (!node.firstChild()) node.parentNode.removeChild(node)
  10634. else if (
  10635. node.tagName == 'span' &&
  10636. (!node.attrs || utils.isEmptyObject(node.attrs))
  10637. ) {
  10638. node.parentNode.removeChild(node, true)
  10639. }
  10640. return
  10641. }
  10642. switch (node.tagName) {
  10643. case 'div':
  10644. if ((val = node.getAttr('cdata_tag'))) {
  10645. node.tagName = val
  10646. node.appendChild(
  10647. UE.uNode.createText(node.getAttr('cdata_data'))
  10648. )
  10649. node.setAttr({
  10650. cdata_tag: '',
  10651. cdata_data: '',
  10652. _ue_custom_node_: ''
  10653. })
  10654. }
  10655. break
  10656. case 'a':
  10657. if ((val = node.getAttr('_href'))) {
  10658. node.setAttr({
  10659. href: utils.html(val),
  10660. _href: ''
  10661. })
  10662. }
  10663. break
  10664. break
  10665. case 'span':
  10666. val = node.getAttr('id')
  10667. if (val && /^_baidu_bookmark_/i.test(val)) {
  10668. node.parentNode.removeChild(node)
  10669. }
  10670. break
  10671. case 'img':
  10672. if ((val = node.getAttr('_src'))) {
  10673. node.setAttr({
  10674. src: node.getAttr('_src'),
  10675. _src: ''
  10676. })
  10677. }
  10678. }
  10679. }
  10680. })
  10681. })
  10682. }
  10683. // plugins/inserthtml.js
  10684. /**
  10685. * 插入html字符串插件
  10686. * @file
  10687. * @since 1.2.6.1
  10688. */
  10689. /**
  10690. * 插入html代码
  10691. * @command inserthtml
  10692. * @method execCommand
  10693. * @param { String } cmd 命令字符串
  10694. * @param { String } html 插入的html字符串
  10695. * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
  10696. * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
  10697. * @example
  10698. * ```javascript
  10699. * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
  10700. * //执行命令,插入<b>CC</b>
  10701. * //插入后的效果 xxx<b>CC</b>xxx
  10702. * //<p>xx|xxx</p> 当前选区为闭合状态
  10703. * //插入<p>CC</p>
  10704. * //结果 <p>xx</p><p>CC</p><p>xxx</p>
  10705. * //<p>xxxx</p>|</p>xxx</p> 当前选区在两个p标签之间
  10706. * //插入 xxxx
  10707. * //结果 <p>xxxx</p><p>xxxx</p></p>xxx</p>
  10708. * ```
  10709. */
  10710. UE.commands['inserthtml'] = {
  10711. execCommand: function (command, html, notNeedFilter) {
  10712. var me = this,
  10713. range,
  10714. div
  10715. if (!html) {
  10716. return
  10717. }
  10718. if (me.fireEvent('beforeinserthtml', html) === true) {
  10719. return
  10720. }
  10721. range = me.selection.getRange()
  10722. div = range.document.createElement('div')
  10723. div.style.display = 'inline'
  10724. if (!notNeedFilter) {
  10725. var root = UE.htmlparser(html)
  10726. //如果给了过滤规则就先进行过滤
  10727. if (me.options.filterRules) {
  10728. UE.filterNode(root, me.options.filterRules)
  10729. }
  10730. //执行默认的处理
  10731. me.filterInputRule(root)
  10732. html = root.toHtml()
  10733. }
  10734. div.innerHTML = utils.trim(html)
  10735. if (!range.collapsed) {
  10736. var tmpNode = range.startContainer
  10737. if (domUtils.isFillChar(tmpNode)) {
  10738. range.setStartBefore(tmpNode)
  10739. }
  10740. tmpNode = range.endContainer
  10741. if (domUtils.isFillChar(tmpNode)) {
  10742. range.setEndAfter(tmpNode)
  10743. }
  10744. range.txtToElmBoundary()
  10745. //结束边界可能放到了br的前边,要把br包含进来
  10746. // x[xxx]<br/>
  10747. if (range.endContainer && range.endContainer.nodeType == 1) {
  10748. tmpNode = range.endContainer.childNodes[range.endOffset]
  10749. if (tmpNode && domUtils.isBr(tmpNode)) {
  10750. range.setEndAfter(tmpNode)
  10751. }
  10752. }
  10753. if (range.startOffset == 0) {
  10754. tmpNode = range.startContainer
  10755. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  10756. tmpNode = range.endContainer
  10757. if (
  10758. range.endOffset ==
  10759. (tmpNode.nodeType == 3
  10760. ? tmpNode.nodeValue.length
  10761. : tmpNode.childNodes.length) &&
  10762. domUtils.isBoundaryNode(tmpNode, 'lastChild')
  10763. ) {
  10764. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  10765. range.setStart(me.body.firstChild, 0).collapse(true)
  10766. }
  10767. }
  10768. }
  10769. !range.collapsed && range.deleteContents()
  10770. if (range.startContainer.nodeType == 1) {
  10771. var child = range.startContainer.childNodes[range.startOffset],
  10772. pre
  10773. if (
  10774. child &&
  10775. domUtils.isBlockElm(child) &&
  10776. (pre = child.previousSibling) &&
  10777. domUtils.isBlockElm(pre)
  10778. ) {
  10779. range.setEnd(pre, pre.childNodes.length).collapse()
  10780. while (child.firstChild) {
  10781. pre.appendChild(child.firstChild)
  10782. }
  10783. domUtils.remove(child)
  10784. }
  10785. }
  10786. }
  10787. var child,
  10788. parent,
  10789. pre,
  10790. tmp,
  10791. hadBreak = 0,
  10792. nextNode
  10793. //如果当前位置选中了fillchar要干掉,要不会产生空行
  10794. if (range.inFillChar()) {
  10795. child = range.startContainer
  10796. if (domUtils.isFillChar(child)) {
  10797. range.setStartBefore(child).collapse(true)
  10798. domUtils.remove(child)
  10799. } else if (domUtils.isFillChar(child, true)) {
  10800. child.nodeValue = child.nodeValue.replace(fillCharReg, '')
  10801. range.startOffset--
  10802. range.collapsed && range.collapse(true)
  10803. }
  10804. }
  10805. //列表单独处理
  10806. var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
  10807. if (li) {
  10808. var next, last
  10809. while ((child = div.firstChild)) {
  10810. //针对hr单独处理一下先
  10811. while (
  10812. child &&
  10813. (child.nodeType == 3 ||
  10814. !domUtils.isBlockElm(child) ||
  10815. child.tagName == 'HR')
  10816. ) {
  10817. next = child.nextSibling
  10818. range.insertNode(child).collapse()
  10819. last = child
  10820. child = next
  10821. }
  10822. if (child) {
  10823. if (/^(ol|ul)$/i.test(child.tagName)) {
  10824. while (child.firstChild) {
  10825. last = child.firstChild
  10826. domUtils.insertAfter(li, child.firstChild)
  10827. li = li.nextSibling
  10828. }
  10829. domUtils.remove(child)
  10830. } else {
  10831. var tmpLi
  10832. next = child.nextSibling
  10833. tmpLi = me.document.createElement('li')
  10834. domUtils.insertAfter(li, tmpLi)
  10835. tmpLi.appendChild(child)
  10836. last = child
  10837. child = next
  10838. li = tmpLi
  10839. }
  10840. }
  10841. }
  10842. li = domUtils.findParentByTagName(range.startContainer, 'li', true)
  10843. if (domUtils.isEmptyBlock(li)) {
  10844. domUtils.remove(li)
  10845. }
  10846. if (last) {
  10847. range.setStartAfter(last).collapse(true).select(true)
  10848. }
  10849. } else {
  10850. while ((child = div.firstChild)) {
  10851. if (hadBreak) {
  10852. var p = me.document.createElement('p')
  10853. while (
  10854. child &&
  10855. (child.nodeType == 3 || !dtd.$block[child.tagName])
  10856. ) {
  10857. nextNode = child.nextSibling
  10858. p.appendChild(child)
  10859. child = nextNode
  10860. }
  10861. if (p.firstChild) {
  10862. child = p
  10863. }
  10864. }
  10865. range.insertNode(child)
  10866. nextNode = child.nextSibling
  10867. if (
  10868. !hadBreak &&
  10869. child.nodeType == domUtils.NODE_ELEMENT &&
  10870. domUtils.isBlockElm(child)
  10871. ) {
  10872. parent = domUtils.findParent(child, function (node) {
  10873. return domUtils.isBlockElm(node)
  10874. })
  10875. if (
  10876. parent &&
  10877. parent.tagName.toLowerCase() != 'body' &&
  10878. !(
  10879. dtd[parent.tagName][child.nodeName] &&
  10880. child.parentNode === parent
  10881. )
  10882. ) {
  10883. if (!dtd[parent.tagName][child.nodeName]) {
  10884. pre = parent
  10885. } else {
  10886. tmp = child.parentNode
  10887. while (tmp !== parent) {
  10888. pre = tmp
  10889. tmp = tmp.parentNode
  10890. }
  10891. }
  10892. domUtils.breakParent(child, pre || tmp)
  10893. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  10894. var pre = child.previousSibling
  10895. domUtils.trimWhiteTextNode(pre)
  10896. if (!pre.childNodes.length) {
  10897. domUtils.remove(pre)
  10898. }
  10899. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  10900. if (
  10901. !browser.ie &&
  10902. (next = child.nextSibling) &&
  10903. domUtils.isBlockElm(next) &&
  10904. next.lastChild &&
  10905. !domUtils.isBr(next.lastChild)
  10906. ) {
  10907. next.appendChild(me.document.createElement('br'))
  10908. }
  10909. hadBreak = 1
  10910. }
  10911. }
  10912. var next = child.nextSibling
  10913. if (!div.firstChild && next && domUtils.isBlockElm(next)) {
  10914. range.setStart(next, 0).collapse(true)
  10915. break
  10916. }
  10917. range.setEndAfter(child).collapse()
  10918. }
  10919. child = range.startContainer
  10920. if (nextNode && domUtils.isBr(nextNode)) {
  10921. domUtils.remove(nextNode)
  10922. }
  10923. //用chrome可能有空白展位符
  10924. if (domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)) {
  10925. if ((nextNode = child.nextSibling)) {
  10926. domUtils.remove(child)
  10927. if (nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]) {
  10928. range.setStart(nextNode, 0).collapse(true).shrinkBoundary()
  10929. }
  10930. } else {
  10931. try {
  10932. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>'
  10933. } catch (e) {
  10934. range.setStartBefore(child)
  10935. domUtils.remove(child)
  10936. }
  10937. }
  10938. }
  10939. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  10940. try {
  10941. range.select(true)
  10942. } catch (e) {}
  10943. }
  10944. setTimeout(function () {
  10945. range = me.selection.getRange()
  10946. range.scrollToView(
  10947. me.autoHeightEnabled,
  10948. me.autoHeightEnabled ? domUtils.getXY(me.iframe).y : 0
  10949. )
  10950. me.fireEvent('afterinserthtml', html)
  10951. }, 200)
  10952. }
  10953. }
  10954. // plugins/autotypeset.js
  10955. /**
  10956. * 自动排版
  10957. * @file
  10958. * @since 1.2.6.1
  10959. */
  10960. /**
  10961. * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。
  10962. * @command autotypeset
  10963. * @method execCommand
  10964. * @param { String } cmd 命令字符串
  10965. * @example
  10966. * ```javascript
  10967. * editor.execCommand( 'autotypeset' );
  10968. * ```
  10969. */
  10970. UE.plugins['autotypeset'] = function () {
  10971. this.setOpt({
  10972. autotypeset: {
  10973. mergeEmptyline: true, //合并空行
  10974. removeClass: true, //去掉冗余的class
  10975. removeEmptyline: false, //去掉空行
  10976. textAlign: 'left', //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
  10977. imageBlockLine: 'center', //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
  10978. pasteFilter: false, //根据规则过滤没事粘贴进来的内容
  10979. clearFontSize: false, //去掉所有的内嵌字号,使用编辑器默认的字号
  10980. clearFontFamily: false, //去掉所有的内嵌字体,使用编辑器默认的字体
  10981. removeEmptyNode: false, // 去掉空节点
  10982. //可以去掉的标签
  10983. removeTagNames: utils.extend({ div: 1 }, dtd.$removeEmpty),
  10984. indent: false, // 行首缩进
  10985. indentValue: '2em', //行首缩进的大小
  10986. bdc2sb: false,
  10987. tobdc: false
  10988. }
  10989. })
  10990. var me = this,
  10991. opt = me.options.autotypeset,
  10992. remainClass = {
  10993. selectTdClass: 1,
  10994. pagebreak: 1,
  10995. anchorclass: 1
  10996. },
  10997. remainTag = {
  10998. li: 1
  10999. },
  11000. tags = {
  11001. div: 1,
  11002. p: 1,
  11003. //trace:2183 这些也认为是行
  11004. blockquote: 1,
  11005. center: 1,
  11006. h1: 1,
  11007. h2: 1,
  11008. h3: 1,
  11009. h4: 1,
  11010. h5: 1,
  11011. h6: 1,
  11012. span: 1
  11013. },
  11014. highlightCont
  11015. //升级了版本,但配置项目里没有autotypeset
  11016. if (!opt) {
  11017. return
  11018. }
  11019. readLocalOpts()
  11020. function isLine(node, notEmpty) {
  11021. if (!node || node.nodeType == 3) return 0
  11022. if (domUtils.isBr(node)) return 1
  11023. if (node && node.parentNode && tags[node.tagName.toLowerCase()]) {
  11024. if (
  11025. (highlightCont && highlightCont.contains(node)) ||
  11026. node.getAttribute('pagebreak')
  11027. ) {
  11028. return 0
  11029. }
  11030. return notEmpty
  11031. ? !domUtils.isEmptyBlock(node)
  11032. : domUtils.isEmptyBlock(
  11033. node,
  11034. new RegExp('[\\s' + domUtils.fillChar + ']', 'g')
  11035. )
  11036. }
  11037. }
  11038. function removeNotAttributeSpan(node) {
  11039. if (!node.style.cssText) {
  11040. domUtils.removeAttributes(node, ['style'])
  11041. if (
  11042. node.tagName.toLowerCase() == 'span' &&
  11043. domUtils.hasNoAttributes(node)
  11044. ) {
  11045. domUtils.remove(node, true)
  11046. }
  11047. }
  11048. }
  11049. function autotype(type, html) {
  11050. var me = this,
  11051. cont
  11052. if (html) {
  11053. if (!opt.pasteFilter) {
  11054. return
  11055. }
  11056. cont = me.document.createElement('div')
  11057. cont.innerHTML = html.html
  11058. } else {
  11059. cont = me.document.body
  11060. }
  11061. var nodes = domUtils.getElementsByTagName(cont, '*')
  11062. // 行首缩进,段落方向,段间距,段内间距
  11063. for (var i = 0, ci; (ci = nodes[i++]); ) {
  11064. if (me.fireEvent('excludeNodeinautotype', ci) === true) {
  11065. continue
  11066. }
  11067. //font-size
  11068. if (opt.clearFontSize && ci.style.fontSize) {
  11069. domUtils.removeStyle(ci, 'font-size')
  11070. removeNotAttributeSpan(ci)
  11071. }
  11072. //font-family
  11073. if (opt.clearFontFamily && ci.style.fontFamily) {
  11074. domUtils.removeStyle(ci, 'font-family')
  11075. removeNotAttributeSpan(ci)
  11076. }
  11077. if (isLine(ci)) {
  11078. //合并空行
  11079. if (opt.mergeEmptyline) {
  11080. var next = ci.nextSibling,
  11081. tmpNode,
  11082. isBr = domUtils.isBr(ci)
  11083. while (isLine(next)) {
  11084. tmpNode = next
  11085. next = tmpNode.nextSibling
  11086. if (isBr && (!next || (next && !domUtils.isBr(next)))) {
  11087. break
  11088. }
  11089. domUtils.remove(tmpNode)
  11090. }
  11091. }
  11092. //去掉空行,保留占位的空行
  11093. if (
  11094. opt.removeEmptyline &&
  11095. domUtils.inDoc(ci, cont) &&
  11096. !remainTag[ci.parentNode.tagName.toLowerCase()]
  11097. ) {
  11098. if (domUtils.isBr(ci)) {
  11099. next = ci.nextSibling
  11100. if (next && !domUtils.isBr(next)) {
  11101. continue
  11102. }
  11103. }
  11104. domUtils.remove(ci)
  11105. continue
  11106. }
  11107. }
  11108. if (isLine(ci, true) && ci.tagName != 'SPAN') {
  11109. if (opt.indent) {
  11110. ci.style.textIndent = opt.indentValue
  11111. }
  11112. if (opt.textAlign) {
  11113. ci.style.textAlign = opt.textAlign
  11114. }
  11115. // if(opt.lineHeight)
  11116. // ci.style.lineHeight = opt.lineHeight + 'cm';
  11117. }
  11118. //去掉class,保留的class不去掉
  11119. if (
  11120. opt.removeClass &&
  11121. ci.className &&
  11122. !remainClass[ci.className.toLowerCase()]
  11123. ) {
  11124. if (highlightCont && highlightCont.contains(ci)) {
  11125. continue
  11126. }
  11127. domUtils.removeAttributes(ci, ['class'])
  11128. }
  11129. //表情不处理
  11130. if (
  11131. opt.imageBlockLine &&
  11132. ci.tagName.toLowerCase() == 'img' &&
  11133. !ci.getAttribute('emotion')
  11134. ) {
  11135. if (html) {
  11136. var img = ci
  11137. switch (opt.imageBlockLine) {
  11138. case 'left':
  11139. case 'right':
  11140. case 'none':
  11141. var pN = img.parentNode,
  11142. tmpNode,
  11143. pre,
  11144. next
  11145. while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
  11146. pN = pN.parentNode
  11147. }
  11148. tmpNode = pN
  11149. if (
  11150. tmpNode.tagName == 'P' &&
  11151. domUtils.getStyle(tmpNode, 'text-align') == 'center'
  11152. ) {
  11153. if (
  11154. !domUtils.isBody(tmpNode) &&
  11155. domUtils.getChildCount(tmpNode, function (node) {
  11156. return (
  11157. !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  11158. )
  11159. }) == 1
  11160. ) {
  11161. pre = tmpNode.previousSibling
  11162. next = tmpNode.nextSibling
  11163. if (
  11164. pre &&
  11165. next &&
  11166. pre.nodeType == 1 &&
  11167. next.nodeType == 1 &&
  11168. pre.tagName == next.tagName &&
  11169. domUtils.isBlockElm(pre)
  11170. ) {
  11171. pre.appendChild(tmpNode.firstChild)
  11172. while (next.firstChild) {
  11173. pre.appendChild(next.firstChild)
  11174. }
  11175. domUtils.remove(tmpNode)
  11176. domUtils.remove(next)
  11177. } else {
  11178. domUtils.setStyle(tmpNode, 'text-align', '')
  11179. }
  11180. }
  11181. }
  11182. domUtils.setStyle(img, 'float', opt.imageBlockLine)
  11183. break
  11184. case 'center':
  11185. if (me.queryCommandValue('imagefloat') != 'center') {
  11186. pN = img.parentNode
  11187. domUtils.setStyle(img, 'float', 'none')
  11188. tmpNode = img
  11189. while (
  11190. pN &&
  11191. domUtils.getChildCount(pN, function (node) {
  11192. return (
  11193. !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  11194. )
  11195. }) == 1 &&
  11196. (dtd.$inline[pN.tagName] || pN.tagName == 'A')
  11197. ) {
  11198. tmpNode = pN
  11199. pN = pN.parentNode
  11200. }
  11201. var pNode = me.document.createElement('p')
  11202. domUtils.setAttributes(pNode, {
  11203. style: 'text-align:center'
  11204. })
  11205. tmpNode.parentNode.insertBefore(pNode, tmpNode)
  11206. pNode.appendChild(tmpNode)
  11207. domUtils.setStyle(tmpNode, 'float', '')
  11208. }
  11209. }
  11210. } else {
  11211. var range = me.selection.getRange()
  11212. range.selectNode(ci).select()
  11213. me.execCommand('imagefloat', opt.imageBlockLine)
  11214. }
  11215. }
  11216. //去掉冗余的标签
  11217. if (opt.removeEmptyNode) {
  11218. if (
  11219. opt.removeTagNames[ci.tagName.toLowerCase()] &&
  11220. domUtils.hasNoAttributes(ci) &&
  11221. domUtils.isEmptyBlock(ci)
  11222. ) {
  11223. domUtils.remove(ci)
  11224. }
  11225. }
  11226. }
  11227. if (opt.tobdc) {
  11228. var root = UE.htmlparser(cont.innerHTML)
  11229. root.traversal(function (node) {
  11230. if (node.type == 'text') {
  11231. node.data = ToDBC(node.data)
  11232. }
  11233. })
  11234. cont.innerHTML = root.toHtml()
  11235. }
  11236. if (opt.bdc2sb) {
  11237. var root = UE.htmlparser(cont.innerHTML)
  11238. root.traversal(function (node) {
  11239. if (node.type == 'text') {
  11240. node.data = DBC2SB(node.data)
  11241. }
  11242. })
  11243. cont.innerHTML = root.toHtml()
  11244. }
  11245. if (html) {
  11246. html.html = cont.innerHTML
  11247. }
  11248. }
  11249. if (opt.pasteFilter) {
  11250. me.addListener('beforepaste', autotype)
  11251. }
  11252. function DBC2SB(str) {
  11253. var result = ''
  11254. for (var i = 0; i < str.length; i++) {
  11255. var code = str.charCodeAt(i) //获取当前字符的unicode编码
  11256. if (code >= 65281 && code <= 65373) {
  11257. //在这个unicode编码范围中的是所有的英文字母已经各种字符
  11258. result += String.fromCharCode(str.charCodeAt(i) - 65248) //把全角字符的unicode编码转换为对应半角字符的unicode码
  11259. } else if (code == 12288) {
  11260. //空格
  11261. result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32)
  11262. } else {
  11263. result += str.charAt(i)
  11264. }
  11265. }
  11266. return result
  11267. }
  11268. function ToDBC(txtstring) {
  11269. txtstring = utils.html(txtstring)
  11270. var tmp = ''
  11271. var mark = '' /*用于判断,如果是html尖括里的标记,则不进行全角的转换*/
  11272. for (var i = 0; i < txtstring.length; i++) {
  11273. if (txtstring.charCodeAt(i) == 32) {
  11274. tmp = tmp + String.fromCharCode(12288)
  11275. } else if (txtstring.charCodeAt(i) < 127) {
  11276. tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248)
  11277. } else {
  11278. tmp += txtstring.charAt(i)
  11279. }
  11280. }
  11281. return tmp
  11282. }
  11283. function readLocalOpts() {
  11284. var cookieOpt = me.getPreferences('autotypeset')
  11285. utils.extend(me.options.autotypeset, cookieOpt)
  11286. }
  11287. me.commands['autotypeset'] = {
  11288. execCommand: function () {
  11289. me.removeListener('beforepaste', autotype)
  11290. if (opt.pasteFilter) {
  11291. me.addListener('beforepaste', autotype)
  11292. }
  11293. autotype.call(me)
  11294. }
  11295. }
  11296. }
  11297. // plugins/autosubmit.js
  11298. /**
  11299. * 快捷键提交
  11300. * @file
  11301. * @since 1.2.6.1
  11302. */
  11303. /**
  11304. * 提交表单
  11305. * @command autosubmit
  11306. * @method execCommand
  11307. * @param { String } cmd 命令字符串
  11308. * @example
  11309. * ```javascript
  11310. * editor.execCommand( 'autosubmit' );
  11311. * ```
  11312. */
  11313. UE.plugin.register('autosubmit', function () {
  11314. return {
  11315. shortcutkey: {
  11316. autosubmit: 'ctrl+13' //手动提交
  11317. },
  11318. commands: {
  11319. autosubmit: {
  11320. execCommand: function () {
  11321. var me = this,
  11322. form = domUtils.findParentByTagName(me.iframe, 'form', false)
  11323. if (form) {
  11324. if (me.fireEvent('beforesubmit') === false) {
  11325. return
  11326. }
  11327. me.sync()
  11328. form.submit()
  11329. }
  11330. }
  11331. }
  11332. }
  11333. }
  11334. })
  11335. // plugins/background.js
  11336. /**
  11337. * 背景插件,为UEditor提供设置背景功能
  11338. * @file
  11339. * @since 1.2.6.1
  11340. */
  11341. UE.plugin.register('background', function () {
  11342. var me = this,
  11343. cssRuleId = 'editor_background',
  11344. isSetColored,
  11345. reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i')
  11346. function stringToObj(str) {
  11347. var obj = {},
  11348. styles = str.split(';')
  11349. utils.each(styles, function (v) {
  11350. var index = v.indexOf(':'),
  11351. key = utils.trim(v.substr(0, index)).toLowerCase()
  11352. key && (obj[key] = utils.trim(v.substr(index + 1) || ''))
  11353. })
  11354. return obj
  11355. }
  11356. function setBackground(obj) {
  11357. if (obj) {
  11358. var styles = []
  11359. for (var name in obj) {
  11360. if (obj.hasOwnProperty(name)) {
  11361. styles.push(name + ':' + obj[name] + '; ')
  11362. }
  11363. }
  11364. utils.cssRule(
  11365. cssRuleId,
  11366. styles.length ? 'body{' + styles.join('') + '}' : '',
  11367. me.document
  11368. )
  11369. } else {
  11370. utils.cssRule(cssRuleId, '', me.document)
  11371. }
  11372. }
  11373. //重写editor.hasContent方法
  11374. var orgFn = me.hasContents
  11375. me.hasContents = function () {
  11376. if (me.queryCommandValue('background')) {
  11377. return true
  11378. }
  11379. return orgFn.apply(me, arguments)
  11380. }
  11381. return {
  11382. bindEvents: {
  11383. getAllHtml: function (type, headHtml) {
  11384. var body = this.body,
  11385. su = domUtils.getComputedStyle(body, 'background-image'),
  11386. url = ''
  11387. if (su.indexOf(me.options.imagePath) > 0) {
  11388. url = su
  11389. .substring(su.indexOf(me.options.imagePath), su.length - 1)
  11390. .replace(/"|\(|\)/gi, '')
  11391. } else {
  11392. url = su != 'none' ? su.replace(/url\("?|"?\)/gi, '') : ''
  11393. }
  11394. var html = '<style type="text/css">body{'
  11395. var bgObj = {
  11396. 'background-color':
  11397. domUtils.getComputedStyle(body, 'background-color') || '#ffffff',
  11398. 'background-image': url ? 'url(' + url + ')' : '',
  11399. 'background-repeat':
  11400. domUtils.getComputedStyle(body, 'background-repeat') || '',
  11401. 'background-position': browser.ie
  11402. ? domUtils.getComputedStyle(body, 'background-position-x') +
  11403. ' ' +
  11404. domUtils.getComputedStyle(body, 'background-position-y')
  11405. : domUtils.getComputedStyle(body, 'background-position'),
  11406. height: domUtils.getComputedStyle(body, 'height')
  11407. }
  11408. for (var name in bgObj) {
  11409. if (bgObj.hasOwnProperty(name)) {
  11410. html += name + ':' + bgObj[name] + '; '
  11411. }
  11412. }
  11413. html += '}</style> '
  11414. headHtml.push(html)
  11415. },
  11416. aftersetcontent: function () {
  11417. if (isSetColored == false) setBackground()
  11418. }
  11419. },
  11420. inputRule: function (root) {
  11421. isSetColored = false
  11422. utils.each(root.getNodesByTagName('p'), function (p) {
  11423. var styles = p.getAttr('data-background')
  11424. if (styles) {
  11425. isSetColored = true
  11426. setBackground(stringToObj(styles))
  11427. p.parentNode.removeChild(p)
  11428. }
  11429. })
  11430. },
  11431. outputRule: function (root) {
  11432. var me = this,
  11433. styles = (utils.cssRule(cssRuleId, me.document) || '')
  11434. .replace(/[\n\r]+/g, '')
  11435. .match(reg)
  11436. if (styles) {
  11437. root.appendChild(
  11438. UE.uNode.createElement(
  11439. '<p style="display:none;" data-background="' +
  11440. utils.trim(styles[1].replace(/"/g, '').replace(/[\s]+/g, ' ')) +
  11441. '"><br/></p>'
  11442. )
  11443. )
  11444. }
  11445. },
  11446. commands: {
  11447. background: {
  11448. execCommand: function (cmd, obj) {
  11449. setBackground(obj)
  11450. },
  11451. queryCommandValue: function () {
  11452. var me = this,
  11453. styles = (utils.cssRule(cssRuleId, me.document) || '')
  11454. .replace(/[\n\r]+/g, '')
  11455. .match(reg)
  11456. return styles ? stringToObj(styles[1]) : null
  11457. },
  11458. notNeedUndo: true
  11459. }
  11460. }
  11461. }
  11462. })
  11463. // plugins/image.js
  11464. /**
  11465. * 图片插入、排版插件
  11466. * @file
  11467. * @since 1.2.6.1
  11468. */
  11469. /**
  11470. * 图片对齐方式
  11471. * @command imagefloat
  11472. * @method execCommand
  11473. * @remind 值center为独占一行居中
  11474. * @param { String } cmd 命令字符串
  11475. * @param { String } align 对齐方式,可传left、right、none、center
  11476. * @remaind center表示图片独占一行
  11477. * @example
  11478. * ```javascript
  11479. * editor.execCommand( 'imagefloat', 'center' );
  11480. * ```
  11481. */
  11482. /**
  11483. * 如果选区所在位置是图片区域
  11484. * @command imagefloat
  11485. * @method queryCommandValue
  11486. * @param { String } cmd 命令字符串
  11487. * @return { String } 返回图片对齐方式
  11488. * @example
  11489. * ```javascript
  11490. * editor.queryCommandValue( 'imagefloat' );
  11491. * ```
  11492. */
  11493. UE.commands['imagefloat'] = {
  11494. execCommand: function (cmd, align) {
  11495. var me = this,
  11496. range = me.selection.getRange()
  11497. if (!range.collapsed) {
  11498. var img = range.getClosedNode()
  11499. if (img && img.tagName == 'IMG') {
  11500. switch (align) {
  11501. case 'left':
  11502. case 'right':
  11503. case 'none':
  11504. var pN = img.parentNode,
  11505. tmpNode,
  11506. pre,
  11507. next
  11508. while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
  11509. pN = pN.parentNode
  11510. }
  11511. tmpNode = pN
  11512. if (
  11513. tmpNode.tagName == 'P' &&
  11514. domUtils.getStyle(tmpNode, 'text-align') == 'center'
  11515. ) {
  11516. if (
  11517. !domUtils.isBody(tmpNode) &&
  11518. domUtils.getChildCount(tmpNode, function (node) {
  11519. return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  11520. }) == 1
  11521. ) {
  11522. pre = tmpNode.previousSibling
  11523. next = tmpNode.nextSibling
  11524. if (
  11525. pre &&
  11526. next &&
  11527. pre.nodeType == 1 &&
  11528. next.nodeType == 1 &&
  11529. pre.tagName == next.tagName &&
  11530. domUtils.isBlockElm(pre)
  11531. ) {
  11532. pre.appendChild(tmpNode.firstChild)
  11533. while (next.firstChild) {
  11534. pre.appendChild(next.firstChild)
  11535. }
  11536. domUtils.remove(tmpNode)
  11537. domUtils.remove(next)
  11538. } else {
  11539. domUtils.setStyle(tmpNode, 'text-align', '')
  11540. }
  11541. }
  11542. range.selectNode(img).select()
  11543. }
  11544. domUtils.setStyle(img, 'float', align == 'none' ? '' : align)
  11545. if (align == 'none') {
  11546. domUtils.removeAttributes(img, 'align')
  11547. }
  11548. break
  11549. case 'center':
  11550. if (me.queryCommandValue('imagefloat') != 'center') {
  11551. pN = img.parentNode
  11552. domUtils.setStyle(img, 'float', '')
  11553. domUtils.removeAttributes(img, 'align')
  11554. tmpNode = img
  11555. while (
  11556. pN &&
  11557. domUtils.getChildCount(pN, function (node) {
  11558. return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  11559. }) == 1 &&
  11560. (dtd.$inline[pN.tagName] || pN.tagName == 'A')
  11561. ) {
  11562. tmpNode = pN
  11563. pN = pN.parentNode
  11564. }
  11565. range.setStartBefore(tmpNode).setCursor(false)
  11566. pN = me.document.createElement('div')
  11567. pN.appendChild(tmpNode)
  11568. domUtils.setStyle(tmpNode, 'float', '')
  11569. me.execCommand(
  11570. 'insertHtml',
  11571. '<p id="_img_parent_tmp" style="text-align:center">' +
  11572. pN.innerHTML +
  11573. '</p>'
  11574. )
  11575. tmpNode = me.document.getElementById('_img_parent_tmp')
  11576. tmpNode.removeAttribute('id')
  11577. tmpNode = tmpNode.firstChild
  11578. range.selectNode(tmpNode).select()
  11579. //去掉后边多余的元素
  11580. next = tmpNode.parentNode.nextSibling
  11581. if (next && domUtils.isEmptyNode(next)) {
  11582. domUtils.remove(next)
  11583. }
  11584. }
  11585. break
  11586. }
  11587. }
  11588. }
  11589. },
  11590. queryCommandValue: function () {
  11591. var range = this.selection.getRange(),
  11592. startNode,
  11593. floatStyle
  11594. if (range.collapsed) {
  11595. return 'none'
  11596. }
  11597. startNode = range.getClosedNode()
  11598. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  11599. floatStyle =
  11600. domUtils.getComputedStyle(startNode, 'float') ||
  11601. startNode.getAttribute('align')
  11602. if (floatStyle == 'none') {
  11603. floatStyle =
  11604. domUtils.getComputedStyle(startNode.parentNode, 'text-align') ==
  11605. 'center'
  11606. ? 'center'
  11607. : floatStyle
  11608. }
  11609. return {
  11610. left: 1,
  11611. right: 1,
  11612. center: 1
  11613. }[floatStyle]
  11614. ? floatStyle
  11615. : 'none'
  11616. }
  11617. return 'none'
  11618. },
  11619. queryCommandState: function () {
  11620. var range = this.selection.getRange(),
  11621. startNode
  11622. if (range.collapsed) return -1
  11623. startNode = range.getClosedNode()
  11624. if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
  11625. return 0
  11626. }
  11627. return -1
  11628. }
  11629. }
  11630. /**
  11631. * 插入图片
  11632. * @command insertimage
  11633. * @method execCommand
  11634. * @param { String } cmd 命令字符串
  11635. * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
  11636. * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
  11637. * 此时数组的每一个元素都是一个Object类型的图片属性集合。
  11638. * @example
  11639. * ```javascript
  11640. * editor.execCommand( 'insertimage', {
  11641. * src:'a/b/c.jpg',
  11642. * width:'100',
  11643. * height:'100'
  11644. * } );
  11645. * ```
  11646. * @example
  11647. * ```javascript
  11648. * editor.execCommand( 'insertimage', [{
  11649. * src:'a/b/c.jpg',
  11650. * width:'100',
  11651. * height:'100'
  11652. * },{
  11653. * src:'a/b/d.jpg',
  11654. * width:'100',
  11655. * height:'100'
  11656. * }] );
  11657. * ```
  11658. */
  11659. UE.commands['insertimage'] = {
  11660. execCommand: function (cmd, opt) {
  11661. opt = utils.isArray(opt) ? opt : [opt]
  11662. if (!opt.length) {
  11663. return
  11664. }
  11665. var me = this,
  11666. range = me.selection.getRange(),
  11667. img = range.getClosedNode()
  11668. if (me.fireEvent('beforeinsertimage', opt) === true) {
  11669. return
  11670. }
  11671. function unhtmlData(imgCi) {
  11672. utils.each(
  11673. 'width,height,border,hspace,vspace'.split(','),
  11674. function (item) {
  11675. if (imgCi[item]) {
  11676. imgCi[item] = parseInt(imgCi[item], 10) || 0
  11677. }
  11678. }
  11679. )
  11680. utils.each('src,_src'.split(','), function (item) {
  11681. if (imgCi[item]) {
  11682. imgCi[item] = utils.unhtmlForUrl(imgCi[item])
  11683. }
  11684. })
  11685. utils.each('title,alt'.split(','), function (item) {
  11686. if (imgCi[item]) {
  11687. imgCi[item] = utils.unhtml(imgCi[item])
  11688. }
  11689. })
  11690. }
  11691. if (
  11692. img &&
  11693. /img/i.test(img.tagName) &&
  11694. (img.className != 'edui-faked-video' ||
  11695. img.className.indexOf('edui-upload-video') != -1) &&
  11696. !img.getAttribute('word_img')
  11697. ) {
  11698. var first = opt.shift()
  11699. var floatStyle = first['floatStyle']
  11700. delete first['floatStyle']
  11701. //// img.style.border = (first.border||0) +"px solid #000";
  11702. //// img.style.margin = (first.margin||0) +"px";
  11703. // img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
  11704. domUtils.setAttributes(img, first)
  11705. me.execCommand('imagefloat', floatStyle)
  11706. if (opt.length > 0) {
  11707. range.setStartAfter(img).setCursor(false, true)
  11708. me.execCommand('insertimage', opt)
  11709. }
  11710. } else {
  11711. var html = [],
  11712. str = '',
  11713. ci
  11714. ci = opt[0]
  11715. if (opt.length == 1) {
  11716. unhtmlData(ci)
  11717. str =
  11718. '<img src="' +
  11719. ci.src +
  11720. '" ' +
  11721. (ci._src ? ' _src="' + ci._src + '" ' : '') +
  11722. (ci.width ? 'width="' + ci.width + '" ' : '') +
  11723. (ci.height ? ' height="' + ci.height + '" ' : '') +
  11724. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right'
  11725. ? ' style="float:' + ci['floatStyle'] + ';"'
  11726. : '') +
  11727. (ci.title && ci.title != '' ? ' title="' + ci.title + '"' : '') +
  11728. (ci.border && ci.border != '0'
  11729. ? ' border="' + ci.border + '"'
  11730. : '') +
  11731. (ci.alt && ci.alt != '' ? ' alt="' + ci.alt + '"' : '') +
  11732. (ci.hspace && ci.hspace != '0'
  11733. ? ' hspace = "' + ci.hspace + '"'
  11734. : '') +
  11735. (ci.vspace && ci.vspace != '0'
  11736. ? ' vspace = "' + ci.vspace + '"'
  11737. : '') +
  11738. '/>'
  11739. if (ci['floatStyle'] == 'center') {
  11740. str = '<p style="text-align: center">' + str + '</p>'
  11741. }
  11742. html.push(str)
  11743. } else {
  11744. for (var i = 0; (ci = opt[i++]); ) {
  11745. unhtmlData(ci)
  11746. str =
  11747. '<p ' +
  11748. (ci['floatStyle'] == 'center'
  11749. ? 'style="text-align: center" '
  11750. : '') +
  11751. '><img src="' +
  11752. ci.src +
  11753. '" ' +
  11754. (ci.width ? 'width="' + ci.width + '" ' : '') +
  11755. (ci._src ? ' _src="' + ci._src + '" ' : '') +
  11756. (ci.height ? ' height="' + ci.height + '" ' : '') +
  11757. ' style="' +
  11758. (ci['floatStyle'] && ci['floatStyle'] != 'center'
  11759. ? 'float:' + ci['floatStyle'] + ';'
  11760. : '') +
  11761. (ci.border || '') +
  11762. '" ' +
  11763. (ci.title ? ' title="' + ci.title + '"' : '') +
  11764. ' /></p>'
  11765. html.push(str)
  11766. }
  11767. }
  11768. me.execCommand('insertHtml', html.join(''))
  11769. }
  11770. me.fireEvent('afterinsertimage', opt)
  11771. }
  11772. }
  11773. // plugins/justify.js
  11774. /**
  11775. * 段落格式
  11776. * @file
  11777. * @since 1.2.6.1
  11778. */
  11779. /**
  11780. * 段落对齐方式
  11781. * @command justify
  11782. * @method execCommand
  11783. * @param { String } cmd 命令字符串
  11784. * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
  11785. * @example
  11786. * ```javascript
  11787. * editor.execCommand( 'justify', 'center' );
  11788. * ```
  11789. */
  11790. /**
  11791. * 如果选区所在位置是段落区域,返回当前段落对齐方式
  11792. * @command justify
  11793. * @method queryCommandValue
  11794. * @param { String } cmd 命令字符串
  11795. * @return { String } 返回段落对齐方式
  11796. * @example
  11797. * ```javascript
  11798. * editor.queryCommandValue( 'justify' );
  11799. * ```
  11800. */
  11801. UE.plugins['justify'] = function () {
  11802. var me = this,
  11803. block = domUtils.isBlockElm,
  11804. defaultValue = {
  11805. left: 1,
  11806. right: 1,
  11807. center: 1,
  11808. justify: 1
  11809. },
  11810. doJustify = function (range, style) {
  11811. var bookmark = range.createBookmark(),
  11812. filterFn = function (node) {
  11813. return node.nodeType == 1
  11814. ? node.tagName.toLowerCase() != 'br' &&
  11815. !domUtils.isBookmarkNode(node)
  11816. : !domUtils.isWhitespace(node)
  11817. }
  11818. range.enlarge(true)
  11819. var bookmark2 = range.createBookmark(),
  11820. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  11821. tmpRange = range.cloneRange(),
  11822. tmpNode
  11823. while (
  11824. current &&
  11825. !(
  11826. domUtils.getPosition(current, bookmark2.end) &
  11827. domUtils.POSITION_FOLLOWING
  11828. )
  11829. ) {
  11830. if (current.nodeType == 3 || !block(current)) {
  11831. tmpRange.setStartBefore(current)
  11832. while (current && current !== bookmark2.end && !block(current)) {
  11833. tmpNode = current
  11834. current = domUtils.getNextDomNode(
  11835. current,
  11836. false,
  11837. null,
  11838. function (node) {
  11839. return !block(node)
  11840. }
  11841. )
  11842. }
  11843. tmpRange.setEndAfter(tmpNode)
  11844. var common = tmpRange.getCommonAncestor()
  11845. if (!domUtils.isBody(common) && block(common)) {
  11846. domUtils.setStyles(
  11847. common,
  11848. utils.isString(style) ? { 'text-align': style } : style
  11849. )
  11850. current = common
  11851. } else {
  11852. var p = range.document.createElement('p')
  11853. domUtils.setStyles(
  11854. p,
  11855. utils.isString(style) ? { 'text-align': style } : style
  11856. )
  11857. var frag = tmpRange.extractContents()
  11858. p.appendChild(frag)
  11859. tmpRange.insertNode(p)
  11860. current = p
  11861. }
  11862. current = domUtils.getNextDomNode(current, false, filterFn)
  11863. } else {
  11864. current = domUtils.getNextDomNode(current, true, filterFn)
  11865. }
  11866. }
  11867. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
  11868. }
  11869. UE.commands['justify'] = {
  11870. execCommand: function (cmdName, align) {
  11871. var range = this.selection.getRange(),
  11872. txt
  11873. //闭合时单独处理
  11874. if (range.collapsed) {
  11875. txt = this.document.createTextNode('p')
  11876. range.insertNode(txt)
  11877. }
  11878. doJustify(range, align)
  11879. if (txt) {
  11880. range.setStartBefore(txt).collapse(true)
  11881. domUtils.remove(txt)
  11882. }
  11883. range.select()
  11884. return true
  11885. },
  11886. queryCommandValue: function () {
  11887. var startNode = this.selection.getStart(),
  11888. value = domUtils.getComputedStyle(startNode, 'text-align')
  11889. return defaultValue[value] ? value : 'left'
  11890. },
  11891. queryCommandState: function () {
  11892. var start = this.selection.getStart(),
  11893. cell =
  11894. start &&
  11895. domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
  11896. return cell ? -1 : 0
  11897. }
  11898. }
  11899. }
  11900. // plugins/font.js
  11901. /**
  11902. * 字体颜色,背景色,字号,字体,下划线,删除线
  11903. * @file
  11904. * @since 1.2.6.1
  11905. */
  11906. /**
  11907. * 字体颜色
  11908. * @command forecolor
  11909. * @method execCommand
  11910. * @param { String } cmd 命令字符串
  11911. * @param { String } value 色值(必须十六进制)
  11912. * @example
  11913. * ```javascript
  11914. * editor.execCommand( 'forecolor', '#000' );
  11915. * ```
  11916. */
  11917. /**
  11918. * 返回选区字体颜色
  11919. * @command forecolor
  11920. * @method queryCommandValue
  11921. * @param { String } cmd 命令字符串
  11922. * @return { String } 返回字体颜色
  11923. * @example
  11924. * ```javascript
  11925. * editor.queryCommandValue( 'forecolor' );
  11926. * ```
  11927. */
  11928. /**
  11929. * 字体背景颜色
  11930. * @command backcolor
  11931. * @method execCommand
  11932. * @param { String } cmd 命令字符串
  11933. * @param { String } value 色值(必须十六进制)
  11934. * @example
  11935. * ```javascript
  11936. * editor.execCommand( 'backcolor', '#000' );
  11937. * ```
  11938. */
  11939. /**
  11940. * 返回选区字体颜色
  11941. * @command backcolor
  11942. * @method queryCommandValue
  11943. * @param { String } cmd 命令字符串
  11944. * @return { String } 返回字体背景颜色
  11945. * @example
  11946. * ```javascript
  11947. * editor.queryCommandValue( 'backcolor' );
  11948. * ```
  11949. */
  11950. /**
  11951. * 字体大小
  11952. * @command fontsize
  11953. * @method execCommand
  11954. * @param { String } cmd 命令字符串
  11955. * @param { String } value 字体大小
  11956. * @example
  11957. * ```javascript
  11958. * editor.execCommand( 'fontsize', '14px' );
  11959. * ```
  11960. */
  11961. /**
  11962. * 返回选区字体大小
  11963. * @command fontsize
  11964. * @method queryCommandValue
  11965. * @param { String } cmd 命令字符串
  11966. * @return { String } 返回字体大小
  11967. * @example
  11968. * ```javascript
  11969. * editor.queryCommandValue( 'fontsize' );
  11970. * ```
  11971. */
  11972. /**
  11973. * 字体样式
  11974. * @command fontfamily
  11975. * @method execCommand
  11976. * @param { String } cmd 命令字符串
  11977. * @param { String } value 字体样式
  11978. * @example
  11979. * ```javascript
  11980. * editor.execCommand( 'fontfamily', '微软雅黑' );
  11981. * ```
  11982. */
  11983. /**
  11984. * 返回选区字体样式
  11985. * @command fontfamily
  11986. * @method queryCommandValue
  11987. * @param { String } cmd 命令字符串
  11988. * @return { String } 返回字体样式
  11989. * @example
  11990. * ```javascript
  11991. * editor.queryCommandValue( 'fontfamily' );
  11992. * ```
  11993. */
  11994. /**
  11995. * 字体下划线,与删除线互斥
  11996. * @command underline
  11997. * @method execCommand
  11998. * @param { String } cmd 命令字符串
  11999. * @example
  12000. * ```javascript
  12001. * editor.execCommand( 'underline' );
  12002. * ```
  12003. */
  12004. /**
  12005. * 字体删除线,与下划线互斥
  12006. * @command strikethrough
  12007. * @method execCommand
  12008. * @param { String } cmd 命令字符串
  12009. * @example
  12010. * ```javascript
  12011. * editor.execCommand( 'strikethrough' );
  12012. * ```
  12013. */
  12014. /**
  12015. * 字体边框
  12016. * @command fontborder
  12017. * @method execCommand
  12018. * @param { String } cmd 命令字符串
  12019. * @example
  12020. * ```javascript
  12021. * editor.execCommand( 'fontborder' );
  12022. * ```
  12023. */
  12024. UE.plugins['font'] = function () {
  12025. var me = this,
  12026. fonts = {
  12027. forecolor: 'color',
  12028. backcolor: 'background-color',
  12029. fontsize: 'font-size',
  12030. fontfamily: 'font-family',
  12031. underline: 'text-decoration',
  12032. strikethrough: 'text-decoration',
  12033. fontborder: 'border'
  12034. },
  12035. needCmd = { underline: 1, strikethrough: 1, fontborder: 1 },
  12036. needSetChild = {
  12037. forecolor: 'color',
  12038. backcolor: 'background-color',
  12039. fontsize: 'font-size',
  12040. fontfamily: 'font-family'
  12041. }
  12042. me.setOpt({
  12043. fontfamily: [
  12044. { name: 'songti', val: '宋体,SimSun' },
  12045. { name: 'yahei', val: '微软雅黑,Microsoft YaHei' },
  12046. { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai' },
  12047. { name: 'heiti', val: '黑体, SimHei' },
  12048. { name: 'lishu', val: '隶书, SimLi' },
  12049. { name: 'andaleMono', val: 'andale mono' },
  12050. { name: 'arial', val: 'arial, helvetica,sans-serif' },
  12051. { name: 'arialBlack', val: 'arial black,avant garde' },
  12052. { name: 'comicSansMs', val: 'comic sans ms' },
  12053. { name: 'impact', val: 'impact,chicago' },
  12054. { name: 'timesNewRoman', val: 'times new roman' }
  12055. ],
  12056. fontsize: [10, 11, 12, 14, 16, 18, 20, 24, 36]
  12057. })
  12058. function mergeWithParent(node) {
  12059. var parent
  12060. while ((parent = node.parentNode)) {
  12061. if (
  12062. parent.tagName == 'SPAN' &&
  12063. domUtils.getChildCount(parent, function (child) {
  12064. return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
  12065. }) == 1
  12066. ) {
  12067. parent.style.cssText += node.style.cssText
  12068. domUtils.remove(node, true)
  12069. node = parent
  12070. } else {
  12071. break
  12072. }
  12073. }
  12074. }
  12075. function mergeChild(rng, cmdName, value) {
  12076. if (needSetChild[cmdName]) {
  12077. rng.adjustmentBoundary()
  12078. if (!rng.collapsed && rng.startContainer.nodeType == 1) {
  12079. var start = rng.startContainer.childNodes[rng.startOffset]
  12080. if (start && domUtils.isTagNode(start, 'span')) {
  12081. var bk = rng.createBookmark()
  12082. utils.each(
  12083. domUtils.getElementsByTagName(start, 'span'),
  12084. function (span) {
  12085. if (!span.parentNode || domUtils.isBookmarkNode(span)) return
  12086. if (
  12087. cmdName == 'backcolor' &&
  12088. domUtils
  12089. .getComputedStyle(span, 'background-color')
  12090. .toLowerCase() === value
  12091. ) {
  12092. return
  12093. }
  12094. domUtils.removeStyle(span, needSetChild[cmdName])
  12095. if (span.style.cssText.replace(/^\s+$/, '').length == 0) {
  12096. domUtils.remove(span, true)
  12097. }
  12098. }
  12099. )
  12100. rng.moveToBookmark(bk)
  12101. }
  12102. }
  12103. }
  12104. }
  12105. function mergesibling(rng, cmdName, value) {
  12106. var collapsed = rng.collapsed,
  12107. bk = rng.createBookmark(),
  12108. common
  12109. if (collapsed) {
  12110. common = bk.start.parentNode
  12111. while (dtd.$inline[common.tagName]) {
  12112. common = common.parentNode
  12113. }
  12114. } else {
  12115. common = domUtils.getCommonAncestor(bk.start, bk.end)
  12116. }
  12117. utils.each(
  12118. domUtils.getElementsByTagName(common, 'span'),
  12119. function (span) {
  12120. if (!span.parentNode || domUtils.isBookmarkNode(span)) return
  12121. if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
  12122. if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
  12123. domUtils.remove(span, true)
  12124. } else {
  12125. domUtils.removeStyle(span, 'border')
  12126. }
  12127. return
  12128. }
  12129. if (
  12130. /border/i.test(span.style.cssText) &&
  12131. span.parentNode.tagName == 'SPAN' &&
  12132. /border/i.test(span.parentNode.style.cssText)
  12133. ) {
  12134. span.style.cssText = span.style.cssText.replace(
  12135. /border[^:]*:[^;]+;?/gi,
  12136. ''
  12137. )
  12138. }
  12139. if (!(cmdName == 'fontborder' && value == 'none')) {
  12140. var next = span.nextSibling
  12141. while (next && next.nodeType == 1 && next.tagName == 'SPAN') {
  12142. if (domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
  12143. span.appendChild(next)
  12144. next = span.nextSibling
  12145. continue
  12146. }
  12147. if (next.style.cssText == span.style.cssText) {
  12148. domUtils.moveChild(next, span)
  12149. domUtils.remove(next)
  12150. }
  12151. if (span.nextSibling === next) break
  12152. next = span.nextSibling
  12153. }
  12154. }
  12155. mergeWithParent(span)
  12156. if (browser.ie && browser.version > 8) {
  12157. //拷贝父亲们的特别的属性,这里只做背景颜色的处理
  12158. var parent = domUtils.findParent(span, function (n) {
  12159. return (
  12160. n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)
  12161. )
  12162. })
  12163. if (parent && !/background-color/.test(span.style.cssText)) {
  12164. span.style.backgroundColor = parent.style.backgroundColor
  12165. }
  12166. }
  12167. }
  12168. )
  12169. rng.moveToBookmark(bk)
  12170. mergeChild(rng, cmdName, value)
  12171. }
  12172. me.addInputRule(function (root) {
  12173. utils.each(
  12174. root.getNodesByTagName('u s del font strike'),
  12175. function (node) {
  12176. if (node.tagName == 'font') {
  12177. var cssStyle = []
  12178. for (var p in node.attrs) {
  12179. switch (p) {
  12180. case 'size':
  12181. cssStyle.push(
  12182. 'font-size:' +
  12183. ({
  12184. 1: '10',
  12185. 2: '12',
  12186. 3: '16',
  12187. 4: '18',
  12188. 5: '24',
  12189. 6: '32',
  12190. 7: '48'
  12191. }[node.attrs[p]] || node.attrs[p]) +
  12192. 'px'
  12193. )
  12194. break
  12195. case 'color':
  12196. cssStyle.push('color:' + node.attrs[p])
  12197. break
  12198. case 'face':
  12199. cssStyle.push('font-family:' + node.attrs[p])
  12200. break
  12201. case 'style':
  12202. cssStyle.push(node.attrs[p])
  12203. }
  12204. }
  12205. node.attrs = {
  12206. style: cssStyle.join(';')
  12207. }
  12208. } else {
  12209. var val = node.tagName == 'u' ? 'underline' : 'line-through'
  12210. node.attrs = {
  12211. style:
  12212. (node.getAttr('style') || '') + 'text-decoration:' + val + ';'
  12213. }
  12214. }
  12215. node.tagName = 'span'
  12216. }
  12217. )
  12218. // utils.each(root.getNodesByTagName('span'), function (node) {
  12219. // var val;
  12220. // if(val = node.getAttr('class')){
  12221. // if(/fontstrikethrough/.test(val)){
  12222. // node.setStyle('text-decoration','line-through');
  12223. // if(node.attrs['class']){
  12224. // node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
  12225. // }else{
  12226. // node.setAttr('class')
  12227. // }
  12228. // }
  12229. // if(/fontborder/.test(val)){
  12230. // node.setStyle('border','1px solid #000');
  12231. // if(node.attrs['class']){
  12232. // node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
  12233. // }else{
  12234. // node.setAttr('class')
  12235. // }
  12236. // }
  12237. // }
  12238. // });
  12239. })
  12240. // me.addOutputRule(function(root){
  12241. // utils.each(root.getNodesByTagName('span'), function (node) {
  12242. // var val;
  12243. // if(val = node.getStyle('text-decoration')){
  12244. // if(/line-through/.test(val)){
  12245. // if(node.attrs['class']){
  12246. // node.attrs['class'] += ' fontstrikethrough';
  12247. // }else{
  12248. // node.setAttr('class','fontstrikethrough')
  12249. // }
  12250. // }
  12251. //
  12252. // node.setStyle('text-decoration')
  12253. // }
  12254. // if(val = node.getStyle('border')){
  12255. // if(/1px/.test(val) && /solid/.test(val)){
  12256. // if(node.attrs['class']){
  12257. // node.attrs['class'] += ' fontborder';
  12258. //
  12259. // }else{
  12260. // node.setAttr('class','fontborder')
  12261. // }
  12262. // }
  12263. // node.setStyle('border')
  12264. //
  12265. // }
  12266. // });
  12267. // });
  12268. for (var p in fonts) {
  12269. ;(function (cmd, style) {
  12270. UE.commands[cmd] = {
  12271. execCommand: function (cmdName, value) {
  12272. value =
  12273. value ||
  12274. (this.queryCommandState(cmdName)
  12275. ? 'none'
  12276. : cmdName == 'underline'
  12277. ? 'underline'
  12278. : cmdName == 'fontborder'
  12279. ? '1px solid #000'
  12280. : 'line-through')
  12281. var me = this,
  12282. range = this.selection.getRange(),
  12283. text
  12284. if (value == 'default') {
  12285. if (range.collapsed) {
  12286. text = me.document.createTextNode('font')
  12287. range.insertNode(text).select()
  12288. }
  12289. me.execCommand('removeFormat', 'span,a', style)
  12290. if (text) {
  12291. range.setStartBefore(text).collapse(true)
  12292. domUtils.remove(text)
  12293. }
  12294. mergesibling(range, cmdName, value)
  12295. range.select()
  12296. } else {
  12297. if (!range.collapsed) {
  12298. if (needCmd[cmd] && me.queryCommandValue(cmd)) {
  12299. me.execCommand('removeFormat', 'span,a', style)
  12300. }
  12301. range = me.selection.getRange()
  12302. range.applyInlineStyle('span', { style: style + ':' + value })
  12303. mergesibling(range, cmdName, value)
  12304. range.select()
  12305. } else {
  12306. var span = domUtils.findParentByTagName(
  12307. range.startContainer,
  12308. 'span',
  12309. true
  12310. )
  12311. text = me.document.createTextNode('font')
  12312. if (
  12313. span &&
  12314. !span.children.length &&
  12315. !span[browser.ie ? 'innerText' : 'textContent'].replace(
  12316. fillCharReg,
  12317. ''
  12318. ).length
  12319. ) {
  12320. //for ie hack when enter
  12321. range.insertNode(text)
  12322. if (needCmd[cmd]) {
  12323. range.selectNode(text).select()
  12324. me.execCommand('removeFormat', 'span,a', style, null)
  12325. span = domUtils.findParentByTagName(text, 'span', true)
  12326. range.setStartBefore(text)
  12327. }
  12328. span && (span.style.cssText += ';' + style + ':' + value)
  12329. range.collapse(true).select()
  12330. } else {
  12331. range.insertNode(text)
  12332. range.selectNode(text).select()
  12333. span = range.document.createElement('span')
  12334. if (needCmd[cmd]) {
  12335. //a标签内的不处理跳过
  12336. if (domUtils.findParentByTagName(text, 'a', true)) {
  12337. range.setStartBefore(text).setCursor()
  12338. domUtils.remove(text)
  12339. return
  12340. }
  12341. me.execCommand('removeFormat', 'span,a', style)
  12342. }
  12343. span.style.cssText = style + ':' + value
  12344. text.parentNode.insertBefore(span, text)
  12345. //修复,span套span 但样式不继承的问题
  12346. if (!browser.ie || (browser.ie && browser.version == 9)) {
  12347. var spanParent = span.parentNode
  12348. while (!domUtils.isBlockElm(spanParent)) {
  12349. if (spanParent.tagName == 'SPAN') {
  12350. //opera合并style不会加入";"
  12351. span.style.cssText =
  12352. spanParent.style.cssText + ';' + span.style.cssText
  12353. }
  12354. spanParent = spanParent.parentNode
  12355. }
  12356. }
  12357. if (opera) {
  12358. setTimeout(function () {
  12359. range.setStart(span, 0).collapse(true)
  12360. mergesibling(range, cmdName, value)
  12361. range.select()
  12362. })
  12363. } else {
  12364. range.setStart(span, 0).collapse(true)
  12365. mergesibling(range, cmdName, value)
  12366. range.select()
  12367. }
  12368. //trace:981
  12369. //domUtils.mergeToParent(span)
  12370. }
  12371. domUtils.remove(text)
  12372. }
  12373. }
  12374. return true
  12375. },
  12376. queryCommandValue: function (cmdName) {
  12377. var startNode = this.selection.getStart()
  12378. //trace:946
  12379. if (cmdName == 'underline' || cmdName == 'strikethrough') {
  12380. var tmpNode = startNode,
  12381. value
  12382. while (
  12383. tmpNode &&
  12384. !domUtils.isBlockElm(tmpNode) &&
  12385. !domUtils.isBody(tmpNode)
  12386. ) {
  12387. if (tmpNode.nodeType == 1) {
  12388. value = domUtils.getComputedStyle(tmpNode, style)
  12389. if (value != 'none') {
  12390. return value
  12391. }
  12392. }
  12393. tmpNode = tmpNode.parentNode
  12394. }
  12395. return 'none'
  12396. }
  12397. if (cmdName == 'fontborder') {
  12398. var tmp = startNode,
  12399. val
  12400. while (tmp && dtd.$inline[tmp.tagName]) {
  12401. if ((val = domUtils.getComputedStyle(tmp, 'border'))) {
  12402. if (/1px/.test(val) && /solid/.test(val)) {
  12403. return val
  12404. }
  12405. }
  12406. tmp = tmp.parentNode
  12407. }
  12408. return ''
  12409. }
  12410. if (cmdName == 'FontSize') {
  12411. var styleVal = domUtils.getComputedStyle(startNode, style),
  12412. tmp = /^([\d\.]+)(\w+)$/.exec(styleVal)
  12413. if (tmp) {
  12414. return Math.floor(tmp[1]) + tmp[2]
  12415. }
  12416. return styleVal
  12417. }
  12418. return domUtils.getComputedStyle(startNode, style)
  12419. },
  12420. queryCommandState: function (cmdName) {
  12421. if (!needCmd[cmdName]) return 0
  12422. var val = this.queryCommandValue(cmdName)
  12423. if (cmdName == 'fontborder') {
  12424. return /1px/.test(val) && /solid/.test(val)
  12425. } else {
  12426. return cmdName == 'underline'
  12427. ? /underline/.test(val)
  12428. : /line\-through/.test(val)
  12429. }
  12430. }
  12431. }
  12432. })(p, fonts[p])
  12433. }
  12434. }
  12435. // plugins/link.js
  12436. /**
  12437. * 超链接
  12438. * @file
  12439. * @since 1.2.6.1
  12440. */
  12441. /**
  12442. * 插入超链接
  12443. * @command link
  12444. * @method execCommand
  12445. * @param { String } cmd 命令字符串
  12446. * @param { Object } options 设置自定义属性,例如:url、title、target
  12447. * @example
  12448. * ```javascript
  12449. * editor.execCommand( 'link', '{
  12450. * url:'ueditor.baidu.com',
  12451. * title:'ueditor',
  12452. * target:'_blank'
  12453. * }' );
  12454. * ```
  12455. */
  12456. /**
  12457. * 返回当前选中的第一个超链接节点
  12458. * @command link
  12459. * @method queryCommandValue
  12460. * @param { String } cmd 命令字符串
  12461. * @return { Element } 超链接节点
  12462. * @example
  12463. * ```javascript
  12464. * editor.queryCommandValue( 'link' );
  12465. * ```
  12466. */
  12467. /**
  12468. * 取消超链接
  12469. * @command unlink
  12470. * @method execCommand
  12471. * @param { String } cmd 命令字符串
  12472. * @example
  12473. * ```javascript
  12474. * editor.execCommand( 'unlink');
  12475. * ```
  12476. */
  12477. UE.plugins['link'] = function () {
  12478. function optimize(range) {
  12479. var start = range.startContainer,
  12480. end = range.endContainer
  12481. if ((start = domUtils.findParentByTagName(start, 'a', true))) {
  12482. range.setStartBefore(start)
  12483. }
  12484. if ((end = domUtils.findParentByTagName(end, 'a', true))) {
  12485. range.setEndAfter(end)
  12486. }
  12487. }
  12488. UE.commands['unlink'] = {
  12489. execCommand: function () {
  12490. var range = this.selection.getRange(),
  12491. bookmark
  12492. if (
  12493. range.collapsed &&
  12494. !domUtils.findParentByTagName(range.startContainer, 'a', true)
  12495. ) {
  12496. return
  12497. }
  12498. bookmark = range.createBookmark()
  12499. optimize(range)
  12500. range.removeInlineStyle('a').moveToBookmark(bookmark).select()
  12501. },
  12502. queryCommandState: function () {
  12503. return !this.highlight && this.queryCommandValue('link') ? 0 : -1
  12504. }
  12505. }
  12506. function doLink(range, opt, me) {
  12507. var rngClone = range.cloneRange(),
  12508. link = me.queryCommandValue('link')
  12509. optimize((range = range.adjustmentBoundary()))
  12510. var start = range.startContainer
  12511. if (start.nodeType == 1 && link) {
  12512. start = start.childNodes[range.startOffset]
  12513. if (
  12514. start &&
  12515. start.nodeType == 1 &&
  12516. start.tagName == 'A' &&
  12517. /^(?:https?|ftp|file)\s*:\s*\/\//.test(
  12518. start[browser.ie ? 'innerText' : 'textContent']
  12519. )
  12520. ) {
  12521. start[browser.ie ? 'innerText' : 'textContent'] = utils.html(
  12522. opt.textValue || opt.href
  12523. )
  12524. }
  12525. }
  12526. if (!rngClone.collapsed || link) {
  12527. range.removeInlineStyle('a')
  12528. rngClone = range.cloneRange()
  12529. }
  12530. if (rngClone.collapsed) {
  12531. var a = range.document.createElement('a'),
  12532. text = ''
  12533. if (opt.textValue) {
  12534. text = utils.html(opt.textValue)
  12535. delete opt.textValue
  12536. } else {
  12537. text = utils.html(opt.href)
  12538. }
  12539. domUtils.setAttributes(a, opt)
  12540. start = domUtils.findParentByTagName(rngClone.startContainer, 'a', true)
  12541. if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
  12542. range.setStartAfter(start).collapse(true)
  12543. }
  12544. a[browser.ie ? 'innerText' : 'textContent'] = text
  12545. range.insertNode(a).selectNode(a)
  12546. } else {
  12547. range.applyInlineStyle('a', opt)
  12548. }
  12549. }
  12550. UE.commands['link'] = {
  12551. execCommand: function (cmdName, opt) {
  12552. var range
  12553. opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g))
  12554. opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g))
  12555. opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g))
  12556. doLink((range = this.selection.getRange()), opt, this)
  12557. //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
  12558. range.collapse().select(true)
  12559. },
  12560. queryCommandValue: function () {
  12561. var range = this.selection.getRange(),
  12562. node
  12563. if (range.collapsed) {
  12564. // node = this.selection.getStart();
  12565. //在ie下getstart()取值偏上了
  12566. node = range.startContainer
  12567. node = node.nodeType == 1 ? node : node.parentNode
  12568. if (
  12569. node &&
  12570. (node = domUtils.findParentByTagName(node, 'a', true)) &&
  12571. !domUtils.isInNodeEndBoundary(range, node)
  12572. ) {
  12573. return node
  12574. }
  12575. } else {
  12576. //trace:1111 如果是<p><a>xx</a></p> startContainer是p就会找不到a
  12577. range.shrinkBoundary()
  12578. var start =
  12579. range.startContainer.nodeType == 3 ||
  12580. !range.startContainer.childNodes[range.startOffset]
  12581. ? range.startContainer
  12582. : range.startContainer.childNodes[range.startOffset],
  12583. end =
  12584. range.endContainer.nodeType == 3 || range.endOffset == 0
  12585. ? range.endContainer
  12586. : range.endContainer.childNodes[range.endOffset - 1],
  12587. common = range.getCommonAncestor()
  12588. node = domUtils.findParentByTagName(common, 'a', true)
  12589. if (!node && common.nodeType == 1) {
  12590. var as = common.getElementsByTagName('a'),
  12591. ps,
  12592. pe
  12593. for (var i = 0, ci; (ci = as[i++]); ) {
  12594. ;(ps = domUtils.getPosition(ci, start)),
  12595. (pe = domUtils.getPosition(ci, end))
  12596. if (
  12597. (ps & domUtils.POSITION_FOLLOWING ||
  12598. ps & domUtils.POSITION_CONTAINS) &&
  12599. (pe & domUtils.POSITION_PRECEDING ||
  12600. pe & domUtils.POSITION_CONTAINS)
  12601. ) {
  12602. node = ci
  12603. break
  12604. }
  12605. }
  12606. }
  12607. return node
  12608. }
  12609. },
  12610. queryCommandState: function () {
  12611. //判断如果是视频的话连接不可用
  12612. //fix 853
  12613. var img = this.selection.getRange().getClosedNode(),
  12614. flag =
  12615. img &&
  12616. (img.className == 'edui-faked-video' ||
  12617. img.className.indexOf('edui-upload-video') != -1)
  12618. return flag ? -1 : 0
  12619. }
  12620. }
  12621. }
  12622. // plugins/iframe.js
  12623. ///import core
  12624. ///import plugins\inserthtml.js
  12625. ///commands 插入框架
  12626. ///commandsName InsertFrame
  12627. ///commandsTitle 插入Iframe
  12628. ///commandsDialog dialogs\insertframe
  12629. UE.plugins['insertframe'] = function () {
  12630. var me = this
  12631. function deleteIframe() {
  12632. me._iframe && delete me._iframe
  12633. }
  12634. me.addListener('selectionchange', function () {
  12635. deleteIframe()
  12636. })
  12637. }
  12638. // plugins/scrawl.js
  12639. ///import core
  12640. ///commands 涂鸦
  12641. ///commandsName Scrawl
  12642. ///commandsTitle 涂鸦
  12643. ///commandsDialog dialogs\scrawl
  12644. UE.commands['scrawl'] = {
  12645. queryCommandState: function () {
  12646. return browser.ie && browser.version <= 8 ? -1 : 0
  12647. }
  12648. }
  12649. // plugins/removeformat.js
  12650. /**
  12651. * 清除格式
  12652. * @file
  12653. * @since 1.2.6.1
  12654. */
  12655. /**
  12656. * 清除文字样式
  12657. * @command removeformat
  12658. * @method execCommand
  12659. * @param { String } cmd 命令字符串
  12660. * @param {String} tags 以逗号隔开的标签。如:strong
  12661. * @param {String} style 样式如:color
  12662. * @param {String} attrs 属性如:width
  12663. * @example
  12664. * ```javascript
  12665. * editor.execCommand( 'removeformat', 'strong','color','width' );
  12666. * ```
  12667. */
  12668. UE.plugins['removeformat'] = function () {
  12669. var me = this
  12670. me.setOpt({
  12671. removeFormatTags:
  12672. 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
  12673. removeFormatAttributes:
  12674. 'class,style,lang,width,height,align,hspace,valign'
  12675. })
  12676. me.commands['removeformat'] = {
  12677. execCommand: function (cmdName, tags, style, attrs, notIncludeA) {
  12678. var tagReg = new RegExp(
  12679. '^(?:' +
  12680. (tags || this.options.removeFormatTags).replace(/,/g, '|') +
  12681. ')$',
  12682. 'i'
  12683. ),
  12684. removeFormatAttributes = style
  12685. ? []
  12686. : (attrs || this.options.removeFormatAttributes).split(','),
  12687. range = new dom.Range(this.document),
  12688. bookmark,
  12689. node,
  12690. parent,
  12691. filter = function (node) {
  12692. return node.nodeType == 1
  12693. }
  12694. function isRedundantSpan(node) {
  12695. if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span') {
  12696. return 0
  12697. }
  12698. if (browser.ie) {
  12699. //ie 下判断实效,所以只能简单用style来判断
  12700. //return node.style.cssText == '' ? 1 : 0;
  12701. var attrs = node.attributes
  12702. if (attrs.length) {
  12703. for (var i = 0, l = attrs.length; i < l; i++) {
  12704. if (attrs[i].specified) {
  12705. return 0
  12706. }
  12707. }
  12708. return 1
  12709. }
  12710. }
  12711. return !node.attributes.length
  12712. }
  12713. function doRemove(range) {
  12714. var bookmark1 = range.createBookmark()
  12715. if (range.collapsed) {
  12716. range.enlarge(true)
  12717. }
  12718. //不能把a标签切了
  12719. if (!notIncludeA) {
  12720. var aNode = domUtils.findParentByTagName(
  12721. range.startContainer,
  12722. 'a',
  12723. true
  12724. )
  12725. if (aNode) {
  12726. range.setStartBefore(aNode)
  12727. }
  12728. aNode = domUtils.findParentByTagName(range.endContainer, 'a', true)
  12729. if (aNode) {
  12730. range.setEndAfter(aNode)
  12731. }
  12732. }
  12733. bookmark = range.createBookmark()
  12734. node = bookmark.start
  12735. //切开始
  12736. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  12737. domUtils.breakParent(node, parent)
  12738. domUtils.clearEmptySibling(node)
  12739. }
  12740. if (bookmark.end) {
  12741. //切结束
  12742. node = bookmark.end
  12743. while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
  12744. domUtils.breakParent(node, parent)
  12745. domUtils.clearEmptySibling(node)
  12746. }
  12747. //开始去除样式
  12748. var current = domUtils.getNextDomNode(
  12749. bookmark.start,
  12750. false,
  12751. filter
  12752. ),
  12753. next
  12754. while (current) {
  12755. if (current == bookmark.end) {
  12756. break
  12757. }
  12758. next = domUtils.getNextDomNode(current, true, filter)
  12759. if (
  12760. !dtd.$empty[current.tagName.toLowerCase()] &&
  12761. !domUtils.isBookmarkNode(current)
  12762. ) {
  12763. if (tagReg.test(current.tagName)) {
  12764. if (style) {
  12765. domUtils.removeStyle(current, style)
  12766. if (
  12767. isRedundantSpan(current) &&
  12768. style != 'text-decoration'
  12769. ) {
  12770. domUtils.remove(current, true)
  12771. }
  12772. } else {
  12773. domUtils.remove(current, true)
  12774. }
  12775. } else {
  12776. //trace:939 不能把list上的样式去掉
  12777. if (
  12778. !dtd.$tableContent[current.tagName] &&
  12779. !dtd.$list[current.tagName]
  12780. ) {
  12781. domUtils.removeAttributes(current, removeFormatAttributes)
  12782. if (isRedundantSpan(current)) {
  12783. domUtils.remove(current, true)
  12784. }
  12785. }
  12786. }
  12787. }
  12788. current = next
  12789. }
  12790. }
  12791. //trace:1035
  12792. //trace:1096 不能把td上的样式去掉,比如边框
  12793. var pN = bookmark.start.parentNode
  12794. if (
  12795. domUtils.isBlockElm(pN) &&
  12796. !dtd.$tableContent[pN.tagName] &&
  12797. !dtd.$list[pN.tagName]
  12798. ) {
  12799. domUtils.removeAttributes(pN, removeFormatAttributes)
  12800. }
  12801. pN = bookmark.end.parentNode
  12802. if (
  12803. bookmark.end &&
  12804. domUtils.isBlockElm(pN) &&
  12805. !dtd.$tableContent[pN.tagName] &&
  12806. !dtd.$list[pN.tagName]
  12807. ) {
  12808. domUtils.removeAttributes(pN, removeFormatAttributes)
  12809. }
  12810. range.moveToBookmark(bookmark).moveToBookmark(bookmark1)
  12811. //清除冗余的代码 <b><bookmark></b>
  12812. var node = range.startContainer,
  12813. tmp,
  12814. collapsed = range.collapsed
  12815. while (
  12816. node.nodeType == 1 &&
  12817. domUtils.isEmptyNode(node) &&
  12818. dtd.$removeEmpty[node.tagName]
  12819. ) {
  12820. tmp = node.parentNode
  12821. range.setStartBefore(node)
  12822. //trace:937
  12823. //更新结束边界
  12824. if (range.startContainer === range.endContainer) {
  12825. range.endOffset--
  12826. }
  12827. domUtils.remove(node)
  12828. node = tmp
  12829. }
  12830. if (!collapsed) {
  12831. node = range.endContainer
  12832. while (
  12833. node.nodeType == 1 &&
  12834. domUtils.isEmptyNode(node) &&
  12835. dtd.$removeEmpty[node.tagName]
  12836. ) {
  12837. tmp = node.parentNode
  12838. range.setEndBefore(node)
  12839. domUtils.remove(node)
  12840. node = tmp
  12841. }
  12842. }
  12843. }
  12844. range = this.selection.getRange()
  12845. doRemove(range)
  12846. range.select()
  12847. }
  12848. }
  12849. }
  12850. // plugins/blockquote.js
  12851. /**
  12852. * 添加引用
  12853. * @file
  12854. * @since 1.2.6.1
  12855. */
  12856. /**
  12857. * 添加引用
  12858. * @command blockquote
  12859. * @method execCommand
  12860. * @param { String } cmd 命令字符串
  12861. * @example
  12862. * ```javascript
  12863. * editor.execCommand( 'blockquote' );
  12864. * ```
  12865. */
  12866. /**
  12867. * 添加引用
  12868. * @command blockquote
  12869. * @method execCommand
  12870. * @param { String } cmd 命令字符串
  12871. * @param { Object } attrs 节点属性
  12872. * @example
  12873. * ```javascript
  12874. * editor.execCommand( 'blockquote',{
  12875. * style: "color: red;"
  12876. * } );
  12877. * ```
  12878. */
  12879. UE.plugins['blockquote'] = function () {
  12880. var me = this
  12881. function getObj(editor) {
  12882. return domUtils.filterNodeList(
  12883. editor.selection.getStartElementPath(),
  12884. 'blockquote'
  12885. )
  12886. }
  12887. me.commands['blockquote'] = {
  12888. execCommand: function (cmdName, attrs) {
  12889. var range = this.selection.getRange(),
  12890. obj = getObj(this),
  12891. blockquote = dtd.blockquote,
  12892. bookmark = range.createBookmark()
  12893. if (obj) {
  12894. var start = range.startContainer,
  12895. startBlock = domUtils.isBlockElm(start)
  12896. ? start
  12897. : domUtils.findParent(start, function (node) {
  12898. return domUtils.isBlockElm(node)
  12899. }),
  12900. end = range.endContainer,
  12901. endBlock = domUtils.isBlockElm(end)
  12902. ? end
  12903. : domUtils.findParent(end, function (node) {
  12904. return domUtils.isBlockElm(node)
  12905. })
  12906. //处理一下li
  12907. startBlock =
  12908. domUtils.findParentByTagName(startBlock, 'li', true) || startBlock
  12909. endBlock =
  12910. domUtils.findParentByTagName(endBlock, 'li', true) || endBlock
  12911. if (
  12912. startBlock.tagName == 'LI' ||
  12913. startBlock.tagName == 'TD' ||
  12914. startBlock === obj ||
  12915. domUtils.isBody(startBlock)
  12916. ) {
  12917. domUtils.remove(obj, true)
  12918. } else {
  12919. domUtils.breakParent(startBlock, obj)
  12920. }
  12921. if (startBlock !== endBlock) {
  12922. obj = domUtils.findParentByTagName(endBlock, 'blockquote')
  12923. if (obj) {
  12924. if (
  12925. endBlock.tagName == 'LI' ||
  12926. endBlock.tagName == 'TD' ||
  12927. domUtils.isBody(endBlock)
  12928. ) {
  12929. obj.parentNode && domUtils.remove(obj, true)
  12930. } else {
  12931. domUtils.breakParent(endBlock, obj)
  12932. }
  12933. }
  12934. }
  12935. var blockquotes = domUtils.getElementsByTagName(
  12936. this.document,
  12937. 'blockquote'
  12938. )
  12939. for (var i = 0, bi; (bi = blockquotes[i++]); ) {
  12940. if (!bi.childNodes.length) {
  12941. domUtils.remove(bi)
  12942. } else if (
  12943. domUtils.getPosition(bi, startBlock) &
  12944. domUtils.POSITION_FOLLOWING &&
  12945. domUtils.getPosition(bi, endBlock) & domUtils.POSITION_PRECEDING
  12946. ) {
  12947. domUtils.remove(bi, true)
  12948. }
  12949. }
  12950. } else {
  12951. var tmpRange = range.cloneRange(),
  12952. node =
  12953. tmpRange.startContainer.nodeType == 1
  12954. ? tmpRange.startContainer
  12955. : tmpRange.startContainer.parentNode,
  12956. preNode = node,
  12957. doEnd = 1
  12958. //调整开始
  12959. while (1) {
  12960. if (domUtils.isBody(node)) {
  12961. if (preNode !== node) {
  12962. if (range.collapsed) {
  12963. tmpRange.selectNode(preNode)
  12964. doEnd = 0
  12965. } else {
  12966. tmpRange.setStartBefore(preNode)
  12967. }
  12968. } else {
  12969. tmpRange.setStart(node, 0)
  12970. }
  12971. break
  12972. }
  12973. if (!blockquote[node.tagName]) {
  12974. if (range.collapsed) {
  12975. tmpRange.selectNode(preNode)
  12976. } else {
  12977. tmpRange.setStartBefore(preNode)
  12978. }
  12979. break
  12980. }
  12981. preNode = node
  12982. node = node.parentNode
  12983. }
  12984. //调整结束
  12985. if (doEnd) {
  12986. preNode =
  12987. node =
  12988. node =
  12989. tmpRange.endContainer.nodeType == 1
  12990. ? tmpRange.endContainer
  12991. : tmpRange.endContainer.parentNode
  12992. while (1) {
  12993. if (domUtils.isBody(node)) {
  12994. if (preNode !== node) {
  12995. tmpRange.setEndAfter(preNode)
  12996. } else {
  12997. tmpRange.setEnd(node, node.childNodes.length)
  12998. }
  12999. break
  13000. }
  13001. if (!blockquote[node.tagName]) {
  13002. tmpRange.setEndAfter(preNode)
  13003. break
  13004. }
  13005. preNode = node
  13006. node = node.parentNode
  13007. }
  13008. }
  13009. node = range.document.createElement('blockquote')
  13010. domUtils.setAttributes(node, attrs)
  13011. node.appendChild(tmpRange.extractContents())
  13012. tmpRange.insertNode(node)
  13013. //去除重复的
  13014. var childs = domUtils.getElementsByTagName(node, 'blockquote')
  13015. for (var i = 0, ci; (ci = childs[i++]); ) {
  13016. if (ci.parentNode) {
  13017. domUtils.remove(ci, true)
  13018. }
  13019. }
  13020. }
  13021. range.moveToBookmark(bookmark).select()
  13022. },
  13023. queryCommandState: function () {
  13024. return getObj(this) ? 1 : 0
  13025. }
  13026. }
  13027. }
  13028. // plugins/convertcase.js
  13029. /**
  13030. * 大小写转换
  13031. * @file
  13032. * @since 1.2.6.1
  13033. */
  13034. /**
  13035. * 把选区内文本变大写,与“tolowercase”命令互斥
  13036. * @command touppercase
  13037. * @method execCommand
  13038. * @param { String } cmd 命令字符串
  13039. * @example
  13040. * ```javascript
  13041. * editor.execCommand( 'touppercase' );
  13042. * ```
  13043. */
  13044. /**
  13045. * 把选区内文本变小写,与“touppercase”命令互斥
  13046. * @command tolowercase
  13047. * @method execCommand
  13048. * @param { String } cmd 命令字符串
  13049. * @example
  13050. * ```javascript
  13051. * editor.execCommand( 'tolowercase' );
  13052. * ```
  13053. */
  13054. UE.commands['touppercase'] = UE.commands['tolowercase'] = {
  13055. execCommand: function (cmd) {
  13056. var me = this
  13057. var rng = me.selection.getRange()
  13058. if (rng.collapsed) {
  13059. return rng
  13060. }
  13061. var bk = rng.createBookmark(),
  13062. bkEnd = bk.end,
  13063. filterFn = function (node) {
  13064. return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
  13065. },
  13066. curNode = domUtils.getNextDomNode(bk.start, false, filterFn)
  13067. while (
  13068. curNode &&
  13069. domUtils.getPosition(curNode, bkEnd) & domUtils.POSITION_PRECEDING
  13070. ) {
  13071. if (curNode.nodeType == 3) {
  13072. curNode.nodeValue =
  13073. curNode.nodeValue[
  13074. cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase'
  13075. ]()
  13076. }
  13077. curNode = domUtils.getNextDomNode(curNode, true, filterFn)
  13078. if (curNode === bkEnd) {
  13079. break
  13080. }
  13081. }
  13082. rng.moveToBookmark(bk).select()
  13083. }
  13084. }
  13085. // plugins/indent.js
  13086. /**
  13087. * 首行缩进
  13088. * @file
  13089. * @since 1.2.6.1
  13090. */
  13091. /**
  13092. * 缩进
  13093. * @command indent
  13094. * @method execCommand
  13095. * @param { String } cmd 命令字符串
  13096. * @example
  13097. * ```javascript
  13098. * editor.execCommand( 'indent' );
  13099. * ```
  13100. */
  13101. UE.commands['indent'] = {
  13102. execCommand: function () {
  13103. var me = this,
  13104. value = me.queryCommandState('indent')
  13105. ? '0em'
  13106. : me.options.indentValue || '2em'
  13107. me.execCommand('Paragraph', 'p', { style: 'text-indent:' + value })
  13108. },
  13109. queryCommandState: function () {
  13110. var pN = domUtils.filterNodeList(
  13111. this.selection.getStartElementPath(),
  13112. 'p h1 h2 h3 h4 h5 h6'
  13113. )
  13114. return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0
  13115. }
  13116. }
  13117. // plugins/print.js
  13118. /**
  13119. * 打印
  13120. * @file
  13121. * @since 1.2.6.1
  13122. */
  13123. /**
  13124. * 打印
  13125. * @command print
  13126. * @method execCommand
  13127. * @param { String } cmd 命令字符串
  13128. * @example
  13129. * ```javascript
  13130. * editor.execCommand( 'print' );
  13131. * ```
  13132. */
  13133. UE.commands['print'] = {
  13134. execCommand: function () {
  13135. this.window.print()
  13136. },
  13137. notNeedUndo: 1
  13138. }
  13139. // plugins/preview.js
  13140. /**
  13141. * 预览
  13142. * @file
  13143. * @since 1.2.6.1
  13144. */
  13145. /**
  13146. * 预览
  13147. * @command preview
  13148. * @method execCommand
  13149. * @param { String } cmd 命令字符串
  13150. * @example
  13151. * ```javascript
  13152. * editor.execCommand( 'preview' );
  13153. * ```
  13154. */
  13155. UE.commands['preview'] = {
  13156. execCommand: function () {
  13157. var w = window.open('', '_blank', ''),
  13158. d = w.document
  13159. d.open()
  13160. d.write(
  13161. '<!DOCTYPE html><html><head><meta charset="utf-8"/><script src="' +
  13162. this.options.UEDITOR_HOME_URL +
  13163. 'ueditor.parse.js"></script><script>' +
  13164. "setTimeout(function(){uParse('div',{rootPath: '" +
  13165. this.options.UEDITOR_HOME_URL +
  13166. "'})},300)" +
  13167. '</script></head><body><div>' +
  13168. this.getContent(null, null, true) +
  13169. '</div></body></html>'
  13170. )
  13171. d.close()
  13172. },
  13173. notNeedUndo: 1
  13174. }
  13175. // plugins/selectall.js
  13176. /**
  13177. * 全选
  13178. * @file
  13179. * @since 1.2.6.1
  13180. */
  13181. /**
  13182. * 选中所有内容
  13183. * @command selectall
  13184. * @method execCommand
  13185. * @param { String } cmd 命令字符串
  13186. * @example
  13187. * ```javascript
  13188. * editor.execCommand( 'selectall' );
  13189. * ```
  13190. */
  13191. UE.plugins['selectall'] = function () {
  13192. var me = this
  13193. me.commands['selectall'] = {
  13194. execCommand: function () {
  13195. //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
  13196. var me = this,
  13197. body = me.body,
  13198. range = me.selection.getRange()
  13199. range.selectNodeContents(body)
  13200. if (domUtils.isEmptyBlock(body)) {
  13201. //opera不能自动合并到元素的里边,要手动处理一下
  13202. if (
  13203. browser.opera &&
  13204. body.firstChild &&
  13205. body.firstChild.nodeType == 1
  13206. ) {
  13207. range.setStartAtFirst(body.firstChild)
  13208. }
  13209. range.collapse(true)
  13210. }
  13211. range.select(true)
  13212. },
  13213. notNeedUndo: 1
  13214. }
  13215. //快捷键
  13216. me.addshortcutkey({
  13217. selectAll: 'ctrl+65'
  13218. })
  13219. }
  13220. // plugins/paragraph.js
  13221. /**
  13222. * 段落样式
  13223. * @file
  13224. * @since 1.2.6.1
  13225. */
  13226. /**
  13227. * 段落格式
  13228. * @command paragraph
  13229. * @method execCommand
  13230. * @param { String } cmd 命令字符串
  13231. * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
  13232. * @param {Object} attrs 标签的属性
  13233. * @example
  13234. * ```javascript
  13235. * editor.execCommand( 'Paragraph','h1','{
  13236. * class:'test'
  13237. * }' );
  13238. * ```
  13239. */
  13240. /**
  13241. * 返回选区内节点标签名
  13242. * @command paragraph
  13243. * @method queryCommandValue
  13244. * @param { String } cmd 命令字符串
  13245. * @return { String } 节点标签名
  13246. * @example
  13247. * ```javascript
  13248. * editor.queryCommandValue( 'Paragraph' );
  13249. * ```
  13250. */
  13251. UE.plugins['paragraph'] = function () {
  13252. var me = this,
  13253. block = domUtils.isBlockElm,
  13254. notExchange = ['TD', 'LI', 'PRE'],
  13255. doParagraph = function (range, style, attrs, sourceCmdName) {
  13256. var bookmark = range.createBookmark(),
  13257. filterFn = function (node) {
  13258. return node.nodeType == 1
  13259. ? node.tagName.toLowerCase() != 'br' &&
  13260. !domUtils.isBookmarkNode(node)
  13261. : !domUtils.isWhitespace(node)
  13262. },
  13263. para
  13264. range.enlarge(true)
  13265. var bookmark2 = range.createBookmark(),
  13266. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  13267. tmpRange = range.cloneRange(),
  13268. tmpNode
  13269. while (
  13270. current &&
  13271. !(
  13272. domUtils.getPosition(current, bookmark2.end) &
  13273. domUtils.POSITION_FOLLOWING
  13274. )
  13275. ) {
  13276. if (current.nodeType == 3 || !block(current)) {
  13277. tmpRange.setStartBefore(current)
  13278. while (current && current !== bookmark2.end && !block(current)) {
  13279. tmpNode = current
  13280. current = domUtils.getNextDomNode(
  13281. current,
  13282. false,
  13283. null,
  13284. function (node) {
  13285. return !block(node)
  13286. }
  13287. )
  13288. }
  13289. tmpRange.setEndAfter(tmpNode)
  13290. para = range.document.createElement(style)
  13291. if (attrs) {
  13292. domUtils.setAttributes(para, attrs)
  13293. if (
  13294. sourceCmdName &&
  13295. sourceCmdName == 'customstyle' &&
  13296. attrs.style
  13297. ) {
  13298. para.style.cssText = attrs.style
  13299. }
  13300. }
  13301. para.appendChild(tmpRange.extractContents())
  13302. //需要内容占位
  13303. if (domUtils.isEmptyNode(para)) {
  13304. domUtils.fillChar(range.document, para)
  13305. }
  13306. tmpRange.insertNode(para)
  13307. var parent = para.parentNode
  13308. //如果para上一级是一个block元素且不是body,td就删除它
  13309. if (
  13310. block(parent) &&
  13311. !domUtils.isBody(para.parentNode) &&
  13312. utils.indexOf(notExchange, parent.tagName) == -1
  13313. ) {
  13314. //存储dir,style
  13315. if (!(sourceCmdName && sourceCmdName == 'customstyle')) {
  13316. parent.getAttribute('dir') &&
  13317. para.setAttribute('dir', parent.getAttribute('dir'))
  13318. //trace:1070
  13319. parent.style.cssText &&
  13320. (para.style.cssText =
  13321. parent.style.cssText + ';' + para.style.cssText)
  13322. //trace:1030
  13323. parent.style.textAlign &&
  13324. !para.style.textAlign &&
  13325. (para.style.textAlign = parent.style.textAlign)
  13326. parent.style.textIndent &&
  13327. !para.style.textIndent &&
  13328. (para.style.textIndent = parent.style.textIndent)
  13329. parent.style.padding &&
  13330. !para.style.padding &&
  13331. (para.style.padding = parent.style.padding)
  13332. }
  13333. //trace:1706 选择的就是h1-6要删除
  13334. if (
  13335. attrs &&
  13336. /h\d/i.test(parent.tagName) &&
  13337. !/h\d/i.test(para.tagName)
  13338. ) {
  13339. domUtils.setAttributes(parent, attrs)
  13340. if (
  13341. sourceCmdName &&
  13342. sourceCmdName == 'customstyle' &&
  13343. attrs.style
  13344. ) {
  13345. parent.style.cssText = attrs.style
  13346. }
  13347. domUtils.remove(para, true)
  13348. para = parent
  13349. } else {
  13350. domUtils.remove(para.parentNode, true)
  13351. }
  13352. }
  13353. if (utils.indexOf(notExchange, parent.tagName) != -1) {
  13354. current = parent
  13355. } else {
  13356. current = para
  13357. }
  13358. current = domUtils.getNextDomNode(current, false, filterFn)
  13359. } else {
  13360. current = domUtils.getNextDomNode(current, true, filterFn)
  13361. }
  13362. }
  13363. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
  13364. }
  13365. me.setOpt('paragraph', {
  13366. p: '',
  13367. h1: '',
  13368. h2: '',
  13369. h3: '',
  13370. h4: '',
  13371. h5: '',
  13372. h6: ''
  13373. })
  13374. me.commands['paragraph'] = {
  13375. execCommand: function (cmdName, style, attrs, sourceCmdName) {
  13376. var range = this.selection.getRange()
  13377. //闭合时单独处理
  13378. if (range.collapsed) {
  13379. var txt = this.document.createTextNode('p')
  13380. range.insertNode(txt)
  13381. //去掉冗余的fillchar
  13382. if (browser.ie) {
  13383. var node = txt.previousSibling
  13384. if (node && domUtils.isWhitespace(node)) {
  13385. domUtils.remove(node)
  13386. }
  13387. node = txt.nextSibling
  13388. if (node && domUtils.isWhitespace(node)) {
  13389. domUtils.remove(node)
  13390. }
  13391. }
  13392. }
  13393. range = doParagraph(range, style, attrs, sourceCmdName)
  13394. if (txt) {
  13395. range.setStartBefore(txt).collapse(true)
  13396. pN = txt.parentNode
  13397. domUtils.remove(txt)
  13398. if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
  13399. domUtils.fillNode(this.document, pN)
  13400. }
  13401. }
  13402. if (
  13403. browser.gecko &&
  13404. range.collapsed &&
  13405. range.startContainer.nodeType == 1
  13406. ) {
  13407. var child = range.startContainer.childNodes[range.startOffset]
  13408. if (
  13409. child &&
  13410. child.nodeType == 1 &&
  13411. child.tagName.toLowerCase() == style
  13412. ) {
  13413. range.setStart(child, 0).collapse(true)
  13414. }
  13415. }
  13416. //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
  13417. range.select()
  13418. return true
  13419. },
  13420. queryCommandValue: function () {
  13421. var node = domUtils.filterNodeList(
  13422. this.selection.getStartElementPath(),
  13423. 'p h1 h2 h3 h4 h5 h6'
  13424. )
  13425. return node ? node.tagName.toLowerCase() : ''
  13426. }
  13427. }
  13428. }
  13429. // plugins/directionality.js
  13430. /**
  13431. * 设置文字输入的方向的插件
  13432. * @file
  13433. * @since 1.2.6.1
  13434. */
  13435. ;(function () {
  13436. var block = domUtils.isBlockElm,
  13437. getObj = function (editor) {
  13438. // var startNode = editor.selection.getStart(),
  13439. // parents;
  13440. // if ( startNode ) {
  13441. // //查找所有的是block的父亲节点
  13442. // parents = domUtils.findParents( startNode, true, block, true );
  13443. // for ( var i = 0,ci; ci = parents[i++]; ) {
  13444. // if ( ci.getAttribute( 'dir' ) ) {
  13445. // return ci;
  13446. // }
  13447. // }
  13448. // }
  13449. return domUtils.filterNodeList(
  13450. editor.selection.getStartElementPath(),
  13451. function (n) {
  13452. return n && n.nodeType == 1 && n.getAttribute('dir')
  13453. }
  13454. )
  13455. },
  13456. doDirectionality = function (range, editor, forward) {
  13457. var bookmark,
  13458. filterFn = function (node) {
  13459. return node.nodeType == 1
  13460. ? !domUtils.isBookmarkNode(node)
  13461. : !domUtils.isWhitespace(node)
  13462. },
  13463. obj = getObj(editor)
  13464. if (obj && range.collapsed) {
  13465. obj.setAttribute('dir', forward)
  13466. return range
  13467. }
  13468. bookmark = range.createBookmark()
  13469. range.enlarge(true)
  13470. var bookmark2 = range.createBookmark(),
  13471. current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
  13472. tmpRange = range.cloneRange(),
  13473. tmpNode
  13474. while (
  13475. current &&
  13476. !(
  13477. domUtils.getPosition(current, bookmark2.end) &
  13478. domUtils.POSITION_FOLLOWING
  13479. )
  13480. ) {
  13481. if (current.nodeType == 3 || !block(current)) {
  13482. tmpRange.setStartBefore(current)
  13483. while (current && current !== bookmark2.end && !block(current)) {
  13484. tmpNode = current
  13485. current = domUtils.getNextDomNode(
  13486. current,
  13487. false,
  13488. null,
  13489. function (node) {
  13490. return !block(node)
  13491. }
  13492. )
  13493. }
  13494. tmpRange.setEndAfter(tmpNode)
  13495. var common = tmpRange.getCommonAncestor()
  13496. if (!domUtils.isBody(common) && block(common)) {
  13497. //遍历到了block节点
  13498. common.setAttribute('dir', forward)
  13499. current = common
  13500. } else {
  13501. //没有遍历到,添加一个block节点
  13502. var p = range.document.createElement('p')
  13503. p.setAttribute('dir', forward)
  13504. var frag = tmpRange.extractContents()
  13505. p.appendChild(frag)
  13506. tmpRange.insertNode(p)
  13507. current = p
  13508. }
  13509. current = domUtils.getNextDomNode(current, false, filterFn)
  13510. } else {
  13511. current = domUtils.getNextDomNode(current, true, filterFn)
  13512. }
  13513. }
  13514. return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
  13515. }
  13516. /**
  13517. * 文字输入方向
  13518. * @command directionality
  13519. * @method execCommand
  13520. * @param { String } cmdName 命令字符串
  13521. * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
  13522. * @example
  13523. * ```javascript
  13524. * editor.execCommand( 'directionality', 'ltr');
  13525. * ```
  13526. */
  13527. /**
  13528. * 查询当前选区的文字输入方向
  13529. * @command directionality
  13530. * @method queryCommandValue
  13531. * @param { String } cmdName 命令字符串
  13532. * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
  13533. * @example
  13534. * ```javascript
  13535. * editor.queryCommandValue( 'directionality');
  13536. * ```
  13537. */
  13538. UE.commands['directionality'] = {
  13539. execCommand: function (cmdName, forward) {
  13540. var range = this.selection.getRange()
  13541. //闭合时单独处理
  13542. if (range.collapsed) {
  13543. var txt = this.document.createTextNode('d')
  13544. range.insertNode(txt)
  13545. }
  13546. doDirectionality(range, this, forward)
  13547. if (txt) {
  13548. range.setStartBefore(txt).collapse(true)
  13549. domUtils.remove(txt)
  13550. }
  13551. range.select()
  13552. return true
  13553. },
  13554. queryCommandValue: function () {
  13555. var node = getObj(this)
  13556. return node ? node.getAttribute('dir') : 'ltr'
  13557. }
  13558. }
  13559. })()
  13560. // plugins/horizontal.js
  13561. /**
  13562. * 插入分割线插件
  13563. * @file
  13564. * @since 1.2.6.1
  13565. */
  13566. /**
  13567. * 插入分割线
  13568. * @command horizontal
  13569. * @method execCommand
  13570. * @param { String } cmdName 命令字符串
  13571. * @example
  13572. * ```javascript
  13573. * editor.execCommand( 'horizontal' );
  13574. * ```
  13575. */
  13576. UE.plugins['horizontal'] = function () {
  13577. var me = this
  13578. me.commands['horizontal'] = {
  13579. execCommand: function (cmdName) {
  13580. var me = this
  13581. if (me.queryCommandState(cmdName) !== -1) {
  13582. me.execCommand('insertHtml', '<hr>')
  13583. var range = me.selection.getRange(),
  13584. start = range.startContainer
  13585. if (start.nodeType == 1 && !start.childNodes[range.startOffset]) {
  13586. var tmp
  13587. if ((tmp = start.childNodes[range.startOffset - 1])) {
  13588. if (tmp.nodeType == 1 && tmp.tagName == 'HR') {
  13589. if (me.options.enterTag == 'p') {
  13590. tmp = me.document.createElement('p')
  13591. range.insertNode(tmp)
  13592. range.setStart(tmp, 0).setCursor()
  13593. } else {
  13594. tmp = me.document.createElement('br')
  13595. range.insertNode(tmp)
  13596. range.setStartBefore(tmp).setCursor()
  13597. }
  13598. }
  13599. }
  13600. }
  13601. return true
  13602. }
  13603. },
  13604. //边界在table里不能加分隔线
  13605. queryCommandState: function () {
  13606. return domUtils.filterNodeList(
  13607. this.selection.getStartElementPath(),
  13608. 'table'
  13609. )
  13610. ? -1
  13611. : 0
  13612. }
  13613. }
  13614. // me.addListener('delkeyup',function(){
  13615. // var rng = this.selection.getRange();
  13616. // if(browser.ie && browser.version > 8){
  13617. // rng.txtToElmBoundary(true);
  13618. // if(domUtils.isStartInblock(rng)){
  13619. // var tmpNode = rng.startContainer;
  13620. // var pre = tmpNode.previousSibling;
  13621. // if(pre && domUtils.isTagNode(pre,'hr')){
  13622. // domUtils.remove(pre);
  13623. // rng.select();
  13624. // return;
  13625. // }
  13626. // }
  13627. // }
  13628. // if(domUtils.isBody(rng.startContainer)){
  13629. // var hr = rng.startContainer.childNodes[rng.startOffset -1];
  13630. // if(hr && hr.nodeName == 'HR'){
  13631. // var next = hr.nextSibling;
  13632. // if(next){
  13633. // rng.setStart(next,0)
  13634. // }else if(hr.previousSibling){
  13635. // rng.setStartAtLast(hr.previousSibling)
  13636. // }else{
  13637. // var p = this.document.createElement('p');
  13638. // hr.parentNode.insertBefore(p,hr);
  13639. // domUtils.fillNode(this.document,p);
  13640. // rng.setStart(p,0);
  13641. // }
  13642. // domUtils.remove(hr);
  13643. // rng.setCursor(false,true);
  13644. // }
  13645. // }
  13646. // })
  13647. me.addListener('delkeydown', function (name, evt) {
  13648. var rng = this.selection.getRange()
  13649. rng.txtToElmBoundary(true)
  13650. if (domUtils.isStartInblock(rng)) {
  13651. var tmpNode = rng.startContainer
  13652. var pre = tmpNode.previousSibling
  13653. if (pre && domUtils.isTagNode(pre, 'hr')) {
  13654. domUtils.remove(pre)
  13655. rng.select()
  13656. domUtils.preventDefault(evt)
  13657. return true
  13658. }
  13659. }
  13660. })
  13661. }
  13662. // plugins/time.js
  13663. /**
  13664. * 插入时间和日期
  13665. * @file
  13666. * @since 1.2.6.1
  13667. */
  13668. /**
  13669. * 插入时间,默认格式:12:59:59
  13670. * @command time
  13671. * @method execCommand
  13672. * @param { String } cmd 命令字符串
  13673. * @example
  13674. * ```javascript
  13675. * editor.execCommand( 'time');
  13676. * ```
  13677. */
  13678. /**
  13679. * 插入日期,默认格式:2013-08-30
  13680. * @command date
  13681. * @method execCommand
  13682. * @param { String } cmd 命令字符串
  13683. * @example
  13684. * ```javascript
  13685. * editor.execCommand( 'date');
  13686. * ```
  13687. */
  13688. UE.commands['time'] = UE.commands['date'] = {
  13689. execCommand: function (cmd, format) {
  13690. var date = new Date()
  13691. function formatTime(date, format) {
  13692. var hh = ('0' + date.getHours()).slice(-2),
  13693. ii = ('0' + date.getMinutes()).slice(-2),
  13694. ss = ('0' + date.getSeconds()).slice(-2)
  13695. format = format || 'hh:ii:ss'
  13696. return format
  13697. .replace(/hh/gi, hh)
  13698. .replace(/ii/gi, ii)
  13699. .replace(/ss/gi, ss)
  13700. }
  13701. function formatDate(date, format) {
  13702. var yyyy = ('000' + date.getFullYear()).slice(-4),
  13703. yy = yyyy.slice(-2),
  13704. mm = ('0' + (date.getMonth() + 1)).slice(-2),
  13705. dd = ('0' + date.getDate()).slice(-2)
  13706. format = format || 'yyyy-mm-dd'
  13707. return format
  13708. .replace(/yyyy/gi, yyyy)
  13709. .replace(/yy/gi, yy)
  13710. .replace(/mm/gi, mm)
  13711. .replace(/dd/gi, dd)
  13712. }
  13713. this.execCommand(
  13714. 'insertHtml',
  13715. cmd == 'time' ? formatTime(date, format) : formatDate(date, format)
  13716. )
  13717. }
  13718. }
  13719. // plugins/rowspacing.js
  13720. /**
  13721. * 段前段后间距插件
  13722. * @file
  13723. * @since 1.2.6.1
  13724. */
  13725. /**
  13726. * 设置段间距
  13727. * @command rowspacing
  13728. * @method execCommand
  13729. * @param { String } cmd 命令字符串
  13730. * @param { String } value 段间距的值,以px为单位
  13731. * @param { String } dir 间距位置,top或bottom,分别表示段前和段后
  13732. * @example
  13733. * ```javascript
  13734. * editor.execCommand( 'rowspacing', '10', 'top' );
  13735. * ```
  13736. */
  13737. UE.plugins['rowspacing'] = function () {
  13738. var me = this
  13739. me.setOpt({
  13740. rowspacingtop: ['5', '10', '15', '20', '25'],
  13741. rowspacingbottom: ['5', '10', '15', '20', '25']
  13742. })
  13743. me.commands['rowspacing'] = {
  13744. execCommand: function (cmdName, value, dir) {
  13745. this.execCommand('paragraph', 'p', {
  13746. style: 'margin-' + dir + ':' + value + 'px'
  13747. })
  13748. return true
  13749. },
  13750. queryCommandValue: function (cmdName, dir) {
  13751. var pN = domUtils.filterNodeList(
  13752. this.selection.getStartElementPath(),
  13753. function (node) {
  13754. return domUtils.isBlockElm(node)
  13755. }
  13756. ),
  13757. value
  13758. //trace:1026
  13759. if (pN) {
  13760. value = domUtils
  13761. .getComputedStyle(pN, 'margin-' + dir)
  13762. .replace(/[^\d]/g, '')
  13763. return !value ? 0 : value
  13764. }
  13765. return 0
  13766. }
  13767. }
  13768. }
  13769. // plugins/lineheight.js
  13770. /**
  13771. * 设置行内间距
  13772. * @file
  13773. * @since 1.2.6.1
  13774. */
  13775. UE.plugins['lineheight'] = function () {
  13776. var me = this
  13777. me.setOpt({ lineheight: ['1', '1.5', '1.75', '2', '3', '4', '5'] })
  13778. /**
  13779. * 行距
  13780. * @command lineheight
  13781. * @method execCommand
  13782. * @param { String } cmdName 命令字符串
  13783. * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75
  13784. * @example
  13785. * ```javascript
  13786. * editor.execCommand( 'lineheight', 1.5);
  13787. * ```
  13788. */
  13789. /**
  13790. * 查询当前选区内容的行高大小
  13791. * @command lineheight
  13792. * @method queryCommandValue
  13793. * @param { String } cmd 命令字符串
  13794. * @return { String } 返回当前行高大小
  13795. * @example
  13796. * ```javascript
  13797. * editor.queryCommandValue( 'lineheight' );
  13798. * ```
  13799. */
  13800. me.commands['lineheight'] = {
  13801. execCommand: function (cmdName, value) {
  13802. this.execCommand('paragraph', 'p', {
  13803. style: 'line-height:' + (value == '1' ? 'normal' : value + 'em')
  13804. })
  13805. return true
  13806. },
  13807. queryCommandValue: function () {
  13808. var pN = domUtils.filterNodeList(
  13809. this.selection.getStartElementPath(),
  13810. function (node) {
  13811. return domUtils.isBlockElm(node)
  13812. }
  13813. )
  13814. if (pN) {
  13815. var value = domUtils.getComputedStyle(pN, 'line-height')
  13816. return value == 'normal' ? 1 : value.replace(/[^\d.]*/gi, '')
  13817. }
  13818. }
  13819. }
  13820. }
  13821. // plugins/insertcode.js
  13822. /**
  13823. * 插入代码插件
  13824. * @file
  13825. * @since 1.2.6.1
  13826. */
  13827. UE.plugins['insertcode'] = function () {
  13828. var me = this
  13829. me.ready(function () {
  13830. utils.cssRule(
  13831. 'pre',
  13832. 'pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}',
  13833. me.document
  13834. )
  13835. })
  13836. me.setOpt('insertcode', {
  13837. as3: 'ActionScript3',
  13838. bash: 'Bash/Shell',
  13839. cpp: 'C/C++',
  13840. css: 'Css',
  13841. cf: 'CodeFunction',
  13842. 'c#': 'C#',
  13843. delphi: 'Delphi',
  13844. diff: 'Diff',
  13845. erlang: 'Erlang',
  13846. groovy: 'Groovy',
  13847. html: 'Html',
  13848. java: 'Java',
  13849. jfx: 'JavaFx',
  13850. js: 'Javascript',
  13851. pl: 'Perl',
  13852. php: 'Php',
  13853. plain: 'Plain Text',
  13854. ps: 'PowerShell',
  13855. python: 'Python',
  13856. ruby: 'Ruby',
  13857. scala: 'Scala',
  13858. sql: 'Sql',
  13859. vb: 'Vb',
  13860. xml: 'Xml'
  13861. })
  13862. /**
  13863. * 插入代码
  13864. * @command insertcode
  13865. * @method execCommand
  13866. * @param { String } cmd 命令字符串
  13867. * @param { String } lang 插入代码的语言
  13868. * @example
  13869. * ```javascript
  13870. * editor.execCommand( 'insertcode', 'javascript' );
  13871. * ```
  13872. */
  13873. /**
  13874. * 如果选区所在位置是插入插入代码区域,返回代码的语言
  13875. * @command insertcode
  13876. * @method queryCommandValue
  13877. * @param { String } cmd 命令字符串
  13878. * @return { String } 返回代码的语言
  13879. * @example
  13880. * ```javascript
  13881. * editor.queryCommandValue( 'insertcode' );
  13882. * ```
  13883. */
  13884. me.commands['insertcode'] = {
  13885. execCommand: function (cmd, lang) {
  13886. var me = this,
  13887. rng = me.selection.getRange(),
  13888. pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
  13889. if (pre) {
  13890. pre.className = 'brush:' + lang + ';toolbar:false;'
  13891. } else {
  13892. var code = ''
  13893. if (rng.collapsed) {
  13894. code =
  13895. browser.ie && browser.ie11below
  13896. ? browser.version <= 8
  13897. ? '&nbsp;'
  13898. : ''
  13899. : '<br/>'
  13900. } else {
  13901. var frag = rng.extractContents()
  13902. var div = me.document.createElement('div')
  13903. div.appendChild(frag)
  13904. utils.each(
  13905. UE.filterNode(
  13906. UE.htmlparser(div.innerHTML.replace(/[\r\t]/g, '')),
  13907. me.options.filterTxtRules
  13908. ).children,
  13909. function (node) {
  13910. if (browser.ie && browser.ie11below && browser.version > 8) {
  13911. if (node.type == 'element') {
  13912. if (node.tagName == 'br') {
  13913. code += '\n'
  13914. } else if (!dtd.$empty[node.tagName]) {
  13915. utils.each(node.children, function (cn) {
  13916. if (cn.type == 'element') {
  13917. if (cn.tagName == 'br') {
  13918. code += '\n'
  13919. } else if (!dtd.$empty[node.tagName]) {
  13920. code += cn.innerText()
  13921. }
  13922. } else {
  13923. code += cn.data
  13924. }
  13925. })
  13926. if (!/\n$/.test(code)) {
  13927. code += '\n'
  13928. }
  13929. }
  13930. } else {
  13931. code += node.data + '\n'
  13932. }
  13933. if (!node.nextSibling() && /\n$/.test(code)) {
  13934. code = code.replace(/\n$/, '')
  13935. }
  13936. } else {
  13937. if (browser.ie && browser.ie11below) {
  13938. if (node.type == 'element') {
  13939. if (node.tagName == 'br') {
  13940. code += '<br>'
  13941. } else if (!dtd.$empty[node.tagName]) {
  13942. utils.each(node.children, function (cn) {
  13943. if (cn.type == 'element') {
  13944. if (cn.tagName == 'br') {
  13945. code += '<br>'
  13946. } else if (!dtd.$empty[node.tagName]) {
  13947. code += cn.innerText()
  13948. }
  13949. } else {
  13950. code += cn.data
  13951. }
  13952. })
  13953. if (!/br>$/.test(code)) {
  13954. code += '<br>'
  13955. }
  13956. }
  13957. } else {
  13958. code += node.data + '<br>'
  13959. }
  13960. if (!node.nextSibling() && /<br>$/.test(code)) {
  13961. code = code.replace(/<br>$/, '')
  13962. }
  13963. } else {
  13964. code +=
  13965. node.type == 'element'
  13966. ? dtd.$empty[node.tagName]
  13967. ? ''
  13968. : node.innerText()
  13969. : node.data
  13970. if (!/br\/?\s*>$/.test(code)) {
  13971. if (!node.nextSibling()) return
  13972. code += '<br>'
  13973. }
  13974. }
  13975. }
  13976. }
  13977. )
  13978. }
  13979. me.execCommand(
  13980. 'inserthtml',
  13981. '<pre id="coder"class="brush:' +
  13982. lang +
  13983. ';toolbar:false">' +
  13984. code +
  13985. '</pre>',
  13986. true
  13987. )
  13988. pre = me.document.getElementById('coder')
  13989. domUtils.removeAttributes(pre, 'id')
  13990. var tmpNode = pre.previousSibling
  13991. if (
  13992. tmpNode &&
  13993. ((tmpNode.nodeType == 3 &&
  13994. tmpNode.nodeValue.length == 1 &&
  13995. browser.ie &&
  13996. browser.version == 6) ||
  13997. domUtils.isEmptyBlock(tmpNode))
  13998. ) {
  13999. domUtils.remove(tmpNode)
  14000. }
  14001. var rng = me.selection.getRange()
  14002. if (domUtils.isEmptyBlock(pre)) {
  14003. rng.setStart(pre, 0).setCursor(false, true)
  14004. } else {
  14005. rng.selectNodeContents(pre).select()
  14006. }
  14007. }
  14008. },
  14009. queryCommandValue: function () {
  14010. var path = this.selection.getStartElementPath()
  14011. var lang = ''
  14012. utils.each(path, function (node) {
  14013. if (node.nodeName == 'PRE') {
  14014. var match = node.className.match(/brush:([^;]+)/)
  14015. lang = match && match[1] ? match[1] : ''
  14016. return false
  14017. }
  14018. })
  14019. return lang
  14020. }
  14021. }
  14022. me.addInputRule(function (root) {
  14023. utils.each(root.getNodesByTagName('pre'), function (pre) {
  14024. var brs = pre.getNodesByTagName('br')
  14025. if (brs.length) {
  14026. browser.ie &&
  14027. browser.ie11below &&
  14028. browser.version > 8 &&
  14029. utils.each(brs, function (br) {
  14030. var txt = UE.uNode.createText('\n')
  14031. br.parentNode.insertBefore(txt, br)
  14032. br.parentNode.removeChild(br)
  14033. })
  14034. return
  14035. }
  14036. if (browser.ie && browser.ie11below && browser.version > 8) return
  14037. var code = pre.innerText().split(/\n/)
  14038. pre.innerHTML('')
  14039. utils.each(code, function (c) {
  14040. if (c.length) {
  14041. pre.appendChild(UE.uNode.createText(c))
  14042. }
  14043. pre.appendChild(UE.uNode.createElement('br'))
  14044. })
  14045. })
  14046. })
  14047. me.addOutputRule(function (root) {
  14048. utils.each(root.getNodesByTagName('pre'), function (pre) {
  14049. var code = ''
  14050. utils.each(pre.children, function (n) {
  14051. if (n.type == 'text') {
  14052. //在ie下文本内容有可能末尾带有\n要去掉
  14053. //trace:3396
  14054. code += n.data.replace(/[ ]/g, '&nbsp;').replace(/\n$/, '')
  14055. } else {
  14056. if (n.tagName == 'br') {
  14057. code += '\n'
  14058. } else {
  14059. code += !dtd.$empty[n.tagName] ? '' : n.innerText()
  14060. }
  14061. }
  14062. })
  14063. pre.innerText(code.replace(/(&nbsp;|\n)+$/, ''))
  14064. })
  14065. })
  14066. //不需要判断highlight的command列表
  14067. me.notNeedCodeQuery = {
  14068. help: 1,
  14069. undo: 1,
  14070. redo: 1,
  14071. source: 1,
  14072. print: 1,
  14073. searchreplace: 1,
  14074. fullscreen: 1,
  14075. preview: 1,
  14076. insertparagraph: 1,
  14077. elementpath: 1,
  14078. insertcode: 1,
  14079. inserthtml: 1,
  14080. selectall: 1
  14081. }
  14082. //将queyCommamndState重置
  14083. var orgQuery = me.queryCommandState
  14084. me.queryCommandState = function (cmd) {
  14085. var me = this
  14086. if (
  14087. !me.notNeedCodeQuery[cmd.toLowerCase()] &&
  14088. me.selection &&
  14089. me.queryCommandValue('insertcode')
  14090. ) {
  14091. return -1
  14092. }
  14093. return UE.Editor.prototype.queryCommandState.apply(this, arguments)
  14094. }
  14095. me.addListener('beforeenterkeydown', function () {
  14096. var rng = me.selection.getRange()
  14097. var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
  14098. if (pre) {
  14099. me.fireEvent('saveScene')
  14100. if (!rng.collapsed) {
  14101. rng.deleteContents()
  14102. }
  14103. if (!browser.ie || browser.ie9above) {
  14104. var tmpNode = me.document.createElement('br'),
  14105. pre
  14106. rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true)
  14107. var next = tmpNode.nextSibling
  14108. if (!next && (!browser.ie || browser.version > 10)) {
  14109. rng.insertNode(tmpNode.cloneNode(false))
  14110. } else {
  14111. rng.setStartAfter(tmpNode)
  14112. }
  14113. pre = tmpNode.previousSibling
  14114. var tmp
  14115. while (pre) {
  14116. tmp = pre
  14117. pre = pre.previousSibling
  14118. if (!pre || pre.nodeName == 'BR') {
  14119. pre = tmp
  14120. break
  14121. }
  14122. }
  14123. if (pre) {
  14124. var str = ''
  14125. while (
  14126. pre &&
  14127. pre.nodeName != 'BR' &&
  14128. new RegExp('^[\\s' + domUtils.fillChar + ']*$').test(
  14129. pre.nodeValue
  14130. )
  14131. ) {
  14132. str += pre.nodeValue
  14133. pre = pre.nextSibling
  14134. }
  14135. if (pre.nodeName != 'BR') {
  14136. var match = pre.nodeValue.match(
  14137. new RegExp('^([\\s' + domUtils.fillChar + ']+)')
  14138. )
  14139. if (match && match[1]) {
  14140. str += match[1]
  14141. }
  14142. }
  14143. if (str) {
  14144. str = me.document.createTextNode(str)
  14145. rng.insertNode(str).setStartAfter(str)
  14146. }
  14147. }
  14148. rng.collapse(true).select(true)
  14149. } else {
  14150. if (browser.version > 8) {
  14151. var txt = me.document.createTextNode('\n')
  14152. var start = rng.startContainer
  14153. if (rng.startOffset == 0) {
  14154. var preNode = start.previousSibling
  14155. if (preNode) {
  14156. rng.insertNode(txt)
  14157. var fillchar = me.document.createTextNode(' ')
  14158. rng
  14159. .setStartAfter(txt)
  14160. .insertNode(fillchar)
  14161. .setStart(fillchar, 0)
  14162. .collapse(true)
  14163. .select(true)
  14164. }
  14165. } else {
  14166. rng.insertNode(txt).setStartAfter(txt)
  14167. var fillchar = me.document.createTextNode(' ')
  14168. start = rng.startContainer.childNodes[rng.startOffset]
  14169. if (start && !/^\n/.test(start.nodeValue)) {
  14170. rng.setStartBefore(txt)
  14171. }
  14172. rng
  14173. .insertNode(fillchar)
  14174. .setStart(fillchar, 0)
  14175. .collapse(true)
  14176. .select(true)
  14177. }
  14178. } else {
  14179. var tmpNode = me.document.createElement('br')
  14180. rng.insertNode(tmpNode)
  14181. rng.insertNode(me.document.createTextNode(domUtils.fillChar))
  14182. rng.setStartAfter(tmpNode)
  14183. pre = tmpNode.previousSibling
  14184. var tmp
  14185. while (pre) {
  14186. tmp = pre
  14187. pre = pre.previousSibling
  14188. if (!pre || pre.nodeName == 'BR') {
  14189. pre = tmp
  14190. break
  14191. }
  14192. }
  14193. if (pre) {
  14194. var str = ''
  14195. while (
  14196. pre &&
  14197. pre.nodeName != 'BR' &&
  14198. new RegExp('^[ ' + domUtils.fillChar + ']*$').test(
  14199. pre.nodeValue
  14200. )
  14201. ) {
  14202. str += pre.nodeValue
  14203. pre = pre.nextSibling
  14204. }
  14205. if (pre.nodeName != 'BR') {
  14206. var match = pre.nodeValue.match(
  14207. new RegExp('^([ ' + domUtils.fillChar + ']+)')
  14208. )
  14209. if (match && match[1]) {
  14210. str += match[1]
  14211. }
  14212. }
  14213. str = me.document.createTextNode(str)
  14214. rng.insertNode(str).setStartAfter(str)
  14215. }
  14216. rng.collapse(true).select()
  14217. }
  14218. }
  14219. me.fireEvent('saveScene')
  14220. return true
  14221. }
  14222. })
  14223. me.addListener('tabkeydown', function (cmd, evt) {
  14224. var rng = me.selection.getRange()
  14225. var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
  14226. if (pre) {
  14227. me.fireEvent('saveScene')
  14228. if (evt.shiftKey) {
  14229. } else {
  14230. if (!rng.collapsed) {
  14231. var bk = rng.createBookmark()
  14232. var start = bk.start.previousSibling
  14233. while (start) {
  14234. if (pre.firstChild === start && !domUtils.isBr(start)) {
  14235. pre.insertBefore(me.document.createTextNode(' '), start)
  14236. break
  14237. }
  14238. if (domUtils.isBr(start)) {
  14239. pre.insertBefore(
  14240. me.document.createTextNode(' '),
  14241. start.nextSibling
  14242. )
  14243. break
  14244. }
  14245. start = start.previousSibling
  14246. }
  14247. var end = bk.end
  14248. start = bk.start.nextSibling
  14249. if (pre.firstChild === bk.start) {
  14250. pre.insertBefore(
  14251. me.document.createTextNode(' '),
  14252. start.nextSibling
  14253. )
  14254. }
  14255. while (start && start !== end) {
  14256. if (domUtils.isBr(start) && start.nextSibling) {
  14257. if (start.nextSibling === end) {
  14258. break
  14259. }
  14260. pre.insertBefore(
  14261. me.document.createTextNode(' '),
  14262. start.nextSibling
  14263. )
  14264. }
  14265. start = start.nextSibling
  14266. }
  14267. rng.moveToBookmark(bk).select()
  14268. } else {
  14269. var tmpNode = me.document.createTextNode(' ')
  14270. rng
  14271. .insertNode(tmpNode)
  14272. .setStartAfter(tmpNode)
  14273. .collapse(true)
  14274. .select(true)
  14275. }
  14276. }
  14277. me.fireEvent('saveScene')
  14278. return true
  14279. }
  14280. })
  14281. me.addListener('beforeinserthtml', function (evtName, html) {
  14282. var me = this,
  14283. rng = me.selection.getRange(),
  14284. pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
  14285. if (pre) {
  14286. if (!rng.collapsed) {
  14287. rng.deleteContents()
  14288. }
  14289. var htmlstr = ''
  14290. if (browser.ie && browser.version > 8) {
  14291. utils.each(
  14292. UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules)
  14293. .children,
  14294. function (node) {
  14295. if (node.type == 'element') {
  14296. if (node.tagName == 'br') {
  14297. htmlstr += '\n'
  14298. } else if (!dtd.$empty[node.tagName]) {
  14299. utils.each(node.children, function (cn) {
  14300. if (cn.type == 'element') {
  14301. if (cn.tagName == 'br') {
  14302. htmlstr += '\n'
  14303. } else if (!dtd.$empty[node.tagName]) {
  14304. htmlstr += cn.innerText()
  14305. }
  14306. } else {
  14307. htmlstr += cn.data
  14308. }
  14309. })
  14310. if (!/\n$/.test(htmlstr)) {
  14311. htmlstr += '\n'
  14312. }
  14313. }
  14314. } else {
  14315. htmlstr += node.data + '\n'
  14316. }
  14317. if (!node.nextSibling() && /\n$/.test(htmlstr)) {
  14318. htmlstr = htmlstr.replace(/\n$/, '')
  14319. }
  14320. }
  14321. )
  14322. var tmpNode = me.document.createTextNode(
  14323. utils.html(htmlstr.replace(/&nbsp;/g, ' '))
  14324. )
  14325. rng.insertNode(tmpNode).selectNode(tmpNode).select()
  14326. } else {
  14327. var frag = me.document.createDocumentFragment()
  14328. utils.each(
  14329. UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules)
  14330. .children,
  14331. function (node) {
  14332. if (node.type == 'element') {
  14333. if (node.tagName == 'br') {
  14334. frag.appendChild(me.document.createElement('br'))
  14335. } else if (!dtd.$empty[node.tagName]) {
  14336. utils.each(node.children, function (cn) {
  14337. if (cn.type == 'element') {
  14338. if (cn.tagName == 'br') {
  14339. frag.appendChild(me.document.createElement('br'))
  14340. } else if (!dtd.$empty[node.tagName]) {
  14341. frag.appendChild(
  14342. me.document.createTextNode(
  14343. utils.html(cn.innerText().replace(/&nbsp;/g, ' '))
  14344. )
  14345. )
  14346. }
  14347. } else {
  14348. frag.appendChild(
  14349. me.document.createTextNode(
  14350. utils.html(cn.data.replace(/&nbsp;/g, ' '))
  14351. )
  14352. )
  14353. }
  14354. })
  14355. if (frag.lastChild.nodeName != 'BR') {
  14356. frag.appendChild(me.document.createElement('br'))
  14357. }
  14358. }
  14359. } else {
  14360. frag.appendChild(
  14361. me.document.createTextNode(
  14362. utils.html(node.data.replace(/&nbsp;/g, ' '))
  14363. )
  14364. )
  14365. }
  14366. if (!node.nextSibling() && frag.lastChild.nodeName == 'BR') {
  14367. frag.removeChild(frag.lastChild)
  14368. }
  14369. }
  14370. )
  14371. rng.insertNode(frag).select()
  14372. }
  14373. return true
  14374. }
  14375. })
  14376. //方向键的处理
  14377. me.addListener('keydown', function (cmd, evt) {
  14378. var me = this,
  14379. keyCode = evt.keyCode || evt.which
  14380. if (keyCode == 40) {
  14381. var rng = me.selection.getRange(),
  14382. pre,
  14383. start = rng.startContainer
  14384. if (
  14385. rng.collapsed &&
  14386. (pre = domUtils.findParentByTagName(
  14387. rng.startContainer,
  14388. 'pre',
  14389. true
  14390. )) &&
  14391. !pre.nextSibling
  14392. ) {
  14393. var last = pre.lastChild
  14394. while (last && last.nodeName == 'BR') {
  14395. last = last.previousSibling
  14396. }
  14397. if (
  14398. last === start ||
  14399. (rng.startContainer === pre &&
  14400. rng.startOffset == pre.childNodes.length)
  14401. ) {
  14402. me.execCommand('insertparagraph')
  14403. domUtils.preventDefault(evt)
  14404. }
  14405. }
  14406. }
  14407. })
  14408. //trace:3395
  14409. me.addListener('delkeydown', function (type, evt) {
  14410. var rng = this.selection.getRange()
  14411. rng.txtToElmBoundary(true)
  14412. var start = rng.startContainer
  14413. if (
  14414. domUtils.isTagNode(start, 'pre') &&
  14415. rng.collapsed &&
  14416. domUtils.isStartInblock(rng)
  14417. ) {
  14418. var p = me.document.createElement('p')
  14419. domUtils.fillNode(me.document, p)
  14420. start.parentNode.insertBefore(p, start)
  14421. domUtils.remove(start)
  14422. rng.setStart(p, 0).setCursor(false, true)
  14423. domUtils.preventDefault(evt)
  14424. return true
  14425. }
  14426. })
  14427. }
  14428. // plugins/cleardoc.js
  14429. /**
  14430. * 清空文档插件
  14431. * @file
  14432. * @since 1.2.6.1
  14433. */
  14434. /**
  14435. * 清空文档
  14436. * @command cleardoc
  14437. * @method execCommand
  14438. * @param { String } cmd 命令字符串
  14439. * @example
  14440. * ```javascript
  14441. * //editor 是编辑器实例
  14442. * editor.execCommand('cleardoc');
  14443. * ```
  14444. */
  14445. UE.commands['cleardoc'] = {
  14446. execCommand: function (cmdName) {
  14447. var me = this,
  14448. enterTag = me.options.enterTag,
  14449. range = me.selection.getRange()
  14450. if (enterTag == 'br') {
  14451. me.body.innerHTML = '<br/>'
  14452. range.setStart(me.body, 0).setCursor()
  14453. } else {
  14454. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>'
  14455. range.setStart(me.body.firstChild, 0).setCursor(false, true)
  14456. }
  14457. setTimeout(function () {
  14458. me.fireEvent('clearDoc')
  14459. }, 0)
  14460. }
  14461. }
  14462. // plugins/anchor.js
  14463. /**
  14464. * 锚点插件,为UEditor提供插入锚点支持
  14465. * @file
  14466. * @since 1.2.6.1
  14467. */
  14468. UE.plugin.register('anchor', function () {
  14469. return {
  14470. bindEvents: {
  14471. ready: function () {
  14472. utils.cssRule(
  14473. 'anchor',
  14474. ".anchorclass{background: url('" +
  14475. this.options.themePath +
  14476. this.options.theme +
  14477. "/images/anchor.gif') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}",
  14478. this.document
  14479. )
  14480. }
  14481. },
  14482. outputRule: function (root) {
  14483. utils.each(root.getNodesByTagName('img'), function (a) {
  14484. var val
  14485. if ((val = a.getAttr('anchorname'))) {
  14486. a.tagName = 'a'
  14487. a.setAttr({
  14488. anchorname: '',
  14489. name: val,
  14490. class: ''
  14491. })
  14492. }
  14493. })
  14494. },
  14495. inputRule: function (root) {
  14496. utils.each(root.getNodesByTagName('a'), function (a) {
  14497. var val
  14498. if ((val = a.getAttr('name')) && !a.getAttr('href')) {
  14499. a.tagName = 'img'
  14500. a.setAttr({
  14501. anchorname: a.getAttr('name'),
  14502. class: 'anchorclass'
  14503. })
  14504. a.setAttr('name')
  14505. }
  14506. })
  14507. },
  14508. commands: {
  14509. /**
  14510. * 插入锚点
  14511. * @command anchor
  14512. * @method execCommand
  14513. * @param { String } cmd 命令字符串
  14514. * @param { String } name 锚点名称字符串
  14515. * @example
  14516. * ```javascript
  14517. * //editor 是编辑器实例
  14518. * editor.execCommand('anchor', 'anchor1');
  14519. * ```
  14520. */
  14521. anchor: {
  14522. execCommand: function (cmd, name) {
  14523. var range = this.selection.getRange(),
  14524. img = range.getClosedNode()
  14525. if (img && img.getAttribute('anchorname')) {
  14526. if (name) {
  14527. img.setAttribute('anchorname', name)
  14528. } else {
  14529. range.setStartBefore(img).setCursor()
  14530. domUtils.remove(img)
  14531. }
  14532. } else {
  14533. if (name) {
  14534. //只在选区的开始插入
  14535. var anchor = this.document.createElement('img')
  14536. range.collapse(true)
  14537. domUtils.setAttributes(anchor, {
  14538. anchorname: name,
  14539. class: 'anchorclass'
  14540. })
  14541. range
  14542. .insertNode(anchor)
  14543. .setStartAfter(anchor)
  14544. .setCursor(false, true)
  14545. }
  14546. }
  14547. }
  14548. }
  14549. }
  14550. }
  14551. })
  14552. // plugins/wordcount.js
  14553. ///import core
  14554. ///commands 字数统计
  14555. ///commandsName WordCount,wordCount
  14556. ///commandsTitle 字数统计
  14557. /*
  14558. * Created by JetBrains WebStorm.
  14559. * User: taoqili
  14560. * Date: 11-9-7
  14561. * Time: 下午8:18
  14562. * To change this template use File | Settings | File Templates.
  14563. */
  14564. UE.plugins['wordcount'] = function () {
  14565. var me = this
  14566. me.setOpt('wordCount', true)
  14567. me.addListener('contentchange', function () {
  14568. me.fireEvent('wordcount')
  14569. })
  14570. var timer
  14571. me.addListener('ready', function () {
  14572. var me = this
  14573. domUtils.on(me.body, 'keyup', function (evt) {
  14574. var code = evt.keyCode || evt.which,
  14575. //忽略的按键,ctr,alt,shift,方向键
  14576. ignores = { 16: 1, 18: 1, 20: 1, 37: 1, 38: 1, 39: 1, 40: 1 }
  14577. if (code in ignores) return
  14578. clearTimeout(timer)
  14579. timer = setTimeout(function () {
  14580. me.fireEvent('wordcount')
  14581. }, 200)
  14582. })
  14583. })
  14584. }
  14585. // plugins/pagebreak.js
  14586. /**
  14587. * 分页功能插件
  14588. * @file
  14589. * @since 1.2.6.1
  14590. */
  14591. UE.plugins['pagebreak'] = function () {
  14592. var me = this,
  14593. notBreakTags = ['td']
  14594. me.setOpt('pageBreakTag', '_ueditor_page_break_tag_')
  14595. function fillNode(node) {
  14596. if (domUtils.isEmptyBlock(node)) {
  14597. var firstChild = node.firstChild,
  14598. tmpNode
  14599. while (
  14600. firstChild &&
  14601. firstChild.nodeType == 1 &&
  14602. domUtils.isEmptyBlock(firstChild)
  14603. ) {
  14604. tmpNode = firstChild
  14605. firstChild = firstChild.firstChild
  14606. }
  14607. !tmpNode && (tmpNode = node)
  14608. domUtils.fillNode(me.document, tmpNode)
  14609. }
  14610. }
  14611. //分页符样式添加
  14612. me.ready(function () {
  14613. utils.cssRule(
  14614. 'pagebreak',
  14615. '.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',
  14616. me.document
  14617. )
  14618. })
  14619. function isHr(node) {
  14620. return (
  14621. node &&
  14622. node.nodeType == 1 &&
  14623. node.tagName == 'HR' &&
  14624. node.className == 'pagebreak'
  14625. )
  14626. }
  14627. me.addInputRule(function (root) {
  14628. root.traversal(function (node) {
  14629. if (node.type == 'text' && node.data == me.options.pageBreakTag) {
  14630. var hr = UE.uNode.createElement(
  14631. '<hr class="pagebreak" noshade="noshade" size="5" style="-webkit-user-select: none;">'
  14632. )
  14633. node.parentNode.insertBefore(hr, node)
  14634. node.parentNode.removeChild(node)
  14635. }
  14636. })
  14637. })
  14638. me.addOutputRule(function (node) {
  14639. utils.each(node.getNodesByTagName('hr'), function (n) {
  14640. if (n.getAttr('class') == 'pagebreak') {
  14641. var txt = UE.uNode.createText(me.options.pageBreakTag)
  14642. n.parentNode.insertBefore(txt, n)
  14643. n.parentNode.removeChild(n)
  14644. }
  14645. })
  14646. })
  14647. /**
  14648. * 插入分页符
  14649. * @command pagebreak
  14650. * @method execCommand
  14651. * @param { String } cmd 命令字符串
  14652. * @remind 在表格中插入分页符会把表格切分成两部分
  14653. * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串,
  14654. * 以便于提交数据到服务器端后处理分页。
  14655. * @example
  14656. * ```javascript
  14657. * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak
  14658. * ```
  14659. */
  14660. me.commands['pagebreak'] = {
  14661. execCommand: function () {
  14662. var range = me.selection.getRange(),
  14663. hr = me.document.createElement('hr')
  14664. domUtils.setAttributes(hr, {
  14665. class: 'pagebreak',
  14666. noshade: 'noshade',
  14667. size: '5'
  14668. })
  14669. domUtils.unSelectable(hr)
  14670. //table单独处理
  14671. var node = domUtils.findParentByTagName(
  14672. range.startContainer,
  14673. notBreakTags,
  14674. true
  14675. ),
  14676. parents = [],
  14677. pN
  14678. if (node) {
  14679. switch (node.tagName) {
  14680. case 'TD':
  14681. pN = node.parentNode
  14682. if (!pN.previousSibling) {
  14683. var table = domUtils.findParentByTagName(pN, 'table')
  14684. // var tableWrapDiv = table.parentNode;
  14685. // if(tableWrapDiv && tableWrapDiv.nodeType == 1
  14686. // && tableWrapDiv.tagName == 'DIV'
  14687. // && tableWrapDiv.getAttribute('dropdrag')
  14688. // ){
  14689. // domUtils.remove(tableWrapDiv,true);
  14690. // }
  14691. table.parentNode.insertBefore(hr, table)
  14692. parents = domUtils.findParents(hr, true)
  14693. } else {
  14694. pN.parentNode.insertBefore(hr, pN)
  14695. parents = domUtils.findParents(hr)
  14696. }
  14697. pN = parents[1]
  14698. if (hr !== pN) {
  14699. domUtils.breakParent(hr, pN)
  14700. }
  14701. //table要重写绑定一下拖拽
  14702. me.fireEvent('afteradjusttable', me.document)
  14703. }
  14704. } else {
  14705. if (!range.collapsed) {
  14706. range.deleteContents()
  14707. var start = range.startContainer
  14708. while (
  14709. !domUtils.isBody(start) &&
  14710. domUtils.isBlockElm(start) &&
  14711. domUtils.isEmptyNode(start)
  14712. ) {
  14713. range.setStartBefore(start).collapse(true)
  14714. domUtils.remove(start)
  14715. start = range.startContainer
  14716. }
  14717. }
  14718. range.insertNode(hr)
  14719. var pN = hr.parentNode,
  14720. nextNode
  14721. while (!domUtils.isBody(pN)) {
  14722. domUtils.breakParent(hr, pN)
  14723. nextNode = hr.nextSibling
  14724. if (nextNode && domUtils.isEmptyBlock(nextNode)) {
  14725. domUtils.remove(nextNode)
  14726. }
  14727. pN = hr.parentNode
  14728. }
  14729. nextNode = hr.nextSibling
  14730. var pre = hr.previousSibling
  14731. if (isHr(pre)) {
  14732. domUtils.remove(pre)
  14733. } else {
  14734. pre && fillNode(pre)
  14735. }
  14736. if (!nextNode) {
  14737. var p = me.document.createElement('p')
  14738. hr.parentNode.appendChild(p)
  14739. domUtils.fillNode(me.document, p)
  14740. range.setStart(p, 0).collapse(true)
  14741. } else {
  14742. if (isHr(nextNode)) {
  14743. domUtils.remove(nextNode)
  14744. } else {
  14745. fillNode(nextNode)
  14746. }
  14747. range.setEndAfter(hr).collapse(false)
  14748. }
  14749. range.select(true)
  14750. }
  14751. }
  14752. }
  14753. }
  14754. // plugins/wordimage.js
  14755. ///import core
  14756. ///commands 本地图片引导上传
  14757. ///commandsName WordImage
  14758. ///commandsTitle 本地图片引导上传
  14759. ///commandsDialog dialogs\wordimage
  14760. UE.plugin.register('wordimage', function () {
  14761. var me = this,
  14762. images = []
  14763. return {
  14764. commands: {
  14765. wordimage: {
  14766. execCommand: function () {
  14767. var images = domUtils.getElementsByTagName(me.body, 'img')
  14768. var urlList = []
  14769. for (var i = 0, ci; (ci = images[i++]); ) {
  14770. var url = ci.getAttribute('word_img')
  14771. url && urlList.push(url)
  14772. }
  14773. return urlList
  14774. },
  14775. queryCommandState: function () {
  14776. images = domUtils.getElementsByTagName(me.body, 'img')
  14777. for (var i = 0, ci; (ci = images[i++]); ) {
  14778. if (ci.getAttribute('word_img')) {
  14779. return 1
  14780. }
  14781. }
  14782. return -1
  14783. },
  14784. notNeedUndo: true
  14785. }
  14786. },
  14787. inputRule: function (root) {
  14788. utils.each(root.getNodesByTagName('img'), function (img) {
  14789. var attrs = img.attrs,
  14790. flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
  14791. opt = me.options,
  14792. src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif'
  14793. if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) {
  14794. img.setAttr({
  14795. width: attrs.width,
  14796. height: attrs.height,
  14797. alt: attrs.alt,
  14798. word_img: attrs.src,
  14799. src: src,
  14800. style:
  14801. 'background:url(' +
  14802. (flag
  14803. ? opt.themePath + opt.theme + '/images/word.gif'
  14804. : opt.langPath + opt.lang + '/images/localimage.png') +
  14805. ') no-repeat center center;border:1px solid #ddd'
  14806. })
  14807. }
  14808. })
  14809. }
  14810. }
  14811. })
  14812. // plugins/dragdrop.js
  14813. UE.plugins['dragdrop'] = function () {
  14814. var me = this
  14815. me.ready(function () {
  14816. domUtils.on(this.body, 'dragend', function () {
  14817. var rng = me.selection.getRange()
  14818. var node = rng.getClosedNode() || me.selection.getStart()
  14819. if (node && node.tagName == 'IMG') {
  14820. var pre = node.previousSibling,
  14821. next
  14822. while ((next = node.nextSibling)) {
  14823. if (
  14824. next.nodeType == 1 &&
  14825. next.tagName == 'SPAN' &&
  14826. !next.firstChild
  14827. ) {
  14828. domUtils.remove(next)
  14829. } else {
  14830. break
  14831. }
  14832. }
  14833. if (
  14834. ((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre)) ||
  14835. !pre) &&
  14836. (!next || (next && !domUtils.isEmptyBlock(next)))
  14837. ) {
  14838. if (pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)) {
  14839. pre.appendChild(node)
  14840. domUtils.moveChild(next, pre)
  14841. domUtils.remove(next)
  14842. } else if (
  14843. next &&
  14844. next.tagName == 'P' &&
  14845. !domUtils.isEmptyBlock(next)
  14846. ) {
  14847. next.insertBefore(node, next.firstChild)
  14848. }
  14849. if (pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)) {
  14850. domUtils.remove(pre)
  14851. }
  14852. if (next && next.tagName == 'P' && domUtils.isEmptyBlock(next)) {
  14853. domUtils.remove(next)
  14854. }
  14855. rng.selectNode(node).select()
  14856. me.fireEvent('saveScene')
  14857. }
  14858. }
  14859. })
  14860. })
  14861. me.addListener('keyup', function (type, evt) {
  14862. var keyCode = evt.keyCode || evt.which
  14863. if (keyCode == 13) {
  14864. var rng = me.selection.getRange(),
  14865. node
  14866. if (
  14867. (node = domUtils.findParentByTagName(rng.startContainer, 'p', true))
  14868. ) {
  14869. if (domUtils.getComputedStyle(node, 'text-align') == 'center') {
  14870. domUtils.removeStyle(node, 'text-align')
  14871. }
  14872. }
  14873. }
  14874. })
  14875. }
  14876. // plugins/undo.js
  14877. /**
  14878. * undo redo
  14879. * @file
  14880. * @since 1.2.6.1
  14881. */
  14882. /**
  14883. * 撤销上一次执行的命令
  14884. * @command undo
  14885. * @method execCommand
  14886. * @param { String } cmd 命令字符串
  14887. * @example
  14888. * ```javascript
  14889. * editor.execCommand( 'undo' );
  14890. * ```
  14891. */
  14892. /**
  14893. * 重做上一次执行的命令
  14894. * @command redo
  14895. * @method execCommand
  14896. * @param { String } cmd 命令字符串
  14897. * @example
  14898. * ```javascript
  14899. * editor.execCommand( 'redo' );
  14900. * ```
  14901. */
  14902. UE.plugins['undo'] = function () {
  14903. var saveSceneTimer
  14904. var me = this,
  14905. maxUndoCount = me.options.maxUndoCount || 20,
  14906. maxInputCount = me.options.maxInputCount || 20,
  14907. fillchar = new RegExp(domUtils.fillChar + '|</hr>', 'gi') // ie会产生多余的</hr>
  14908. var noNeedFillCharTags = {
  14909. ol: 1,
  14910. ul: 1,
  14911. table: 1,
  14912. tbody: 1,
  14913. tr: 1,
  14914. body: 1
  14915. }
  14916. var orgState = me.options.autoClearEmptyNode
  14917. function compareAddr(indexA, indexB) {
  14918. if (indexA.length != indexB.length) return 0
  14919. for (var i = 0, l = indexA.length; i < l; i++) {
  14920. if (indexA[i] != indexB[i]) return 0
  14921. }
  14922. return 1
  14923. }
  14924. function compareRangeAddress(rngAddrA, rngAddrB) {
  14925. if (rngAddrA.collapsed != rngAddrB.collapsed) {
  14926. return 0
  14927. }
  14928. if (
  14929. !compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) ||
  14930. !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)
  14931. ) {
  14932. return 0
  14933. }
  14934. return 1
  14935. }
  14936. function UndoManager() {
  14937. this.list = []
  14938. this.index = 0
  14939. this.hasUndo = false
  14940. this.hasRedo = false
  14941. this.undo = function () {
  14942. if (this.hasUndo) {
  14943. if (!this.list[this.index - 1] && this.list.length == 1) {
  14944. this.reset()
  14945. return
  14946. }
  14947. while (
  14948. this.list[this.index].content == this.list[this.index - 1].content
  14949. ) {
  14950. this.index--
  14951. if (this.index == 0) {
  14952. return this.restore(0)
  14953. }
  14954. }
  14955. this.restore(--this.index)
  14956. }
  14957. }
  14958. this.redo = function () {
  14959. if (this.hasRedo) {
  14960. while (
  14961. this.list[this.index].content == this.list[this.index + 1].content
  14962. ) {
  14963. this.index++
  14964. if (this.index == this.list.length - 1) {
  14965. return this.restore(this.index)
  14966. }
  14967. }
  14968. this.restore(++this.index)
  14969. }
  14970. }
  14971. this.restore = function () {
  14972. var me = this.editor
  14973. var scene = this.list[this.index]
  14974. var root = UE.htmlparser(scene.content.replace(fillchar, ''))
  14975. me.options.autoClearEmptyNode = false
  14976. me.filterInputRule(root)
  14977. me.options.autoClearEmptyNode = orgState
  14978. //trace:873
  14979. //去掉展位符
  14980. me.document.body.innerHTML = root.toHtml()
  14981. me.fireEvent('afterscencerestore')
  14982. //处理undo后空格不展位的问题
  14983. if (browser.ie) {
  14984. utils.each(
  14985. domUtils.getElementsByTagName(me.document, 'td th caption p'),
  14986. function (node) {
  14987. if (domUtils.isEmptyNode(node)) {
  14988. domUtils.fillNode(me.document, node)
  14989. }
  14990. }
  14991. )
  14992. }
  14993. try {
  14994. var rng = new dom.Range(me.document).moveToAddress(scene.address)
  14995. rng.select(
  14996. noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]
  14997. )
  14998. } catch (e) {}
  14999. this.update()
  15000. this.clearKey()
  15001. //不能把自己reset了
  15002. me.fireEvent('reset', true)
  15003. }
  15004. this.getScene = function () {
  15005. var me = this.editor
  15006. var rng = me.selection.getRange(),
  15007. rngAddress = rng.createAddress(false, true)
  15008. me.fireEvent('beforegetscene')
  15009. var root = UE.htmlparser(me.body.innerHTML)
  15010. me.options.autoClearEmptyNode = false
  15011. me.filterOutputRule(root)
  15012. me.options.autoClearEmptyNode = orgState
  15013. var cont = root.toHtml()
  15014. //trace:3461
  15015. //这个会引起回退时导致空格丢失的情况
  15016. // browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
  15017. me.fireEvent('aftergetscene')
  15018. return {
  15019. address: rngAddress,
  15020. content: cont
  15021. }
  15022. }
  15023. this.save = function (notCompareRange, notSetCursor) {
  15024. clearTimeout(saveSceneTimer)
  15025. var currentScene = this.getScene(notSetCursor),
  15026. lastScene = this.list[this.index]
  15027. if (lastScene && lastScene.content != currentScene.content) {
  15028. me.trigger('contentchange')
  15029. }
  15030. //内容相同位置相同不存
  15031. if (
  15032. lastScene &&
  15033. lastScene.content == currentScene.content &&
  15034. (notCompareRange
  15035. ? 1
  15036. : compareRangeAddress(lastScene.address, currentScene.address))
  15037. ) {
  15038. return
  15039. }
  15040. this.list = this.list.slice(0, this.index + 1)
  15041. this.list.push(currentScene)
  15042. //如果大于最大数量了,就把最前的剔除
  15043. if (this.list.length > maxUndoCount) {
  15044. this.list.shift()
  15045. }
  15046. this.index = this.list.length - 1
  15047. this.clearKey()
  15048. //跟新undo/redo状态
  15049. this.update()
  15050. }
  15051. this.update = function () {
  15052. this.hasRedo = !!this.list[this.index + 1]
  15053. this.hasUndo = !!this.list[this.index - 1]
  15054. }
  15055. this.reset = function () {
  15056. this.list = []
  15057. this.index = 0
  15058. this.hasUndo = false
  15059. this.hasRedo = false
  15060. this.clearKey()
  15061. }
  15062. this.clearKey = function () {
  15063. keycont = 0
  15064. lastKeyCode = null
  15065. }
  15066. }
  15067. me.undoManger = new UndoManager()
  15068. me.undoManger.editor = me
  15069. function saveScene() {
  15070. this.undoManger.save()
  15071. }
  15072. me.addListener('saveScene', function () {
  15073. var args = Array.prototype.splice.call(arguments, 1)
  15074. this.undoManger.save.apply(this.undoManger, args)
  15075. })
  15076. // me.addListener('beforeexeccommand', saveScene);
  15077. // me.addListener('afterexeccommand', saveScene);
  15078. me.addListener('reset', function (type, exclude) {
  15079. if (!exclude) {
  15080. this.undoManger.reset()
  15081. }
  15082. })
  15083. me.commands['redo'] = me.commands['undo'] = {
  15084. execCommand: function (cmdName) {
  15085. this.undoManger[cmdName]()
  15086. },
  15087. queryCommandState: function (cmdName) {
  15088. return this.undoManger[
  15089. 'has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')
  15090. ]
  15091. ? 0
  15092. : -1
  15093. },
  15094. notNeedUndo: 1
  15095. }
  15096. var keys = {
  15097. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  15098. /*Shift*/ 16: 1,
  15099. /*Ctrl*/ 17: 1,
  15100. /*Alt*/ 18: 1,
  15101. 37: 1,
  15102. 38: 1,
  15103. 39: 1,
  15104. 40: 1
  15105. },
  15106. keycont = 0,
  15107. lastKeyCode
  15108. //输入法状态下不计算字符数
  15109. var inputType = false
  15110. me.addListener('ready', function () {
  15111. domUtils.on(this.body, 'compositionstart', function () {
  15112. inputType = true
  15113. })
  15114. domUtils.on(this.body, 'compositionend', function () {
  15115. inputType = false
  15116. })
  15117. })
  15118. //快捷键
  15119. me.addshortcutkey({
  15120. Undo: 'ctrl+90', //undo
  15121. Redo: 'ctrl+89' //redo
  15122. })
  15123. var isCollapsed = true
  15124. me.addListener('keydown', function (type, evt) {
  15125. var me = this
  15126. var keyCode = evt.keyCode || evt.which
  15127. if (
  15128. !keys[keyCode] &&
  15129. !evt.ctrlKey &&
  15130. !evt.metaKey &&
  15131. !evt.shiftKey &&
  15132. !evt.altKey
  15133. ) {
  15134. if (inputType) return
  15135. if (!me.selection.getRange().collapsed) {
  15136. me.undoManger.save(false, true)
  15137. isCollapsed = false
  15138. return
  15139. }
  15140. if (me.undoManger.list.length == 0) {
  15141. me.undoManger.save(true)
  15142. }
  15143. clearTimeout(saveSceneTimer)
  15144. function save(cont) {
  15145. cont.undoManger.save(false, true)
  15146. cont.fireEvent('selectionchange')
  15147. }
  15148. saveSceneTimer = setTimeout(function () {
  15149. if (inputType) {
  15150. var interalTimer = setInterval(function () {
  15151. if (!inputType) {
  15152. save(me)
  15153. clearInterval(interalTimer)
  15154. }
  15155. }, 300)
  15156. return
  15157. }
  15158. save(me)
  15159. }, 200)
  15160. lastKeyCode = keyCode
  15161. keycont++
  15162. if (keycont >= maxInputCount) {
  15163. save(me)
  15164. }
  15165. }
  15166. })
  15167. me.addListener('keyup', function (type, evt) {
  15168. var keyCode = evt.keyCode || evt.which
  15169. if (
  15170. !keys[keyCode] &&
  15171. !evt.ctrlKey &&
  15172. !evt.metaKey &&
  15173. !evt.shiftKey &&
  15174. !evt.altKey
  15175. ) {
  15176. if (inputType) return
  15177. if (!isCollapsed) {
  15178. this.undoManger.save(false, true)
  15179. isCollapsed = true
  15180. }
  15181. }
  15182. })
  15183. //扩展实例,添加关闭和开启命令undo
  15184. me.stopCmdUndo = function () {
  15185. me.__hasEnterExecCommand = true
  15186. }
  15187. me.startCmdUndo = function () {
  15188. me.__hasEnterExecCommand = false
  15189. }
  15190. }
  15191. // plugins/copy.js
  15192. UE.plugin.register('copy', function () {
  15193. var me = this
  15194. function initZeroClipboard() {
  15195. ZeroClipboard.config({
  15196. debug: false,
  15197. swfPath:
  15198. me.options.UEDITOR_HOME_URL +
  15199. 'third-party/zeroclipboard/ZeroClipboard.swf'
  15200. })
  15201. var client = (me.zeroclipboard = new ZeroClipboard())
  15202. // 复制内容
  15203. client.on('copy', function (e) {
  15204. var client = e.client,
  15205. rng = me.selection.getRange(),
  15206. div = document.createElement('div')
  15207. div.appendChild(rng.cloneContents())
  15208. client.setText(div.innerText || div.textContent)
  15209. client.setHtml(div.innerHTML)
  15210. rng.select()
  15211. })
  15212. // hover事件传递到target
  15213. client.on('mouseover mouseout', function (e) {
  15214. var target = e.target
  15215. if (e.type == 'mouseover') {
  15216. domUtils.addClass(target, 'edui-state-hover')
  15217. } else if (e.type == 'mouseout') {
  15218. domUtils.removeClasses(target, 'edui-state-hover')
  15219. }
  15220. })
  15221. // flash加载不成功
  15222. client.on('wrongflash noflash', function () {
  15223. ZeroClipboard.destroy()
  15224. })
  15225. }
  15226. return {
  15227. bindEvents: {
  15228. ready: function () {
  15229. if (!browser.ie) {
  15230. if (window.ZeroClipboard) {
  15231. initZeroClipboard()
  15232. } else {
  15233. utils.loadFile(
  15234. document,
  15235. {
  15236. src:
  15237. me.options.UEDITOR_HOME_URL +
  15238. 'third-party/zeroclipboard/ZeroClipboard.js',
  15239. tag: 'script',
  15240. type: 'text/javascript',
  15241. defer: 'defer'
  15242. },
  15243. function () {
  15244. initZeroClipboard()
  15245. }
  15246. )
  15247. }
  15248. }
  15249. }
  15250. },
  15251. commands: {
  15252. copy: {
  15253. execCommand: function (cmd) {
  15254. if (!me.document.execCommand('copy')) {
  15255. alert(me.getLang('copymsg'))
  15256. }
  15257. }
  15258. }
  15259. }
  15260. }
  15261. })
  15262. // plugins/paste.js
  15263. ///import core
  15264. ///import plugins/inserthtml.js
  15265. ///import plugins/undo.js
  15266. ///import plugins/serialize.js
  15267. ///commands 粘贴
  15268. ///commandsName PastePlain
  15269. ///commandsTitle 纯文本粘贴模式
  15270. /**
  15271. * @description 粘贴
  15272. * @author zhanyi
  15273. */
  15274. UE.plugins['paste'] = function () {
  15275. function getClipboardData(callback) {
  15276. var doc = this.document
  15277. if (doc.getElementById('baidu_pastebin')) {
  15278. return
  15279. }
  15280. var range = this.selection.getRange(),
  15281. bk = range.createBookmark(),
  15282. //创建剪贴的容器div
  15283. pastebin = doc.createElement('div')
  15284. pastebin.id = 'baidu_pastebin'
  15285. // Safari 要求div必须有内容,才能粘贴内容进来
  15286. browser.webkit &&
  15287. pastebin.appendChild(
  15288. doc.createTextNode(domUtils.fillChar + domUtils.fillChar)
  15289. )
  15290. doc.body.appendChild(pastebin)
  15291. //trace:717 隐藏的span不能得到top
  15292. //bk.start.innerHTML = '&nbsp;';
  15293. bk.start.style.display = ''
  15294. pastebin.style.cssText =
  15295. 'position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:' +
  15296. //要在现在光标平行的位置加入,否则会出现跳动的问题
  15297. domUtils.getXY(bk.start).y +
  15298. 'px'
  15299. range.selectNodeContents(pastebin).select(true)
  15300. setTimeout(function () {
  15301. if (browser.webkit) {
  15302. for (
  15303. var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi;
  15304. (pi = pastebins[i++]);
  15305. ) {
  15306. if (domUtils.isEmptyNode(pi)) {
  15307. domUtils.remove(pi)
  15308. } else {
  15309. pastebin = pi
  15310. break
  15311. }
  15312. }
  15313. }
  15314. try {
  15315. pastebin.parentNode.removeChild(pastebin)
  15316. } catch (e) {}
  15317. range.moveToBookmark(bk).select(true)
  15318. callback(pastebin)
  15319. }, 0)
  15320. }
  15321. var me = this
  15322. me.setOpt({
  15323. retainOnlyLabelPasted: false
  15324. })
  15325. var txtContent, htmlContent, address
  15326. function getPureHtml(html) {
  15327. return html.replace(
  15328. /<(\/?)([\w\-]+)([^>]*)>/gi,
  15329. function (a, b, tagName, attrs) {
  15330. tagName = tagName.toLowerCase()
  15331. if ({ img: 1 }[tagName]) {
  15332. return a
  15333. }
  15334. attrs = attrs.replace(
  15335. /([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi,
  15336. function (str, atr, val) {
  15337. if (
  15338. {
  15339. src: 1,
  15340. href: 1,
  15341. name: 1
  15342. }[atr.toLowerCase()]
  15343. ) {
  15344. return atr + '=' + val + ' '
  15345. }
  15346. return ''
  15347. }
  15348. )
  15349. if (
  15350. {
  15351. span: 1,
  15352. div: 1
  15353. }[tagName]
  15354. ) {
  15355. return ''
  15356. } else {
  15357. return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
  15358. }
  15359. }
  15360. )
  15361. }
  15362. function filter(div) {
  15363. var html
  15364. if (div.firstChild) {
  15365. //去掉cut中添加的边界值
  15366. var nodes = domUtils.getElementsByTagName(div, 'span')
  15367. for (var i = 0, ni; (ni = nodes[i++]); ) {
  15368. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  15369. domUtils.remove(ni)
  15370. }
  15371. }
  15372. if (browser.webkit) {
  15373. var brs = div.querySelectorAll('div br')
  15374. for (var i = 0, bi; (bi = brs[i++]); ) {
  15375. var pN = bi.parentNode
  15376. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  15377. pN.innerHTML = '<p><br/></p>'
  15378. domUtils.remove(pN)
  15379. }
  15380. }
  15381. var divs = div.querySelectorAll('#baidu_pastebin')
  15382. for (var i = 0, di; (di = divs[i++]); ) {
  15383. var tmpP = me.document.createElement('p')
  15384. di.parentNode.insertBefore(tmpP, di)
  15385. while (di.firstChild) {
  15386. tmpP.appendChild(di.firstChild)
  15387. }
  15388. domUtils.remove(di)
  15389. }
  15390. var metas = div.querySelectorAll('meta')
  15391. for (var i = 0, ci; (ci = metas[i++]); ) {
  15392. domUtils.remove(ci)
  15393. }
  15394. var brs = div.querySelectorAll('br')
  15395. for (i = 0; (ci = brs[i++]); ) {
  15396. if (/^apple-/i.test(ci.className)) {
  15397. domUtils.remove(ci)
  15398. }
  15399. }
  15400. }
  15401. if (browser.gecko) {
  15402. var dirtyNodes = div.querySelectorAll('[_moz_dirty]')
  15403. for (i = 0; (ci = dirtyNodes[i++]); ) {
  15404. ci.removeAttribute('_moz_dirty')
  15405. }
  15406. }
  15407. if (!browser.ie) {
  15408. var spans = div.querySelectorAll('span.Apple-style-span')
  15409. for (var i = 0, ci; (ci = spans[i++]); ) {
  15410. domUtils.remove(ci, true)
  15411. }
  15412. }
  15413. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  15414. html = div.innerHTML //.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  15415. //过滤word粘贴过来的冗余属性
  15416. html = UE.filterWord(html)
  15417. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  15418. var root = UE.htmlparser(html)
  15419. //如果给了过滤规则就先进行过滤
  15420. if (me.options.filterRules) {
  15421. UE.filterNode(root, me.options.filterRules)
  15422. }
  15423. //执行默认的处理
  15424. me.filterInputRule(root)
  15425. //针对chrome的处理
  15426. if (browser.webkit) {
  15427. var br = root.lastChild()
  15428. if (br && br.type == 'element' && br.tagName == 'br') {
  15429. root.removeChild(br)
  15430. }
  15431. utils.each(me.body.querySelectorAll('div'), function (node) {
  15432. if (domUtils.isEmptyBlock(node)) {
  15433. domUtils.remove(node, true)
  15434. }
  15435. })
  15436. }
  15437. html = { html: root.toHtml() }
  15438. me.fireEvent('beforepaste', html, root)
  15439. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  15440. if (!html.html) {
  15441. return
  15442. }
  15443. root = UE.htmlparser(html.html, true)
  15444. //如果开启了纯文本模式
  15445. if (me.queryCommandState('pasteplain') === 1) {
  15446. me.execCommand(
  15447. 'insertHtml',
  15448. UE.filterNode(root, me.options.filterTxtRules).toHtml(),
  15449. true
  15450. )
  15451. } else {
  15452. //文本模式
  15453. UE.filterNode(root, me.options.filterTxtRules)
  15454. txtContent = root.toHtml()
  15455. //完全模式
  15456. htmlContent = html.html
  15457. address = me.selection.getRange().createAddress(true)
  15458. me.execCommand(
  15459. 'insertHtml',
  15460. me.getOpt('retainOnlyLabelPasted') === true
  15461. ? getPureHtml(htmlContent)
  15462. : htmlContent,
  15463. true
  15464. )
  15465. }
  15466. me.fireEvent('afterpaste', html)
  15467. }
  15468. }
  15469. me.addListener('pasteTransfer', function (cmd, plainType) {
  15470. if (address && txtContent && htmlContent && txtContent != htmlContent) {
  15471. var range = me.selection.getRange()
  15472. range.moveToAddress(address, true)
  15473. if (!range.collapsed) {
  15474. while (!domUtils.isBody(range.startContainer)) {
  15475. var start = range.startContainer
  15476. if (start.nodeType == 1) {
  15477. start = start.childNodes[range.startOffset]
  15478. if (!start) {
  15479. range.setStartBefore(range.startContainer)
  15480. continue
  15481. }
  15482. var pre = start.previousSibling
  15483. if (
  15484. pre &&
  15485. pre.nodeType == 3 &&
  15486. new RegExp('^[\n\r\t ' + domUtils.fillChar + ']*$').test(
  15487. pre.nodeValue
  15488. )
  15489. ) {
  15490. range.setStartBefore(pre)
  15491. }
  15492. }
  15493. if (range.startOffset == 0) {
  15494. range.setStartBefore(range.startContainer)
  15495. } else {
  15496. break
  15497. }
  15498. }
  15499. while (!domUtils.isBody(range.endContainer)) {
  15500. var end = range.endContainer
  15501. if (end.nodeType == 1) {
  15502. end = end.childNodes[range.endOffset]
  15503. if (!end) {
  15504. range.setEndAfter(range.endContainer)
  15505. continue
  15506. }
  15507. var next = end.nextSibling
  15508. if (
  15509. next &&
  15510. next.nodeType == 3 &&
  15511. new RegExp('^[\n\r\t' + domUtils.fillChar + ']*$').test(
  15512. next.nodeValue
  15513. )
  15514. ) {
  15515. range.setEndAfter(next)
  15516. }
  15517. }
  15518. if (
  15519. range.endOffset ==
  15520. range.endContainer[
  15521. range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'
  15522. ].length
  15523. ) {
  15524. range.setEndAfter(range.endContainer)
  15525. } else {
  15526. break
  15527. }
  15528. }
  15529. }
  15530. range.deleteContents()
  15531. range.select(true)
  15532. me.__hasEnterExecCommand = true
  15533. var html = htmlContent
  15534. if (plainType === 2) {
  15535. html = getPureHtml(html)
  15536. } else if (plainType) {
  15537. html = txtContent
  15538. }
  15539. me.execCommand('inserthtml', html, true)
  15540. me.__hasEnterExecCommand = false
  15541. var rng = me.selection.getRange()
  15542. while (
  15543. !domUtils.isBody(rng.startContainer) &&
  15544. !rng.startOffset &&
  15545. rng.startContainer[
  15546. rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'
  15547. ].length
  15548. ) {
  15549. rng.setStartBefore(rng.startContainer)
  15550. }
  15551. var tmpAddress = rng.createAddress(true)
  15552. address.endAddress = tmpAddress.startAddress
  15553. }
  15554. })
  15555. me.addListener('ready', function () {
  15556. domUtils.on(me.body, 'cut', function () {
  15557. var range = me.selection.getRange()
  15558. if (!range.collapsed && me.undoManger) {
  15559. me.undoManger.save()
  15560. }
  15561. })
  15562. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  15563. domUtils.on(
  15564. me.body,
  15565. browser.ie || browser.opera ? 'keydown' : 'paste',
  15566. function (e) {
  15567. if (
  15568. (browser.ie || browser.opera) &&
  15569. ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')
  15570. ) {
  15571. return
  15572. }
  15573. getClipboardData.call(me, function (div) {
  15574. filter(div)
  15575. })
  15576. }
  15577. )
  15578. })
  15579. me.commands['paste'] = {
  15580. execCommand: function (cmd) {
  15581. if (browser.ie) {
  15582. getClipboardData.call(me, function (div) {
  15583. filter(div)
  15584. })
  15585. me.document.execCommand('paste')
  15586. } else {
  15587. alert(me.getLang('pastemsg'))
  15588. }
  15589. }
  15590. }
  15591. }
  15592. // plugins/puretxtpaste.js
  15593. /**
  15594. * 纯文本粘贴插件
  15595. * @file
  15596. * @since 1.2.6.1
  15597. */
  15598. UE.plugins['pasteplain'] = function () {
  15599. var me = this
  15600. me.setOpt({
  15601. pasteplain: false,
  15602. filterTxtRules: (function () {
  15603. function transP(node) {
  15604. node.tagName = 'p'
  15605. node.setStyle()
  15606. }
  15607. function removeNode(node) {
  15608. node.parentNode.removeChild(node, true)
  15609. }
  15610. return {
  15611. //直接删除及其字节点内容
  15612. '-': 'script style object iframe embed input select',
  15613. p: { $: {} },
  15614. br: { $: {} },
  15615. div: function (node) {
  15616. var tmpNode,
  15617. p = UE.uNode.createElement('p')
  15618. while ((tmpNode = node.firstChild())) {
  15619. if (
  15620. tmpNode.type == 'text' ||
  15621. !UE.dom.dtd.$block[tmpNode.tagName]
  15622. ) {
  15623. p.appendChild(tmpNode)
  15624. } else {
  15625. if (p.firstChild()) {
  15626. node.parentNode.insertBefore(p, node)
  15627. p = UE.uNode.createElement('p')
  15628. } else {
  15629. node.parentNode.insertBefore(tmpNode, node)
  15630. }
  15631. }
  15632. }
  15633. if (p.firstChild()) {
  15634. node.parentNode.insertBefore(p, node)
  15635. }
  15636. node.parentNode.removeChild(node)
  15637. },
  15638. ol: removeNode,
  15639. ul: removeNode,
  15640. dl: removeNode,
  15641. dt: removeNode,
  15642. dd: removeNode,
  15643. li: removeNode,
  15644. caption: transP,
  15645. th: transP,
  15646. tr: transP,
  15647. h1: transP,
  15648. h2: transP,
  15649. h3: transP,
  15650. h4: transP,
  15651. h5: transP,
  15652. h6: transP,
  15653. td: function (node) {
  15654. //没有内容的td直接删掉
  15655. var txt = !!node.innerText()
  15656. if (txt) {
  15657. node.parentNode.insertAfter(
  15658. UE.uNode.createText(' &nbsp; &nbsp;'),
  15659. node
  15660. )
  15661. }
  15662. node.parentNode.removeChild(node, node.innerText())
  15663. }
  15664. }
  15665. })()
  15666. })
  15667. //暂时这里支持一下老版本的属性
  15668. var pasteplain = me.options.pasteplain
  15669. /**
  15670. * 启用或取消纯文本粘贴模式
  15671. * @command pasteplain
  15672. * @method execCommand
  15673. * @param { String } cmd 命令字符串
  15674. * @example
  15675. * ```javascript
  15676. * editor.queryCommandState( 'pasteplain' );
  15677. * ```
  15678. */
  15679. /**
  15680. * 查询当前是否处于纯文本粘贴模式
  15681. * @command pasteplain
  15682. * @method queryCommandState
  15683. * @param { String } cmd 命令字符串
  15684. * @return { int } 如果处于纯文本模式,返回1,否则,返回0
  15685. * @example
  15686. * ```javascript
  15687. * editor.queryCommandState( 'pasteplain' );
  15688. * ```
  15689. */
  15690. me.commands['pasteplain'] = {
  15691. queryCommandState: function () {
  15692. return pasteplain ? 1 : 0
  15693. },
  15694. execCommand: function () {
  15695. pasteplain = !pasteplain | 0
  15696. },
  15697. notNeedUndo: 1
  15698. }
  15699. }
  15700. // plugins/list.js
  15701. /**
  15702. * 有序列表,无序列表插件
  15703. * @file
  15704. * @since 1.2.6.1
  15705. */
  15706. UE.plugins['list'] = function () {
  15707. var me = this,
  15708. notExchange = {
  15709. TD: 1,
  15710. PRE: 1,
  15711. BLOCKQUOTE: 1
  15712. }
  15713. var customStyle = {
  15714. cn: 'cn-1-',
  15715. cn1: 'cn-2-',
  15716. cn2: 'cn-3-',
  15717. num: 'num-1-',
  15718. num1: 'num-2-',
  15719. num2: 'num-3-',
  15720. dash: 'dash',
  15721. dot: 'dot'
  15722. }
  15723. me.setOpt({
  15724. autoTransWordToList: false,
  15725. insertorderedlist: {
  15726. num: '',
  15727. num1: '',
  15728. num2: '',
  15729. cn: '',
  15730. cn1: '',
  15731. cn2: '',
  15732. decimal: '',
  15733. 'lower-alpha': '',
  15734. 'lower-roman': '',
  15735. 'upper-alpha': '',
  15736. 'upper-roman': ''
  15737. },
  15738. insertunorderedlist: {
  15739. circle: '',
  15740. disc: '',
  15741. square: '',
  15742. dash: '',
  15743. dot: ''
  15744. },
  15745. listDefaultPaddingLeft: '30',
  15746. listiconpath: 'http://bs.baidu.com/listicon/',
  15747. maxListLevel: -1, //-1不限制
  15748. disablePInList: false
  15749. })
  15750. function listToArray(list) {
  15751. var arr = []
  15752. for (var p in list) {
  15753. arr.push(p)
  15754. }
  15755. return arr
  15756. }
  15757. var listStyle = {
  15758. OL: listToArray(me.options.insertorderedlist),
  15759. UL: listToArray(me.options.insertunorderedlist)
  15760. }
  15761. var liiconpath = me.options.listiconpath
  15762. //根据用户配置,调整customStyle
  15763. for (var s in customStyle) {
  15764. if (
  15765. !me.options.insertorderedlist.hasOwnProperty(s) &&
  15766. !me.options.insertunorderedlist.hasOwnProperty(s)
  15767. ) {
  15768. delete customStyle[s]
  15769. }
  15770. }
  15771. me.ready(function () {
  15772. var customCss = []
  15773. for (var p in customStyle) {
  15774. if (p == 'dash' || p == 'dot') {
  15775. customCss.push(
  15776. 'li.list-' +
  15777. customStyle[p] +
  15778. '{background-image:url(' +
  15779. liiconpath +
  15780. customStyle[p] +
  15781. '.gif)}'
  15782. )
  15783. customCss.push(
  15784. 'ul.custom_' +
  15785. p +
  15786. '{list-style:none;}ul.custom_' +
  15787. p +
  15788. ' li{background-position:0 3px;background-repeat:no-repeat}'
  15789. )
  15790. } else {
  15791. for (var i = 0; i < 99; i++) {
  15792. customCss.push(
  15793. 'li.list-' +
  15794. customStyle[p] +
  15795. i +
  15796. '{background-image:url(' +
  15797. liiconpath +
  15798. 'list-' +
  15799. customStyle[p] +
  15800. i +
  15801. '.gif)}'
  15802. )
  15803. }
  15804. customCss.push(
  15805. 'ol.custom_' +
  15806. p +
  15807. '{list-style:none;}ol.custom_' +
  15808. p +
  15809. ' li{background-position:0 3px;background-repeat:no-repeat}'
  15810. )
  15811. }
  15812. switch (p) {
  15813. case 'cn':
  15814. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
  15815. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
  15816. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
  15817. break
  15818. case 'cn1':
  15819. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:30px}')
  15820. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
  15821. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
  15822. break
  15823. case 'cn2':
  15824. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:40px}')
  15825. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:55px}')
  15826. customCss.push('li.list-' + p + '-paddingleft-3{padding-left:68px}')
  15827. break
  15828. case 'num':
  15829. case 'num1':
  15830. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
  15831. break
  15832. case 'num2':
  15833. customCss.push('li.list-' + p + '-paddingleft-1{padding-left:35px}')
  15834. customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
  15835. break
  15836. case 'dash':
  15837. customCss.push('li.list-' + p + '-paddingleft{padding-left:35px}')
  15838. break
  15839. case 'dot':
  15840. customCss.push('li.list-' + p + '-paddingleft{padding-left:20px}')
  15841. }
  15842. }
  15843. customCss.push('.list-paddingleft-1{padding-left:0}')
  15844. customCss.push(
  15845. '.list-paddingleft-2{padding-left:' +
  15846. me.options.listDefaultPaddingLeft +
  15847. 'px}'
  15848. )
  15849. customCss.push(
  15850. '.list-paddingleft-3{padding-left:' +
  15851. me.options.listDefaultPaddingLeft * 2 +
  15852. 'px}'
  15853. )
  15854. //如果不给宽度会在自定应样式里出现滚动条
  15855. utils.cssRule(
  15856. 'list',
  15857. 'ol,ul{margin:0;pading:0;' +
  15858. (browser.ie ? '' : 'width:95%') +
  15859. '}li{clear:both;}' +
  15860. customCss.join('\n'),
  15861. me.document
  15862. )
  15863. })
  15864. //单独处理剪切的问题
  15865. me.ready(function () {
  15866. domUtils.on(me.body, 'cut', function () {
  15867. setTimeout(function () {
  15868. var rng = me.selection.getRange(),
  15869. li
  15870. //trace:3416
  15871. if (!rng.collapsed) {
  15872. if (
  15873. (li = domUtils.findParentByTagName(
  15874. rng.startContainer,
  15875. 'li',
  15876. true
  15877. ))
  15878. ) {
  15879. if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
  15880. var pn = li.parentNode,
  15881. node
  15882. if ((node = pn.previousSibling)) {
  15883. domUtils.remove(pn)
  15884. rng.setStartAtLast(node).collapse(true)
  15885. rng.select(true)
  15886. } else if ((node = pn.nextSibling)) {
  15887. domUtils.remove(pn)
  15888. rng.setStartAtFirst(node).collapse(true)
  15889. rng.select(true)
  15890. } else {
  15891. var tmpNode = me.document.createElement('p')
  15892. domUtils.fillNode(me.document, tmpNode)
  15893. pn.parentNode.insertBefore(tmpNode, pn)
  15894. domUtils.remove(pn)
  15895. rng.setStart(tmpNode, 0).collapse(true)
  15896. rng.select(true)
  15897. }
  15898. }
  15899. }
  15900. }
  15901. })
  15902. })
  15903. })
  15904. function getStyle(node) {
  15905. var cls = node.className
  15906. if (domUtils.hasClass(node, /custom_/)) {
  15907. return cls.match(/custom_(\w+)/)[1]
  15908. }
  15909. return domUtils.getStyle(node, 'list-style-type')
  15910. }
  15911. me.addListener('beforepaste', function (type, html) {
  15912. var me = this,
  15913. rng = me.selection.getRange(),
  15914. li
  15915. var root = UE.htmlparser(html.html, true)
  15916. if ((li = domUtils.findParentByTagName(rng.startContainer, 'li', true))) {
  15917. var list = li.parentNode,
  15918. tagName = list.tagName == 'OL' ? 'ul' : 'ol'
  15919. utils.each(root.getNodesByTagName(tagName), function (n) {
  15920. n.tagName = list.tagName
  15921. n.setAttr()
  15922. if (n.parentNode === root) {
  15923. type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
  15924. } else {
  15925. var className = n.parentNode.getAttr('class')
  15926. if (className && /custom_/.test(className)) {
  15927. type = className.match(/custom_(\w+)/)[1]
  15928. } else {
  15929. type = n.parentNode.getStyle('list-style-type')
  15930. }
  15931. if (!type) {
  15932. type = list.tagName == 'OL' ? 'decimal' : 'disc'
  15933. }
  15934. }
  15935. var index = utils.indexOf(listStyle[list.tagName], type)
  15936. if (n.parentNode !== root)
  15937. index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
  15938. var currentStyle = listStyle[list.tagName][index]
  15939. if (customStyle[currentStyle]) {
  15940. n.setAttr('class', 'custom_' + currentStyle)
  15941. } else {
  15942. n.setStyle('list-style-type', currentStyle)
  15943. }
  15944. })
  15945. }
  15946. html.html = root.toHtml()
  15947. })
  15948. //导出时,去掉p标签
  15949. me.getOpt('disablePInList') === true &&
  15950. me.addOutputRule(function (root) {
  15951. utils.each(root.getNodesByTagName('li'), function (li) {
  15952. var newChildrens = [],
  15953. index = 0
  15954. utils.each(li.children, function (n) {
  15955. if (n.tagName == 'p') {
  15956. var tmpNode
  15957. while ((tmpNode = n.children.pop())) {
  15958. newChildrens.splice(index, 0, tmpNode)
  15959. tmpNode.parentNode = li
  15960. lastNode = tmpNode
  15961. }
  15962. tmpNode = newChildrens[newChildrens.length - 1]
  15963. if (
  15964. !tmpNode ||
  15965. tmpNode.type != 'element' ||
  15966. tmpNode.tagName != 'br'
  15967. ) {
  15968. var br = UE.uNode.createElement('br')
  15969. br.parentNode = li
  15970. newChildrens.push(br)
  15971. }
  15972. index = newChildrens.length
  15973. }
  15974. })
  15975. if (newChildrens.length) {
  15976. li.children = newChildrens
  15977. }
  15978. })
  15979. })
  15980. //进入编辑器的li要套p标签
  15981. me.addInputRule(function (root) {
  15982. utils.each(root.getNodesByTagName('li'), function (li) {
  15983. var tmpP = UE.uNode.createElement('p')
  15984. for (var i = 0, ci; (ci = li.children[i]); ) {
  15985. if (ci.type == 'text' || dtd.p[ci.tagName]) {
  15986. tmpP.appendChild(ci)
  15987. } else {
  15988. if (tmpP.firstChild()) {
  15989. li.insertBefore(tmpP, ci)
  15990. tmpP = UE.uNode.createElement('p')
  15991. i = i + 2
  15992. } else {
  15993. i++
  15994. }
  15995. }
  15996. }
  15997. if ((tmpP.firstChild() && !tmpP.parentNode) || !li.firstChild()) {
  15998. li.appendChild(tmpP)
  15999. }
  16000. //trace:3357
  16001. //p不能为空
  16002. if (!tmpP.firstChild()) {
  16003. tmpP.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
  16004. }
  16005. //去掉末尾的空白
  16006. var p = li.firstChild()
  16007. var lastChild = p.lastChild()
  16008. if (
  16009. lastChild &&
  16010. lastChild.type == 'text' &&
  16011. /^\s*$/.test(lastChild.data)
  16012. ) {
  16013. p.removeChild(lastChild)
  16014. }
  16015. })
  16016. if (me.options.autoTransWordToList) {
  16017. var orderlisttype = {
  16018. num1: /^\d+\)/,
  16019. decimal: /^\d+\./,
  16020. 'lower-alpha': /^[a-z]+\)/,
  16021. 'upper-alpha': /^[A-Z]+\./,
  16022. cn: /^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
  16023. cn2: /^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
  16024. },
  16025. unorderlisttype = {
  16026. square: 'n'
  16027. }
  16028. function checkListType(content, container) {
  16029. var span = container.firstChild()
  16030. if (
  16031. span &&
  16032. span.type == 'element' &&
  16033. span.tagName == 'span' &&
  16034. /Wingdings|Symbol/.test(span.getStyle('font-family'))
  16035. ) {
  16036. for (var p in unorderlisttype) {
  16037. if (unorderlisttype[p] == span.data) {
  16038. return p
  16039. }
  16040. }
  16041. return 'disc'
  16042. }
  16043. for (var p in orderlisttype) {
  16044. if (orderlisttype[p].test(content)) {
  16045. return p
  16046. }
  16047. }
  16048. }
  16049. utils.each(root.getNodesByTagName('p'), function (node) {
  16050. if (node.getAttr('class') != 'MsoListParagraph') {
  16051. return
  16052. }
  16053. //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视
  16054. node.setStyle('margin', '')
  16055. node.setStyle('margin-left', '')
  16056. node.setAttr('class', '')
  16057. function appendLi(list, p, type) {
  16058. if (list.tagName == 'ol') {
  16059. if (browser.ie) {
  16060. var first = p.firstChild()
  16061. if (
  16062. first.type == 'element' &&
  16063. first.tagName == 'span' &&
  16064. orderlisttype[type].test(first.innerText())
  16065. ) {
  16066. p.removeChild(first)
  16067. }
  16068. } else {
  16069. p.innerHTML(p.innerHTML().replace(orderlisttype[type], ''))
  16070. }
  16071. } else {
  16072. p.removeChild(p.firstChild())
  16073. }
  16074. var li = UE.uNode.createElement('li')
  16075. li.appendChild(p)
  16076. list.appendChild(li)
  16077. }
  16078. var tmp = node,
  16079. type,
  16080. cacheNode = node
  16081. if (
  16082. node.parentNode.tagName != 'li' &&
  16083. (type = checkListType(node.innerText(), node))
  16084. ) {
  16085. var list = UE.uNode.createElement(
  16086. me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul'
  16087. )
  16088. if (customStyle[type]) {
  16089. list.setAttr('class', 'custom_' + type)
  16090. } else {
  16091. list.setStyle('list-style-type', type)
  16092. }
  16093. while (
  16094. node &&
  16095. node.parentNode.tagName != 'li' &&
  16096. checkListType(node.innerText(), node)
  16097. ) {
  16098. tmp = node.nextSibling()
  16099. if (!tmp) {
  16100. node.parentNode.insertBefore(list, node)
  16101. }
  16102. appendLi(list, node, type)
  16103. node = tmp
  16104. }
  16105. if (!list.parentNode && node && node.parentNode) {
  16106. node.parentNode.insertBefore(list, node)
  16107. }
  16108. }
  16109. var span = cacheNode.firstChild()
  16110. if (
  16111. span &&
  16112. span.type == 'element' &&
  16113. span.tagName == 'span' &&
  16114. /^\s*(&nbsp;)+\s*$/.test(span.innerText())
  16115. ) {
  16116. span.parentNode.removeChild(span)
  16117. }
  16118. })
  16119. }
  16120. })
  16121. //调整索引标签
  16122. me.addListener('contentchange', function () {
  16123. adjustListStyle(me.document)
  16124. })
  16125. function adjustListStyle(doc, ignore) {
  16126. utils.each(domUtils.getElementsByTagName(doc, 'ol ul'), function (node) {
  16127. if (!domUtils.inDoc(node, doc)) return
  16128. var parent = node.parentNode
  16129. if (parent.tagName == node.tagName) {
  16130. var nodeStyleType =
  16131. getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc'),
  16132. parentStyleType =
  16133. getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc')
  16134. if (nodeStyleType == parentStyleType) {
  16135. var styleIndex = utils.indexOf(
  16136. listStyle[node.tagName],
  16137. nodeStyleType
  16138. )
  16139. styleIndex =
  16140. styleIndex + 1 == listStyle[node.tagName].length
  16141. ? 0
  16142. : styleIndex + 1
  16143. setListStyle(node, listStyle[node.tagName][styleIndex])
  16144. }
  16145. }
  16146. var index = 0,
  16147. type = 2
  16148. if (domUtils.hasClass(node, /custom_/)) {
  16149. if (
  16150. !(
  16151. /[ou]l/i.test(parent.tagName) &&
  16152. domUtils.hasClass(parent, /custom_/)
  16153. )
  16154. ) {
  16155. type = 1
  16156. }
  16157. } else {
  16158. if (
  16159. /[ou]l/i.test(parent.tagName) &&
  16160. domUtils.hasClass(parent, /custom_/)
  16161. ) {
  16162. type = 3
  16163. }
  16164. }
  16165. var style = domUtils.getStyle(node, 'list-style-type')
  16166. style && (node.style.cssText = 'list-style-type:' + style)
  16167. node.className =
  16168. utils.trim(node.className.replace(/list-paddingleft-\w+/, '')) +
  16169. ' list-paddingleft-' +
  16170. type
  16171. utils.each(domUtils.getElementsByTagName(node, 'li'), function (li) {
  16172. li.style.cssText && (li.style.cssText = '')
  16173. if (!li.firstChild) {
  16174. domUtils.remove(li)
  16175. return
  16176. }
  16177. if (li.parentNode !== node) {
  16178. return
  16179. }
  16180. index++
  16181. if (domUtils.hasClass(node, /custom_/)) {
  16182. var paddingLeft = 1,
  16183. currentStyle = getStyle(node)
  16184. if (node.tagName == 'OL') {
  16185. if (currentStyle) {
  16186. switch (currentStyle) {
  16187. case 'cn':
  16188. case 'cn1':
  16189. case 'cn2':
  16190. if (
  16191. index > 10 &&
  16192. (index % 10 == 0 || (index > 10 && index < 20))
  16193. ) {
  16194. paddingLeft = 2
  16195. } else if (index > 20) {
  16196. paddingLeft = 3
  16197. }
  16198. break
  16199. case 'num2':
  16200. if (index > 9) {
  16201. paddingLeft = 2
  16202. }
  16203. }
  16204. }
  16205. li.className =
  16206. 'list-' +
  16207. customStyle[currentStyle] +
  16208. index +
  16209. ' ' +
  16210. 'list-' +
  16211. currentStyle +
  16212. '-paddingleft-' +
  16213. paddingLeft
  16214. } else {
  16215. li.className =
  16216. 'list-' +
  16217. customStyle[currentStyle] +
  16218. ' ' +
  16219. 'list-' +
  16220. currentStyle +
  16221. '-paddingleft'
  16222. }
  16223. } else {
  16224. li.className = li.className.replace(/list-[\w\-]+/gi, '')
  16225. }
  16226. var className = li.getAttribute('class')
  16227. if (className !== null && !className.replace(/\s/g, '')) {
  16228. domUtils.removeAttributes(li, 'class')
  16229. }
  16230. })
  16231. !ignore &&
  16232. adjustList(
  16233. node,
  16234. node.tagName.toLowerCase(),
  16235. getStyle(node) || domUtils.getStyle(node, 'list-style-type'),
  16236. true
  16237. )
  16238. })
  16239. }
  16240. function adjustList(list, tag, style, ignoreEmpty) {
  16241. var nextList = list.nextSibling
  16242. if (
  16243. nextList &&
  16244. nextList.nodeType == 1 &&
  16245. nextList.tagName.toLowerCase() == tag &&
  16246. (getStyle(nextList) ||
  16247. domUtils.getStyle(nextList, 'list-style-type') ||
  16248. (tag == 'ol' ? 'decimal' : 'disc')) == style
  16249. ) {
  16250. domUtils.moveChild(nextList, list)
  16251. if (nextList.childNodes.length == 0) {
  16252. domUtils.remove(nextList)
  16253. }
  16254. }
  16255. if (nextList && domUtils.isFillChar(nextList)) {
  16256. domUtils.remove(nextList)
  16257. }
  16258. var preList = list.previousSibling
  16259. if (
  16260. preList &&
  16261. preList.nodeType == 1 &&
  16262. preList.tagName.toLowerCase() == tag &&
  16263. (getStyle(preList) ||
  16264. domUtils.getStyle(preList, 'list-style-type') ||
  16265. (tag == 'ol' ? 'decimal' : 'disc')) == style
  16266. ) {
  16267. domUtils.moveChild(list, preList)
  16268. }
  16269. if (preList && domUtils.isFillChar(preList)) {
  16270. domUtils.remove(preList)
  16271. }
  16272. !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list)
  16273. if (getStyle(list)) {
  16274. adjustListStyle(list.ownerDocument, true)
  16275. }
  16276. }
  16277. function setListStyle(list, style) {
  16278. if (customStyle[style]) {
  16279. list.className = 'custom_' + style
  16280. }
  16281. try {
  16282. domUtils.setStyle(list, 'list-style-type', style)
  16283. } catch (e) {}
  16284. }
  16285. function clearEmptySibling(node) {
  16286. var tmpNode = node.previousSibling
  16287. if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
  16288. domUtils.remove(tmpNode)
  16289. }
  16290. tmpNode = node.nextSibling
  16291. if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
  16292. domUtils.remove(tmpNode)
  16293. }
  16294. }
  16295. me.addListener('keydown', function (type, evt) {
  16296. function preventAndSave() {
  16297. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
  16298. me.fireEvent('contentchange')
  16299. me.undoManger && me.undoManger.save()
  16300. }
  16301. function findList(node, filterFn) {
  16302. while (node && !domUtils.isBody(node)) {
  16303. if (filterFn(node)) {
  16304. return null
  16305. }
  16306. if (node.nodeType == 1 && /[ou]l/i.test(node.tagName)) {
  16307. return node
  16308. }
  16309. node = node.parentNode
  16310. }
  16311. return null
  16312. }
  16313. var keyCode = evt.keyCode || evt.which
  16314. if (keyCode == 13 && !evt.shiftKey) {
  16315. //回车
  16316. var rng = me.selection.getRange(),
  16317. parent = domUtils.findParent(
  16318. rng.startContainer,
  16319. function (node) {
  16320. return domUtils.isBlockElm(node)
  16321. },
  16322. true
  16323. ),
  16324. li = domUtils.findParentByTagName(rng.startContainer, 'li', true)
  16325. if (parent && parent.tagName != 'PRE' && !li) {
  16326. var html = parent.innerHTML.replace(
  16327. new RegExp(domUtils.fillChar, 'g'),
  16328. ''
  16329. )
  16330. if (/^\s*1\s*\.[^\d]/.test(html)) {
  16331. parent.innerHTML = html.replace(/^\s*1\s*\./, '')
  16332. rng.setStartAtLast(parent).collapse(true).select()
  16333. me.__hasEnterExecCommand = true
  16334. me.execCommand('insertorderedlist')
  16335. me.__hasEnterExecCommand = false
  16336. }
  16337. }
  16338. var range = me.selection.getRange(),
  16339. start = findList(range.startContainer, function (node) {
  16340. return node.tagName == 'TABLE'
  16341. }),
  16342. end = range.collapsed
  16343. ? start
  16344. : findList(range.endContainer, function (node) {
  16345. return node.tagName == 'TABLE'
  16346. })
  16347. if (start && end && start === end) {
  16348. if (!range.collapsed) {
  16349. start = domUtils.findParentByTagName(
  16350. range.startContainer,
  16351. 'li',
  16352. true
  16353. )
  16354. end = domUtils.findParentByTagName(range.endContainer, 'li', true)
  16355. if (start && end && start === end) {
  16356. range.deleteContents()
  16357. li = domUtils.findParentByTagName(
  16358. range.startContainer,
  16359. 'li',
  16360. true
  16361. )
  16362. if (li && domUtils.isEmptyBlock(li)) {
  16363. pre = li.previousSibling
  16364. next = li.nextSibling
  16365. p = me.document.createElement('p')
  16366. domUtils.fillNode(me.document, p)
  16367. parentList = li.parentNode
  16368. if (pre && next) {
  16369. range.setStart(next, 0).collapse(true).select(true)
  16370. domUtils.remove(li)
  16371. } else {
  16372. if ((!pre && !next) || !pre) {
  16373. parentList.parentNode.insertBefore(p, parentList)
  16374. } else {
  16375. li.parentNode.parentNode.insertBefore(
  16376. p,
  16377. parentList.nextSibling
  16378. )
  16379. }
  16380. domUtils.remove(li)
  16381. if (!parentList.firstChild) {
  16382. domUtils.remove(parentList)
  16383. }
  16384. range.setStart(p, 0).setCursor()
  16385. }
  16386. preventAndSave()
  16387. return
  16388. }
  16389. } else {
  16390. var tmpRange = range.cloneRange(),
  16391. bk = tmpRange.collapse(false).createBookmark()
  16392. range.deleteContents()
  16393. tmpRange.moveToBookmark(bk)
  16394. var li = domUtils.findParentByTagName(
  16395. tmpRange.startContainer,
  16396. 'li',
  16397. true
  16398. )
  16399. clearEmptySibling(li)
  16400. tmpRange.select()
  16401. preventAndSave()
  16402. return
  16403. }
  16404. }
  16405. li = domUtils.findParentByTagName(range.startContainer, 'li', true)
  16406. if (li) {
  16407. if (domUtils.isEmptyBlock(li)) {
  16408. bk = range.createBookmark()
  16409. var parentList = li.parentNode
  16410. if (li !== parentList.lastChild) {
  16411. domUtils.breakParent(li, parentList)
  16412. clearEmptySibling(li)
  16413. } else {
  16414. parentList.parentNode.insertBefore(li, parentList.nextSibling)
  16415. if (domUtils.isEmptyNode(parentList)) {
  16416. domUtils.remove(parentList)
  16417. }
  16418. }
  16419. //嵌套不处理
  16420. if (!dtd.$list[li.parentNode.tagName]) {
  16421. if (!domUtils.isBlockElm(li.firstChild)) {
  16422. p = me.document.createElement('p')
  16423. li.parentNode.insertBefore(p, li)
  16424. while (li.firstChild) {
  16425. p.appendChild(li.firstChild)
  16426. }
  16427. domUtils.remove(li)
  16428. } else {
  16429. domUtils.remove(li, true)
  16430. }
  16431. }
  16432. range.moveToBookmark(bk).select()
  16433. } else {
  16434. var first = li.firstChild
  16435. if (!first || !domUtils.isBlockElm(first)) {
  16436. var p = me.document.createElement('p')
  16437. !li.firstChild && domUtils.fillNode(me.document, p)
  16438. while (li.firstChild) {
  16439. p.appendChild(li.firstChild)
  16440. }
  16441. li.appendChild(p)
  16442. first = p
  16443. }
  16444. var span = me.document.createElement('span')
  16445. range.insertNode(span)
  16446. domUtils.breakParent(span, li)
  16447. var nextLi = span.nextSibling
  16448. first = nextLi.firstChild
  16449. if (!first) {
  16450. p = me.document.createElement('p')
  16451. domUtils.fillNode(me.document, p)
  16452. nextLi.appendChild(p)
  16453. first = p
  16454. }
  16455. if (domUtils.isEmptyNode(first)) {
  16456. first.innerHTML = ''
  16457. domUtils.fillNode(me.document, first)
  16458. }
  16459. range.setStart(first, 0).collapse(true).shrinkBoundary().select()
  16460. domUtils.remove(span)
  16461. var pre = nextLi.previousSibling
  16462. if (pre && domUtils.isEmptyBlock(pre)) {
  16463. pre.innerHTML = '<p></p>'
  16464. domUtils.fillNode(me.document, pre.firstChild)
  16465. }
  16466. }
  16467. // }
  16468. preventAndSave()
  16469. }
  16470. }
  16471. }
  16472. if (keyCode == 8) {
  16473. //修中ie中li下的问题
  16474. range = me.selection.getRange()
  16475. if (range.collapsed && domUtils.isStartInblock(range)) {
  16476. tmpRange = range.cloneRange().trimBoundary()
  16477. li = domUtils.findParentByTagName(range.startContainer, 'li', true)
  16478. //要在li的最左边,才能处理
  16479. if (li && domUtils.isStartInblock(tmpRange)) {
  16480. start = domUtils.findParentByTagName(
  16481. range.startContainer,
  16482. 'p',
  16483. true
  16484. )
  16485. if (start && start !== li.firstChild) {
  16486. var parentList = domUtils.findParentByTagName(start, ['ol', 'ul'])
  16487. domUtils.breakParent(start, parentList)
  16488. clearEmptySibling(start)
  16489. me.fireEvent('contentchange')
  16490. range.setStart(start, 0).setCursor(false, true)
  16491. me.fireEvent('saveScene')
  16492. domUtils.preventDefault(evt)
  16493. return
  16494. }
  16495. if (li && (pre = li.previousSibling)) {
  16496. if (keyCode == 46 && li.childNodes.length) {
  16497. return
  16498. }
  16499. //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
  16500. if (dtd.$list[pre.tagName]) {
  16501. pre = pre.lastChild
  16502. }
  16503. me.undoManger && me.undoManger.save()
  16504. first = li.firstChild
  16505. if (domUtils.isBlockElm(first)) {
  16506. if (domUtils.isEmptyNode(first)) {
  16507. // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
  16508. pre.appendChild(first)
  16509. range.setStart(first, 0).setCursor(false, true)
  16510. //first不是唯一的节点
  16511. while (li.firstChild) {
  16512. pre.appendChild(li.firstChild)
  16513. }
  16514. } else {
  16515. span = me.document.createElement('span')
  16516. range.insertNode(span)
  16517. //判断pre是否是空的节点,如果是<p><br/></p>类型的空节点,干掉p标签防止它占位
  16518. if (domUtils.isEmptyBlock(pre)) {
  16519. pre.innerHTML = ''
  16520. }
  16521. domUtils.moveChild(li, pre)
  16522. range.setStartBefore(span).collapse(true).select(true)
  16523. domUtils.remove(span)
  16524. }
  16525. } else {
  16526. if (domUtils.isEmptyNode(li)) {
  16527. var p = me.document.createElement('p')
  16528. pre.appendChild(p)
  16529. range.setStart(p, 0).setCursor()
  16530. // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
  16531. } else {
  16532. range
  16533. .setEnd(pre, pre.childNodes.length)
  16534. .collapse()
  16535. .select(true)
  16536. while (li.firstChild) {
  16537. pre.appendChild(li.firstChild)
  16538. }
  16539. }
  16540. }
  16541. domUtils.remove(li)
  16542. me.fireEvent('contentchange')
  16543. me.fireEvent('saveScene')
  16544. domUtils.preventDefault(evt)
  16545. return
  16546. }
  16547. //trace:980
  16548. if (li && !li.previousSibling) {
  16549. var parentList = li.parentNode
  16550. var bk = range.createBookmark()
  16551. if (domUtils.isTagNode(parentList.parentNode, 'ol ul')) {
  16552. parentList.parentNode.insertBefore(li, parentList)
  16553. if (domUtils.isEmptyNode(parentList)) {
  16554. domUtils.remove(parentList)
  16555. }
  16556. } else {
  16557. while (li.firstChild) {
  16558. parentList.parentNode.insertBefore(li.firstChild, parentList)
  16559. }
  16560. domUtils.remove(li)
  16561. if (domUtils.isEmptyNode(parentList)) {
  16562. domUtils.remove(parentList)
  16563. }
  16564. }
  16565. range.moveToBookmark(bk).setCursor(false, true)
  16566. me.fireEvent('contentchange')
  16567. me.fireEvent('saveScene')
  16568. domUtils.preventDefault(evt)
  16569. return
  16570. }
  16571. }
  16572. }
  16573. }
  16574. })
  16575. me.addListener('keyup', function (type, evt) {
  16576. var keyCode = evt.keyCode || evt.which
  16577. if (keyCode == 8) {
  16578. var rng = me.selection.getRange(),
  16579. list
  16580. if (
  16581. (list = domUtils.findParentByTagName(
  16582. rng.startContainer,
  16583. ['ol', 'ul'],
  16584. true
  16585. ))
  16586. ) {
  16587. adjustList(
  16588. list,
  16589. list.tagName.toLowerCase(),
  16590. getStyle(list) ||
  16591. domUtils.getComputedStyle(list, 'list-style-type'),
  16592. true
  16593. )
  16594. }
  16595. }
  16596. })
  16597. //处理tab键
  16598. me.addListener('tabkeydown', function () {
  16599. var range = me.selection.getRange()
  16600. //控制级数
  16601. function checkLevel(li) {
  16602. if (me.options.maxListLevel != -1) {
  16603. var level = li.parentNode,
  16604. levelNum = 0
  16605. while (/[ou]l/i.test(level.tagName)) {
  16606. levelNum++
  16607. level = level.parentNode
  16608. }
  16609. if (levelNum >= me.options.maxListLevel) {
  16610. return true
  16611. }
  16612. }
  16613. }
  16614. //只以开始为准
  16615. //todo 后续改进
  16616. var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
  16617. if (li) {
  16618. var bk
  16619. if (range.collapsed) {
  16620. if (checkLevel(li)) return true
  16621. var parentLi = li.parentNode,
  16622. list = me.document.createElement(parentLi.tagName),
  16623. index = utils.indexOf(
  16624. listStyle[list.tagName],
  16625. getStyle(parentLi) ||
  16626. domUtils.getComputedStyle(parentLi, 'list-style-type')
  16627. )
  16628. index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
  16629. var currentStyle = listStyle[list.tagName][index]
  16630. setListStyle(list, currentStyle)
  16631. if (domUtils.isStartInblock(range)) {
  16632. me.fireEvent('saveScene')
  16633. bk = range.createBookmark()
  16634. parentLi.insertBefore(list, li)
  16635. list.appendChild(li)
  16636. adjustList(list, list.tagName.toLowerCase(), currentStyle)
  16637. me.fireEvent('contentchange')
  16638. range.moveToBookmark(bk).select(true)
  16639. return true
  16640. }
  16641. } else {
  16642. me.fireEvent('saveScene')
  16643. bk = range.createBookmark()
  16644. for (
  16645. var i = 0, closeList, parents = domUtils.findParents(li), ci;
  16646. (ci = parents[i++]);
  16647. ) {
  16648. if (domUtils.isTagNode(ci, 'ol ul')) {
  16649. closeList = ci
  16650. break
  16651. }
  16652. }
  16653. var current = li
  16654. if (bk.end) {
  16655. while (
  16656. current &&
  16657. !(
  16658. domUtils.getPosition(current, bk.end) &
  16659. domUtils.POSITION_FOLLOWING
  16660. )
  16661. ) {
  16662. if (checkLevel(current)) {
  16663. current = domUtils.getNextDomNode(
  16664. current,
  16665. false,
  16666. null,
  16667. function (node) {
  16668. return node !== closeList
  16669. }
  16670. )
  16671. continue
  16672. }
  16673. var parentLi = current.parentNode,
  16674. list = me.document.createElement(parentLi.tagName),
  16675. index = utils.indexOf(
  16676. listStyle[list.tagName],
  16677. getStyle(parentLi) ||
  16678. domUtils.getComputedStyle(parentLi, 'list-style-type')
  16679. )
  16680. var currentIndex =
  16681. index + 1 == listStyle[list.tagName].length ? 0 : index + 1
  16682. var currentStyle = listStyle[list.tagName][currentIndex]
  16683. setListStyle(list, currentStyle)
  16684. parentLi.insertBefore(list, current)
  16685. while (
  16686. current &&
  16687. !(
  16688. domUtils.getPosition(current, bk.end) &
  16689. domUtils.POSITION_FOLLOWING
  16690. )
  16691. ) {
  16692. li = current.nextSibling
  16693. list.appendChild(current)
  16694. if (!li || domUtils.isTagNode(li, 'ol ul')) {
  16695. if (li) {
  16696. while ((li = li.firstChild)) {
  16697. if (li.tagName == 'LI') {
  16698. break
  16699. }
  16700. }
  16701. } else {
  16702. li = domUtils.getNextDomNode(
  16703. current,
  16704. false,
  16705. null,
  16706. function (node) {
  16707. return node !== closeList
  16708. }
  16709. )
  16710. }
  16711. break
  16712. }
  16713. current = li
  16714. }
  16715. adjustList(list, list.tagName.toLowerCase(), currentStyle)
  16716. current = li
  16717. }
  16718. }
  16719. me.fireEvent('contentchange')
  16720. range.moveToBookmark(bk).select()
  16721. return true
  16722. }
  16723. }
  16724. })
  16725. function getLi(start) {
  16726. while (start && !domUtils.isBody(start)) {
  16727. if (start.nodeName == 'TABLE') {
  16728. return null
  16729. }
  16730. if (start.nodeName == 'LI') {
  16731. return start
  16732. }
  16733. start = start.parentNode
  16734. }
  16735. }
  16736. /**
  16737. * 有序列表,与“insertunorderedlist”命令互斥
  16738. * @command insertorderedlist
  16739. * @method execCommand
  16740. * @param { String } command 命令字符串
  16741. * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
  16742. * @example
  16743. * ```javascript
  16744. * editor.execCommand( 'insertorderedlist','decimal');
  16745. * ```
  16746. */
  16747. /**
  16748. * 查询当前选区内容是否有序列表
  16749. * @command insertorderedlist
  16750. * @method queryCommandState
  16751. * @param { String } cmd 命令字符串
  16752. * @return { int } 如果当前选区是有序列表返回1,否则返回0
  16753. * @example
  16754. * ```javascript
  16755. * editor.queryCommandState( 'insertorderedlist' );
  16756. * ```
  16757. */
  16758. /**
  16759. * 查询当前选区内容是否有序列表
  16760. * @command insertorderedlist
  16761. * @method queryCommandValue
  16762. * @param { String } cmd 命令字符串
  16763. * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
  16764. * @example
  16765. * ```javascript
  16766. * editor.queryCommandValue( 'insertorderedlist' );
  16767. * ```
  16768. */
  16769. /**
  16770. * 无序列表,与“insertorderedlist”命令互斥
  16771. * @command insertunorderedlist
  16772. * @method execCommand
  16773. * @param { String } command 命令字符串
  16774. * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
  16775. * @example
  16776. * ```javascript
  16777. * editor.execCommand( 'insertunorderedlist','circle');
  16778. * ```
  16779. */
  16780. /**
  16781. * 查询当前是否有word文档粘贴进来的图片
  16782. * @command insertunorderedlist
  16783. * @method insertunorderedlist
  16784. * @param { String } command 命令字符串
  16785. * @return { int } 如果当前选区是无序列表返回1,否则返回0
  16786. * @example
  16787. * ```javascript
  16788. * editor.queryCommandState( 'insertunorderedlist' );
  16789. * ```
  16790. */
  16791. /**
  16792. * 查询当前选区内容是否有序列表
  16793. * @command insertunorderedlist
  16794. * @method queryCommandValue
  16795. * @param { String } command 命令字符串
  16796. * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
  16797. * @example
  16798. * ```javascript
  16799. * editor.queryCommandValue( 'insertunorderedlist' );
  16800. * ```
  16801. */
  16802. me.commands['insertorderedlist'] = me.commands['insertunorderedlist'] = {
  16803. execCommand: function (command, style) {
  16804. if (!style) {
  16805. style =
  16806. command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc'
  16807. }
  16808. var me = this,
  16809. range = this.selection.getRange(),
  16810. filterFn = function (node) {
  16811. return node.nodeType == 1
  16812. ? node.tagName.toLowerCase() != 'br'
  16813. : !domUtils.isWhitespace(node)
  16814. },
  16815. tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul',
  16816. frag = me.document.createDocumentFragment()
  16817. //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
  16818. //range.shrinkBoundary();//.adjustmentBoundary();
  16819. range.adjustmentBoundary().shrinkBoundary()
  16820. var bko = range.createBookmark(true),
  16821. start = getLi(me.document.getElementById(bko.start)),
  16822. modifyStart = 0,
  16823. end = getLi(me.document.getElementById(bko.end)),
  16824. modifyEnd = 0,
  16825. startParent,
  16826. endParent,
  16827. list,
  16828. tmp
  16829. if (start || end) {
  16830. start && (startParent = start.parentNode)
  16831. if (!bko.end) {
  16832. end = start
  16833. }
  16834. end && (endParent = end.parentNode)
  16835. if (startParent === endParent) {
  16836. while (start !== end) {
  16837. tmp = start
  16838. start = start.nextSibling
  16839. if (!domUtils.isBlockElm(tmp.firstChild)) {
  16840. var p = me.document.createElement('p')
  16841. while (tmp.firstChild) {
  16842. p.appendChild(tmp.firstChild)
  16843. }
  16844. tmp.appendChild(p)
  16845. }
  16846. frag.appendChild(tmp)
  16847. }
  16848. tmp = me.document.createElement('span')
  16849. startParent.insertBefore(tmp, end)
  16850. if (!domUtils.isBlockElm(end.firstChild)) {
  16851. p = me.document.createElement('p')
  16852. while (end.firstChild) {
  16853. p.appendChild(end.firstChild)
  16854. }
  16855. end.appendChild(p)
  16856. }
  16857. frag.appendChild(end)
  16858. domUtils.breakParent(tmp, startParent)
  16859. if (domUtils.isEmptyNode(tmp.previousSibling)) {
  16860. domUtils.remove(tmp.previousSibling)
  16861. }
  16862. if (domUtils.isEmptyNode(tmp.nextSibling)) {
  16863. domUtils.remove(tmp.nextSibling)
  16864. }
  16865. var nodeStyle =
  16866. getStyle(startParent) ||
  16867. domUtils.getComputedStyle(startParent, 'list-style-type') ||
  16868. (command.toLowerCase() == 'insertorderedlist'
  16869. ? 'decimal'
  16870. : 'disc')
  16871. if (
  16872. startParent.tagName.toLowerCase() == tag &&
  16873. nodeStyle == style
  16874. ) {
  16875. for (
  16876. var i = 0, ci, tmpFrag = me.document.createDocumentFragment();
  16877. (ci = frag.firstChild);
  16878. ) {
  16879. if (domUtils.isTagNode(ci, 'ol ul')) {
  16880. // 删除时,子列表不处理
  16881. // utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
  16882. // while(li.firstChild){
  16883. // tmpFrag.appendChild(li.firstChild);
  16884. // }
  16885. //
  16886. // });
  16887. tmpFrag.appendChild(ci)
  16888. } else {
  16889. while (ci.firstChild) {
  16890. tmpFrag.appendChild(ci.firstChild)
  16891. domUtils.remove(ci)
  16892. }
  16893. }
  16894. }
  16895. tmp.parentNode.insertBefore(tmpFrag, tmp)
  16896. } else {
  16897. list = me.document.createElement(tag)
  16898. setListStyle(list, style)
  16899. list.appendChild(frag)
  16900. tmp.parentNode.insertBefore(list, tmp)
  16901. }
  16902. domUtils.remove(tmp)
  16903. list && adjustList(list, tag, style)
  16904. range.moveToBookmark(bko).select()
  16905. return
  16906. }
  16907. //开始
  16908. if (start) {
  16909. while (start) {
  16910. tmp = start.nextSibling
  16911. if (domUtils.isTagNode(start, 'ol ul')) {
  16912. frag.appendChild(start)
  16913. } else {
  16914. var tmpfrag = me.document.createDocumentFragment(),
  16915. hasBlock = 0
  16916. while (start.firstChild) {
  16917. if (domUtils.isBlockElm(start.firstChild)) {
  16918. hasBlock = 1
  16919. }
  16920. tmpfrag.appendChild(start.firstChild)
  16921. }
  16922. if (!hasBlock) {
  16923. var tmpP = me.document.createElement('p')
  16924. tmpP.appendChild(tmpfrag)
  16925. frag.appendChild(tmpP)
  16926. } else {
  16927. frag.appendChild(tmpfrag)
  16928. }
  16929. domUtils.remove(start)
  16930. }
  16931. start = tmp
  16932. }
  16933. startParent.parentNode.insertBefore(frag, startParent.nextSibling)
  16934. if (domUtils.isEmptyNode(startParent)) {
  16935. range.setStartBefore(startParent)
  16936. domUtils.remove(startParent)
  16937. } else {
  16938. range.setStartAfter(startParent)
  16939. }
  16940. modifyStart = 1
  16941. }
  16942. if (end && domUtils.inDoc(endParent, me.document)) {
  16943. //结束
  16944. start = endParent.firstChild
  16945. while (start && start !== end) {
  16946. tmp = start.nextSibling
  16947. if (domUtils.isTagNode(start, 'ol ul')) {
  16948. frag.appendChild(start)
  16949. } else {
  16950. tmpfrag = me.document.createDocumentFragment()
  16951. hasBlock = 0
  16952. while (start.firstChild) {
  16953. if (domUtils.isBlockElm(start.firstChild)) {
  16954. hasBlock = 1
  16955. }
  16956. tmpfrag.appendChild(start.firstChild)
  16957. }
  16958. if (!hasBlock) {
  16959. tmpP = me.document.createElement('p')
  16960. tmpP.appendChild(tmpfrag)
  16961. frag.appendChild(tmpP)
  16962. } else {
  16963. frag.appendChild(tmpfrag)
  16964. }
  16965. domUtils.remove(start)
  16966. }
  16967. start = tmp
  16968. }
  16969. var tmpDiv = domUtils.createElement(me.document, 'div', {
  16970. tmpDiv: 1
  16971. })
  16972. domUtils.moveChild(end, tmpDiv)
  16973. frag.appendChild(tmpDiv)
  16974. domUtils.remove(end)
  16975. endParent.parentNode.insertBefore(frag, endParent)
  16976. range.setEndBefore(endParent)
  16977. if (domUtils.isEmptyNode(endParent)) {
  16978. domUtils.remove(endParent)
  16979. }
  16980. modifyEnd = 1
  16981. }
  16982. }
  16983. if (!modifyStart) {
  16984. range.setStartBefore(me.document.getElementById(bko.start))
  16985. }
  16986. if (bko.end && !modifyEnd) {
  16987. range.setEndAfter(me.document.getElementById(bko.end))
  16988. }
  16989. range.enlarge(true, function (node) {
  16990. return notExchange[node.tagName]
  16991. })
  16992. frag = me.document.createDocumentFragment()
  16993. var bk = range.createBookmark(),
  16994. current = domUtils.getNextDomNode(bk.start, false, filterFn),
  16995. tmpRange = range.cloneRange(),
  16996. tmpNode,
  16997. block = domUtils.isBlockElm
  16998. while (
  16999. current &&
  17000. current !== bk.end &&
  17001. domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING
  17002. ) {
  17003. if (current.nodeType == 3 || dtd.li[current.tagName]) {
  17004. if (current.nodeType == 1 && dtd.$list[current.tagName]) {
  17005. while (current.firstChild) {
  17006. frag.appendChild(current.firstChild)
  17007. }
  17008. tmpNode = domUtils.getNextDomNode(current, false, filterFn)
  17009. domUtils.remove(current)
  17010. current = tmpNode
  17011. continue
  17012. }
  17013. tmpNode = current
  17014. tmpRange.setStartBefore(current)
  17015. while (
  17016. current &&
  17017. current !== bk.end &&
  17018. (!block(current) || domUtils.isBookmarkNode(current))
  17019. ) {
  17020. tmpNode = current
  17021. current = domUtils.getNextDomNode(
  17022. current,
  17023. false,
  17024. null,
  17025. function (node) {
  17026. return !notExchange[node.tagName]
  17027. }
  17028. )
  17029. }
  17030. if (current && block(current)) {
  17031. tmp = domUtils.getNextDomNode(tmpNode, false, filterFn)
  17032. if (tmp && domUtils.isBookmarkNode(tmp)) {
  17033. current = domUtils.getNextDomNode(tmp, false, filterFn)
  17034. tmpNode = tmp
  17035. }
  17036. }
  17037. tmpRange.setEndAfter(tmpNode)
  17038. current = domUtils.getNextDomNode(tmpNode, false, filterFn)
  17039. var li = range.document.createElement('li')
  17040. li.appendChild(tmpRange.extractContents())
  17041. if (domUtils.isEmptyNode(li)) {
  17042. var tmpNode = range.document.createElement('p')
  17043. while (li.firstChild) {
  17044. tmpNode.appendChild(li.firstChild)
  17045. }
  17046. li.appendChild(tmpNode)
  17047. }
  17048. frag.appendChild(li)
  17049. } else {
  17050. current = domUtils.getNextDomNode(current, true, filterFn)
  17051. }
  17052. }
  17053. range.moveToBookmark(bk).collapse(true)
  17054. list = me.document.createElement(tag)
  17055. setListStyle(list, style)
  17056. list.appendChild(frag)
  17057. range.insertNode(list)
  17058. //当前list上下看能否合并
  17059. adjustList(list, tag, style)
  17060. //去掉冗余的tmpDiv
  17061. for (
  17062. var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div');
  17063. (ci = tmpDivs[i++]);
  17064. ) {
  17065. if (ci.getAttribute('tmpDiv')) {
  17066. domUtils.remove(ci, true)
  17067. }
  17068. }
  17069. range.moveToBookmark(bko).select()
  17070. },
  17071. queryCommandState: function (command) {
  17072. var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
  17073. var path = this.selection.getStartElementPath()
  17074. for (var i = 0, ci; (ci = path[i++]); ) {
  17075. if (ci.nodeName == 'TABLE') {
  17076. return 0
  17077. }
  17078. if (tag == ci.nodeName.toLowerCase()) {
  17079. return 1
  17080. }
  17081. }
  17082. return 0
  17083. },
  17084. queryCommandValue: function (command) {
  17085. var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
  17086. var path = this.selection.getStartElementPath(),
  17087. node
  17088. for (var i = 0, ci; (ci = path[i++]); ) {
  17089. if (ci.nodeName == 'TABLE') {
  17090. node = null
  17091. break
  17092. }
  17093. if (tag == ci.nodeName.toLowerCase()) {
  17094. node = ci
  17095. break
  17096. }
  17097. }
  17098. return node
  17099. ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type')
  17100. : null
  17101. }
  17102. }
  17103. }
  17104. // plugins/source.js
  17105. /**
  17106. * 源码编辑插件
  17107. * @file
  17108. * @since 1.2.6.1
  17109. */
  17110. ;(function () {
  17111. var sourceEditors = {
  17112. textarea: function (editor, holder) {
  17113. var textarea = holder.ownerDocument.createElement('textarea')
  17114. textarea.style.cssText =
  17115. 'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;'
  17116. // todo: IE下只有onresize属性可用... 很纠结
  17117. if (browser.ie && browser.version < 8) {
  17118. textarea.style.width = holder.offsetWidth + 'px'
  17119. textarea.style.height = holder.offsetHeight + 'px'
  17120. holder.onresize = function () {
  17121. textarea.style.width = holder.offsetWidth + 'px'
  17122. textarea.style.height = holder.offsetHeight + 'px'
  17123. }
  17124. }
  17125. holder.appendChild(textarea)
  17126. return {
  17127. setContent: function (content) {
  17128. textarea.value = content
  17129. },
  17130. getContent: function () {
  17131. return textarea.value
  17132. },
  17133. select: function () {
  17134. var range
  17135. if (browser.ie) {
  17136. range = textarea.createTextRange()
  17137. range.collapse(true)
  17138. range.select()
  17139. } else {
  17140. //todo: chrome下无法设置焦点
  17141. textarea.setSelectionRange(0, 0)
  17142. textarea.focus()
  17143. }
  17144. },
  17145. dispose: function () {
  17146. holder.removeChild(textarea)
  17147. // todo
  17148. holder.onresize = null
  17149. textarea = null
  17150. holder = null
  17151. }
  17152. }
  17153. },
  17154. codemirror: function (editor, holder) {
  17155. var codeEditor = window.CodeMirror(holder, {
  17156. mode: 'text/html',
  17157. tabMode: 'indent',
  17158. lineNumbers: true,
  17159. lineWrapping: true
  17160. })
  17161. var dom = codeEditor.getWrapperElement()
  17162. dom.style.cssText =
  17163. 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;'
  17164. codeEditor.getScrollerElement().style.cssText =
  17165. 'position:absolute;left:0;top:0;width:100%;height:100%;'
  17166. codeEditor.refresh()
  17167. return {
  17168. getCodeMirror: function () {
  17169. return codeEditor
  17170. },
  17171. setContent: function (content) {
  17172. codeEditor.setValue(content)
  17173. },
  17174. getContent: function () {
  17175. return codeEditor.getValue()
  17176. },
  17177. select: function () {
  17178. codeEditor.focus()
  17179. },
  17180. dispose: function () {
  17181. holder.removeChild(dom)
  17182. dom = null
  17183. codeEditor = null
  17184. }
  17185. }
  17186. }
  17187. }
  17188. UE.plugins['source'] = function () {
  17189. var me = this
  17190. var opt = this.options
  17191. var sourceMode = false
  17192. var sourceEditor
  17193. var orgSetContent
  17194. opt.sourceEditor = browser.ie
  17195. ? 'textarea'
  17196. : opt.sourceEditor || 'codemirror'
  17197. me.setOpt({
  17198. sourceEditorFirst: false
  17199. })
  17200. function createSourceEditor(holder) {
  17201. return sourceEditors[
  17202. opt.sourceEditor == 'codemirror' && window.CodeMirror
  17203. ? 'codemirror'
  17204. : 'textarea'
  17205. ](me, holder)
  17206. }
  17207. var bakCssText
  17208. //解决在源码模式下getContent不能得到最新的内容问题
  17209. var oldGetContent, bakAddress
  17210. /**
  17211. * 切换源码模式和编辑模式
  17212. * @command source
  17213. * @method execCommand
  17214. * @param { String } cmd 命令字符串
  17215. * @example
  17216. * ```javascript
  17217. * editor.execCommand( 'source');
  17218. * ```
  17219. */
  17220. /**
  17221. * 查询当前编辑区域的状态是源码模式还是可视化模式
  17222. * @command source
  17223. * @method queryCommandState
  17224. * @param { String } cmd 命令字符串
  17225. * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
  17226. * @example
  17227. * ```javascript
  17228. * editor.queryCommandState( 'source' );
  17229. * ```
  17230. */
  17231. me.commands['source'] = {
  17232. execCommand: function () {
  17233. sourceMode = !sourceMode
  17234. if (sourceMode) {
  17235. bakAddress = me.selection.getRange().createAddress(false, true)
  17236. me.undoManger && me.undoManger.save(true)
  17237. if (browser.gecko) {
  17238. me.body.contentEditable = false
  17239. }
  17240. bakCssText = me.iframe.style.cssText
  17241. me.iframe.style.cssText +=
  17242. 'position:absolute;left:-32768px;top:-32768px;'
  17243. me.fireEvent('beforegetcontent')
  17244. var root = UE.htmlparser(me.body.innerHTML)
  17245. me.filterOutputRule(root)
  17246. root.traversal(function (node) {
  17247. if (node.type == 'element') {
  17248. switch (node.tagName) {
  17249. case 'td':
  17250. case 'th':
  17251. case 'caption':
  17252. if (node.children && node.children.length == 1) {
  17253. if (node.firstChild().tagName == 'br') {
  17254. node.removeChild(node.firstChild())
  17255. }
  17256. }
  17257. break
  17258. case 'pre':
  17259. node.innerText(node.innerText().replace(/&nbsp;/g, ' '))
  17260. }
  17261. }
  17262. })
  17263. me.fireEvent('aftergetcontent')
  17264. var content = root.toHtml(true)
  17265. sourceEditor = createSourceEditor(me.iframe.parentNode)
  17266. sourceEditor.setContent(content)
  17267. orgSetContent = me.setContent
  17268. me.setContent = function (html) {
  17269. //这里暂时不触发事件,防止报错
  17270. var root = UE.htmlparser(html)
  17271. me.filterInputRule(root)
  17272. html = root.toHtml()
  17273. sourceEditor.setContent(html)
  17274. }
  17275. setTimeout(function () {
  17276. sourceEditor.select()
  17277. me.addListener('fullscreenchanged', function () {
  17278. try {
  17279. sourceEditor.getCodeMirror().refresh()
  17280. } catch (e) {}
  17281. })
  17282. })
  17283. //重置getContent,源码模式下取值也能是最新的数据
  17284. oldGetContent = me.getContent
  17285. me.getContent = function () {
  17286. return (
  17287. sourceEditor.getContent() ||
  17288. '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  17289. )
  17290. }
  17291. } else {
  17292. me.iframe.style.cssText = bakCssText
  17293. var cont =
  17294. sourceEditor.getContent() ||
  17295. '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  17296. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  17297. cont = cont.replace(
  17298. new RegExp('[\\r\\t\\n ]*</?(\\w+)\\s*(?:[^>]*)>', 'g'),
  17299. function (a, b) {
  17300. if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
  17301. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, '')
  17302. }
  17303. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, '')
  17304. }
  17305. )
  17306. me.setContent = orgSetContent
  17307. me.setContent(cont)
  17308. sourceEditor.dispose()
  17309. sourceEditor = null
  17310. //还原getContent方法
  17311. me.getContent = oldGetContent
  17312. var first = me.body.firstChild
  17313. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  17314. if (!first) {
  17315. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  17316. first = me.body.firstChild
  17317. }
  17318. //要在ifm为显示时ff才能取到selection,否则报错
  17319. //这里不能比较位置了
  17320. me.undoManger && me.undoManger.save(true)
  17321. if (browser.gecko) {
  17322. var input = document.createElement('input')
  17323. input.style.cssText = 'position:absolute;left:0;top:-32768px'
  17324. document.body.appendChild(input)
  17325. me.body.contentEditable = false
  17326. setTimeout(function () {
  17327. domUtils.setViewportOffset(input, { left: -32768, top: 0 })
  17328. input.focus()
  17329. setTimeout(function () {
  17330. me.body.contentEditable = true
  17331. me.selection.getRange().moveToAddress(bakAddress).select(true)
  17332. domUtils.remove(input)
  17333. })
  17334. })
  17335. } else {
  17336. //ie下有可能报错,比如在代码顶头的情况
  17337. try {
  17338. me.selection.getRange().moveToAddress(bakAddress).select(true)
  17339. } catch (e) {}
  17340. }
  17341. }
  17342. this.fireEvent('sourcemodechanged', sourceMode)
  17343. },
  17344. queryCommandState: function () {
  17345. return sourceMode | 0
  17346. },
  17347. notNeedUndo: 1
  17348. }
  17349. var oldQueryCommandState = me.queryCommandState
  17350. me.queryCommandState = function (cmdName) {
  17351. cmdName = cmdName.toLowerCase()
  17352. if (sourceMode) {
  17353. //源码模式下可以开启的命令
  17354. return cmdName in
  17355. {
  17356. source: 1,
  17357. fullscreen: 1
  17358. }
  17359. ? 1
  17360. : -1
  17361. }
  17362. return oldQueryCommandState.apply(this, arguments)
  17363. }
  17364. if (opt.sourceEditor == 'codemirror') {
  17365. me.addListener('ready', function () {
  17366. utils.loadFile(
  17367. document,
  17368. {
  17369. src:
  17370. opt.codeMirrorJsUrl ||
  17371. opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.js',
  17372. tag: 'script',
  17373. type: 'text/javascript',
  17374. defer: 'defer'
  17375. },
  17376. function () {
  17377. if (opt.sourceEditorFirst) {
  17378. setTimeout(function () {
  17379. me.execCommand('source')
  17380. }, 0)
  17381. }
  17382. }
  17383. )
  17384. utils.loadFile(document, {
  17385. tag: 'link',
  17386. rel: 'stylesheet',
  17387. type: 'text/css',
  17388. href:
  17389. opt.codeMirrorCssUrl ||
  17390. opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.css'
  17391. })
  17392. })
  17393. }
  17394. }
  17395. })()
  17396. // plugins/enterkey.js
  17397. ///import core
  17398. ///import plugins/undo.js
  17399. ///commands 设置回车标签p或br
  17400. ///commandsName EnterKey
  17401. ///commandsTitle 设置回车标签p或br
  17402. /**
  17403. * @description 处理回车
  17404. * @author zhanyi
  17405. */
  17406. UE.plugins['enterkey'] = function () {
  17407. var hTag,
  17408. me = this,
  17409. tag = me.options.enterTag
  17410. me.addListener('keyup', function (type, evt) {
  17411. var keyCode = evt.keyCode || evt.which
  17412. if (keyCode == 13) {
  17413. var range = me.selection.getRange(),
  17414. start = range.startContainer,
  17415. doSave
  17416. //修正在h1-h6里边回车后不能嵌套p的问题
  17417. if (!browser.ie) {
  17418. if (/h\d/i.test(hTag)) {
  17419. if (browser.gecko) {
  17420. var h = domUtils.findParentByTagName(
  17421. start,
  17422. [
  17423. 'h1',
  17424. 'h2',
  17425. 'h3',
  17426. 'h4',
  17427. 'h5',
  17428. 'h6',
  17429. 'blockquote',
  17430. 'caption',
  17431. 'table'
  17432. ],
  17433. true
  17434. )
  17435. if (!h) {
  17436. me.document.execCommand('formatBlock', false, '<p>')
  17437. doSave = 1
  17438. }
  17439. } else {
  17440. //chrome remove div
  17441. if (start.nodeType == 1) {
  17442. var tmp = me.document.createTextNode(''),
  17443. div
  17444. range.insertNode(tmp)
  17445. div = domUtils.findParentByTagName(tmp, 'div', true)
  17446. if (div) {
  17447. var p = me.document.createElement('p')
  17448. while (div.firstChild) {
  17449. p.appendChild(div.firstChild)
  17450. }
  17451. div.parentNode.insertBefore(p, div)
  17452. domUtils.remove(div)
  17453. range.setStartBefore(tmp).setCursor()
  17454. doSave = 1
  17455. }
  17456. domUtils.remove(tmp)
  17457. }
  17458. }
  17459. if (me.undoManger && doSave) {
  17460. me.undoManger.save()
  17461. }
  17462. }
  17463. //没有站位符,会出现多行的问题
  17464. browser.opera && range.select()
  17465. } else {
  17466. me.fireEvent('saveScene', true, true)
  17467. }
  17468. }
  17469. })
  17470. me.addListener('keydown', function (type, evt) {
  17471. var keyCode = evt.keyCode || evt.which
  17472. if (keyCode == 13) {
  17473. //回车
  17474. if (me.fireEvent('beforeenterkeydown')) {
  17475. domUtils.preventDefault(evt)
  17476. return
  17477. }
  17478. me.fireEvent('saveScene', true, true)
  17479. hTag = ''
  17480. var range = me.selection.getRange()
  17481. if (!range.collapsed) {
  17482. //跨td不能删
  17483. var start = range.startContainer,
  17484. end = range.endContainer,
  17485. startTd = domUtils.findParentByTagName(start, 'td', true),
  17486. endTd = domUtils.findParentByTagName(end, 'td', true)
  17487. if (
  17488. (startTd && endTd && startTd !== endTd) ||
  17489. (!startTd && endTd) ||
  17490. (startTd && !endTd)
  17491. ) {
  17492. evt.preventDefault
  17493. ? evt.preventDefault()
  17494. : (evt.returnValue = false)
  17495. return
  17496. }
  17497. }
  17498. if (tag == 'p') {
  17499. if (!browser.ie) {
  17500. start = domUtils.findParentByTagName(
  17501. range.startContainer,
  17502. [
  17503. 'ol',
  17504. 'ul',
  17505. 'p',
  17506. 'h1',
  17507. 'h2',
  17508. 'h3',
  17509. 'h4',
  17510. 'h5',
  17511. 'h6',
  17512. 'blockquote',
  17513. 'caption'
  17514. ],
  17515. true
  17516. )
  17517. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  17518. //trace:2431
  17519. if (!start && !browser.opera) {
  17520. me.document.execCommand('formatBlock', false, '<p>')
  17521. if (browser.gecko) {
  17522. range = me.selection.getRange()
  17523. start = domUtils.findParentByTagName(
  17524. range.startContainer,
  17525. 'p',
  17526. true
  17527. )
  17528. start && domUtils.removeDirtyAttr(start)
  17529. }
  17530. } else {
  17531. hTag = start.tagName
  17532. start.tagName.toLowerCase() == 'p' &&
  17533. browser.gecko &&
  17534. domUtils.removeDirtyAttr(start)
  17535. }
  17536. }
  17537. } else {
  17538. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
  17539. if (!range.collapsed) {
  17540. range.deleteContents()
  17541. start = range.startContainer
  17542. if (
  17543. start.nodeType == 1 &&
  17544. (start = start.childNodes[range.startOffset])
  17545. ) {
  17546. while (start.nodeType == 1) {
  17547. if (dtd.$empty[start.tagName]) {
  17548. range.setStartBefore(start).setCursor()
  17549. if (me.undoManger) {
  17550. me.undoManger.save()
  17551. }
  17552. return false
  17553. }
  17554. if (!start.firstChild) {
  17555. var br = range.document.createElement('br')
  17556. start.appendChild(br)
  17557. range.setStart(start, 0).setCursor()
  17558. if (me.undoManger) {
  17559. me.undoManger.save()
  17560. }
  17561. return false
  17562. }
  17563. start = start.firstChild
  17564. }
  17565. if (
  17566. start === range.startContainer.childNodes[range.startOffset]
  17567. ) {
  17568. br = range.document.createElement('br')
  17569. range.insertNode(br).setCursor()
  17570. } else {
  17571. range.setStart(start, 0).setCursor()
  17572. }
  17573. } else {
  17574. br = range.document.createElement('br')
  17575. range.insertNode(br).setStartAfter(br).setCursor()
  17576. }
  17577. } else {
  17578. br = range.document.createElement('br')
  17579. range.insertNode(br)
  17580. var parent = br.parentNode
  17581. if (parent.lastChild === br) {
  17582. br.parentNode.insertBefore(br.cloneNode(true), br)
  17583. range.setStartBefore(br)
  17584. } else {
  17585. range.setStartAfter(br)
  17586. }
  17587. range.setCursor()
  17588. }
  17589. }
  17590. }
  17591. })
  17592. }
  17593. // plugins/keystrokes.js
  17594. /* 处理特殊键的兼容性问题 */
  17595. UE.plugins['keystrokes'] = function () {
  17596. var me = this
  17597. var collapsed = true
  17598. me.addListener('keydown', function (type, evt) {
  17599. var keyCode = evt.keyCode || evt.which,
  17600. rng = me.selection.getRange()
  17601. //处理全选的情况
  17602. if (
  17603. !rng.collapsed &&
  17604. !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) &&
  17605. ((keyCode >= 65 && keyCode <= 90) ||
  17606. (keyCode >= 48 && keyCode <= 57) ||
  17607. (keyCode >= 96 && keyCode <= 111) ||
  17608. {
  17609. 13: 1,
  17610. 8: 1,
  17611. 46: 1
  17612. }[keyCode])
  17613. ) {
  17614. var tmpNode = rng.startContainer
  17615. if (domUtils.isFillChar(tmpNode)) {
  17616. rng.setStartBefore(tmpNode)
  17617. }
  17618. tmpNode = rng.endContainer
  17619. if (domUtils.isFillChar(tmpNode)) {
  17620. rng.setEndAfter(tmpNode)
  17621. }
  17622. rng.txtToElmBoundary()
  17623. //结束边界可能放到了br的前边,要把br包含进来
  17624. // x[xxx]<br/>
  17625. if (rng.endContainer && rng.endContainer.nodeType == 1) {
  17626. tmpNode = rng.endContainer.childNodes[rng.endOffset]
  17627. if (tmpNode && domUtils.isBr(tmpNode)) {
  17628. rng.setEndAfter(tmpNode)
  17629. }
  17630. }
  17631. if (rng.startOffset == 0) {
  17632. tmpNode = rng.startContainer
  17633. if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
  17634. tmpNode = rng.endContainer
  17635. if (
  17636. rng.endOffset ==
  17637. (tmpNode.nodeType == 3
  17638. ? tmpNode.nodeValue.length
  17639. : tmpNode.childNodes.length) &&
  17640. domUtils.isBoundaryNode(tmpNode, 'lastChild')
  17641. ) {
  17642. me.fireEvent('saveScene')
  17643. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>'
  17644. rng.setStart(me.body.firstChild, 0).setCursor(false, true)
  17645. me._selectionChange()
  17646. return
  17647. }
  17648. }
  17649. }
  17650. }
  17651. //处理backspace
  17652. if (keyCode == keymap.Backspace) {
  17653. rng = me.selection.getRange()
  17654. collapsed = rng.collapsed
  17655. if (me.fireEvent('delkeydown', evt)) {
  17656. return
  17657. }
  17658. var start, end
  17659. //避免按两次删除才能生效的问题
  17660. if (rng.collapsed && rng.inFillChar()) {
  17661. start = rng.startContainer
  17662. if (domUtils.isFillChar(start)) {
  17663. rng.setStartBefore(start).shrinkBoundary(true).collapse(true)
  17664. domUtils.remove(start)
  17665. } else {
  17666. start.nodeValue = start.nodeValue.replace(
  17667. new RegExp('^' + domUtils.fillChar),
  17668. ''
  17669. )
  17670. rng.startOffset--
  17671. rng.collapse(true).select(true)
  17672. }
  17673. }
  17674. //解决选中control元素不能删除的问题
  17675. if ((start = rng.getClosedNode())) {
  17676. me.fireEvent('saveScene')
  17677. rng.setStartBefore(start)
  17678. domUtils.remove(start)
  17679. rng.setCursor()
  17680. me.fireEvent('saveScene')
  17681. domUtils.preventDefault(evt)
  17682. return
  17683. }
  17684. //阻止在table上的删除
  17685. if (!browser.ie) {
  17686. start = domUtils.findParentByTagName(
  17687. rng.startContainer,
  17688. 'table',
  17689. true
  17690. )
  17691. end = domUtils.findParentByTagName(rng.endContainer, 'table', true)
  17692. if ((start && !end) || (!start && end) || start !== end) {
  17693. evt.preventDefault()
  17694. return
  17695. }
  17696. }
  17697. }
  17698. //处理tab键的逻辑
  17699. if (keyCode == keymap.Tab) {
  17700. //不处理以下标签
  17701. var excludeTagNameForTabKey = {
  17702. ol: 1,
  17703. ul: 1,
  17704. table: 1
  17705. }
  17706. //处理组件里的tab按下事件
  17707. if (me.fireEvent('tabkeydown', evt)) {
  17708. domUtils.preventDefault(evt)
  17709. return
  17710. }
  17711. var range = me.selection.getRange()
  17712. me.fireEvent('saveScene')
  17713. for (
  17714. var i = 0,
  17715. txt = '',
  17716. tabSize = me.options.tabSize || 4,
  17717. tabNode = me.options.tabNode || '&nbsp;';
  17718. i < tabSize;
  17719. i++
  17720. ) {
  17721. txt += tabNode
  17722. }
  17723. var span = me.document.createElement('span')
  17724. span.innerHTML = txt + domUtils.fillChar
  17725. if (range.collapsed) {
  17726. range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
  17727. } else {
  17728. var filterFn = function (node) {
  17729. return (
  17730. domUtils.isBlockElm(node) &&
  17731. !excludeTagNameForTabKey[node.tagName.toLowerCase()]
  17732. )
  17733. }
  17734. //普通的情况
  17735. start = domUtils.findParent(range.startContainer, filterFn, true)
  17736. end = domUtils.findParent(range.endContainer, filterFn, true)
  17737. if (start && end && start === end) {
  17738. range.deleteContents()
  17739. range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
  17740. } else {
  17741. var bookmark = range.createBookmark()
  17742. range.enlarge(true)
  17743. var bookmark2 = range.createBookmark(),
  17744. current = domUtils.getNextDomNode(
  17745. bookmark2.start,
  17746. false,
  17747. filterFn
  17748. )
  17749. while (
  17750. current &&
  17751. !(
  17752. domUtils.getPosition(current, bookmark2.end) &
  17753. domUtils.POSITION_FOLLOWING
  17754. )
  17755. ) {
  17756. current.insertBefore(
  17757. span.cloneNode(true).firstChild,
  17758. current.firstChild
  17759. )
  17760. current = domUtils.getNextDomNode(current, false, filterFn)
  17761. }
  17762. range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select()
  17763. }
  17764. }
  17765. domUtils.preventDefault(evt)
  17766. }
  17767. //trace:1634
  17768. //ff的del键在容器空的时候,也会删除
  17769. if (browser.gecko && keyCode == 46) {
  17770. range = me.selection.getRange()
  17771. if (range.collapsed) {
  17772. start = range.startContainer
  17773. if (domUtils.isEmptyBlock(start)) {
  17774. var parent = start.parentNode
  17775. while (
  17776. domUtils.getChildCount(parent) == 1 &&
  17777. !domUtils.isBody(parent)
  17778. ) {
  17779. start = parent
  17780. parent = parent.parentNode
  17781. }
  17782. if (start === parent.lastChild) evt.preventDefault()
  17783. return
  17784. }
  17785. }
  17786. }
  17787. })
  17788. me.addListener('keyup', function (type, evt) {
  17789. var keyCode = evt.keyCode || evt.which,
  17790. rng,
  17791. me = this
  17792. if (keyCode == keymap.Backspace) {
  17793. if (me.fireEvent('delkeyup')) {
  17794. return
  17795. }
  17796. rng = me.selection.getRange()
  17797. if (rng.collapsed) {
  17798. var tmpNode,
  17799. autoClearTagName = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
  17800. if (
  17801. (tmpNode = domUtils.findParentByTagName(
  17802. rng.startContainer,
  17803. autoClearTagName,
  17804. true
  17805. ))
  17806. ) {
  17807. if (domUtils.isEmptyBlock(tmpNode)) {
  17808. var pre = tmpNode.previousSibling
  17809. if (pre && pre.nodeName != 'TABLE') {
  17810. domUtils.remove(tmpNode)
  17811. rng.setStartAtLast(pre).setCursor(false, true)
  17812. return
  17813. } else {
  17814. var next = tmpNode.nextSibling
  17815. if (next && next.nodeName != 'TABLE') {
  17816. domUtils.remove(tmpNode)
  17817. rng.setStartAtFirst(next).setCursor(false, true)
  17818. return
  17819. }
  17820. }
  17821. }
  17822. }
  17823. //处理当删除到body时,要重新给p标签展位
  17824. if (domUtils.isBody(rng.startContainer)) {
  17825. var tmpNode = domUtils.createElement(me.document, 'p', {
  17826. innerHTML: browser.ie ? domUtils.fillChar : '<br/>'
  17827. })
  17828. rng.insertNode(tmpNode).setStart(tmpNode, 0).setCursor(false, true)
  17829. }
  17830. }
  17831. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  17832. if (
  17833. !collapsed &&
  17834. (rng.startContainer.nodeType == 3 ||
  17835. (rng.startContainer.nodeType == 1 &&
  17836. domUtils.isEmptyBlock(rng.startContainer)))
  17837. ) {
  17838. if (browser.ie) {
  17839. var span = rng.document.createElement('span')
  17840. rng.insertNode(span).setStartBefore(span).collapse(true)
  17841. rng.select()
  17842. domUtils.remove(span)
  17843. } else {
  17844. rng.select()
  17845. }
  17846. }
  17847. }
  17848. })
  17849. }
  17850. // plugins/fiximgclick.js
  17851. ///import core
  17852. ///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
  17853. ///commandsName FixImgClick
  17854. ///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小
  17855. //修复chrome下图片不能点击的问题,出现八个角可改变大小
  17856. UE.plugins['fiximgclick'] = (function () {
  17857. var elementUpdated = false
  17858. function Scale() {
  17859. this.editor = null
  17860. this.resizer = null
  17861. this.cover = null
  17862. this.doc = document
  17863. this.prePos = { x: 0, y: 0 }
  17864. this.startPos = { x: 0, y: 0 }
  17865. }
  17866. ;(function () {
  17867. var rect = [
  17868. //[left, top, width, height]
  17869. [0, 0, -1, -1],
  17870. [0, 0, 0, -1],
  17871. [0, 0, 1, -1],
  17872. [0, 0, -1, 0],
  17873. [0, 0, 1, 0],
  17874. [0, 0, -1, 1],
  17875. [0, 0, 0, 1],
  17876. [0, 0, 1, 1]
  17877. ]
  17878. Scale.prototype = {
  17879. init: function (editor) {
  17880. var me = this
  17881. me.editor = editor
  17882. me.startPos = this.prePos = { x: 0, y: 0 }
  17883. me.dragId = -1
  17884. var hands = [],
  17885. cover = (me.cover = document.createElement('div')),
  17886. resizer = (me.resizer = document.createElement('div'))
  17887. cover.id = me.editor.ui.id + '_imagescale_cover'
  17888. cover.style.cssText =
  17889. 'position:absolute;display:none;z-index:' +
  17890. me.editor.options.zIndex +
  17891. ';filter:alpha(opacity=0); opacity:0;background:#CCC;'
  17892. domUtils.on(cover, 'mousedown click', function () {
  17893. me.hide()
  17894. })
  17895. for (i = 0; i < 8; i++) {
  17896. hands.push(
  17897. '<span class="edui-editor-imagescale-hand' + i + '"></span>'
  17898. )
  17899. }
  17900. resizer.id = me.editor.ui.id + '_imagescale'
  17901. resizer.className = 'edui-editor-imagescale'
  17902. resizer.innerHTML = hands.join('')
  17903. resizer.style.cssText +=
  17904. ';display:none;border:1px solid #3b77ff;z-index:' +
  17905. me.editor.options.zIndex +
  17906. ';'
  17907. me.editor.ui.getDom().appendChild(cover)
  17908. me.editor.ui.getDom().appendChild(resizer)
  17909. me.initStyle()
  17910. me.initEvents()
  17911. },
  17912. initStyle: function () {
  17913. utils.cssRule(
  17914. 'imagescale',
  17915. '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' +
  17916. '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}' +
  17917. '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}' +
  17918. '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}' +
  17919. '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}' +
  17920. '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}' +
  17921. '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}' +
  17922. '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}' +
  17923. '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}' +
  17924. '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}'
  17925. )
  17926. },
  17927. initEvents: function () {
  17928. var me = this
  17929. me.startPos.x = me.startPos.y = 0
  17930. me.isDraging = false
  17931. },
  17932. _eventHandler: function (e) {
  17933. var me = this
  17934. switch (e.type) {
  17935. case 'mousedown':
  17936. var hand = e.target || e.srcElement,
  17937. hand
  17938. if (
  17939. hand.className.indexOf('edui-editor-imagescale-hand') != -1 &&
  17940. me.dragId == -1
  17941. ) {
  17942. me.dragId = hand.className.slice(-1)
  17943. me.startPos.x = me.prePos.x = e.clientX
  17944. me.startPos.y = me.prePos.y = e.clientY
  17945. domUtils.on(me.doc, 'mousemove', me.proxy(me._eventHandler, me))
  17946. }
  17947. break
  17948. case 'mousemove':
  17949. if (me.dragId != -1) {
  17950. me.updateContainerStyle(me.dragId, {
  17951. x: e.clientX - me.prePos.x,
  17952. y: e.clientY - me.prePos.y
  17953. })
  17954. me.prePos.x = e.clientX
  17955. me.prePos.y = e.clientY
  17956. elementUpdated = true
  17957. me.updateTargetElement()
  17958. }
  17959. break
  17960. case 'mouseup':
  17961. if (me.dragId != -1) {
  17962. me.updateContainerStyle(me.dragId, {
  17963. x: e.clientX - me.prePos.x,
  17964. y: e.clientY - me.prePos.y
  17965. })
  17966. me.updateTargetElement()
  17967. if (me.target.parentNode) me.attachTo(me.target)
  17968. me.dragId = -1
  17969. }
  17970. domUtils.un(me.doc, 'mousemove', me.proxy(me._eventHandler, me))
  17971. //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
  17972. if (elementUpdated) {
  17973. elementUpdated = false
  17974. me.editor.fireEvent('contentchange')
  17975. }
  17976. break
  17977. default:
  17978. break
  17979. }
  17980. },
  17981. updateTargetElement: function () {
  17982. var me = this
  17983. domUtils.setStyles(me.target, {
  17984. width: me.resizer.style.width,
  17985. height: me.resizer.style.height
  17986. })
  17987. me.target.width = parseInt(me.resizer.style.width)
  17988. me.target.height = parseInt(me.resizer.style.height)
  17989. me.attachTo(me.target)
  17990. },
  17991. updateContainerStyle: function (dir, offset) {
  17992. var me = this,
  17993. dom = me.resizer,
  17994. tmp
  17995. if (rect[dir][0] != 0) {
  17996. tmp = parseInt(dom.style.left) + offset.x
  17997. dom.style.left = me._validScaledProp('left', tmp) + 'px'
  17998. }
  17999. if (rect[dir][1] != 0) {
  18000. tmp = parseInt(dom.style.top) + offset.y
  18001. dom.style.top = me._validScaledProp('top', tmp) + 'px'
  18002. }
  18003. if (rect[dir][2] != 0) {
  18004. tmp = dom.clientWidth + rect[dir][2] * offset.x
  18005. dom.style.width = me._validScaledProp('width', tmp) + 'px'
  18006. }
  18007. if (rect[dir][3] != 0) {
  18008. tmp = dom.clientHeight + rect[dir][3] * offset.y
  18009. dom.style.height = me._validScaledProp('height', tmp) + 'px'
  18010. }
  18011. },
  18012. _validScaledProp: function (prop, value) {
  18013. var ele = this.resizer,
  18014. wrap = document
  18015. value = isNaN(value) ? 0 : value
  18016. switch (prop) {
  18017. case 'left':
  18018. return value < 0
  18019. ? 0
  18020. : value + ele.clientWidth > wrap.clientWidth
  18021. ? wrap.clientWidth - ele.clientWidth
  18022. : value
  18023. case 'top':
  18024. return value < 0
  18025. ? 0
  18026. : value + ele.clientHeight > wrap.clientHeight
  18027. ? wrap.clientHeight - ele.clientHeight
  18028. : value
  18029. case 'width':
  18030. return value <= 0
  18031. ? 1
  18032. : value + ele.offsetLeft > wrap.clientWidth
  18033. ? wrap.clientWidth - ele.offsetLeft
  18034. : value
  18035. case 'height':
  18036. return value <= 0
  18037. ? 1
  18038. : value + ele.offsetTop > wrap.clientHeight
  18039. ? wrap.clientHeight - ele.offsetTop
  18040. : value
  18041. }
  18042. },
  18043. hideCover: function () {
  18044. this.cover.style.display = 'none'
  18045. },
  18046. showCover: function () {
  18047. var me = this,
  18048. editorPos = domUtils.getXY(me.editor.ui.getDom()),
  18049. iframePos = domUtils.getXY(me.editor.iframe)
  18050. domUtils.setStyles(me.cover, {
  18051. width: me.editor.iframe.offsetWidth + 'px',
  18052. height: me.editor.iframe.offsetHeight + 'px',
  18053. top: iframePos.y - editorPos.y + 'px',
  18054. left: iframePos.x - editorPos.x + 'px',
  18055. position: 'absolute',
  18056. display: ''
  18057. })
  18058. },
  18059. show: function (targetObj) {
  18060. var me = this
  18061. me.resizer.style.display = 'block'
  18062. if (targetObj) me.attachTo(targetObj)
  18063. domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me))
  18064. domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me))
  18065. me.showCover()
  18066. me.editor.fireEvent('afterscaleshow', me)
  18067. me.editor.fireEvent('saveScene')
  18068. },
  18069. hide: function () {
  18070. var me = this
  18071. me.hideCover()
  18072. me.resizer.style.display = 'none'
  18073. domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me))
  18074. domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me))
  18075. me.editor.fireEvent('afterscalehide', me)
  18076. },
  18077. proxy: function (fn, context) {
  18078. return function (e) {
  18079. return fn.apply(context || this, arguments)
  18080. }
  18081. },
  18082. attachTo: function (targetObj) {
  18083. var me = this,
  18084. target = (me.target = targetObj),
  18085. resizer = this.resizer,
  18086. imgPos = domUtils.getXY(target),
  18087. iframePos = domUtils.getXY(me.editor.iframe),
  18088. editorPos = domUtils.getXY(resizer.parentNode)
  18089. domUtils.setStyles(resizer, {
  18090. width: target.width + 'px',
  18091. height: target.height + 'px',
  18092. left:
  18093. iframePos.x +
  18094. imgPos.x -
  18095. me.editor.document.body.scrollLeft -
  18096. editorPos.x -
  18097. parseInt(resizer.style.borderLeftWidth) +
  18098. 'px',
  18099. top:
  18100. iframePos.y +
  18101. imgPos.y -
  18102. me.editor.document.body.scrollTop -
  18103. editorPos.y -
  18104. parseInt(resizer.style.borderTopWidth) +
  18105. 'px'
  18106. })
  18107. }
  18108. }
  18109. })()
  18110. return function () {
  18111. var me = this,
  18112. imageScale
  18113. me.setOpt('imageScaleEnabled', true)
  18114. if (!browser.ie && me.options.imageScaleEnabled) {
  18115. me.addListener('click', function (type, e) {
  18116. var range = me.selection.getRange(),
  18117. img = range.getClosedNode()
  18118. if (
  18119. img &&
  18120. img.tagName == 'IMG' &&
  18121. me.body.contentEditable != 'false'
  18122. ) {
  18123. if (
  18124. img.className.indexOf('edui-faked-music') != -1 ||
  18125. img.getAttribute('anchorname') ||
  18126. domUtils.hasClass(img, 'loadingclass') ||
  18127. domUtils.hasClass(img, 'loaderrorclass')
  18128. ) {
  18129. return
  18130. }
  18131. if (!imageScale) {
  18132. imageScale = new Scale()
  18133. imageScale.init(me)
  18134. me.ui.getDom().appendChild(imageScale.resizer)
  18135. var _keyDownHandler = function (e) {
  18136. imageScale.hide()
  18137. if (imageScale.target)
  18138. me.selection
  18139. .getRange()
  18140. .selectNode(imageScale.target)
  18141. .select()
  18142. },
  18143. _mouseDownHandler = function (e) {
  18144. var ele = e.target || e.srcElement
  18145. if (
  18146. ele &&
  18147. (ele.className === undefined ||
  18148. ele.className.indexOf('edui-editor-imagescale') == -1)
  18149. ) {
  18150. _keyDownHandler(e)
  18151. }
  18152. },
  18153. timer
  18154. me.addListener('afterscaleshow', function (e) {
  18155. me.addListener('beforekeydown', _keyDownHandler)
  18156. me.addListener('beforemousedown', _mouseDownHandler)
  18157. domUtils.on(document, 'keydown', _keyDownHandler)
  18158. domUtils.on(document, 'mousedown', _mouseDownHandler)
  18159. me.selection.getNative().removeAllRanges()
  18160. })
  18161. me.addListener('afterscalehide', function (e) {
  18162. me.removeListener('beforekeydown', _keyDownHandler)
  18163. me.removeListener('beforemousedown', _mouseDownHandler)
  18164. domUtils.un(document, 'keydown', _keyDownHandler)
  18165. domUtils.un(document, 'mousedown', _mouseDownHandler)
  18166. var target = imageScale.target
  18167. if (target.parentNode) {
  18168. me.selection.getRange().selectNode(target).select()
  18169. }
  18170. })
  18171. //TODO 有iframe的情况,mousedown不能往下传。。
  18172. domUtils.on(imageScale.resizer, 'mousedown', function (e) {
  18173. me.selection.getNative().removeAllRanges()
  18174. var ele = e.target || e.srcElement
  18175. if (
  18176. ele &&
  18177. ele.className.indexOf('edui-editor-imagescale-hand') == -1
  18178. ) {
  18179. timer = setTimeout(function () {
  18180. imageScale.hide()
  18181. if (imageScale.target)
  18182. me.selection.getRange().selectNode(ele).select()
  18183. }, 200)
  18184. }
  18185. })
  18186. domUtils.on(imageScale.resizer, 'mouseup', function (e) {
  18187. var ele = e.target || e.srcElement
  18188. if (
  18189. ele &&
  18190. ele.className.indexOf('edui-editor-imagescale-hand') == -1
  18191. ) {
  18192. clearTimeout(timer)
  18193. }
  18194. })
  18195. }
  18196. imageScale.show(img)
  18197. } else {
  18198. if (imageScale && imageScale.resizer.style.display != 'none')
  18199. imageScale.hide()
  18200. }
  18201. })
  18202. }
  18203. if (browser.webkit) {
  18204. me.addListener('click', function (type, e) {
  18205. if (e.target.tagName == 'IMG' && me.body.contentEditable != 'false') {
  18206. var range = new dom.Range(me.document)
  18207. range.selectNode(e.target).select()
  18208. }
  18209. })
  18210. }
  18211. }
  18212. })()
  18213. // plugins/autolink.js
  18214. ///import core
  18215. ///commands 为非ie浏览器自动添加a标签
  18216. ///commandsName AutoLink
  18217. ///commandsTitle 自动增加链接
  18218. /**
  18219. * @description 为非ie浏览器自动添加a标签
  18220. * @author zhanyi
  18221. */
  18222. UE.plugin.register(
  18223. 'autolink',
  18224. function () {
  18225. var cont = 0
  18226. return !browser.ie
  18227. ? {
  18228. bindEvents: {
  18229. reset: function () {
  18230. cont = 0
  18231. },
  18232. keydown: function (type, evt) {
  18233. var me = this
  18234. var keyCode = evt.keyCode || evt.which
  18235. if (keyCode == 32 || keyCode == 13) {
  18236. var sel = me.selection.getNative(),
  18237. range = sel.getRangeAt(0).cloneRange(),
  18238. offset,
  18239. charCode
  18240. var start = range.startContainer
  18241. while (start.nodeType == 1 && range.startOffset > 0) {
  18242. start =
  18243. range.startContainer.childNodes[range.startOffset - 1]
  18244. if (!start) {
  18245. break
  18246. }
  18247. range.setStart(
  18248. start,
  18249. start.nodeType == 1
  18250. ? start.childNodes.length
  18251. : start.nodeValue.length
  18252. )
  18253. range.collapse(true)
  18254. start = range.startContainer
  18255. }
  18256. do {
  18257. if (range.startOffset == 0) {
  18258. start = range.startContainer.previousSibling
  18259. while (start && start.nodeType == 1) {
  18260. start = start.lastChild
  18261. }
  18262. if (!start || domUtils.isFillChar(start)) {
  18263. break
  18264. }
  18265. offset = start.nodeValue.length
  18266. } else {
  18267. start = range.startContainer
  18268. offset = range.startOffset
  18269. }
  18270. range.setStart(start, offset - 1)
  18271. charCode = range.toString().charCodeAt(0)
  18272. } while (charCode != 160 && charCode != 32)
  18273. if (
  18274. range
  18275. .toString()
  18276. .replace(new RegExp(domUtils.fillChar, 'g'), '')
  18277. .match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)
  18278. ) {
  18279. while (range.toString().length) {
  18280. if (
  18281. /^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(
  18282. range.toString()
  18283. )
  18284. ) {
  18285. break
  18286. }
  18287. try {
  18288. range.setStart(
  18289. range.startContainer,
  18290. range.startOffset + 1
  18291. )
  18292. } catch (e) {
  18293. //trace:2121
  18294. var start = range.startContainer
  18295. while (!(next = start.nextSibling)) {
  18296. if (domUtils.isBody(start)) {
  18297. return
  18298. }
  18299. start = start.parentNode
  18300. }
  18301. range.setStart(next, 0)
  18302. }
  18303. }
  18304. //range的开始边界已经在a标签里的不再处理
  18305. if (
  18306. domUtils.findParentByTagName(
  18307. range.startContainer,
  18308. 'a',
  18309. true
  18310. )
  18311. ) {
  18312. return
  18313. }
  18314. var a = me.document.createElement('a'),
  18315. text = me.document.createTextNode(' '),
  18316. href
  18317. me.undoManger && me.undoManger.save()
  18318. a.appendChild(range.extractContents())
  18319. a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, '')
  18320. href = a
  18321. .getAttribute('href')
  18322. .replace(new RegExp(domUtils.fillChar, 'g'), '')
  18323. href = /^(?:https?:\/\/)/gi.test(href)
  18324. ? href
  18325. : 'http://' + href
  18326. a.setAttribute('_src', utils.html(href))
  18327. a.href = utils.html(href)
  18328. range.insertNode(a)
  18329. a.parentNode.insertBefore(text, a.nextSibling)
  18330. range.setStart(text, 0)
  18331. range.collapse(true)
  18332. sel.removeAllRanges()
  18333. sel.addRange(range)
  18334. me.undoManger && me.undoManger.save()
  18335. }
  18336. }
  18337. }
  18338. }
  18339. }
  18340. : {}
  18341. },
  18342. function () {
  18343. var keyCodes = {
  18344. 37: 1,
  18345. 38: 1,
  18346. 39: 1,
  18347. 40: 1,
  18348. 13: 1,
  18349. 32: 1
  18350. }
  18351. function checkIsCludeLink(node) {
  18352. if (node.nodeType == 3) {
  18353. return null
  18354. }
  18355. if (node.nodeName == 'A') {
  18356. return node
  18357. }
  18358. var lastChild = node.lastChild
  18359. while (lastChild) {
  18360. if (lastChild.nodeName == 'A') {
  18361. return lastChild
  18362. }
  18363. if (lastChild.nodeType == 3) {
  18364. if (domUtils.isWhitespace(lastChild)) {
  18365. lastChild = lastChild.previousSibling
  18366. continue
  18367. }
  18368. return null
  18369. }
  18370. lastChild = lastChild.lastChild
  18371. }
  18372. }
  18373. browser.ie &&
  18374. this.addListener('keyup', function (cmd, evt) {
  18375. var me = this,
  18376. keyCode = evt.keyCode
  18377. if (keyCodes[keyCode]) {
  18378. var rng = me.selection.getRange()
  18379. var start = rng.startContainer
  18380. if (keyCode == 13) {
  18381. while (
  18382. start &&
  18383. !domUtils.isBody(start) &&
  18384. !domUtils.isBlockElm(start)
  18385. ) {
  18386. start = start.parentNode
  18387. }
  18388. if (start && !domUtils.isBody(start) && start.nodeName == 'P') {
  18389. var pre = start.previousSibling
  18390. if (pre && pre.nodeType == 1) {
  18391. var pre = checkIsCludeLink(pre)
  18392. if (pre && !pre.getAttribute('_href')) {
  18393. domUtils.remove(pre, true)
  18394. }
  18395. }
  18396. }
  18397. } else if (keyCode == 32) {
  18398. if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) {
  18399. start = start.previousSibling
  18400. if (
  18401. start &&
  18402. start.nodeName == 'A' &&
  18403. !start.getAttribute('_href')
  18404. ) {
  18405. domUtils.remove(start, true)
  18406. }
  18407. }
  18408. } else {
  18409. start = domUtils.findParentByTagName(start, 'a', true)
  18410. if (start && !start.getAttribute('_href')) {
  18411. var bk = rng.createBookmark()
  18412. domUtils.remove(start, true)
  18413. rng.moveToBookmark(bk).select(true)
  18414. }
  18415. }
  18416. }
  18417. })
  18418. }
  18419. )
  18420. // plugins/autoheight.js
  18421. ///import core
  18422. ///commands 当输入内容超过编辑器高度时,编辑器自动增高
  18423. ///commandsName AutoHeight,autoHeightEnabled
  18424. ///commandsTitle 自动增高
  18425. /**
  18426. * @description 自动伸展
  18427. * @author zhanyi
  18428. */
  18429. UE.plugins['autoheight'] = function () {
  18430. var me = this
  18431. //提供开关,就算加载也可以关闭
  18432. me.autoHeightEnabled = me.options.autoHeightEnabled !== false
  18433. if (!me.autoHeightEnabled) {
  18434. return
  18435. }
  18436. var bakOverflow,
  18437. lastHeight = 0,
  18438. options = me.options,
  18439. currentHeight,
  18440. timer
  18441. function adjustHeight() {
  18442. var me = this
  18443. clearTimeout(timer)
  18444. if (isFullscreen) return
  18445. if (
  18446. !me.queryCommandState ||
  18447. (me.queryCommandState && me.queryCommandState('source') != 1)
  18448. ) {
  18449. timer = setTimeout(function () {
  18450. var node = me.body.lastChild
  18451. while (node && node.nodeType != 1) {
  18452. node = node.previousSibling
  18453. }
  18454. if (node && node.nodeType == 1) {
  18455. node.style.clear = 'both'
  18456. currentHeight = Math.max(
  18457. domUtils.getXY(node).y + node.offsetHeight + 25,
  18458. Math.max(options.minFrameHeight, options.initialFrameHeight)
  18459. )
  18460. if (currentHeight != lastHeight) {
  18461. if (
  18462. currentHeight !== parseInt(me.iframe.parentNode.style.height)
  18463. ) {
  18464. me.iframe.parentNode.style.height = currentHeight + 'px'
  18465. }
  18466. me.body.style.height = currentHeight + 'px'
  18467. lastHeight = currentHeight
  18468. }
  18469. domUtils.removeStyle(node, 'clear')
  18470. }
  18471. }, 50)
  18472. }
  18473. }
  18474. var isFullscreen
  18475. me.addListener('fullscreenchanged', function (cmd, f) {
  18476. isFullscreen = f
  18477. })
  18478. me.addListener('destroy', function () {
  18479. me.removeListener(
  18480. 'contentchange afterinserthtml keyup mouseup',
  18481. adjustHeight
  18482. )
  18483. })
  18484. me.enableAutoHeight = function () {
  18485. var me = this
  18486. if (!me.autoHeightEnabled) {
  18487. return
  18488. }
  18489. var doc = me.document
  18490. me.autoHeightEnabled = true
  18491. bakOverflow = doc.body.style.overflowY
  18492. doc.body.style.overflowY = 'hidden'
  18493. me.addListener(
  18494. 'contentchange afterinserthtml keyup mouseup',
  18495. adjustHeight
  18496. )
  18497. //ff不给事件算得不对
  18498. setTimeout(
  18499. function () {
  18500. adjustHeight.call(me)
  18501. },
  18502. browser.gecko ? 100 : 0
  18503. )
  18504. me.fireEvent('autoheightchanged', me.autoHeightEnabled)
  18505. }
  18506. me.disableAutoHeight = function () {
  18507. me.body.style.overflowY = bakOverflow || ''
  18508. me.removeListener('contentchange', adjustHeight)
  18509. me.removeListener('keyup', adjustHeight)
  18510. me.removeListener('mouseup', adjustHeight)
  18511. me.autoHeightEnabled = false
  18512. me.fireEvent('autoheightchanged', me.autoHeightEnabled)
  18513. }
  18514. me.on('setHeight', function () {
  18515. me.disableAutoHeight()
  18516. })
  18517. me.addListener('ready', function () {
  18518. me.enableAutoHeight()
  18519. //trace:1764
  18520. var timer
  18521. domUtils.on(
  18522. browser.ie ? me.body : me.document,
  18523. browser.webkit ? 'dragover' : 'drop',
  18524. function () {
  18525. clearTimeout(timer)
  18526. timer = setTimeout(function () {
  18527. //trace:3681
  18528. adjustHeight.call(me)
  18529. }, 100)
  18530. }
  18531. )
  18532. //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
  18533. var lastScrollY
  18534. window.onscroll = function () {
  18535. if (lastScrollY === null) {
  18536. lastScrollY = this.scrollY
  18537. } else if (this.scrollY == 0 && lastScrollY != 0) {
  18538. me.window.scrollTo(0, 0)
  18539. lastScrollY = null
  18540. }
  18541. }
  18542. })
  18543. }
  18544. // plugins/autofloat.js
  18545. ///import core
  18546. ///commands 悬浮工具栏
  18547. ///commandsName AutoFloat,autoFloatEnabled
  18548. ///commandsTitle 悬浮工具栏
  18549. /**
  18550. * modified by chengchao01
  18551. * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉!
  18552. */
  18553. UE.plugins['autofloat'] = function () {
  18554. var me = this,
  18555. lang = me.getLang()
  18556. me.setOpt({
  18557. topOffset: 0
  18558. })
  18559. var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
  18560. topOffset = me.options.topOffset
  18561. //如果不固定toolbar的位置,则直接退出
  18562. if (!optsAutoFloatEnabled) {
  18563. return
  18564. }
  18565. var uiUtils = UE.ui.uiUtils,
  18566. LteIE6 = browser.ie && browser.version <= 6,
  18567. quirks = browser.quirks
  18568. function checkHasUI() {
  18569. if (!UE.ui) {
  18570. alert(lang.autofloatMsg)
  18571. return 0
  18572. }
  18573. return 1
  18574. }
  18575. function fixIE6FixedPos() {
  18576. var docStyle = document.body.style
  18577. docStyle.backgroundImage = 'url("about:blank")'
  18578. docStyle.backgroundAttachment = 'fixed'
  18579. }
  18580. var bakCssText,
  18581. placeHolder = document.createElement('div'),
  18582. toolbarBox,
  18583. orgTop,
  18584. getPosition,
  18585. flag = true //ie7模式下需要偏移
  18586. function setFloating() {
  18587. var toobarBoxPos = domUtils.getXY(toolbarBox),
  18588. origalFloat = domUtils.getComputedStyle(toolbarBox, 'position'),
  18589. origalLeft = domUtils.getComputedStyle(toolbarBox, 'left')
  18590. toolbarBox.style.width = toolbarBox.offsetWidth + 'px'
  18591. toolbarBox.style.zIndex = me.options.zIndex * 1 + 1
  18592. toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox)
  18593. if (LteIE6 || (quirks && browser.ie)) {
  18594. if (toolbarBox.style.position != 'absolute') {
  18595. toolbarBox.style.position = 'absolute'
  18596. }
  18597. toolbarBox.style.top =
  18598. (document.body.scrollTop || document.documentElement.scrollTop) -
  18599. orgTop +
  18600. topOffset +
  18601. 'px'
  18602. } else {
  18603. if (browser.ie7Compat && flag) {
  18604. flag = false
  18605. toolbarBox.style.left =
  18606. domUtils.getXY(toolbarBox).x -
  18607. document.documentElement.getBoundingClientRect().left +
  18608. 2 +
  18609. 'px'
  18610. }
  18611. if (toolbarBox.style.position != 'fixed') {
  18612. toolbarBox.style.position = 'fixed'
  18613. toolbarBox.style.top = topOffset + 'px'
  18614. ;(origalFloat == 'absolute' || origalFloat == 'relative') &&
  18615. parseFloat(origalLeft) &&
  18616. (toolbarBox.style.left = toobarBoxPos.x + 'px')
  18617. }
  18618. }
  18619. }
  18620. function unsetFloating() {
  18621. flag = true
  18622. if (placeHolder.parentNode) {
  18623. placeHolder.parentNode.removeChild(placeHolder)
  18624. }
  18625. toolbarBox.style.cssText = bakCssText
  18626. }
  18627. function updateFloating() {
  18628. var rect3 = getPosition(me.container)
  18629. var offset = me.options.toolbarTopOffset || 0
  18630. if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
  18631. setFloating()
  18632. } else {
  18633. unsetFloating()
  18634. }
  18635. }
  18636. var defer_updateFloating = utils.defer(
  18637. function () {
  18638. updateFloating()
  18639. },
  18640. browser.ie ? 200 : 100,
  18641. true
  18642. )
  18643. me.addListener('destroy', function () {
  18644. domUtils.un(window, ['scroll', 'resize'], updateFloating)
  18645. me.removeListener('keydown', defer_updateFloating)
  18646. })
  18647. me.addListener('ready', function () {
  18648. if (checkHasUI(me)) {
  18649. //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断
  18650. if (!me.ui) {
  18651. return
  18652. }
  18653. getPosition = uiUtils.getClientRect
  18654. toolbarBox = me.ui.getDom('toolbarbox')
  18655. orgTop = getPosition(toolbarBox).top
  18656. bakCssText = toolbarBox.style.cssText
  18657. placeHolder.style.height = toolbarBox.offsetHeight + 'px'
  18658. if (LteIE6) {
  18659. fixIE6FixedPos()
  18660. }
  18661. domUtils.on(window, ['scroll', 'resize'], updateFloating)
  18662. me.addListener('keydown', defer_updateFloating)
  18663. me.addListener('beforefullscreenchange', function (t, enabled) {
  18664. if (enabled) {
  18665. unsetFloating()
  18666. }
  18667. })
  18668. me.addListener('fullscreenchanged', function (t, enabled) {
  18669. if (!enabled) {
  18670. updateFloating()
  18671. }
  18672. })
  18673. me.addListener('sourcemodechanged', function (t, enabled) {
  18674. setTimeout(function () {
  18675. updateFloating()
  18676. }, 0)
  18677. })
  18678. me.addListener('clearDoc', function () {
  18679. setTimeout(function () {
  18680. updateFloating()
  18681. }, 0)
  18682. })
  18683. }
  18684. })
  18685. }
  18686. // plugins/video.js
  18687. /**
  18688. * video插件, 为UEditor提供视频插入支持
  18689. * @file
  18690. * @since 1.2.6.1
  18691. */
  18692. UE.plugins['video'] = function () {
  18693. var me = this
  18694. /**
  18695. * 创建插入视频字符窜
  18696. * @param url 视频地址
  18697. * @param width 视频宽度
  18698. * @param height 视频高度
  18699. * @param align 视频对齐
  18700. * @param toEmbed 是否以flash代替显示
  18701. * @param addParagraph 是否需要添加P 标签
  18702. */
  18703. function creatInsertStr(url, width, height, id, align, classname, type) {
  18704. url = utils.unhtmlForUrl(url)
  18705. align = utils.unhtml(align)
  18706. classname = utils.unhtml(classname).trim()
  18707. width = parseInt(width, 10) || 0
  18708. height = parseInt(height, 10) || 0
  18709. var str
  18710. switch (type) {
  18711. case 'image':
  18712. str =
  18713. '<img ' +
  18714. (id ? 'id="' + id + '"' : '') +
  18715. ' width="' +
  18716. width +
  18717. '" height="' +
  18718. height +
  18719. '" _url="' +
  18720. url +
  18721. '" class="' +
  18722. classname.replace(/\bvideo-js\b/, '') +
  18723. '"' +
  18724. ' src="' +
  18725. me.options.UEDITOR_HOME_URL +
  18726. 'themes/default/images/spacer.gif" style="background:url(' +
  18727. me.options.UEDITOR_HOME_URL +
  18728. 'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;' +
  18729. (align ? 'float:' + align + ';' : '') +
  18730. '" />'
  18731. break
  18732. case 'embed':
  18733. str =
  18734. '<embed type="application/x-shockwave-flash" class="' +
  18735. classname +
  18736. '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
  18737. ' src="' +
  18738. utils.html(url) +
  18739. '" width="' +
  18740. width +
  18741. '" height="' +
  18742. height +
  18743. '"' +
  18744. (align ? ' style="float:' + align + '"' : '') +
  18745. ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >'
  18746. break
  18747. case 'video':
  18748. var ext = url.substr(url.lastIndexOf('.') + 1)
  18749. if (ext == 'ogv') ext = 'ogg'
  18750. str =
  18751. '<video' +
  18752. (id ? ' id="' + id + '"' : '') +
  18753. ' class="' +
  18754. classname +
  18755. ' video-js" ' +
  18756. (align ? ' style="float:' + align + '"' : '') +
  18757. ' controls preload="none" width="' +
  18758. width +
  18759. '" height="' +
  18760. height +
  18761. '" src="' +
  18762. url +
  18763. '" data-setup="{}">' +
  18764. '<source src="' +
  18765. url +
  18766. '" type="video/' +
  18767. ext +
  18768. '" /></video>'
  18769. break
  18770. }
  18771. return str
  18772. }
  18773. function switchImgAndVideo(root, img2video) {
  18774. utils.each(
  18775. root.getNodesByTagName(img2video ? 'img' : 'embed video'),
  18776. function (node) {
  18777. var className = node.getAttr('class')
  18778. if (className && className.indexOf('edui-faked-video') != -1) {
  18779. var html = creatInsertStr(
  18780. img2video ? node.getAttr('_url') : node.getAttr('src'),
  18781. node.getAttr('width'),
  18782. node.getAttr('height'),
  18783. null,
  18784. node.getStyle('float') || '',
  18785. className,
  18786. img2video ? 'embed' : 'image'
  18787. )
  18788. node.parentNode.replaceChild(UE.uNode.createElement(html), node)
  18789. }
  18790. if (className && className.indexOf('edui-upload-video') != -1) {
  18791. var html = creatInsertStr(
  18792. img2video ? node.getAttr('_url') : node.getAttr('src'),
  18793. node.getAttr('width'),
  18794. node.getAttr('height'),
  18795. null,
  18796. node.getStyle('float') || '',
  18797. className,
  18798. img2video ? 'video' : 'image'
  18799. )
  18800. node.parentNode.replaceChild(UE.uNode.createElement(html), node)
  18801. }
  18802. }
  18803. )
  18804. }
  18805. me.addOutputRule(function (root) {
  18806. switchImgAndVideo(root, true)
  18807. })
  18808. me.addInputRule(function (root) {
  18809. switchImgAndVideo(root)
  18810. })
  18811. /**
  18812. * 插入视频
  18813. * @command insertvideo
  18814. * @method execCommand
  18815. * @param { String } cmd 命令字符串
  18816. * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性
  18817. * @example
  18818. * ```javascript
  18819. *
  18820. * var videoAttr = {
  18821. * //视频地址
  18822. * url: 'http://www.youku.com/xxx',
  18823. * //视频宽高值, 单位px
  18824. * width: 200,
  18825. * height: 100
  18826. * };
  18827. *
  18828. * //editor 是编辑器实例
  18829. * //向编辑器插入单个视频
  18830. * editor.execCommand( 'insertvideo', videoAttr );
  18831. * ```
  18832. */
  18833. /**
  18834. * 插入视频
  18835. * @command insertvideo
  18836. * @method execCommand
  18837. * @param { String } cmd 命令字符串
  18838. * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性
  18839. * @example
  18840. * ```javascript
  18841. *
  18842. * var videoAttr1 = {
  18843. * //视频地址
  18844. * url: 'http://www.youku.com/xxx',
  18845. * //视频宽高值, 单位px
  18846. * width: 200,
  18847. * height: 100
  18848. * },
  18849. * videoAttr2 = {
  18850. * //视频地址
  18851. * url: 'http://www.youku.com/xxx',
  18852. * //视频宽高值, 单位px
  18853. * width: 200,
  18854. * height: 100
  18855. * }
  18856. *
  18857. * //editor 是编辑器实例
  18858. * //该方法将会向编辑器内插入两个视频
  18859. * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] );
  18860. * ```
  18861. */
  18862. /**
  18863. * 查询当前光标所在处是否是一个视频
  18864. * @command insertvideo
  18865. * @method queryCommandState
  18866. * @param { String } cmd 需要查询的命令字符串
  18867. * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0
  18868. * @example
  18869. * ```javascript
  18870. *
  18871. * //editor 是编辑器实例
  18872. * editor.queryCommandState( 'insertvideo' );
  18873. * ```
  18874. */
  18875. me.commands['insertvideo'] = {
  18876. execCommand: function (cmd, videoObjs, type) {
  18877. videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs]
  18878. var html = [],
  18879. id = 'tmpVedio',
  18880. cl
  18881. for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
  18882. vi = videoObjs[i]
  18883. cl =
  18884. type == 'upload'
  18885. ? 'edui-upload-video video-js vjs-default-skin'
  18886. : 'edui-faked-video'
  18887. html.push(
  18888. creatInsertStr(
  18889. vi.url,
  18890. vi.width || 420,
  18891. vi.height || 280,
  18892. id + i,
  18893. null,
  18894. cl,
  18895. 'image'
  18896. )
  18897. )
  18898. }
  18899. me.execCommand('inserthtml', html.join(''), true)
  18900. var rng = this.selection.getRange()
  18901. for (var i = 0, len = videoObjs.length; i < len; i++) {
  18902. var img = this.document.getElementById('tmpVedio' + i)
  18903. domUtils.removeAttributes(img, 'id')
  18904. rng.selectNode(img).select()
  18905. me.execCommand('imagefloat', videoObjs[i].align)
  18906. }
  18907. },
  18908. queryCommandState: function () {
  18909. var img = me.selection.getRange().getClosedNode(),
  18910. flag =
  18911. img &&
  18912. (img.className == 'edui-faked-video' ||
  18913. img.className.indexOf('edui-upload-video') != -1)
  18914. return flag ? 1 : 0
  18915. }
  18916. }
  18917. }
  18918. // plugins/table.core.js
  18919. /**
  18920. * Created with JetBrains WebStorm.
  18921. * User: taoqili
  18922. * Date: 13-1-18
  18923. * Time: 上午11:09
  18924. * To change this template use File | Settings | File Templates.
  18925. */
  18926. /**
  18927. * UE表格操作类
  18928. * @param table
  18929. * @constructor
  18930. */
  18931. ;(function () {
  18932. var UETable = (UE.UETable = function (table) {
  18933. this.table = table
  18934. this.indexTable = []
  18935. this.selectedTds = []
  18936. this.cellsRange = {}
  18937. this.update(table)
  18938. })
  18939. //===以下为静态工具方法===
  18940. UETable.removeSelectedClass = function (cells) {
  18941. utils.each(cells, function (cell) {
  18942. domUtils.removeClasses(cell, 'selectTdClass')
  18943. })
  18944. }
  18945. UETable.addSelectedClass = function (cells) {
  18946. utils.each(cells, function (cell) {
  18947. domUtils.addClass(cell, 'selectTdClass')
  18948. })
  18949. }
  18950. UETable.isEmptyBlock = function (node) {
  18951. var reg = new RegExp(domUtils.fillChar, 'g')
  18952. if (
  18953. node[browser.ie ? 'innerText' : 'textContent']
  18954. .replace(/^\s*$/, '')
  18955. .replace(reg, '').length > 0
  18956. ) {
  18957. return 0
  18958. }
  18959. for (var i in dtd.$isNotEmpty)
  18960. if (dtd.$isNotEmpty.hasOwnProperty(i)) {
  18961. if (node.getElementsByTagName(i).length) {
  18962. return 0
  18963. }
  18964. }
  18965. return 1
  18966. }
  18967. UETable.getWidth = function (cell) {
  18968. if (!cell) return 0
  18969. return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
  18970. }
  18971. /**
  18972. * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的
  18973. * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态;
  18974. * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组
  18975. * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null
  18976. */
  18977. UETable.getTableCellAlignState = function (cells) {
  18978. !utils.isArray(cells) && (cells = [cells])
  18979. var result = {},
  18980. status = ['align', 'valign'],
  18981. tempStatus = null,
  18982. isSame = true //状态是否相同
  18983. utils.each(cells, function (cellNode) {
  18984. utils.each(status, function (currentState) {
  18985. tempStatus = cellNode.getAttribute(currentState)
  18986. if (!result[currentState] && tempStatus) {
  18987. result[currentState] = tempStatus
  18988. } else if (
  18989. !result[currentState] ||
  18990. tempStatus !== result[currentState]
  18991. ) {
  18992. isSame = false
  18993. return false
  18994. }
  18995. })
  18996. return isSame
  18997. })
  18998. return isSame ? result : null
  18999. }
  19000. /**
  19001. * 根据当前选区获取相关的table信息
  19002. * @return {Object}
  19003. */
  19004. UETable.getTableItemsByRange = function (editor) {
  19005. var start = editor.selection.getStart()
  19006. //ff下会选中bookmark
  19007. if (
  19008. start &&
  19009. start.id &&
  19010. start.id.indexOf('_baidu_bookmark_start_') === 0 &&
  19011. start.nextSibling
  19012. ) {
  19013. start = start.nextSibling
  19014. }
  19015. //在table或者td边缘有可能存在选中tr的情况
  19016. var cell =
  19017. start && domUtils.findParentByTagName(start, ['td', 'th'], true),
  19018. tr = cell && cell.parentNode,
  19019. caption = start && domUtils.findParentByTagName(start, 'caption', true),
  19020. table = caption ? caption.parentNode : tr && tr.parentNode.parentNode
  19021. return {
  19022. cell: cell,
  19023. tr: tr,
  19024. table: table,
  19025. caption: caption
  19026. }
  19027. }
  19028. UETable.getUETableBySelected = function (editor) {
  19029. var table = UETable.getTableItemsByRange(editor).table
  19030. if (table && table.ueTable && table.ueTable.selectedTds.length) {
  19031. return table.ueTable
  19032. }
  19033. return null
  19034. }
  19035. UETable.getDefaultValue = function (editor, table) {
  19036. var borderMap = {
  19037. thin: '0px',
  19038. medium: '1px',
  19039. thick: '2px'
  19040. },
  19041. tableBorder,
  19042. tdPadding,
  19043. tdBorder,
  19044. tmpValue
  19045. if (!table) {
  19046. table = editor.document.createElement('table')
  19047. table.insertRow(0).insertCell(0).innerHTML = 'xxx'
  19048. editor.body.appendChild(table)
  19049. var td = table.getElementsByTagName('td')[0]
  19050. tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
  19051. tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19052. tmpValue = domUtils.getComputedStyle(td, 'padding-left')
  19053. tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19054. tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
  19055. tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19056. domUtils.remove(table)
  19057. return {
  19058. tableBorder: tableBorder,
  19059. tdPadding: tdPadding,
  19060. tdBorder: tdBorder
  19061. }
  19062. } else {
  19063. td = table.getElementsByTagName('td')[0]
  19064. tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
  19065. tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19066. tmpValue = domUtils.getComputedStyle(td, 'padding-left')
  19067. tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19068. tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
  19069. tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
  19070. return {
  19071. tableBorder: tableBorder,
  19072. tdPadding: tdPadding,
  19073. tdBorder: tdBorder
  19074. }
  19075. }
  19076. }
  19077. /**
  19078. * 根据当前点击的td或者table获取索引对象
  19079. * @param tdOrTable
  19080. */
  19081. UETable.getUETable = function (tdOrTable) {
  19082. var tag = tdOrTable.tagName.toLowerCase()
  19083. tdOrTable =
  19084. tag == 'td' || tag == 'th' || tag == 'caption'
  19085. ? domUtils.findParentByTagName(tdOrTable, 'table', true)
  19086. : tdOrTable
  19087. if (!tdOrTable.ueTable) {
  19088. tdOrTable.ueTable = new UETable(tdOrTable)
  19089. }
  19090. return tdOrTable.ueTable
  19091. }
  19092. UETable.cloneCell = function (cell, ignoreMerge, keepPro) {
  19093. if (!cell || utils.isString(cell)) {
  19094. return this.table.ownerDocument.createElement(cell || 'td')
  19095. }
  19096. var flag = domUtils.hasClass(cell, 'selectTdClass')
  19097. flag && domUtils.removeClasses(cell, 'selectTdClass')
  19098. var tmpCell = cell.cloneNode(true)
  19099. if (ignoreMerge) {
  19100. tmpCell.rowSpan = tmpCell.colSpan = 1
  19101. }
  19102. //去掉宽高
  19103. !keepPro && domUtils.removeAttributes(tmpCell, 'width height')
  19104. !keepPro && domUtils.removeAttributes(tmpCell, 'style')
  19105. tmpCell.style.borderLeftStyle = ''
  19106. tmpCell.style.borderTopStyle = ''
  19107. tmpCell.style.borderLeftColor = cell.style.borderRightColor
  19108. tmpCell.style.borderLeftWidth = cell.style.borderRightWidth
  19109. tmpCell.style.borderTopColor = cell.style.borderBottomColor
  19110. tmpCell.style.borderTopWidth = cell.style.borderBottomWidth
  19111. flag && domUtils.addClass(cell, 'selectTdClass')
  19112. return tmpCell
  19113. }
  19114. UETable.prototype = {
  19115. getMaxRows: function () {
  19116. var rows = this.table.rows,
  19117. maxLen = 1
  19118. for (var i = 0, row; (row = rows[i]); i++) {
  19119. var currentMax = 1
  19120. for (var j = 0, cj; (cj = row.cells[j++]); ) {
  19121. currentMax = Math.max(cj.rowSpan || 1, currentMax)
  19122. }
  19123. maxLen = Math.max(currentMax + i, maxLen)
  19124. }
  19125. return maxLen
  19126. },
  19127. /**
  19128. * 获取当前表格的最大列数
  19129. */
  19130. getMaxCols: function () {
  19131. var rows = this.table.rows,
  19132. maxLen = 0,
  19133. cellRows = {}
  19134. for (var i = 0, row; (row = rows[i]); i++) {
  19135. var cellsNum = 0
  19136. for (var j = 0, cj; (cj = row.cells[j++]); ) {
  19137. cellsNum += cj.colSpan || 1
  19138. if (cj.rowSpan && cj.rowSpan > 1) {
  19139. for (var k = 1; k < cj.rowSpan; k++) {
  19140. if (!cellRows['row_' + (i + k)]) {
  19141. cellRows['row_' + (i + k)] = cj.colSpan || 1
  19142. } else {
  19143. cellRows['row_' + (i + k)]++
  19144. }
  19145. }
  19146. }
  19147. }
  19148. cellsNum += cellRows['row_' + i] || 0
  19149. maxLen = Math.max(cellsNum, maxLen)
  19150. }
  19151. return maxLen
  19152. },
  19153. getCellColIndex: function (cell) {},
  19154. /**
  19155. * 获取当前cell旁边的单元格,
  19156. * @param cell
  19157. * @param right
  19158. */
  19159. getHSideCell: function (cell, right) {
  19160. try {
  19161. var cellInfo = this.getCellInfo(cell),
  19162. previewRowIndex,
  19163. previewColIndex
  19164. var len = this.selectedTds.length,
  19165. range = this.cellsRange
  19166. //首行或者首列没有前置单元格
  19167. if (
  19168. (!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
  19169. (right &&
  19170. (!len
  19171. ? cellInfo.colIndex == this.colsNum - 1
  19172. : range.endColIndex == this.colsNum - 1))
  19173. )
  19174. return null
  19175. previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex
  19176. previewColIndex = !right
  19177. ? !len
  19178. ? cellInfo.colIndex < 1
  19179. ? 0
  19180. : cellInfo.colIndex - 1
  19181. : range.beginColIndex - 1
  19182. : !len
  19183. ? cellInfo.colIndex + 1
  19184. : range.endColIndex + 1
  19185. return this.getCell(
  19186. this.indexTable[previewRowIndex][previewColIndex].rowIndex,
  19187. this.indexTable[previewRowIndex][previewColIndex].cellIndex
  19188. )
  19189. } catch (e) {
  19190. showError(e)
  19191. }
  19192. },
  19193. getTabNextCell: function (cell, preRowIndex) {
  19194. var cellInfo = this.getCellInfo(cell),
  19195. rowIndex = preRowIndex || cellInfo.rowIndex,
  19196. colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1),
  19197. nextCell
  19198. try {
  19199. nextCell = this.getCell(
  19200. this.indexTable[rowIndex][colIndex].rowIndex,
  19201. this.indexTable[rowIndex][colIndex].cellIndex
  19202. )
  19203. } catch (e) {
  19204. try {
  19205. rowIndex = rowIndex * 1 + 1
  19206. colIndex = 0
  19207. nextCell = this.getCell(
  19208. this.indexTable[rowIndex][colIndex].rowIndex,
  19209. this.indexTable[rowIndex][colIndex].cellIndex
  19210. )
  19211. } catch (e) {}
  19212. }
  19213. return nextCell
  19214. },
  19215. /**
  19216. * 获取视觉上的后置单元格
  19217. * @param cell
  19218. * @param bottom
  19219. */
  19220. getVSideCell: function (cell, bottom, ignoreRange) {
  19221. try {
  19222. var cellInfo = this.getCellInfo(cell),
  19223. nextRowIndex,
  19224. nextColIndex
  19225. var len = this.selectedTds.length && !ignoreRange,
  19226. range = this.cellsRange
  19227. //末行或者末列没有后置单元格
  19228. if (
  19229. (!bottom && cellInfo.rowIndex == 0) ||
  19230. (bottom &&
  19231. (!len
  19232. ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1
  19233. : range.endRowIndex == this.rowsNum - 1))
  19234. )
  19235. return null
  19236. nextRowIndex = !bottom
  19237. ? !len
  19238. ? cellInfo.rowIndex - 1
  19239. : range.beginRowIndex - 1
  19240. : !len
  19241. ? cellInfo.rowIndex + cellInfo.rowSpan
  19242. : range.endRowIndex + 1
  19243. nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex
  19244. return this.getCell(
  19245. this.indexTable[nextRowIndex][nextColIndex].rowIndex,
  19246. this.indexTable[nextRowIndex][nextColIndex].cellIndex
  19247. )
  19248. } catch (e) {
  19249. showError(e)
  19250. }
  19251. },
  19252. /**
  19253. * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同
  19254. */
  19255. getSameEndPosCells: function (cell, xOrY) {
  19256. try {
  19257. var flag = xOrY.toLowerCase() === 'x',
  19258. end =
  19259. domUtils.getXY(cell)[flag ? 'x' : 'y'] +
  19260. cell['offset' + (flag ? 'Width' : 'Height')],
  19261. rows = this.table.rows,
  19262. cells = null,
  19263. returns = []
  19264. for (var i = 0; i < this.rowsNum; i++) {
  19265. cells = rows[i].cells
  19266. for (var j = 0, tmpCell; (tmpCell = cells[j++]); ) {
  19267. var tmpEnd =
  19268. domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] +
  19269. tmpCell['offset' + (flag ? 'Width' : 'Height')]
  19270. //对应行的td已经被上面行rowSpan了
  19271. if (tmpEnd > end && flag) break
  19272. if (cell == tmpCell || end == tmpEnd) {
  19273. //只获取单一的单元格
  19274. //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能
  19275. if (tmpCell[flag ? 'colSpan' : 'rowSpan'] == 1) {
  19276. returns.push(tmpCell)
  19277. }
  19278. if (flag) break
  19279. }
  19280. }
  19281. }
  19282. return returns
  19283. } catch (e) {
  19284. showError(e)
  19285. }
  19286. },
  19287. setCellContent: function (cell, content) {
  19288. cell.innerHTML = content || (browser.ie ? domUtils.fillChar : '<br />')
  19289. },
  19290. cloneCell: UETable.cloneCell,
  19291. /**
  19292. * 获取跟当前单元格的右边竖线为左边的所有未合并单元格
  19293. */
  19294. getSameStartPosXCells: function (cell) {
  19295. try {
  19296. var start = domUtils.getXY(cell).x + cell.offsetWidth,
  19297. rows = this.table.rows,
  19298. cells,
  19299. returns = []
  19300. for (var i = 0; i < this.rowsNum; i++) {
  19301. cells = rows[i].cells
  19302. for (var j = 0, tmpCell; (tmpCell = cells[j++]); ) {
  19303. var tmpStart = domUtils.getXY(tmpCell).x
  19304. if (tmpStart > start) break
  19305. if (tmpStart == start && tmpCell.colSpan == 1) {
  19306. returns.push(tmpCell)
  19307. break
  19308. }
  19309. }
  19310. }
  19311. return returns
  19312. } catch (e) {
  19313. showError(e)
  19314. }
  19315. },
  19316. /**
  19317. * 更新table对应的索引表
  19318. */
  19319. update: function (table) {
  19320. this.table = table || this.table
  19321. this.selectedTds = []
  19322. this.cellsRange = {}
  19323. this.indexTable = []
  19324. var rows = this.table.rows,
  19325. rowsNum = this.getMaxRows(),
  19326. dNum = rowsNum - rows.length,
  19327. colsNum = this.getMaxCols()
  19328. while (dNum--) {
  19329. this.table.insertRow(rows.length)
  19330. }
  19331. this.rowsNum = rowsNum
  19332. this.colsNum = colsNum
  19333. for (var i = 0, len = rows.length; i < len; i++) {
  19334. this.indexTable[i] = new Array(colsNum)
  19335. }
  19336. //填充索引表
  19337. for (var rowIndex = 0, row; (row = rows[rowIndex]); rowIndex++) {
  19338. for (
  19339. var cellIndex = 0, cell, cells = row.cells;
  19340. (cell = cells[cellIndex]);
  19341. cellIndex++
  19342. ) {
  19343. //修正整行被rowSpan时导致的行数计算错误
  19344. if (cell.rowSpan > rowsNum) {
  19345. cell.rowSpan = rowsNum
  19346. }
  19347. var colIndex = cellIndex,
  19348. rowSpan = cell.rowSpan || 1,
  19349. colSpan = cell.colSpan || 1
  19350. //当已经被上一行rowSpan或者被前一列colSpan了,则跳到下一个单元格进行
  19351. while (this.indexTable[rowIndex][colIndex]) colIndex++
  19352. for (var j = 0; j < rowSpan; j++) {
  19353. for (var k = 0; k < colSpan; k++) {
  19354. this.indexTable[rowIndex + j][colIndex + k] = {
  19355. rowIndex: rowIndex,
  19356. cellIndex: cellIndex,
  19357. colIndex: colIndex,
  19358. rowSpan: rowSpan,
  19359. colSpan: colSpan
  19360. }
  19361. }
  19362. }
  19363. }
  19364. }
  19365. //修复残缺td
  19366. for (j = 0; j < rowsNum; j++) {
  19367. for (k = 0; k < colsNum; k++) {
  19368. if (this.indexTable[j][k] === undefined) {
  19369. row = rows[j]
  19370. cell = row.cells[row.cells.length - 1]
  19371. cell = cell
  19372. ? cell.cloneNode(true)
  19373. : this.table.ownerDocument.createElement('td')
  19374. this.setCellContent(cell)
  19375. if (cell.colSpan !== 1) cell.colSpan = 1
  19376. if (cell.rowSpan !== 1) cell.rowSpan = 1
  19377. row.appendChild(cell)
  19378. this.indexTable[j][k] = {
  19379. rowIndex: j,
  19380. cellIndex: cell.cellIndex,
  19381. colIndex: k,
  19382. rowSpan: 1,
  19383. colSpan: 1
  19384. }
  19385. }
  19386. }
  19387. }
  19388. //当框选后删除行或者列后撤销,需要重建选区。
  19389. var tds = domUtils.getElementsByTagName(this.table, 'td'),
  19390. selectTds = []
  19391. utils.each(tds, function (td) {
  19392. if (domUtils.hasClass(td, 'selectTdClass')) {
  19393. selectTds.push(td)
  19394. }
  19395. })
  19396. if (selectTds.length) {
  19397. var start = selectTds[0],
  19398. end = selectTds[selectTds.length - 1],
  19399. startInfo = this.getCellInfo(start),
  19400. endInfo = this.getCellInfo(end)
  19401. this.selectedTds = selectTds
  19402. this.cellsRange = {
  19403. beginRowIndex: startInfo.rowIndex,
  19404. beginColIndex: startInfo.colIndex,
  19405. endRowIndex: endInfo.rowIndex + endInfo.rowSpan - 1,
  19406. endColIndex: endInfo.colIndex + endInfo.colSpan - 1
  19407. }
  19408. }
  19409. //给第一行设置firstRow的样式名称,在排序图标的样式上使用到
  19410. if (!domUtils.hasClass(this.table.rows[0], 'firstRow')) {
  19411. domUtils.addClass(this.table.rows[0], 'firstRow')
  19412. for (var i = 1; i < this.table.rows.length; i++) {
  19413. domUtils.removeClasses(this.table.rows[i], 'firstRow')
  19414. }
  19415. }
  19416. },
  19417. /**
  19418. * 获取单元格的索引信息
  19419. */
  19420. getCellInfo: function (cell) {
  19421. if (!cell) return
  19422. var cellIndex = cell.cellIndex,
  19423. rowIndex = cell.parentNode.rowIndex,
  19424. rowInfo = this.indexTable[rowIndex],
  19425. numCols = this.colsNum
  19426. for (var colIndex = cellIndex; colIndex < numCols; colIndex++) {
  19427. var cellInfo = rowInfo[colIndex]
  19428. if (
  19429. cellInfo.rowIndex === rowIndex &&
  19430. cellInfo.cellIndex === cellIndex
  19431. ) {
  19432. return cellInfo
  19433. }
  19434. }
  19435. },
  19436. /**
  19437. * 根据行列号获取单元格
  19438. */
  19439. getCell: function (rowIndex, cellIndex) {
  19440. return (
  19441. (rowIndex < this.rowsNum &&
  19442. this.table.rows[rowIndex].cells[cellIndex]) ||
  19443. null
  19444. )
  19445. },
  19446. /**
  19447. * 删除单元格
  19448. */
  19449. deleteCell: function (cell, rowIndex) {
  19450. rowIndex =
  19451. typeof rowIndex == 'number' ? rowIndex : cell.parentNode.rowIndex
  19452. var row = this.table.rows[rowIndex]
  19453. row.deleteCell(cell.cellIndex)
  19454. },
  19455. /**
  19456. * 根据始末两个单元格获取被框选的所有单元格范围
  19457. */
  19458. getCellsRange: function (cellA, cellB) {
  19459. function checkRange(
  19460. beginRowIndex,
  19461. beginColIndex,
  19462. endRowIndex,
  19463. endColIndex
  19464. ) {
  19465. var tmpBeginRowIndex = beginRowIndex,
  19466. tmpBeginColIndex = beginColIndex,
  19467. tmpEndRowIndex = endRowIndex,
  19468. tmpEndColIndex = endColIndex,
  19469. cellInfo,
  19470. colIndex,
  19471. rowIndex
  19472. // 通过indexTable检查是否存在超出TableRange上边界的情况
  19473. if (beginRowIndex > 0) {
  19474. for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
  19475. cellInfo = me.indexTable[beginRowIndex][colIndex]
  19476. rowIndex = cellInfo.rowIndex
  19477. if (rowIndex < beginRowIndex) {
  19478. tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex)
  19479. }
  19480. }
  19481. }
  19482. // 通过indexTable检查是否存在超出TableRange右边界的情况
  19483. if (endColIndex < me.colsNum) {
  19484. for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
  19485. cellInfo = me.indexTable[rowIndex][endColIndex]
  19486. colIndex = cellInfo.colIndex + cellInfo.colSpan - 1
  19487. if (colIndex > endColIndex) {
  19488. tmpEndColIndex = Math.max(colIndex, tmpEndColIndex)
  19489. }
  19490. }
  19491. }
  19492. // 检查是否有超出TableRange下边界的情况
  19493. if (endRowIndex < me.rowsNum) {
  19494. for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
  19495. cellInfo = me.indexTable[endRowIndex][colIndex]
  19496. rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1
  19497. if (rowIndex > endRowIndex) {
  19498. tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex)
  19499. }
  19500. }
  19501. }
  19502. // 检查是否有超出TableRange左边界的情况
  19503. if (beginColIndex > 0) {
  19504. for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
  19505. cellInfo = me.indexTable[rowIndex][beginColIndex]
  19506. colIndex = cellInfo.colIndex
  19507. if (colIndex < beginColIndex) {
  19508. tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex)
  19509. }
  19510. }
  19511. }
  19512. //递归调用直至所有完成所有框选单元格的扩展
  19513. if (
  19514. tmpBeginRowIndex != beginRowIndex ||
  19515. tmpBeginColIndex != beginColIndex ||
  19516. tmpEndRowIndex != endRowIndex ||
  19517. tmpEndColIndex != endColIndex
  19518. ) {
  19519. return checkRange(
  19520. tmpBeginRowIndex,
  19521. tmpBeginColIndex,
  19522. tmpEndRowIndex,
  19523. tmpEndColIndex
  19524. )
  19525. } else {
  19526. // 不需要扩展TableRange的情况
  19527. return {
  19528. beginRowIndex: beginRowIndex,
  19529. beginColIndex: beginColIndex,
  19530. endRowIndex: endRowIndex,
  19531. endColIndex: endColIndex
  19532. }
  19533. }
  19534. }
  19535. try {
  19536. var me = this,
  19537. cellAInfo = me.getCellInfo(cellA)
  19538. if (cellA === cellB) {
  19539. return {
  19540. beginRowIndex: cellAInfo.rowIndex,
  19541. beginColIndex: cellAInfo.colIndex,
  19542. endRowIndex: cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
  19543. endColIndex: cellAInfo.colIndex + cellAInfo.colSpan - 1
  19544. }
  19545. }
  19546. var cellBInfo = me.getCellInfo(cellB)
  19547. // 计算TableRange的四个边
  19548. var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex),
  19549. beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex),
  19550. endRowIndex = Math.max(
  19551. cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
  19552. cellBInfo.rowIndex + cellBInfo.rowSpan - 1
  19553. ),
  19554. endColIndex = Math.max(
  19555. cellAInfo.colIndex + cellAInfo.colSpan - 1,
  19556. cellBInfo.colIndex + cellBInfo.colSpan - 1
  19557. )
  19558. return checkRange(
  19559. beginRowIndex,
  19560. beginColIndex,
  19561. endRowIndex,
  19562. endColIndex
  19563. )
  19564. } catch (e) {
  19565. //throw e;
  19566. }
  19567. },
  19568. /**
  19569. * 依据cellsRange获取对应的单元格集合
  19570. */
  19571. getCells: function (range) {
  19572. //每次获取cells之前必须先清除上次的选择,否则会对后续获取操作造成影响
  19573. this.clearSelected()
  19574. var beginRowIndex = range.beginRowIndex,
  19575. beginColIndex = range.beginColIndex,
  19576. endRowIndex = range.endRowIndex,
  19577. endColIndex = range.endColIndex,
  19578. cellInfo,
  19579. rowIndex,
  19580. colIndex,
  19581. tdHash = {},
  19582. returnTds = []
  19583. for (var i = beginRowIndex; i <= endRowIndex; i++) {
  19584. for (var j = beginColIndex; j <= endColIndex; j++) {
  19585. cellInfo = this.indexTable[i][j]
  19586. rowIndex = cellInfo.rowIndex
  19587. colIndex = cellInfo.colIndex
  19588. // 如果Cells里已经包含了此Cell则跳过
  19589. var key = rowIndex + '|' + colIndex
  19590. if (tdHash[key]) continue
  19591. tdHash[key] = 1
  19592. if (
  19593. rowIndex < i ||
  19594. colIndex < j ||
  19595. rowIndex + cellInfo.rowSpan - 1 > endRowIndex ||
  19596. colIndex + cellInfo.colSpan - 1 > endColIndex
  19597. ) {
  19598. return null
  19599. }
  19600. returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex))
  19601. }
  19602. }
  19603. return returnTds
  19604. },
  19605. /**
  19606. * 清理已经选中的单元格
  19607. */
  19608. clearSelected: function () {
  19609. UETable.removeSelectedClass(this.selectedTds)
  19610. this.selectedTds = []
  19611. this.cellsRange = {}
  19612. },
  19613. /**
  19614. * 根据range设置已经选中的单元格
  19615. */
  19616. setSelected: function (range) {
  19617. var cells = this.getCells(range)
  19618. UETable.addSelectedClass(cells)
  19619. this.selectedTds = cells
  19620. this.cellsRange = range
  19621. },
  19622. isFullRow: function () {
  19623. var range = this.cellsRange
  19624. return range.endColIndex - range.beginColIndex + 1 == this.colsNum
  19625. },
  19626. isFullCol: function () {
  19627. var range = this.cellsRange,
  19628. table = this.table,
  19629. ths = table.getElementsByTagName('th'),
  19630. rows = range.endRowIndex - range.beginRowIndex + 1
  19631. return !ths.length
  19632. ? rows == this.rowsNum
  19633. : rows == this.rowsNum || rows == this.rowsNum - 1
  19634. },
  19635. /**
  19636. * 获取视觉上的前置单元格,默认是左边,top传入时
  19637. * @param cell
  19638. * @param top
  19639. */
  19640. getNextCell: function (cell, bottom, ignoreRange) {
  19641. try {
  19642. var cellInfo = this.getCellInfo(cell),
  19643. nextRowIndex,
  19644. nextColIndex
  19645. var len = this.selectedTds.length && !ignoreRange,
  19646. range = this.cellsRange
  19647. //末行或者末列没有后置单元格
  19648. if (
  19649. (!bottom && cellInfo.rowIndex == 0) ||
  19650. (bottom &&
  19651. (!len
  19652. ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1
  19653. : range.endRowIndex == this.rowsNum - 1))
  19654. )
  19655. return null
  19656. nextRowIndex = !bottom
  19657. ? !len
  19658. ? cellInfo.rowIndex - 1
  19659. : range.beginRowIndex - 1
  19660. : !len
  19661. ? cellInfo.rowIndex + cellInfo.rowSpan
  19662. : range.endRowIndex + 1
  19663. nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex
  19664. return this.getCell(
  19665. this.indexTable[nextRowIndex][nextColIndex].rowIndex,
  19666. this.indexTable[nextRowIndex][nextColIndex].cellIndex
  19667. )
  19668. } catch (e) {
  19669. showError(e)
  19670. }
  19671. },
  19672. getPreviewCell: function (cell, top) {
  19673. try {
  19674. var cellInfo = this.getCellInfo(cell),
  19675. previewRowIndex,
  19676. previewColIndex
  19677. var len = this.selectedTds.length,
  19678. range = this.cellsRange
  19679. //首行或者首列没有前置单元格
  19680. if (
  19681. (!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
  19682. (top &&
  19683. (!len
  19684. ? cellInfo.rowIndex > this.colsNum - 1
  19685. : range.endColIndex == this.colsNum - 1))
  19686. )
  19687. return null
  19688. previewRowIndex = !top
  19689. ? !len
  19690. ? cellInfo.rowIndex
  19691. : range.beginRowIndex
  19692. : !len
  19693. ? cellInfo.rowIndex < 1
  19694. ? 0
  19695. : cellInfo.rowIndex - 1
  19696. : range.beginRowIndex
  19697. previewColIndex = !top
  19698. ? !len
  19699. ? cellInfo.colIndex < 1
  19700. ? 0
  19701. : cellInfo.colIndex - 1
  19702. : range.beginColIndex - 1
  19703. : !len
  19704. ? cellInfo.colIndex
  19705. : range.endColIndex + 1
  19706. return this.getCell(
  19707. this.indexTable[previewRowIndex][previewColIndex].rowIndex,
  19708. this.indexTable[previewRowIndex][previewColIndex].cellIndex
  19709. )
  19710. } catch (e) {
  19711. showError(e)
  19712. }
  19713. },
  19714. /**
  19715. * 移动单元格中的内容
  19716. */
  19717. moveContent: function (cellTo, cellFrom) {
  19718. if (UETable.isEmptyBlock(cellFrom)) return
  19719. if (UETable.isEmptyBlock(cellTo)) {
  19720. cellTo.innerHTML = cellFrom.innerHTML
  19721. return
  19722. }
  19723. var child = cellTo.lastChild
  19724. if (child.nodeType == 3 || !dtd.$block[child.tagName]) {
  19725. cellTo.appendChild(cellTo.ownerDocument.createElement('br'))
  19726. }
  19727. while ((child = cellFrom.firstChild)) {
  19728. cellTo.appendChild(child)
  19729. }
  19730. },
  19731. /**
  19732. * 向右合并单元格
  19733. */
  19734. mergeRight: function (cell) {
  19735. var cellInfo = this.getCellInfo(cell),
  19736. rightColIndex = cellInfo.colIndex + cellInfo.colSpan,
  19737. rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex],
  19738. rightCell = this.getCell(
  19739. rightCellInfo.rowIndex,
  19740. rightCellInfo.cellIndex
  19741. )
  19742. //合并
  19743. cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan
  19744. //被合并的单元格不应存在宽度属性
  19745. cell.removeAttribute('width')
  19746. //移动内容
  19747. this.moveContent(cell, rightCell)
  19748. //删掉被合并的Cell
  19749. this.deleteCell(rightCell, rightCellInfo.rowIndex)
  19750. this.update()
  19751. },
  19752. /**
  19753. * 向下合并单元格
  19754. */
  19755. mergeDown: function (cell) {
  19756. var cellInfo = this.getCellInfo(cell),
  19757. downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan,
  19758. downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex],
  19759. downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex)
  19760. cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan
  19761. cell.removeAttribute('height')
  19762. this.moveContent(cell, downCell)
  19763. this.deleteCell(downCell, downCellInfo.rowIndex)
  19764. this.update()
  19765. },
  19766. /**
  19767. * 合并整个range中的内容
  19768. */
  19769. mergeRange: function () {
  19770. //由于合并操作可以在任意时刻进行,所以无法通过鼠标位置等信息实时生成range,只能通过缓存实例中的cellsRange对象来访问
  19771. var range = this.cellsRange,
  19772. leftTopCell = this.getCell(
  19773. range.beginRowIndex,
  19774. this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex
  19775. )
  19776. if (
  19777. leftTopCell.tagName == 'TH' &&
  19778. range.endRowIndex !== range.beginRowIndex
  19779. ) {
  19780. var index = this.indexTable,
  19781. info = this.getCellInfo(leftTopCell)
  19782. leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex)
  19783. range = this.getCellsRange(
  19784. leftTopCell,
  19785. this.getCell(
  19786. index[this.rowsNum - 1][info.colIndex].rowIndex,
  19787. index[this.rowsNum - 1][info.colIndex].cellIndex
  19788. )
  19789. )
  19790. }
  19791. // 删除剩余的Cells
  19792. var cells = this.getCells(range)
  19793. for (var i = 0, ci; (ci = cells[i++]); ) {
  19794. if (ci !== leftTopCell) {
  19795. this.moveContent(leftTopCell, ci)
  19796. this.deleteCell(ci)
  19797. }
  19798. }
  19799. // 修改左上角Cell的rowSpan和colSpan,并调整宽度属性设置
  19800. leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1
  19801. leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute('height')
  19802. leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1
  19803. leftTopCell.colSpan > 1 && leftTopCell.removeAttribute('width')
  19804. if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) {
  19805. leftTopCell.colSpan = 1
  19806. }
  19807. if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) {
  19808. var rowIndex = leftTopCell.parentNode.rowIndex
  19809. //解决IE下的表格操作问题
  19810. if (this.table.deleteRow) {
  19811. for (
  19812. var i = rowIndex + 1,
  19813. curIndex = rowIndex + 1,
  19814. len = leftTopCell.rowSpan;
  19815. i < len;
  19816. i++
  19817. ) {
  19818. this.table.deleteRow(curIndex)
  19819. }
  19820. } else {
  19821. for (var i = 0, len = leftTopCell.rowSpan - 1; i < len; i++) {
  19822. var row = this.table.rows[rowIndex + 1]
  19823. row.parentNode.removeChild(row)
  19824. }
  19825. }
  19826. leftTopCell.rowSpan = 1
  19827. }
  19828. this.update()
  19829. },
  19830. /**
  19831. * 插入一行单元格
  19832. */
  19833. insertRow: function (rowIndex, sourceCell) {
  19834. var numCols = this.colsNum,
  19835. table = this.table,
  19836. row = table.insertRow(rowIndex),
  19837. cell,
  19838. isInsertTitle =
  19839. typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH'
  19840. function replaceTdToTh(colIndex, cell, tableRow) {
  19841. if (colIndex == 0) {
  19842. var tr = tableRow.nextSibling || tableRow.previousSibling,
  19843. th = tr.cells[colIndex]
  19844. if (th.tagName == 'TH') {
  19845. th = cell.ownerDocument.createElement('th')
  19846. th.appendChild(cell.firstChild)
  19847. tableRow.insertBefore(th, cell)
  19848. domUtils.remove(cell)
  19849. }
  19850. } else {
  19851. if (cell.tagName == 'TH') {
  19852. var td = cell.ownerDocument.createElement('td')
  19853. td.appendChild(cell.firstChild)
  19854. tableRow.insertBefore(td, cell)
  19855. domUtils.remove(cell)
  19856. }
  19857. }
  19858. }
  19859. //首行直接插入,无需考虑部分单元格被rowspan的情况
  19860. if (rowIndex == 0 || rowIndex == this.rowsNum) {
  19861. for (var colIndex = 0; colIndex < numCols; colIndex++) {
  19862. cell = this.cloneCell(sourceCell, true)
  19863. this.setCellContent(cell)
  19864. cell.getAttribute('vAlign') &&
  19865. cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
  19866. row.appendChild(cell)
  19867. if (!isInsertTitle) replaceTdToTh(colIndex, cell, row)
  19868. }
  19869. } else {
  19870. var infoRow = this.indexTable[rowIndex],
  19871. cellIndex = 0
  19872. for (colIndex = 0; colIndex < numCols; colIndex++) {
  19873. var cellInfo = infoRow[colIndex]
  19874. //如果存在某个单元格的rowspan穿过待插入行的位置,则修改该单元格的rowspan即可,无需插入单元格
  19875. if (cellInfo.rowIndex < rowIndex) {
  19876. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
  19877. cell.rowSpan = cellInfo.rowSpan + 1
  19878. } else {
  19879. cell = this.cloneCell(sourceCell, true)
  19880. this.setCellContent(cell)
  19881. row.appendChild(cell)
  19882. }
  19883. if (!isInsertTitle) replaceTdToTh(colIndex, cell, row)
  19884. }
  19885. }
  19886. //框选时插入不触发contentchange,需要手动更新索引。
  19887. this.update()
  19888. return row
  19889. },
  19890. /**
  19891. * 删除一行单元格
  19892. * @param rowIndex
  19893. */
  19894. deleteRow: function (rowIndex) {
  19895. var row = this.table.rows[rowIndex],
  19896. infoRow = this.indexTable[rowIndex],
  19897. colsNum = this.colsNum,
  19898. count = 0 //处理计数
  19899. for (var colIndex = 0; colIndex < colsNum; ) {
  19900. var cellInfo = infoRow[colIndex],
  19901. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
  19902. if (cell.rowSpan > 1) {
  19903. if (cellInfo.rowIndex == rowIndex) {
  19904. var clone = cell.cloneNode(true)
  19905. clone.rowSpan = cell.rowSpan - 1
  19906. clone.innerHTML = ''
  19907. cell.rowSpan = 1
  19908. var nextRowIndex = rowIndex + 1,
  19909. nextRow = this.table.rows[nextRowIndex],
  19910. insertCellIndex,
  19911. preMerged =
  19912. this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count
  19913. if (preMerged < colIndex) {
  19914. insertCellIndex = colIndex - preMerged - 1
  19915. //nextRow.insertCell(insertCellIndex);
  19916. domUtils.insertAfter(nextRow.cells[insertCellIndex], clone)
  19917. } else {
  19918. if (nextRow.cells.length)
  19919. nextRow.insertBefore(clone, nextRow.cells[0])
  19920. }
  19921. count += 1
  19922. //cell.parentNode.removeChild(cell);
  19923. }
  19924. }
  19925. colIndex += cell.colSpan || 1
  19926. }
  19927. var deleteTds = [],
  19928. cacheMap = {}
  19929. for (colIndex = 0; colIndex < colsNum; colIndex++) {
  19930. var tmpRowIndex = infoRow[colIndex].rowIndex,
  19931. tmpCellIndex = infoRow[colIndex].cellIndex,
  19932. key = tmpRowIndex + '_' + tmpCellIndex
  19933. if (cacheMap[key]) continue
  19934. cacheMap[key] = 1
  19935. cell = this.getCell(tmpRowIndex, tmpCellIndex)
  19936. deleteTds.push(cell)
  19937. }
  19938. var mergeTds = []
  19939. utils.each(deleteTds, function (td) {
  19940. if (td.rowSpan == 1) {
  19941. td.parentNode.removeChild(td)
  19942. } else {
  19943. mergeTds.push(td)
  19944. }
  19945. })
  19946. utils.each(mergeTds, function (td) {
  19947. td.rowSpan--
  19948. })
  19949. row.parentNode.removeChild(row)
  19950. //浏览器方法本身存在bug,采用自定义方法删除
  19951. //this.table.deleteRow(rowIndex);
  19952. this.update()
  19953. },
  19954. insertCol: function (colIndex, sourceCell, defaultValue) {
  19955. var rowsNum = this.rowsNum,
  19956. rowIndex = 0,
  19957. tableRow,
  19958. cell,
  19959. backWidth = parseInt(
  19960. (this.table.offsetWidth -
  19961. (this.colsNum + 1) * 20 -
  19962. (this.colsNum + 1)) /
  19963. (this.colsNum + 1),
  19964. 10
  19965. ),
  19966. isInsertTitleCol =
  19967. typeof sourceCell == 'string' && sourceCell.toUpperCase() == 'TH'
  19968. function replaceTdToTh(rowIndex, cell, tableRow) {
  19969. if (rowIndex == 0) {
  19970. var th = cell.nextSibling || cell.previousSibling
  19971. if (th.tagName == 'TH') {
  19972. th = cell.ownerDocument.createElement('th')
  19973. th.appendChild(cell.firstChild)
  19974. tableRow.insertBefore(th, cell)
  19975. domUtils.remove(cell)
  19976. }
  19977. } else {
  19978. if (cell.tagName == 'TH') {
  19979. var td = cell.ownerDocument.createElement('td')
  19980. td.appendChild(cell.firstChild)
  19981. tableRow.insertBefore(td, cell)
  19982. domUtils.remove(cell)
  19983. }
  19984. }
  19985. }
  19986. var preCell
  19987. if (colIndex == 0 || colIndex == this.colsNum) {
  19988. for (; rowIndex < rowsNum; rowIndex++) {
  19989. tableRow = this.table.rows[rowIndex]
  19990. preCell =
  19991. tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length]
  19992. cell = this.cloneCell(sourceCell, true) //tableRow.insertCell(colIndex == 0 ? colIndex : tableRow.cells.length);
  19993. this.setCellContent(cell)
  19994. cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
  19995. preCell && cell.setAttribute('width', preCell.getAttribute('width'))
  19996. if (!colIndex) {
  19997. tableRow.insertBefore(cell, tableRow.cells[0])
  19998. } else {
  19999. domUtils.insertAfter(
  20000. tableRow.cells[tableRow.cells.length - 1],
  20001. cell
  20002. )
  20003. }
  20004. if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow)
  20005. }
  20006. } else {
  20007. for (; rowIndex < rowsNum; rowIndex++) {
  20008. var cellInfo = this.indexTable[rowIndex][colIndex]
  20009. if (cellInfo.colIndex < colIndex) {
  20010. cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
  20011. cell.colSpan = cellInfo.colSpan + 1
  20012. } else {
  20013. tableRow = this.table.rows[rowIndex]
  20014. preCell = tableRow.cells[cellInfo.cellIndex]
  20015. cell = this.cloneCell(sourceCell, true) //tableRow.insertCell(cellInfo.cellIndex);
  20016. this.setCellContent(cell)
  20017. cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
  20018. preCell &&
  20019. cell.setAttribute('width', preCell.getAttribute('width'))
  20020. //防止IE下报错
  20021. preCell
  20022. ? tableRow.insertBefore(cell, preCell)
  20023. : tableRow.appendChild(cell)
  20024. }
  20025. if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow)
  20026. }
  20027. }
  20028. //框选时插入不触发contentchange,需要手动更新索引
  20029. this.update()
  20030. this.updateWidth(
  20031. backWidth,
  20032. defaultValue || { tdPadding: 10, tdBorder: 1 }
  20033. )
  20034. },
  20035. updateWidth: function (width, defaultValue) {
  20036. var table = this.table,
  20037. tmpWidth =
  20038. UETable.getWidth(table) -
  20039. defaultValue.tdPadding * 2 -
  20040. defaultValue.tdBorder +
  20041. width
  20042. if (tmpWidth < table.ownerDocument.body.offsetWidth) {
  20043. table.setAttribute('width', tmpWidth)
  20044. return
  20045. }
  20046. var tds = domUtils.getElementsByTagName(this.table, 'td th')
  20047. utils.each(tds, function (td) {
  20048. td.setAttribute('width', width)
  20049. })
  20050. },
  20051. deleteCol: function (colIndex) {
  20052. var indexTable = this.indexTable,
  20053. tableRows = this.table.rows,
  20054. backTableWidth = this.table.getAttribute('width'),
  20055. backTdWidth = 0,
  20056. rowsNum = this.rowsNum,
  20057. cacheMap = {}
  20058. for (var rowIndex = 0; rowIndex < rowsNum; ) {
  20059. var infoRow = indexTable[rowIndex],
  20060. cellInfo = infoRow[colIndex],
  20061. key = cellInfo.rowIndex + '_' + cellInfo.colIndex
  20062. // 跳过已经处理过的Cell
  20063. if (cacheMap[key]) continue
  20064. cacheMap[key] = 1
  20065. var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
  20066. if (!backTdWidth)
  20067. backTdWidth =
  20068. cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0)
  20069. // 如果Cell的colSpan大于1, 就修改colSpan, 否则就删掉这个Cell
  20070. if (cell.colSpan > 1) {
  20071. cell.colSpan--
  20072. } else {
  20073. tableRows[rowIndex].deleteCell(cellInfo.cellIndex)
  20074. }
  20075. rowIndex += cellInfo.rowSpan || 1
  20076. }
  20077. this.table.setAttribute('width', backTableWidth - backTdWidth)
  20078. this.update()
  20079. },
  20080. splitToCells: function (cell) {
  20081. var me = this,
  20082. cells = this.splitToRows(cell)
  20083. utils.each(cells, function (cell) {
  20084. me.splitToCols(cell)
  20085. })
  20086. },
  20087. splitToRows: function (cell) {
  20088. var cellInfo = this.getCellInfo(cell),
  20089. rowIndex = cellInfo.rowIndex,
  20090. colIndex = cellInfo.colIndex,
  20091. results = []
  20092. // 修改Cell的rowSpan
  20093. cell.rowSpan = 1
  20094. results.push(cell)
  20095. // 补齐单元格
  20096. for (
  20097. var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan;
  20098. i < endRow;
  20099. i++
  20100. ) {
  20101. if (i == rowIndex) continue
  20102. var tableRow = this.table.rows[i],
  20103. tmpCell = tableRow.insertCell(
  20104. colIndex - this.getPreviewMergedCellsNum(i, colIndex)
  20105. )
  20106. tmpCell.colSpan = cellInfo.colSpan
  20107. this.setCellContent(tmpCell)
  20108. tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'))
  20109. tmpCell.setAttribute('align', cell.getAttribute('align'))
  20110. if (cell.style.cssText) {
  20111. tmpCell.style.cssText = cell.style.cssText
  20112. }
  20113. results.push(tmpCell)
  20114. }
  20115. this.update()
  20116. return results
  20117. },
  20118. getPreviewMergedCellsNum: function (rowIndex, colIndex) {
  20119. var indexRow = this.indexTable[rowIndex],
  20120. num = 0
  20121. for (var i = 0; i < colIndex; ) {
  20122. var colSpan = indexRow[i].colSpan,
  20123. tmpRowIndex = indexRow[i].rowIndex
  20124. num += colSpan - (tmpRowIndex == rowIndex ? 1 : 0)
  20125. i += colSpan
  20126. }
  20127. return num
  20128. },
  20129. splitToCols: function (cell) {
  20130. var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0),
  20131. cellInfo = this.getCellInfo(cell),
  20132. rowIndex = cellInfo.rowIndex,
  20133. colIndex = cellInfo.colIndex,
  20134. results = []
  20135. // 修改Cell的rowSpan
  20136. cell.colSpan = 1
  20137. cell.setAttribute('width', backWidth)
  20138. results.push(cell)
  20139. // 补齐单元格
  20140. for (
  20141. var j = colIndex, endCol = colIndex + cellInfo.colSpan;
  20142. j < endCol;
  20143. j++
  20144. ) {
  20145. if (j == colIndex) continue
  20146. var tableRow = this.table.rows[rowIndex],
  20147. tmpCell = tableRow.insertCell(
  20148. this.indexTable[rowIndex][j].cellIndex + 1
  20149. )
  20150. tmpCell.rowSpan = cellInfo.rowSpan
  20151. this.setCellContent(tmpCell)
  20152. tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'))
  20153. tmpCell.setAttribute('align', cell.getAttribute('align'))
  20154. tmpCell.setAttribute('width', backWidth)
  20155. if (cell.style.cssText) {
  20156. tmpCell.style.cssText = cell.style.cssText
  20157. }
  20158. //处理th的情况
  20159. if (cell.tagName == 'TH') {
  20160. var th = cell.ownerDocument.createElement('th')
  20161. th.appendChild(tmpCell.firstChild)
  20162. th.setAttribute('vAlign', cell.getAttribute('vAlign'))
  20163. th.rowSpan = tmpCell.rowSpan
  20164. tableRow.insertBefore(th, tmpCell)
  20165. domUtils.remove(tmpCell)
  20166. }
  20167. results.push(tmpCell)
  20168. }
  20169. this.update()
  20170. return results
  20171. },
  20172. isLastCell: function (cell, rowsNum, colsNum) {
  20173. rowsNum = rowsNum || this.rowsNum
  20174. colsNum = colsNum || this.colsNum
  20175. var cellInfo = this.getCellInfo(cell)
  20176. return (
  20177. cellInfo.rowIndex + cellInfo.rowSpan == rowsNum &&
  20178. cellInfo.colIndex + cellInfo.colSpan == colsNum
  20179. )
  20180. },
  20181. getLastCell: function (cells) {
  20182. cells = cells || this.table.getElementsByTagName('td')
  20183. var firstInfo = this.getCellInfo(cells[0])
  20184. var me = this,
  20185. last = cells[0],
  20186. tr = last.parentNode,
  20187. cellsNum = 0,
  20188. cols = 0,
  20189. rows
  20190. utils.each(cells, function (cell) {
  20191. if (cell.parentNode == tr) cols += cell.colSpan || 1
  20192. cellsNum += cell.rowSpan * cell.colSpan || 1
  20193. })
  20194. rows = cellsNum / cols
  20195. utils.each(cells, function (cell) {
  20196. if (me.isLastCell(cell, rows, cols)) {
  20197. last = cell
  20198. return false
  20199. }
  20200. })
  20201. return last
  20202. },
  20203. selectRow: function (rowIndex) {
  20204. var indexRow = this.indexTable[rowIndex],
  20205. start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex),
  20206. end = this.getCell(
  20207. indexRow[this.colsNum - 1].rowIndex,
  20208. indexRow[this.colsNum - 1].cellIndex
  20209. ),
  20210. range = this.getCellsRange(start, end)
  20211. this.setSelected(range)
  20212. },
  20213. selectTable: function () {
  20214. var tds = this.table.getElementsByTagName('td'),
  20215. range = this.getCellsRange(tds[0], tds[tds.length - 1])
  20216. this.setSelected(range)
  20217. },
  20218. setBackground: function (cells, value) {
  20219. if (typeof value === 'string') {
  20220. utils.each(cells, function (cell) {
  20221. cell.style.backgroundColor = value
  20222. })
  20223. } else if (typeof value === 'object') {
  20224. value = utils.extend(
  20225. {
  20226. repeat: true,
  20227. colorList: ['#ddd', '#fff']
  20228. },
  20229. value
  20230. )
  20231. var rowIndex = this.getCellInfo(cells[0]).rowIndex,
  20232. count = 0,
  20233. colors = value.colorList,
  20234. getColor = function (list, index, repeat) {
  20235. return list[index]
  20236. ? list[index]
  20237. : repeat
  20238. ? list[index % list.length]
  20239. : ''
  20240. }
  20241. for (var i = 0, cell; (cell = cells[i++]); ) {
  20242. var cellInfo = this.getCellInfo(cell)
  20243. cell.style.backgroundColor = getColor(
  20244. colors,
  20245. rowIndex + count == cellInfo.rowIndex ? count : ++count,
  20246. value.repeat
  20247. )
  20248. }
  20249. }
  20250. },
  20251. removeBackground: function (cells) {
  20252. utils.each(cells, function (cell) {
  20253. cell.style.backgroundColor = ''
  20254. })
  20255. }
  20256. }
  20257. function showError(e) {}
  20258. })()
  20259. // plugins/table.cmds.js
  20260. /**
  20261. * Created with JetBrains PhpStorm.
  20262. * User: taoqili
  20263. * Date: 13-2-20
  20264. * Time: 下午6:25
  20265. * To change this template use File | Settings | File Templates.
  20266. */
  20267. ;(function () {
  20268. var UT = UE.UETable,
  20269. getTableItemsByRange = function (editor) {
  20270. return UT.getTableItemsByRange(editor)
  20271. },
  20272. getUETableBySelected = function (editor) {
  20273. return UT.getUETableBySelected(editor)
  20274. },
  20275. getDefaultValue = function (editor, table) {
  20276. return UT.getDefaultValue(editor, table)
  20277. },
  20278. getUETable = function (tdOrTable) {
  20279. return UT.getUETable(tdOrTable)
  20280. }
  20281. UE.commands['inserttable'] = {
  20282. queryCommandState: function () {
  20283. return getTableItemsByRange(this).table ? -1 : 0
  20284. },
  20285. execCommand: function (cmd, opt) {
  20286. function createTable(opt, tdWidth) {
  20287. var html = [],
  20288. rowsNum = opt.numRows,
  20289. colsNum = opt.numCols
  20290. for (var r = 0; r < rowsNum; r++) {
  20291. html.push('<tr' + (r == 0 ? ' class="firstRow"' : '') + '>')
  20292. for (var c = 0; c < colsNum; c++) {
  20293. html.push(
  20294. '<td width="' +
  20295. tdWidth +
  20296. '" vAlign="' +
  20297. opt.tdvalign +
  20298. '" >' +
  20299. (browser.ie && browser.version < 11
  20300. ? domUtils.fillChar
  20301. : '<br/>') +
  20302. '</td>'
  20303. )
  20304. }
  20305. html.push('</tr>')
  20306. }
  20307. //禁止指定table-width
  20308. return '<table><tbody>' + html.join('') + '</tbody></table>'
  20309. }
  20310. if (!opt) {
  20311. opt = utils.extend(
  20312. {},
  20313. {
  20314. numCols: this.options.defaultCols,
  20315. numRows: this.options.defaultRows,
  20316. tdvalign: this.options.tdvalign
  20317. }
  20318. )
  20319. }
  20320. var me = this
  20321. var range = this.selection.getRange(),
  20322. start = range.startContainer,
  20323. firstParentBlock =
  20324. domUtils.findParent(
  20325. start,
  20326. function (node) {
  20327. return domUtils.isBlockElm(node)
  20328. },
  20329. true
  20330. ) || me.body
  20331. var defaultValue = getDefaultValue(me),
  20332. tableWidth = firstParentBlock.offsetWidth,
  20333. tdWidth = Math.floor(
  20334. tableWidth / opt.numCols -
  20335. defaultValue.tdPadding * 2 -
  20336. defaultValue.tdBorder
  20337. )
  20338. //todo其他属性
  20339. !opt.tdvalign && (opt.tdvalign = me.options.tdvalign)
  20340. me.execCommand('inserthtml', createTable(opt, tdWidth))
  20341. }
  20342. }
  20343. UE.commands['insertparagraphbeforetable'] = {
  20344. queryCommandState: function () {
  20345. return getTableItemsByRange(this).cell ? 0 : -1
  20346. },
  20347. execCommand: function () {
  20348. var table = getTableItemsByRange(this).table
  20349. if (table) {
  20350. var p = this.document.createElement('p')
  20351. p.innerHTML = browser.ie ? '&nbsp;' : '<br />'
  20352. table.parentNode.insertBefore(p, table)
  20353. this.selection.getRange().setStart(p, 0).setCursor()
  20354. }
  20355. }
  20356. }
  20357. UE.commands['deletetable'] = {
  20358. queryCommandState: function () {
  20359. var rng = this.selection.getRange()
  20360. return domUtils.findParentByTagName(rng.startContainer, 'table', true)
  20361. ? 0
  20362. : -1
  20363. },
  20364. execCommand: function (cmd, table) {
  20365. var rng = this.selection.getRange()
  20366. table =
  20367. table ||
  20368. domUtils.findParentByTagName(rng.startContainer, 'table', true)
  20369. if (table) {
  20370. var next = table.nextSibling
  20371. if (!next) {
  20372. next = domUtils.createElement(this.document, 'p', {
  20373. innerHTML: browser.ie ? domUtils.fillChar : '<br/>'
  20374. })
  20375. table.parentNode.insertBefore(next, table)
  20376. }
  20377. domUtils.remove(table)
  20378. rng = this.selection.getRange()
  20379. if (next.nodeType == 3) {
  20380. rng.setStartBefore(next)
  20381. } else {
  20382. rng.setStart(next, 0)
  20383. }
  20384. rng.setCursor(false, true)
  20385. this.fireEvent('tablehasdeleted')
  20386. }
  20387. }
  20388. }
  20389. UE.commands['cellalign'] = {
  20390. queryCommandState: function () {
  20391. return getSelectedArr(this).length ? 0 : -1
  20392. },
  20393. execCommand: function (cmd, align) {
  20394. var selectedTds = getSelectedArr(this)
  20395. if (selectedTds.length) {
  20396. for (var i = 0, ci; (ci = selectedTds[i++]); ) {
  20397. ci.setAttribute('align', align)
  20398. }
  20399. }
  20400. }
  20401. }
  20402. UE.commands['cellvalign'] = {
  20403. queryCommandState: function () {
  20404. return getSelectedArr(this).length ? 0 : -1
  20405. },
  20406. execCommand: function (cmd, valign) {
  20407. var selectedTds = getSelectedArr(this)
  20408. if (selectedTds.length) {
  20409. for (var i = 0, ci; (ci = selectedTds[i++]); ) {
  20410. ci.setAttribute('vAlign', valign)
  20411. }
  20412. }
  20413. }
  20414. }
  20415. UE.commands['insertcaption'] = {
  20416. queryCommandState: function () {
  20417. var table = getTableItemsByRange(this).table
  20418. if (table) {
  20419. return table.getElementsByTagName('caption').length == 0 ? 1 : -1
  20420. }
  20421. return -1
  20422. },
  20423. execCommand: function () {
  20424. var table = getTableItemsByRange(this).table
  20425. if (table) {
  20426. var caption = this.document.createElement('caption')
  20427. caption.innerHTML = browser.ie ? domUtils.fillChar : '<br/>'
  20428. table.insertBefore(caption, table.firstChild)
  20429. var range = this.selection.getRange()
  20430. range.setStart(caption, 0).setCursor()
  20431. }
  20432. }
  20433. }
  20434. UE.commands['deletecaption'] = {
  20435. queryCommandState: function () {
  20436. var rng = this.selection.getRange(),
  20437. table = domUtils.findParentByTagName(rng.startContainer, 'table')
  20438. if (table) {
  20439. return table.getElementsByTagName('caption').length == 0 ? -1 : 1
  20440. }
  20441. return -1
  20442. },
  20443. execCommand: function () {
  20444. var rng = this.selection.getRange(),
  20445. table = domUtils.findParentByTagName(rng.startContainer, 'table')
  20446. if (table) {
  20447. domUtils.remove(table.getElementsByTagName('caption')[0])
  20448. var range = this.selection.getRange()
  20449. range.setStart(table.rows[0].cells[0], 0).setCursor()
  20450. }
  20451. }
  20452. }
  20453. UE.commands['inserttitle'] = {
  20454. queryCommandState: function () {
  20455. var table = getTableItemsByRange(this).table
  20456. if (table) {
  20457. var firstRow = table.rows[0]
  20458. return firstRow.cells[
  20459. firstRow.cells.length - 1
  20460. ].tagName.toLowerCase() != 'th'
  20461. ? 0
  20462. : -1
  20463. }
  20464. return -1
  20465. },
  20466. execCommand: function () {
  20467. var table = getTableItemsByRange(this).table
  20468. if (table) {
  20469. getUETable(table).insertRow(0, 'th')
  20470. }
  20471. var th = table.getElementsByTagName('th')[0]
  20472. this.selection.getRange().setStart(th, 0).setCursor(false, true)
  20473. }
  20474. }
  20475. UE.commands['deletetitle'] = {
  20476. queryCommandState: function () {
  20477. var table = getTableItemsByRange(this).table
  20478. if (table) {
  20479. var firstRow = table.rows[0]
  20480. return firstRow.cells[
  20481. firstRow.cells.length - 1
  20482. ].tagName.toLowerCase() == 'th'
  20483. ? 0
  20484. : -1
  20485. }
  20486. return -1
  20487. },
  20488. execCommand: function () {
  20489. var table = getTableItemsByRange(this).table
  20490. if (table) {
  20491. domUtils.remove(table.rows[0])
  20492. }
  20493. var td = table.getElementsByTagName('td')[0]
  20494. this.selection.getRange().setStart(td, 0).setCursor(false, true)
  20495. }
  20496. }
  20497. UE.commands['inserttitlecol'] = {
  20498. queryCommandState: function () {
  20499. var table = getTableItemsByRange(this).table
  20500. if (table) {
  20501. var lastRow = table.rows[table.rows.length - 1]
  20502. return lastRow.getElementsByTagName('th').length ? -1 : 0
  20503. }
  20504. return -1
  20505. },
  20506. execCommand: function (cmd) {
  20507. var table = getTableItemsByRange(this).table
  20508. if (table) {
  20509. getUETable(table).insertCol(0, 'th')
  20510. }
  20511. resetTdWidth(table, this)
  20512. var th = table.getElementsByTagName('th')[0]
  20513. this.selection.getRange().setStart(th, 0).setCursor(false, true)
  20514. }
  20515. }
  20516. UE.commands['deletetitlecol'] = {
  20517. queryCommandState: function () {
  20518. var table = getTableItemsByRange(this).table
  20519. if (table) {
  20520. var lastRow = table.rows[table.rows.length - 1]
  20521. return lastRow.getElementsByTagName('th').length ? 0 : -1
  20522. }
  20523. return -1
  20524. },
  20525. execCommand: function () {
  20526. var table = getTableItemsByRange(this).table
  20527. if (table) {
  20528. for (var i = 0; i < table.rows.length; i++) {
  20529. domUtils.remove(table.rows[i].children[0])
  20530. }
  20531. }
  20532. resetTdWidth(table, this)
  20533. var td = table.getElementsByTagName('td')[0]
  20534. this.selection.getRange().setStart(td, 0).setCursor(false, true)
  20535. }
  20536. }
  20537. UE.commands['mergeright'] = {
  20538. queryCommandState: function (cmd) {
  20539. var tableItems = getTableItemsByRange(this),
  20540. table = tableItems.table,
  20541. cell = tableItems.cell
  20542. if (!table || !cell) return -1
  20543. var ut = getUETable(table)
  20544. if (ut.selectedTds.length) return -1
  20545. var cellInfo = ut.getCellInfo(cell),
  20546. rightColIndex = cellInfo.colIndex + cellInfo.colSpan
  20547. if (rightColIndex >= ut.colsNum) return -1 // 如果处于最右边则不能向右合并
  20548. var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex],
  20549. rightCell =
  20550. table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex]
  20551. if (!rightCell || cell.tagName != rightCell.tagName) return -1 // TH和TD不能相互合并
  20552. // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
  20553. return rightCellInfo.rowIndex == cellInfo.rowIndex &&
  20554. rightCellInfo.rowSpan == cellInfo.rowSpan
  20555. ? 0
  20556. : -1
  20557. },
  20558. execCommand: function (cmd) {
  20559. var rng = this.selection.getRange(),
  20560. bk = rng.createBookmark(true)
  20561. var cell = getTableItemsByRange(this).cell,
  20562. ut = getUETable(cell)
  20563. ut.mergeRight(cell)
  20564. rng.moveToBookmark(bk).select()
  20565. }
  20566. }
  20567. UE.commands['mergedown'] = {
  20568. queryCommandState: function (cmd) {
  20569. var tableItems = getTableItemsByRange(this),
  20570. table = tableItems.table,
  20571. cell = tableItems.cell
  20572. if (!table || !cell) return -1
  20573. var ut = getUETable(table)
  20574. if (ut.selectedTds.length) return -1
  20575. var cellInfo = ut.getCellInfo(cell),
  20576. downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan
  20577. if (downRowIndex >= ut.rowsNum) return -1 // 如果处于最下边则不能向下合并
  20578. var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex],
  20579. downCell =
  20580. table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex]
  20581. if (!downCell || cell.tagName != downCell.tagName) return -1 // TH和TD不能相互合并
  20582. // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
  20583. return downCellInfo.colIndex == cellInfo.colIndex &&
  20584. downCellInfo.colSpan == cellInfo.colSpan
  20585. ? 0
  20586. : -1
  20587. },
  20588. execCommand: function () {
  20589. var rng = this.selection.getRange(),
  20590. bk = rng.createBookmark(true)
  20591. var cell = getTableItemsByRange(this).cell,
  20592. ut = getUETable(cell)
  20593. ut.mergeDown(cell)
  20594. rng.moveToBookmark(bk).select()
  20595. }
  20596. }
  20597. UE.commands['mergecells'] = {
  20598. queryCommandState: function () {
  20599. return getUETableBySelected(this) ? 0 : -1
  20600. },
  20601. execCommand: function () {
  20602. var ut = getUETableBySelected(this)
  20603. if (ut && ut.selectedTds.length) {
  20604. var cell = ut.selectedTds[0]
  20605. ut.mergeRange()
  20606. var rng = this.selection.getRange()
  20607. if (domUtils.isEmptyBlock(cell)) {
  20608. rng.setStart(cell, 0).collapse(true)
  20609. } else {
  20610. rng.selectNodeContents(cell)
  20611. }
  20612. rng.select()
  20613. }
  20614. }
  20615. }
  20616. UE.commands['insertrow'] = {
  20617. queryCommandState: function () {
  20618. var tableItems = getTableItemsByRange(this),
  20619. cell = tableItems.cell
  20620. return cell &&
  20621. (cell.tagName == 'TD' ||
  20622. (cell.tagName == 'TH' &&
  20623. tableItems.tr !== tableItems.table.rows[0])) &&
  20624. getUETable(tableItems.table).rowsNum < this.options.maxRowNum
  20625. ? 0
  20626. : -1
  20627. },
  20628. execCommand: function () {
  20629. var rng = this.selection.getRange(),
  20630. bk = rng.createBookmark(true)
  20631. var tableItems = getTableItemsByRange(this),
  20632. cell = tableItems.cell,
  20633. table = tableItems.table,
  20634. ut = getUETable(table),
  20635. cellInfo = ut.getCellInfo(cell)
  20636. //ut.insertRow(!ut.selectedTds.length ? cellInfo.rowIndex:ut.cellsRange.beginRowIndex,'');
  20637. if (!ut.selectedTds.length) {
  20638. ut.insertRow(cellInfo.rowIndex, cell)
  20639. } else {
  20640. var range = ut.cellsRange
  20641. for (
  20642. var i = 0, len = range.endRowIndex - range.beginRowIndex + 1;
  20643. i < len;
  20644. i++
  20645. ) {
  20646. ut.insertRow(range.beginRowIndex, cell)
  20647. }
  20648. }
  20649. rng.moveToBookmark(bk).select()
  20650. if (table.getAttribute('interlaced') === 'enabled')
  20651. this.fireEvent('interlacetable', table)
  20652. }
  20653. }
  20654. //后插入行
  20655. UE.commands['insertrownext'] = {
  20656. queryCommandState: function () {
  20657. var tableItems = getTableItemsByRange(this),
  20658. cell = tableItems.cell
  20659. return cell &&
  20660. cell.tagName == 'TD' &&
  20661. getUETable(tableItems.table).rowsNum < this.options.maxRowNum
  20662. ? 0
  20663. : -1
  20664. },
  20665. execCommand: function () {
  20666. var rng = this.selection.getRange(),
  20667. bk = rng.createBookmark(true)
  20668. var tableItems = getTableItemsByRange(this),
  20669. cell = tableItems.cell,
  20670. table = tableItems.table,
  20671. ut = getUETable(table),
  20672. cellInfo = ut.getCellInfo(cell)
  20673. //ut.insertRow(!ut.selectedTds.length? cellInfo.rowIndex + cellInfo.rowSpan : ut.cellsRange.endRowIndex + 1,'');
  20674. if (!ut.selectedTds.length) {
  20675. ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell)
  20676. } else {
  20677. var range = ut.cellsRange
  20678. for (
  20679. var i = 0, len = range.endRowIndex - range.beginRowIndex + 1;
  20680. i < len;
  20681. i++
  20682. ) {
  20683. ut.insertRow(range.endRowIndex + 1, cell)
  20684. }
  20685. }
  20686. rng.moveToBookmark(bk).select()
  20687. if (table.getAttribute('interlaced') === 'enabled')
  20688. this.fireEvent('interlacetable', table)
  20689. }
  20690. }
  20691. UE.commands['deleterow'] = {
  20692. queryCommandState: function () {
  20693. var tableItems = getTableItemsByRange(this)
  20694. return tableItems.cell ? 0 : -1
  20695. },
  20696. execCommand: function () {
  20697. var cell = getTableItemsByRange(this).cell,
  20698. ut = getUETable(cell),
  20699. cellsRange = ut.cellsRange,
  20700. cellInfo = ut.getCellInfo(cell),
  20701. preCell = ut.getVSideCell(cell),
  20702. nextCell = ut.getVSideCell(cell, true),
  20703. rng = this.selection.getRange()
  20704. if (utils.isEmptyObject(cellsRange)) {
  20705. ut.deleteRow(cellInfo.rowIndex)
  20706. } else {
  20707. for (
  20708. var i = cellsRange.beginRowIndex;
  20709. i < cellsRange.endRowIndex + 1;
  20710. i++
  20711. ) {
  20712. ut.deleteRow(cellsRange.beginRowIndex)
  20713. }
  20714. }
  20715. var table = ut.table
  20716. if (!table.getElementsByTagName('td').length) {
  20717. var nextSibling = table.nextSibling
  20718. domUtils.remove(table)
  20719. if (nextSibling) {
  20720. rng.setStart(nextSibling, 0).setCursor(false, true)
  20721. }
  20722. } else {
  20723. if (
  20724. cellInfo.rowSpan == 1 ||
  20725. cellInfo.rowSpan ==
  20726. cellsRange.endRowIndex - cellsRange.beginRowIndex + 1
  20727. ) {
  20728. if (nextCell || preCell)
  20729. rng.selectNodeContents(nextCell || preCell).setCursor(false, true)
  20730. } else {
  20731. var newCell = ut.getCell(
  20732. cellInfo.rowIndex,
  20733. ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex
  20734. )
  20735. if (newCell) rng.selectNodeContents(newCell).setCursor(false, true)
  20736. }
  20737. }
  20738. if (table.getAttribute('interlaced') === 'enabled')
  20739. this.fireEvent('interlacetable', table)
  20740. }
  20741. }
  20742. UE.commands['insertcol'] = {
  20743. queryCommandState: function (cmd) {
  20744. var tableItems = getTableItemsByRange(this),
  20745. cell = tableItems.cell
  20746. return cell &&
  20747. (cell.tagName == 'TD' ||
  20748. (cell.tagName == 'TH' && cell !== tableItems.tr.cells[0])) &&
  20749. getUETable(tableItems.table).colsNum < this.options.maxColNum
  20750. ? 0
  20751. : -1
  20752. },
  20753. execCommand: function (cmd) {
  20754. var rng = this.selection.getRange(),
  20755. bk = rng.createBookmark(true)
  20756. if (this.queryCommandState(cmd) == -1) return
  20757. var cell = getTableItemsByRange(this).cell,
  20758. ut = getUETable(cell),
  20759. cellInfo = ut.getCellInfo(cell)
  20760. //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex:ut.cellsRange.beginColIndex);
  20761. if (!ut.selectedTds.length) {
  20762. ut.insertCol(cellInfo.colIndex, cell)
  20763. } else {
  20764. var range = ut.cellsRange
  20765. for (
  20766. var i = 0, len = range.endColIndex - range.beginColIndex + 1;
  20767. i < len;
  20768. i++
  20769. ) {
  20770. ut.insertCol(range.beginColIndex, cell)
  20771. }
  20772. }
  20773. rng.moveToBookmark(bk).select(true)
  20774. }
  20775. }
  20776. UE.commands['insertcolnext'] = {
  20777. queryCommandState: function () {
  20778. var tableItems = getTableItemsByRange(this),
  20779. cell = tableItems.cell
  20780. return cell &&
  20781. getUETable(tableItems.table).colsNum < this.options.maxColNum
  20782. ? 0
  20783. : -1
  20784. },
  20785. execCommand: function () {
  20786. var rng = this.selection.getRange(),
  20787. bk = rng.createBookmark(true)
  20788. var cell = getTableItemsByRange(this).cell,
  20789. ut = getUETable(cell),
  20790. cellInfo = ut.getCellInfo(cell)
  20791. //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex + cellInfo.colSpan:ut.cellsRange.endColIndex +1);
  20792. if (!ut.selectedTds.length) {
  20793. ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell)
  20794. } else {
  20795. var range = ut.cellsRange
  20796. for (
  20797. var i = 0, len = range.endColIndex - range.beginColIndex + 1;
  20798. i < len;
  20799. i++
  20800. ) {
  20801. ut.insertCol(range.endColIndex + 1, cell)
  20802. }
  20803. }
  20804. rng.moveToBookmark(bk).select()
  20805. }
  20806. }
  20807. UE.commands['deletecol'] = {
  20808. queryCommandState: function () {
  20809. var tableItems = getTableItemsByRange(this)
  20810. return tableItems.cell ? 0 : -1
  20811. },
  20812. execCommand: function () {
  20813. var cell = getTableItemsByRange(this).cell,
  20814. ut = getUETable(cell),
  20815. range = ut.cellsRange,
  20816. cellInfo = ut.getCellInfo(cell),
  20817. preCell = ut.getHSideCell(cell),
  20818. nextCell = ut.getHSideCell(cell, true)
  20819. if (utils.isEmptyObject(range)) {
  20820. ut.deleteCol(cellInfo.colIndex)
  20821. } else {
  20822. for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) {
  20823. ut.deleteCol(range.beginColIndex)
  20824. }
  20825. }
  20826. var table = ut.table,
  20827. rng = this.selection.getRange()
  20828. if (!table.getElementsByTagName('td').length) {
  20829. var nextSibling = table.nextSibling
  20830. domUtils.remove(table)
  20831. if (nextSibling) {
  20832. rng.setStart(nextSibling, 0).setCursor(false, true)
  20833. }
  20834. } else {
  20835. if (domUtils.inDoc(cell, this.document)) {
  20836. rng.setStart(cell, 0).setCursor(false, true)
  20837. } else {
  20838. if (nextCell && domUtils.inDoc(nextCell, this.document)) {
  20839. rng.selectNodeContents(nextCell).setCursor(false, true)
  20840. } else {
  20841. if (preCell && domUtils.inDoc(preCell, this.document)) {
  20842. rng.selectNodeContents(preCell).setCursor(true, true)
  20843. }
  20844. }
  20845. }
  20846. }
  20847. }
  20848. }
  20849. UE.commands['splittocells'] = {
  20850. queryCommandState: function () {
  20851. var tableItems = getTableItemsByRange(this),
  20852. cell = tableItems.cell
  20853. if (!cell) return -1
  20854. var ut = getUETable(tableItems.table)
  20855. if (ut.selectedTds.length > 0) return -1
  20856. return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1
  20857. },
  20858. execCommand: function () {
  20859. var rng = this.selection.getRange(),
  20860. bk = rng.createBookmark(true)
  20861. var cell = getTableItemsByRange(this).cell,
  20862. ut = getUETable(cell)
  20863. ut.splitToCells(cell)
  20864. rng.moveToBookmark(bk).select()
  20865. }
  20866. }
  20867. UE.commands['splittorows'] = {
  20868. queryCommandState: function () {
  20869. var tableItems = getTableItemsByRange(this),
  20870. cell = tableItems.cell
  20871. if (!cell) return -1
  20872. var ut = getUETable(tableItems.table)
  20873. if (ut.selectedTds.length > 0) return -1
  20874. return cell && cell.rowSpan > 1 ? 0 : -1
  20875. },
  20876. execCommand: function () {
  20877. var rng = this.selection.getRange(),
  20878. bk = rng.createBookmark(true)
  20879. var cell = getTableItemsByRange(this).cell,
  20880. ut = getUETable(cell)
  20881. ut.splitToRows(cell)
  20882. rng.moveToBookmark(bk).select()
  20883. }
  20884. }
  20885. UE.commands['splittocols'] = {
  20886. queryCommandState: function () {
  20887. var tableItems = getTableItemsByRange(this),
  20888. cell = tableItems.cell
  20889. if (!cell) return -1
  20890. var ut = getUETable(tableItems.table)
  20891. if (ut.selectedTds.length > 0) return -1
  20892. return cell && cell.colSpan > 1 ? 0 : -1
  20893. },
  20894. execCommand: function () {
  20895. var rng = this.selection.getRange(),
  20896. bk = rng.createBookmark(true)
  20897. var cell = getTableItemsByRange(this).cell,
  20898. ut = getUETable(cell)
  20899. ut.splitToCols(cell)
  20900. rng.moveToBookmark(bk).select()
  20901. }
  20902. }
  20903. UE.commands['adaptbytext'] = UE.commands['adaptbywindow'] = {
  20904. queryCommandState: function () {
  20905. return getTableItemsByRange(this).table ? 0 : -1
  20906. },
  20907. execCommand: function (cmd) {
  20908. var tableItems = getTableItemsByRange(this),
  20909. table = tableItems.table
  20910. if (table) {
  20911. if (cmd == 'adaptbywindow') {
  20912. resetTdWidth(table, this)
  20913. } else {
  20914. var cells = domUtils.getElementsByTagName(table, 'td th')
  20915. utils.each(cells, function (cell) {
  20916. cell.removeAttribute('width')
  20917. })
  20918. table.removeAttribute('width')
  20919. }
  20920. }
  20921. }
  20922. }
  20923. //平均分配各列
  20924. UE.commands['averagedistributecol'] = {
  20925. queryCommandState: function () {
  20926. var ut = getUETableBySelected(this)
  20927. if (!ut) return -1
  20928. return ut.isFullRow() || ut.isFullCol() ? 0 : -1
  20929. },
  20930. execCommand: function (cmd) {
  20931. var me = this,
  20932. ut = getUETableBySelected(me)
  20933. function getAverageWidth() {
  20934. var tb = ut.table,
  20935. averageWidth,
  20936. sumWidth = 0,
  20937. colsNum = 0,
  20938. tbAttr = getDefaultValue(me, tb)
  20939. if (ut.isFullRow()) {
  20940. sumWidth = tb.offsetWidth
  20941. colsNum = ut.colsNum
  20942. } else {
  20943. var begin = ut.cellsRange.beginColIndex,
  20944. end = ut.cellsRange.endColIndex,
  20945. node
  20946. for (var i = begin; i <= end; ) {
  20947. node = ut.selectedTds[i]
  20948. sumWidth += node.offsetWidth
  20949. i += node.colSpan
  20950. colsNum += 1
  20951. }
  20952. }
  20953. averageWidth =
  20954. Math.ceil(sumWidth / colsNum) -
  20955. tbAttr.tdBorder * 2 -
  20956. tbAttr.tdPadding * 2
  20957. return averageWidth
  20958. }
  20959. function setAverageWidth(averageWidth) {
  20960. utils.each(
  20961. domUtils.getElementsByTagName(ut.table, 'th'),
  20962. function (node) {
  20963. node.setAttribute('width', '')
  20964. }
  20965. )
  20966. var cells = ut.isFullRow()
  20967. ? domUtils.getElementsByTagName(ut.table, 'td')
  20968. : ut.selectedTds
  20969. utils.each(cells, function (node) {
  20970. if (node.colSpan == 1) {
  20971. node.setAttribute('width', averageWidth)
  20972. }
  20973. })
  20974. }
  20975. if (ut && ut.selectedTds.length) {
  20976. setAverageWidth(getAverageWidth())
  20977. }
  20978. }
  20979. }
  20980. //平均分配各行
  20981. UE.commands['averagedistributerow'] = {
  20982. queryCommandState: function () {
  20983. var ut = getUETableBySelected(this)
  20984. if (!ut) return -1
  20985. if (ut.selectedTds && /th/gi.test(ut.selectedTds[0].tagName)) return -1
  20986. return ut.isFullRow() || ut.isFullCol() ? 0 : -1
  20987. },
  20988. execCommand: function (cmd) {
  20989. var me = this,
  20990. ut = getUETableBySelected(me)
  20991. function getAverageHeight() {
  20992. var averageHeight,
  20993. rowNum,
  20994. sumHeight = 0,
  20995. tb = ut.table,
  20996. tbAttr = getDefaultValue(me, tb),
  20997. tdpadding = parseInt(
  20998. domUtils.getComputedStyle(
  20999. tb.getElementsByTagName('td')[0],
  21000. 'padding-top'
  21001. )
  21002. )
  21003. if (ut.isFullCol()) {
  21004. var captionArr = domUtils.getElementsByTagName(tb, 'caption'),
  21005. thArr = domUtils.getElementsByTagName(tb, 'th'),
  21006. captionHeight,
  21007. thHeight
  21008. if (captionArr.length > 0) {
  21009. captionHeight = captionArr[0].offsetHeight
  21010. }
  21011. if (thArr.length > 0) {
  21012. thHeight = thArr[0].offsetHeight
  21013. }
  21014. sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0)
  21015. rowNum = thArr.length == 0 ? ut.rowsNum : ut.rowsNum - 1
  21016. } else {
  21017. var begin = ut.cellsRange.beginRowIndex,
  21018. end = ut.cellsRange.endRowIndex,
  21019. count = 0,
  21020. trs = domUtils.getElementsByTagName(tb, 'tr')
  21021. for (var i = begin; i <= end; i++) {
  21022. sumHeight += trs[i].offsetHeight
  21023. count += 1
  21024. }
  21025. rowNum = count
  21026. }
  21027. //ie8下是混杂模式
  21028. if (browser.ie && browser.version < 9) {
  21029. averageHeight = Math.ceil(sumHeight / rowNum)
  21030. } else {
  21031. averageHeight =
  21032. Math.ceil(sumHeight / rowNum) -
  21033. tbAttr.tdBorder * 2 -
  21034. tdpadding * 2
  21035. }
  21036. return averageHeight
  21037. }
  21038. function setAverageHeight(averageHeight) {
  21039. var cells = ut.isFullCol()
  21040. ? domUtils.getElementsByTagName(ut.table, 'td')
  21041. : ut.selectedTds
  21042. utils.each(cells, function (node) {
  21043. if (node.rowSpan == 1) {
  21044. node.setAttribute('height', averageHeight)
  21045. }
  21046. })
  21047. }
  21048. if (ut && ut.selectedTds.length) {
  21049. setAverageHeight(getAverageHeight())
  21050. }
  21051. }
  21052. }
  21053. //单元格对齐方式
  21054. UE.commands['cellalignment'] = {
  21055. queryCommandState: function () {
  21056. return getTableItemsByRange(this).table ? 0 : -1
  21057. },
  21058. execCommand: function (cmd, data) {
  21059. var me = this,
  21060. ut = getUETableBySelected(me)
  21061. if (!ut) {
  21062. var start = me.selection.getStart(),
  21063. cell =
  21064. start &&
  21065. domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
  21066. if (!/caption/gi.test(cell.tagName)) {
  21067. domUtils.setAttributes(cell, data)
  21068. } else {
  21069. cell.style.textAlign = data.align
  21070. cell.style.verticalAlign = data.vAlign
  21071. }
  21072. me.selection.getRange().setCursor(true)
  21073. } else {
  21074. utils.each(ut.selectedTds, function (cell) {
  21075. domUtils.setAttributes(cell, data)
  21076. })
  21077. }
  21078. },
  21079. /**
  21080. * 查询当前点击的单元格的对齐状态, 如果当前已经选择了多个单元格, 则会返回所有单元格经过统一协调过后的状态
  21081. * @see UE.UETable.getTableCellAlignState
  21082. */
  21083. queryCommandValue: function (cmd) {
  21084. var activeMenuCell = getTableItemsByRange(this).cell
  21085. if (!activeMenuCell) {
  21086. activeMenuCell = getSelectedArr(this)[0]
  21087. }
  21088. if (!activeMenuCell) {
  21089. return null
  21090. } else {
  21091. //获取同时选中的其他单元格
  21092. var cells = UE.UETable.getUETable(activeMenuCell).selectedTds
  21093. !cells.length && (cells = activeMenuCell)
  21094. return UE.UETable.getTableCellAlignState(cells)
  21095. }
  21096. }
  21097. }
  21098. //表格对齐方式
  21099. UE.commands['tablealignment'] = {
  21100. queryCommandState: function () {
  21101. if (browser.ie && browser.version < 8) {
  21102. return -1
  21103. }
  21104. return getTableItemsByRange(this).table ? 0 : -1
  21105. },
  21106. execCommand: function (cmd, value) {
  21107. var me = this,
  21108. start = me.selection.getStart(),
  21109. table = start && domUtils.findParentByTagName(start, ['table'], true)
  21110. if (table) {
  21111. table.setAttribute('align', value)
  21112. }
  21113. }
  21114. }
  21115. //表格属性
  21116. UE.commands['edittable'] = {
  21117. queryCommandState: function () {
  21118. return getTableItemsByRange(this).table ? 0 : -1
  21119. },
  21120. execCommand: function (cmd, color) {
  21121. var rng = this.selection.getRange(),
  21122. table = domUtils.findParentByTagName(rng.startContainer, 'table')
  21123. if (table) {
  21124. var arr = domUtils
  21125. .getElementsByTagName(table, 'td')
  21126. .concat(
  21127. domUtils.getElementsByTagName(table, 'th'),
  21128. domUtils.getElementsByTagName(table, 'caption')
  21129. )
  21130. utils.each(arr, function (node) {
  21131. node.style.borderColor = color
  21132. })
  21133. }
  21134. }
  21135. }
  21136. //单元格属性
  21137. UE.commands['edittd'] = {
  21138. queryCommandState: function () {
  21139. return getTableItemsByRange(this).table ? 0 : -1
  21140. },
  21141. execCommand: function (cmd, bkColor) {
  21142. var me = this,
  21143. ut = getUETableBySelected(me)
  21144. if (!ut) {
  21145. var start = me.selection.getStart(),
  21146. cell =
  21147. start &&
  21148. domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
  21149. if (cell) {
  21150. cell.style.backgroundColor = bkColor
  21151. }
  21152. } else {
  21153. utils.each(ut.selectedTds, function (cell) {
  21154. cell.style.backgroundColor = bkColor
  21155. })
  21156. }
  21157. }
  21158. }
  21159. UE.commands['settablebackground'] = {
  21160. queryCommandState: function () {
  21161. return getSelectedArr(this).length > 1 ? 0 : -1
  21162. },
  21163. execCommand: function (cmd, value) {
  21164. var cells, ut
  21165. cells = getSelectedArr(this)
  21166. ut = getUETable(cells[0])
  21167. ut.setBackground(cells, value)
  21168. }
  21169. }
  21170. UE.commands['cleartablebackground'] = {
  21171. queryCommandState: function () {
  21172. var cells = getSelectedArr(this)
  21173. if (!cells.length) return -1
  21174. for (var i = 0, cell; (cell = cells[i++]); ) {
  21175. if (cell.style.backgroundColor !== '') return 0
  21176. }
  21177. return -1
  21178. },
  21179. execCommand: function () {
  21180. var cells = getSelectedArr(this),
  21181. ut = getUETable(cells[0])
  21182. ut.removeBackground(cells)
  21183. }
  21184. }
  21185. UE.commands['interlacetable'] = UE.commands['uninterlacetable'] = {
  21186. queryCommandState: function (cmd) {
  21187. var table = getTableItemsByRange(this).table
  21188. if (!table) return -1
  21189. var interlaced = table.getAttribute('interlaced')
  21190. if (cmd == 'interlacetable') {
  21191. //TODO 待定
  21192. //是否需要待定,如果设置,则命令只能单次执行成功,但反射具备toggle效果;否则可以覆盖前次命令,但反射将不存在toggle效果
  21193. return interlaced === 'enabled' ? -1 : 0
  21194. } else {
  21195. return !interlaced || interlaced === 'disabled' ? -1 : 0
  21196. }
  21197. },
  21198. execCommand: function (cmd, classList) {
  21199. var table = getTableItemsByRange(this).table
  21200. if (cmd == 'interlacetable') {
  21201. table.setAttribute('interlaced', 'enabled')
  21202. this.fireEvent('interlacetable', table, classList)
  21203. } else {
  21204. table.setAttribute('interlaced', 'disabled')
  21205. this.fireEvent('uninterlacetable', table)
  21206. }
  21207. }
  21208. }
  21209. UE.commands['setbordervisible'] = {
  21210. queryCommandState: function (cmd) {
  21211. var table = getTableItemsByRange(this).table
  21212. if (!table) return -1
  21213. return 0
  21214. },
  21215. execCommand: function () {
  21216. var table = getTableItemsByRange(this).table
  21217. utils.each(domUtils.getElementsByTagName(table, 'td'), function (td) {
  21218. td.style.borderWidth = '1px'
  21219. td.style.borderStyle = 'solid'
  21220. })
  21221. }
  21222. }
  21223. function resetTdWidth(table, editor) {
  21224. var tds = domUtils.getElementsByTagName(table, 'td th')
  21225. utils.each(tds, function (td) {
  21226. td.removeAttribute('width')
  21227. })
  21228. table.setAttribute(
  21229. 'width',
  21230. getTableWidth(editor, true, getDefaultValue(editor, table))
  21231. )
  21232. var tdsWidths = []
  21233. setTimeout(function () {
  21234. utils.each(tds, function (td) {
  21235. td.colSpan == 1 && tdsWidths.push(td.offsetWidth)
  21236. })
  21237. utils.each(tds, function (td, i) {
  21238. td.colSpan == 1 && td.setAttribute('width', tdsWidths[i] + '')
  21239. })
  21240. }, 0)
  21241. }
  21242. function getTableWidth(editor, needIEHack, defaultValue) {
  21243. var body = editor.body
  21244. return (
  21245. body.offsetWidth -
  21246. (needIEHack
  21247. ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2
  21248. : 0) -
  21249. defaultValue.tableBorder * 2 -
  21250. (editor.options.offsetWidth || 0)
  21251. )
  21252. }
  21253. function getSelectedArr(editor) {
  21254. var cell = getTableItemsByRange(editor).cell
  21255. if (cell) {
  21256. var ut = getUETable(cell)
  21257. return ut.selectedTds.length ? ut.selectedTds : [cell]
  21258. } else {
  21259. return []
  21260. }
  21261. }
  21262. })()
  21263. // plugins/table.action.js
  21264. /**
  21265. * Created with JetBrains PhpStorm.
  21266. * User: taoqili
  21267. * Date: 12-10-12
  21268. * Time: 上午10:05
  21269. * To change this template use File | Settings | File Templates.
  21270. */
  21271. UE.plugins['table'] = function () {
  21272. var me = this,
  21273. tabTimer = null,
  21274. //拖动计时器
  21275. tableDragTimer = null,
  21276. //双击计时器
  21277. tableResizeTimer = null,
  21278. //单元格最小宽度
  21279. cellMinWidth = 5,
  21280. isInResizeBuffer = false,
  21281. //单元格边框大小
  21282. cellBorderWidth = 5,
  21283. //鼠标偏移距离
  21284. offsetOfTableCell = 10,
  21285. //记录在有限时间内的点击状态, 共有3个取值, 0, 1, 2。 0代表未初始化, 1代表单击了1次,2代表2次
  21286. singleClickState = 0,
  21287. userActionStatus = null,
  21288. //双击允许的时间范围
  21289. dblclickTime = 360,
  21290. UT = UE.UETable,
  21291. getUETable = function (tdOrTable) {
  21292. return UT.getUETable(tdOrTable)
  21293. },
  21294. getUETableBySelected = function (editor) {
  21295. return UT.getUETableBySelected(editor)
  21296. },
  21297. getDefaultValue = function (editor, table) {
  21298. return UT.getDefaultValue(editor, table)
  21299. },
  21300. removeSelectedClass = function (cells) {
  21301. return UT.removeSelectedClass(cells)
  21302. }
  21303. function showError(e) {
  21304. // throw e;
  21305. }
  21306. me.ready(function () {
  21307. var me = this
  21308. var orgGetText = me.selection.getText
  21309. me.selection.getText = function () {
  21310. var table = getUETableBySelected(me)
  21311. if (table) {
  21312. var str = ''
  21313. utils.each(table.selectedTds, function (td) {
  21314. str += td[browser.ie ? 'innerText' : 'textContent']
  21315. })
  21316. return str
  21317. } else {
  21318. return orgGetText.call(me.selection)
  21319. }
  21320. }
  21321. })
  21322. //处理拖动及框选相关方法
  21323. var startTd = null, //鼠标按下时的锚点td
  21324. currentTd = null, //当前鼠标经过时的td
  21325. onDrag = '', //指示当前拖动状态,其值可为"","h","v" ,分别表示未拖动状态,横向拖动状态,纵向拖动状态,用于鼠标移动过程中的判断
  21326. onBorder = false, //检测鼠标按下时是否处在单元格边缘位置
  21327. dragButton = null,
  21328. dragOver = false,
  21329. dragLine = null, //模拟的拖动线
  21330. dragTd = null //发生拖动的目标td
  21331. var mousedown = false,
  21332. //todo 判断混乱模式
  21333. needIEHack = true
  21334. me.setOpt({
  21335. maxColNum: 20,
  21336. maxRowNum: 100,
  21337. defaultCols: 5,
  21338. defaultRows: 5,
  21339. tdvalign: 'top',
  21340. cursorpath: me.options.UEDITOR_HOME_URL + 'themes/default/images/cursor_',
  21341. tableDragable: false,
  21342. classList: [
  21343. 'ue-table-interlace-color-single',
  21344. 'ue-table-interlace-color-double'
  21345. ]
  21346. })
  21347. me.getUETable = getUETable
  21348. var commands = {
  21349. deletetable: 1,
  21350. inserttable: 1,
  21351. cellvalign: 1,
  21352. insertcaption: 1,
  21353. deletecaption: 1,
  21354. inserttitle: 1,
  21355. deletetitle: 1,
  21356. mergeright: 1,
  21357. mergedown: 1,
  21358. mergecells: 1,
  21359. insertrow: 1,
  21360. insertrownext: 1,
  21361. deleterow: 1,
  21362. insertcol: 1,
  21363. insertcolnext: 1,
  21364. deletecol: 1,
  21365. splittocells: 1,
  21366. splittorows: 1,
  21367. splittocols: 1,
  21368. adaptbytext: 1,
  21369. adaptbywindow: 1,
  21370. adaptbycustomer: 1,
  21371. insertparagraph: 1,
  21372. insertparagraphbeforetable: 1,
  21373. averagedistributecol: 1,
  21374. averagedistributerow: 1
  21375. }
  21376. me.ready(function () {
  21377. utils.cssRule(
  21378. 'table',
  21379. //选中的td上的样式
  21380. '.selectTdClass{background-color:#edf5fa !important}' +
  21381. 'table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}' +
  21382. //插入的表格的默认样式
  21383. 'table{margin-bottom:10px;border-collapse:collapse;display:table;}' +
  21384. 'td,th{padding: 5px 10px;border: 1px solid #DDD;}' +
  21385. 'caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}' +
  21386. 'th{border-top:1px solid #BBB;background-color:#F7F7F7;}' +
  21387. 'table tr.firstRow th{border-top-width:2px;}' +
  21388. '.ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }' +
  21389. 'td p{margin:0;padding:0;}',
  21390. me.document
  21391. )
  21392. var tableCopyList, isFullCol, isFullRow
  21393. //注册del/backspace事件
  21394. me.addListener('keydown', function (cmd, evt) {
  21395. var me = this
  21396. var keyCode = evt.keyCode || evt.which
  21397. if (keyCode == 8) {
  21398. var ut = getUETableBySelected(me)
  21399. if (ut && ut.selectedTds.length) {
  21400. if (ut.isFullCol()) {
  21401. me.execCommand('deletecol')
  21402. } else if (ut.isFullRow()) {
  21403. me.execCommand('deleterow')
  21404. } else {
  21405. me.fireEvent('delcells')
  21406. }
  21407. domUtils.preventDefault(evt)
  21408. }
  21409. var caption = domUtils.findParentByTagName(
  21410. me.selection.getStart(),
  21411. 'caption',
  21412. true
  21413. ),
  21414. range = me.selection.getRange()
  21415. if (range.collapsed && caption && isEmptyBlock(caption)) {
  21416. me.fireEvent('saveScene')
  21417. var table = caption.parentNode
  21418. domUtils.remove(caption)
  21419. if (table) {
  21420. range.setStart(table.rows[0].cells[0], 0).setCursor(false, true)
  21421. }
  21422. me.fireEvent('saveScene')
  21423. }
  21424. }
  21425. if (keyCode == 46) {
  21426. ut = getUETableBySelected(me)
  21427. if (ut) {
  21428. me.fireEvent('saveScene')
  21429. for (var i = 0, ci; (ci = ut.selectedTds[i++]); ) {
  21430. domUtils.fillNode(me.document, ci)
  21431. }
  21432. me.fireEvent('saveScene')
  21433. domUtils.preventDefault(evt)
  21434. }
  21435. }
  21436. if (keyCode == 13) {
  21437. var rng = me.selection.getRange(),
  21438. caption = domUtils.findParentByTagName(
  21439. rng.startContainer,
  21440. 'caption',
  21441. true
  21442. )
  21443. if (caption) {
  21444. var table = domUtils.findParentByTagName(caption, 'table')
  21445. if (!rng.collapsed) {
  21446. rng.deleteContents()
  21447. me.fireEvent('saveScene')
  21448. } else {
  21449. if (caption) {
  21450. rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true)
  21451. }
  21452. }
  21453. domUtils.preventDefault(evt)
  21454. return
  21455. }
  21456. if (rng.collapsed) {
  21457. var table = domUtils.findParentByTagName(
  21458. rng.startContainer,
  21459. 'table'
  21460. )
  21461. if (table) {
  21462. var cell = table.rows[0].cells[0],
  21463. start = domUtils.findParentByTagName(
  21464. me.selection.getStart(),
  21465. ['td', 'th'],
  21466. true
  21467. ),
  21468. preNode = table.previousSibling
  21469. if (
  21470. cell === start &&
  21471. (!preNode ||
  21472. (preNode.nodeType == 1 && preNode.tagName == 'TABLE')) &&
  21473. domUtils.isStartInblock(rng)
  21474. ) {
  21475. var first = domUtils.findParent(
  21476. me.selection.getStart(),
  21477. function (n) {
  21478. return domUtils.isBlockElm(n)
  21479. },
  21480. true
  21481. )
  21482. if (
  21483. first &&
  21484. (/t(h|d)/i.test(first.tagName) || first === start.firstChild)
  21485. ) {
  21486. me.execCommand('insertparagraphbeforetable')
  21487. domUtils.preventDefault(evt)
  21488. }
  21489. }
  21490. }
  21491. }
  21492. }
  21493. if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == '67') {
  21494. tableCopyList = null
  21495. var ut = getUETableBySelected(me)
  21496. if (ut) {
  21497. var tds = ut.selectedTds
  21498. isFullCol = ut.isFullCol()
  21499. isFullRow = ut.isFullRow()
  21500. tableCopyList = [[ut.cloneCell(tds[0], null, true)]]
  21501. for (var i = 1, ci; (ci = tds[i]); i++) {
  21502. if (ci.parentNode !== tds[i - 1].parentNode) {
  21503. tableCopyList.push([ut.cloneCell(ci, null, true)])
  21504. } else {
  21505. tableCopyList[tableCopyList.length - 1].push(
  21506. ut.cloneCell(ci, null, true)
  21507. )
  21508. }
  21509. }
  21510. }
  21511. }
  21512. })
  21513. me.addListener('tablehasdeleted', function () {
  21514. toggleDraggableState(this, false, '', null)
  21515. if (dragButton) domUtils.remove(dragButton)
  21516. })
  21517. me.addListener('beforepaste', function (cmd, html) {
  21518. var me = this
  21519. var rng = me.selection.getRange()
  21520. if (domUtils.findParentByTagName(rng.startContainer, 'caption', true)) {
  21521. var div = me.document.createElement('div')
  21522. div.innerHTML = html.html
  21523. //trace:3729
  21524. html.html = div[browser.ie9below ? 'innerText' : 'textContent']
  21525. return
  21526. }
  21527. var table = getUETableBySelected(me)
  21528. if (tableCopyList) {
  21529. me.fireEvent('saveScene')
  21530. var rng = me.selection.getRange()
  21531. var td = domUtils.findParentByTagName(
  21532. rng.startContainer,
  21533. ['td', 'th'],
  21534. true
  21535. ),
  21536. tmpNode,
  21537. preNode
  21538. if (td) {
  21539. var ut = getUETable(td)
  21540. if (isFullRow) {
  21541. var rowIndex = ut.getCellInfo(td).rowIndex
  21542. if (td.tagName == 'TH') {
  21543. rowIndex++
  21544. }
  21545. for (var i = 0, ci; (ci = tableCopyList[i++]); ) {
  21546. var tr = ut.insertRow(rowIndex++, 'td')
  21547. for (var j = 0, cj; (cj = ci[j]); j++) {
  21548. var cell = tr.cells[j]
  21549. if (!cell) {
  21550. cell = tr.insertCell(j)
  21551. }
  21552. cell.innerHTML = cj.innerHTML
  21553. cj.getAttribute('width') &&
  21554. cell.setAttribute('width', cj.getAttribute('width'))
  21555. cj.getAttribute('vAlign') &&
  21556. cell.setAttribute('vAlign', cj.getAttribute('vAlign'))
  21557. cj.getAttribute('align') &&
  21558. cell.setAttribute('align', cj.getAttribute('align'))
  21559. cj.style.cssText && (cell.style.cssText = cj.style.cssText)
  21560. }
  21561. for (var j = 0, cj; (cj = tr.cells[j]); j++) {
  21562. if (!ci[j]) break
  21563. cj.innerHTML = ci[j].innerHTML
  21564. ci[j].getAttribute('width') &&
  21565. cj.setAttribute('width', ci[j].getAttribute('width'))
  21566. ci[j].getAttribute('vAlign') &&
  21567. cj.setAttribute('vAlign', ci[j].getAttribute('vAlign'))
  21568. ci[j].getAttribute('align') &&
  21569. cj.setAttribute('align', ci[j].getAttribute('align'))
  21570. ci[j].style.cssText &&
  21571. (cj.style.cssText = ci[j].style.cssText)
  21572. }
  21573. }
  21574. } else {
  21575. if (isFullCol) {
  21576. cellInfo = ut.getCellInfo(td)
  21577. var maxColNum = 0
  21578. for (var j = 0, ci = tableCopyList[0], cj; (cj = ci[j++]); ) {
  21579. maxColNum += cj.colSpan || 1
  21580. }
  21581. me.__hasEnterExecCommand = true
  21582. for (i = 0; i < maxColNum; i++) {
  21583. me.execCommand('insertcol')
  21584. }
  21585. me.__hasEnterExecCommand = false
  21586. td = ut.table.rows[0].cells[cellInfo.cellIndex]
  21587. if (td.tagName == 'TH') {
  21588. td = ut.table.rows[1].cells[cellInfo.cellIndex]
  21589. }
  21590. }
  21591. for (var i = 0, ci; (ci = tableCopyList[i++]); ) {
  21592. tmpNode = td
  21593. for (var j = 0, cj; (cj = ci[j++]); ) {
  21594. if (td) {
  21595. td.innerHTML = cj.innerHTML
  21596. //todo 定制处理
  21597. cj.getAttribute('width') &&
  21598. td.setAttribute('width', cj.getAttribute('width'))
  21599. cj.getAttribute('vAlign') &&
  21600. td.setAttribute('vAlign', cj.getAttribute('vAlign'))
  21601. cj.getAttribute('align') &&
  21602. td.setAttribute('align', cj.getAttribute('align'))
  21603. cj.style.cssText && (td.style.cssText = cj.style.cssText)
  21604. preNode = td
  21605. td = td.nextSibling
  21606. } else {
  21607. var cloneTd = cj.cloneNode(true)
  21608. domUtils.removeAttributes(cloneTd, [
  21609. 'class',
  21610. 'rowSpan',
  21611. 'colSpan'
  21612. ])
  21613. preNode.parentNode.appendChild(cloneTd)
  21614. }
  21615. }
  21616. td = ut.getNextCell(tmpNode, true, true)
  21617. if (!tableCopyList[i]) break
  21618. if (!td) {
  21619. var cellInfo = ut.getCellInfo(tmpNode)
  21620. ut.table.insertRow(ut.table.rows.length)
  21621. ut.update()
  21622. td = ut.getVSideCell(tmpNode, true)
  21623. }
  21624. }
  21625. }
  21626. ut.update()
  21627. } else {
  21628. table = me.document.createElement('table')
  21629. for (var i = 0, ci; (ci = tableCopyList[i++]); ) {
  21630. var tr = table.insertRow(table.rows.length)
  21631. for (var j = 0, cj; (cj = ci[j++]); ) {
  21632. cloneTd = UT.cloneCell(cj, null, true)
  21633. domUtils.removeAttributes(cloneTd, ['class'])
  21634. tr.appendChild(cloneTd)
  21635. }
  21636. if (j == 2 && cloneTd.rowSpan > 1) {
  21637. cloneTd.rowSpan = 1
  21638. }
  21639. }
  21640. var defaultValue = getDefaultValue(me),
  21641. width =
  21642. me.body.offsetWidth -
  21643. (needIEHack
  21644. ? parseInt(
  21645. domUtils.getComputedStyle(me.body, 'margin-left'),
  21646. 10
  21647. ) * 2
  21648. : 0) -
  21649. defaultValue.tableBorder * 2 -
  21650. (me.options.offsetWidth || 0)
  21651. me.execCommand(
  21652. 'insertHTML',
  21653. '<table ' +
  21654. (isFullCol && isFullRow ? 'width="' + width + '"' : '') +
  21655. '>' +
  21656. table.innerHTML
  21657. .replace(/>\s*</g, '><')
  21658. .replace(/\bth\b/gi, 'td') +
  21659. '</table>'
  21660. )
  21661. }
  21662. me.fireEvent('contentchange')
  21663. me.fireEvent('saveScene')
  21664. html.html = ''
  21665. return true
  21666. } else {
  21667. var div = me.document.createElement('div'),
  21668. tables
  21669. div.innerHTML = html.html
  21670. tables = div.getElementsByTagName('table')
  21671. if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) {
  21672. utils.each(tables, function (t) {
  21673. domUtils.remove(t)
  21674. })
  21675. if (
  21676. domUtils.findParentByTagName(
  21677. me.selection.getStart(),
  21678. 'caption',
  21679. true
  21680. )
  21681. ) {
  21682. div.innerHTML = div[browser.ie ? 'innerText' : 'textContent']
  21683. }
  21684. } else {
  21685. utils.each(tables, function (table) {
  21686. removeStyleSize(table, true)
  21687. domUtils.removeAttributes(table, ['style', 'border'])
  21688. utils.each(
  21689. domUtils.getElementsByTagName(table, 'td'),
  21690. function (td) {
  21691. if (isEmptyBlock(td)) {
  21692. domUtils.fillNode(me.document, td)
  21693. }
  21694. removeStyleSize(td, true)
  21695. // domUtils.removeAttributes(td, ['style'])
  21696. }
  21697. )
  21698. })
  21699. }
  21700. html.html = div.innerHTML
  21701. }
  21702. })
  21703. me.addListener('afterpaste', function () {
  21704. utils.each(
  21705. domUtils.getElementsByTagName(me.body, 'table'),
  21706. function (table) {
  21707. if (table.offsetWidth > me.body.offsetWidth) {
  21708. var defaultValue = getDefaultValue(me, table)
  21709. table.style.width =
  21710. me.body.offsetWidth -
  21711. (needIEHack
  21712. ? parseInt(
  21713. domUtils.getComputedStyle(me.body, 'margin-left'),
  21714. 10
  21715. ) * 2
  21716. : 0) -
  21717. defaultValue.tableBorder * 2 -
  21718. (me.options.offsetWidth || 0) +
  21719. 'px'
  21720. }
  21721. }
  21722. )
  21723. })
  21724. me.addListener('blur', function () {
  21725. tableCopyList = null
  21726. })
  21727. var timer
  21728. me.addListener('keydown', function () {
  21729. clearTimeout(timer)
  21730. timer = setTimeout(function () {
  21731. var rng = me.selection.getRange(),
  21732. cell = domUtils.findParentByTagName(
  21733. rng.startContainer,
  21734. ['th', 'td'],
  21735. true
  21736. )
  21737. if (cell) {
  21738. var table = cell.parentNode.parentNode.parentNode
  21739. if (table.offsetWidth > table.getAttribute('width')) {
  21740. cell.style.wordBreak = 'break-all'
  21741. }
  21742. }
  21743. }, 100)
  21744. })
  21745. me.addListener('selectionchange', function () {
  21746. toggleDraggableState(me, false, '', null)
  21747. })
  21748. //内容变化时触发索引更新
  21749. //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新
  21750. me.addListener('contentchange', function () {
  21751. var me = this
  21752. //尽可能排除一些不需要更新的状况
  21753. hideDragLine(me)
  21754. if (getUETableBySelected(me)) return
  21755. var rng = me.selection.getRange()
  21756. var start = rng.startContainer
  21757. start = domUtils.findParentByTagName(start, ['td', 'th'], true)
  21758. utils.each(
  21759. domUtils.getElementsByTagName(me.document, 'table'),
  21760. function (table) {
  21761. if (me.fireEvent('excludetable', table) === true) return
  21762. table.ueTable = new UT(table)
  21763. //trace:3742
  21764. // utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) {
  21765. //
  21766. // if (domUtils.isEmptyBlock(td) && td !== start) {
  21767. // domUtils.fillNode(me.document, td);
  21768. // if (browser.ie && browser.version == 6) {
  21769. // td.innerHTML = '&nbsp;'
  21770. // }
  21771. // }
  21772. // });
  21773. // utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) {
  21774. // if (domUtils.isEmptyBlock(th) && th !== start) {
  21775. // domUtils.fillNode(me.document, th);
  21776. // if (browser.ie && browser.version == 6) {
  21777. // th.innerHTML = '&nbsp;'
  21778. // }
  21779. // }
  21780. // });
  21781. table.onmouseover = function () {
  21782. me.fireEvent('tablemouseover', table)
  21783. }
  21784. table.onmousemove = function () {
  21785. me.fireEvent('tablemousemove', table)
  21786. me.options.tableDragable && toggleDragButton(true, this, me)
  21787. utils.defer(function () {
  21788. me.fireEvent('contentchange', 50)
  21789. }, true)
  21790. }
  21791. table.onmouseout = function () {
  21792. me.fireEvent('tablemouseout', table)
  21793. toggleDraggableState(me, false, '', null)
  21794. hideDragLine(me)
  21795. }
  21796. table.onclick = function (evt) {
  21797. evt = me.window.event || evt
  21798. var target = getParentTdOrTh(evt.target || evt.srcElement)
  21799. if (!target) return
  21800. var ut = getUETable(target),
  21801. table = ut.table,
  21802. cellInfo = ut.getCellInfo(target),
  21803. cellsRange,
  21804. rng = me.selection.getRange()
  21805. // if ("topLeft" == inPosition(table, mouseCoords(evt))) {
  21806. // cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell());
  21807. // ut.setSelected(cellsRange);
  21808. // return;
  21809. // }
  21810. // if ("bottomRight" == inPosition(table, mouseCoords(evt))) {
  21811. //
  21812. // return;
  21813. // }
  21814. if (inTableSide(table, target, evt, true)) {
  21815. var endTdCol = ut.getCell(
  21816. ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex,
  21817. ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex
  21818. )
  21819. if (evt.shiftKey && ut.selectedTds.length) {
  21820. if (ut.selectedTds[0] !== endTdCol) {
  21821. cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol)
  21822. ut.setSelected(cellsRange)
  21823. } else {
  21824. rng && rng.selectNodeContents(endTdCol).select()
  21825. }
  21826. } else {
  21827. if (target !== endTdCol) {
  21828. cellsRange = ut.getCellsRange(target, endTdCol)
  21829. ut.setSelected(cellsRange)
  21830. } else {
  21831. rng && rng.selectNodeContents(endTdCol).select()
  21832. }
  21833. }
  21834. return
  21835. }
  21836. if (inTableSide(table, target, evt)) {
  21837. var endTdRow = ut.getCell(
  21838. ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex,
  21839. ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex
  21840. )
  21841. if (evt.shiftKey && ut.selectedTds.length) {
  21842. if (ut.selectedTds[0] !== endTdRow) {
  21843. cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow)
  21844. ut.setSelected(cellsRange)
  21845. } else {
  21846. rng && rng.selectNodeContents(endTdRow).select()
  21847. }
  21848. } else {
  21849. if (target !== endTdRow) {
  21850. cellsRange = ut.getCellsRange(target, endTdRow)
  21851. ut.setSelected(cellsRange)
  21852. } else {
  21853. rng && rng.selectNodeContents(endTdRow).select()
  21854. }
  21855. }
  21856. }
  21857. }
  21858. }
  21859. )
  21860. switchBorderColor(me, true)
  21861. })
  21862. domUtils.on(me.document, 'mousemove', mouseMoveEvent)
  21863. domUtils.on(me.document, 'mouseout', function (evt) {
  21864. var target = evt.target || evt.srcElement
  21865. if (target.tagName == 'TABLE') {
  21866. toggleDraggableState(me, false, '', null)
  21867. }
  21868. })
  21869. /**
  21870. * 表格隔行变色
  21871. */
  21872. me.addListener('interlacetable', function (type, table, classList) {
  21873. if (!table) return
  21874. var me = this,
  21875. rows = table.rows,
  21876. len = rows.length,
  21877. getClass = function (list, index, repeat) {
  21878. return list[index]
  21879. ? list[index]
  21880. : repeat
  21881. ? list[index % list.length]
  21882. : ''
  21883. }
  21884. for (var i = 0; i < len; i++) {
  21885. rows[i].className = getClass(
  21886. classList || me.options.classList,
  21887. i,
  21888. true
  21889. )
  21890. }
  21891. })
  21892. me.addListener('uninterlacetable', function (type, table) {
  21893. if (!table) return
  21894. var me = this,
  21895. rows = table.rows,
  21896. classList = me.options.classList,
  21897. len = rows.length
  21898. for (var i = 0; i < len; i++) {
  21899. domUtils.removeClasses(rows[i], classList)
  21900. }
  21901. })
  21902. me.addListener('mousedown', mouseDownEvent)
  21903. me.addListener('mouseup', mouseUpEvent)
  21904. //拖动的时候触发mouseup
  21905. domUtils.on(me.body, 'dragstart', function (evt) {
  21906. mouseUpEvent.call(me, 'dragstart', evt)
  21907. })
  21908. me.addOutputRule(function (root) {
  21909. utils.each(root.getNodesByTagName('div'), function (n) {
  21910. if (n.getAttr('id') == 'ue_tableDragLine') {
  21911. n.parentNode.removeChild(n)
  21912. }
  21913. })
  21914. })
  21915. var currentRowIndex = 0
  21916. me.addListener('mousedown', function () {
  21917. currentRowIndex = 0
  21918. })
  21919. me.addListener('tabkeydown', function () {
  21920. var range = this.selection.getRange(),
  21921. common = range.getCommonAncestor(true, true),
  21922. table = domUtils.findParentByTagName(common, 'table')
  21923. if (table) {
  21924. if (domUtils.findParentByTagName(common, 'caption', true)) {
  21925. var cell = domUtils.getElementsByTagName(table, 'th td')
  21926. if (cell && cell.length) {
  21927. range.setStart(cell[0], 0).setCursor(false, true)
  21928. }
  21929. } else {
  21930. var cell = domUtils.findParentByTagName(common, ['td', 'th'], true),
  21931. ua = getUETable(cell)
  21932. currentRowIndex =
  21933. cell.rowSpan > 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex
  21934. var nextCell = ua.getTabNextCell(cell, currentRowIndex)
  21935. if (nextCell) {
  21936. if (isEmptyBlock(nextCell)) {
  21937. range.setStart(nextCell, 0).setCursor(false, true)
  21938. } else {
  21939. range.selectNodeContents(nextCell).select()
  21940. }
  21941. } else {
  21942. me.fireEvent('saveScene')
  21943. me.__hasEnterExecCommand = true
  21944. this.execCommand('insertrownext')
  21945. me.__hasEnterExecCommand = false
  21946. range = this.selection.getRange()
  21947. range
  21948. .setStart(table.rows[table.rows.length - 1].cells[0], 0)
  21949. .setCursor()
  21950. me.fireEvent('saveScene')
  21951. }
  21952. }
  21953. return true
  21954. }
  21955. })
  21956. browser.ie &&
  21957. me.addListener('selectionchange', function () {
  21958. toggleDraggableState(this, false, '', null)
  21959. })
  21960. me.addListener('keydown', function (type, evt) {
  21961. var me = this
  21962. //处理在表格的最后一个输入tab产生新的表格
  21963. var keyCode = evt.keyCode || evt.which
  21964. if (keyCode == 8 || keyCode == 46) {
  21965. return
  21966. }
  21967. var notCtrlKey =
  21968. !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey
  21969. notCtrlKey &&
  21970. removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td'))
  21971. var ut = getUETableBySelected(me)
  21972. if (!ut) return
  21973. notCtrlKey && ut.clearSelected()
  21974. })
  21975. me.addListener('beforegetcontent', function () {
  21976. switchBorderColor(this, false)
  21977. browser.ie &&
  21978. utils.each(
  21979. this.document.getElementsByTagName('caption'),
  21980. function (ci) {
  21981. if (domUtils.isEmptyNode(ci)) {
  21982. ci.innerHTML = '&nbsp;'
  21983. }
  21984. }
  21985. )
  21986. })
  21987. me.addListener('aftergetcontent', function () {
  21988. switchBorderColor(this, true)
  21989. })
  21990. me.addListener('getAllHtml', function () {
  21991. removeSelectedClass(me.document.getElementsByTagName('td'))
  21992. })
  21993. //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况
  21994. me.addListener('fullscreenchanged', function (type, fullscreen) {
  21995. if (!fullscreen) {
  21996. var ratio = this.body.offsetWidth / document.body.offsetWidth,
  21997. tables = domUtils.getElementsByTagName(this.body, 'table')
  21998. utils.each(tables, function (table) {
  21999. if (table.offsetWidth < me.body.offsetWidth) return false
  22000. var tds = domUtils.getElementsByTagName(table, 'td'),
  22001. backWidths = []
  22002. utils.each(tds, function (td) {
  22003. backWidths.push(td.offsetWidth)
  22004. })
  22005. for (var i = 0, td; (td = tds[i]); i++) {
  22006. td.setAttribute('width', Math.floor(backWidths[i] * ratio))
  22007. }
  22008. table.setAttribute(
  22009. 'width',
  22010. Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me)))
  22011. )
  22012. })
  22013. }
  22014. })
  22015. //重写execCommand命令,用于处理框选时的处理
  22016. var oldExecCommand = me.execCommand
  22017. me.execCommand = function (cmd, datatat) {
  22018. var me = this,
  22019. args = arguments
  22020. cmd = cmd.toLowerCase()
  22021. var ut = getUETableBySelected(me),
  22022. tds,
  22023. range = new dom.Range(me.document),
  22024. cmdFun = me.commands[cmd] || UE.commands[cmd],
  22025. result
  22026. if (!cmdFun) return
  22027. if (
  22028. ut &&
  22029. !commands[cmd] &&
  22030. !cmdFun.notNeedUndo &&
  22031. !me.__hasEnterExecCommand
  22032. ) {
  22033. me.__hasEnterExecCommand = true
  22034. me.fireEvent('beforeexeccommand', cmd)
  22035. tds = ut.selectedTds
  22036. var lastState = -2,
  22037. lastValue = -2,
  22038. value,
  22039. state
  22040. for (var i = 0, td; (td = tds[i]); i++) {
  22041. if (isEmptyBlock(td)) {
  22042. range.setStart(td, 0).setCursor(false, true)
  22043. } else {
  22044. range.selectNode(td).select(true)
  22045. }
  22046. state = me.queryCommandState(cmd)
  22047. value = me.queryCommandValue(cmd)
  22048. if (state != -1) {
  22049. if (lastState !== state || lastValue !== value) {
  22050. me._ignoreContentChange = true
  22051. result = oldExecCommand.apply(me, arguments)
  22052. me._ignoreContentChange = false
  22053. }
  22054. lastState = me.queryCommandState(cmd)
  22055. lastValue = me.queryCommandValue(cmd)
  22056. if (domUtils.isEmptyBlock(td)) {
  22057. domUtils.fillNode(me.document, td)
  22058. }
  22059. }
  22060. }
  22061. range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true)
  22062. me.fireEvent('contentchange')
  22063. me.fireEvent('afterexeccommand', cmd)
  22064. me.__hasEnterExecCommand = false
  22065. me._selectionChange()
  22066. } else {
  22067. result = oldExecCommand.apply(me, arguments)
  22068. }
  22069. return result
  22070. }
  22071. })
  22072. /**
  22073. * 删除obj的宽高style,改成属性宽高
  22074. * @param obj
  22075. * @param replaceToProperty
  22076. */
  22077. function removeStyleSize(obj, replaceToProperty) {
  22078. removeStyle(obj, 'width', true)
  22079. removeStyle(obj, 'height', true)
  22080. }
  22081. function removeStyle(obj, styleName, replaceToProperty) {
  22082. if (obj.style[styleName]) {
  22083. replaceToProperty &&
  22084. obj.setAttribute(styleName, parseInt(obj.style[styleName], 10))
  22085. obj.style[styleName] = ''
  22086. }
  22087. }
  22088. function getParentTdOrTh(ele) {
  22089. if (ele.tagName == 'TD' || ele.tagName == 'TH') return ele
  22090. var td
  22091. if (
  22092. (td =
  22093. domUtils.findParentByTagName(ele, 'td', true) ||
  22094. domUtils.findParentByTagName(ele, 'th', true))
  22095. )
  22096. return td
  22097. return null
  22098. }
  22099. function isEmptyBlock(node) {
  22100. var reg = new RegExp(domUtils.fillChar, 'g')
  22101. if (
  22102. node[browser.ie ? 'innerText' : 'textContent']
  22103. .replace(/^\s*$/, '')
  22104. .replace(reg, '').length > 0
  22105. ) {
  22106. return 0
  22107. }
  22108. for (var n in dtd.$isNotEmpty) {
  22109. if (node.getElementsByTagName(n).length) {
  22110. return 0
  22111. }
  22112. }
  22113. return 1
  22114. }
  22115. function mouseCoords(evt) {
  22116. if (evt.pageX || evt.pageY) {
  22117. return { x: evt.pageX, y: evt.pageY }
  22118. }
  22119. return {
  22120. x:
  22121. evt.clientX +
  22122. me.document.body.scrollLeft -
  22123. me.document.body.clientLeft,
  22124. y: evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
  22125. }
  22126. }
  22127. function mouseMoveEvent(evt) {
  22128. if (isEditorDisabled()) {
  22129. return
  22130. }
  22131. try {
  22132. //普通状态下鼠标移动
  22133. var target = getParentTdOrTh(evt.target || evt.srcElement),
  22134. pos
  22135. //区分用户的行为是拖动还是双击
  22136. if (isInResizeBuffer) {
  22137. me.body.style.webkitUserSelect = 'none'
  22138. if (
  22139. Math.abs(userActionStatus.x - evt.clientX) > offsetOfTableCell ||
  22140. Math.abs(userActionStatus.y - evt.clientY) > offsetOfTableCell
  22141. ) {
  22142. clearTableDragTimer()
  22143. isInResizeBuffer = false
  22144. singleClickState = 0
  22145. //drag action
  22146. tableBorderDrag(evt)
  22147. }
  22148. }
  22149. //修改单元格大小时的鼠标移动
  22150. if (onDrag && dragTd) {
  22151. singleClickState = 0
  22152. me.body.style.webkitUserSelect = 'none'
  22153. me.selection
  22154. .getNative()
  22155. [browser.ie9below ? 'empty' : 'removeAllRanges']()
  22156. pos = mouseCoords(evt)
  22157. toggleDraggableState(me, true, onDrag, pos, target)
  22158. if (onDrag == 'h') {
  22159. dragLine.style.left = getPermissionX(dragTd, evt) + 'px'
  22160. } else if (onDrag == 'v') {
  22161. dragLine.style.top = getPermissionY(dragTd, evt) + 'px'
  22162. }
  22163. return
  22164. }
  22165. //当鼠标处于table上时,修改移动过程中的光标状态
  22166. if (target) {
  22167. //针对使用table作为容器的组件不触发拖拽效果
  22168. if (me.fireEvent('excludetable', target) === true) return
  22169. pos = mouseCoords(evt)
  22170. var state = getRelation(target, pos),
  22171. table = domUtils.findParentByTagName(target, 'table', true)
  22172. if (inTableSide(table, target, evt, true)) {
  22173. if (me.fireEvent('excludetable', table) === true) return
  22174. me.body.style.cursor =
  22175. 'url(' + me.options.cursorpath + 'h.png),pointer'
  22176. } else if (inTableSide(table, target, evt)) {
  22177. if (me.fireEvent('excludetable', table) === true) return
  22178. me.body.style.cursor =
  22179. 'url(' + me.options.cursorpath + 'v.png),pointer'
  22180. } else {
  22181. me.body.style.cursor = 'text'
  22182. var curCell = target
  22183. if (/\d/.test(state)) {
  22184. state = state.replace(/\d/, '')
  22185. target = getUETable(target).getPreviewCell(target, state == 'v')
  22186. }
  22187. //位于第一行的顶部或者第一列的左边时不可拖动
  22188. toggleDraggableState(
  22189. me,
  22190. target ? !!state : false,
  22191. target ? state : '',
  22192. pos,
  22193. target
  22194. )
  22195. }
  22196. } else {
  22197. toggleDragButton(false, table, me)
  22198. }
  22199. } catch (e) {
  22200. showError(e)
  22201. }
  22202. }
  22203. var dragButtonTimer
  22204. function toggleDragButton(show, table, editor) {
  22205. if (!show) {
  22206. if (dragOver) return
  22207. dragButtonTimer = setTimeout(function () {
  22208. !dragOver &&
  22209. dragButton &&
  22210. dragButton.parentNode &&
  22211. dragButton.parentNode.removeChild(dragButton)
  22212. }, 2000)
  22213. } else {
  22214. createDragButton(table, editor)
  22215. }
  22216. }
  22217. function createDragButton(table, editor) {
  22218. var pos = domUtils.getXY(table),
  22219. doc = table.ownerDocument
  22220. if (dragButton && dragButton.parentNode) return dragButton
  22221. dragButton = doc.createElement('div')
  22222. dragButton.contentEditable = false
  22223. dragButton.innerHTML = ''
  22224. dragButton.style.cssText =
  22225. 'width:15px;height:15px;background-image:url(' +
  22226. editor.options.UEDITOR_HOME_URL +
  22227. 'dialogs/table/dragicon.png);position: absolute;cursor:move;top:' +
  22228. (pos.y - 15) +
  22229. 'px;left:' +
  22230. pos.x +
  22231. 'px;'
  22232. domUtils.unSelectable(dragButton)
  22233. dragButton.onmouseover = function (evt) {
  22234. dragOver = true
  22235. }
  22236. dragButton.onmouseout = function (evt) {
  22237. dragOver = false
  22238. }
  22239. domUtils.on(dragButton, 'click', function (type, evt) {
  22240. doClick(evt, this)
  22241. })
  22242. domUtils.on(dragButton, 'dblclick', function (type, evt) {
  22243. doDblClick(evt)
  22244. })
  22245. domUtils.on(dragButton, 'dragstart', function (type, evt) {
  22246. domUtils.preventDefault(evt)
  22247. })
  22248. var timer
  22249. function doClick(evt, button) {
  22250. // 部分浏览器下需要清理
  22251. clearTimeout(timer)
  22252. timer = setTimeout(function () {
  22253. editor.fireEvent('tableClicked', table, button)
  22254. }, 300)
  22255. }
  22256. function doDblClick(evt) {
  22257. clearTimeout(timer)
  22258. var ut = getUETable(table),
  22259. start = table.rows[0].cells[0],
  22260. end = ut.getLastCell(),
  22261. range = ut.getCellsRange(start, end)
  22262. editor.selection.getRange().setStart(start, 0).setCursor(false, true)
  22263. ut.setSelected(range)
  22264. }
  22265. doc.body.appendChild(dragButton)
  22266. }
  22267. // function inPosition(table, pos) {
  22268. // var tablePos = domUtils.getXY(table),
  22269. // width = table.offsetWidth,
  22270. // height = table.offsetHeight;
  22271. // if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) {
  22272. // return "topLeft";
  22273. // } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) {
  22274. // return "bottomRight";
  22275. // }
  22276. // }
  22277. function inTableSide(table, cell, evt, top) {
  22278. var pos = mouseCoords(evt),
  22279. state = getRelation(cell, pos)
  22280. if (top) {
  22281. var caption = table.getElementsByTagName('caption')[0],
  22282. capHeight = caption ? caption.offsetHeight : 0
  22283. return state == 'v1' && pos.y - domUtils.getXY(table).y - capHeight < 8
  22284. } else {
  22285. return state == 'h1' && pos.x - domUtils.getXY(table).x < 8
  22286. }
  22287. }
  22288. /**
  22289. * 获取拖动时允许的X轴坐标
  22290. * @param dragTd
  22291. * @param evt
  22292. */
  22293. function getPermissionX(dragTd, evt) {
  22294. var ut = getUETable(dragTd)
  22295. if (ut) {
  22296. var preTd = ut.getSameEndPosCells(dragTd, 'x')[0],
  22297. nextTd = ut.getSameStartPosXCells(dragTd)[0],
  22298. mouseX = mouseCoords(evt).x,
  22299. left =
  22300. (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20,
  22301. right = nextTd
  22302. ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20
  22303. : me.body.offsetWidth + 5 ||
  22304. parseInt(domUtils.getComputedStyle(me.body, 'width'), 10)
  22305. left += cellMinWidth
  22306. right -= cellMinWidth
  22307. return mouseX < left ? left : mouseX > right ? right : mouseX
  22308. }
  22309. }
  22310. /**
  22311. * 获取拖动时允许的Y轴坐标
  22312. */
  22313. function getPermissionY(dragTd, evt) {
  22314. try {
  22315. var top = domUtils.getXY(dragTd).y,
  22316. mousePosY = mouseCoords(evt).y
  22317. return mousePosY < top ? top : mousePosY
  22318. } catch (e) {
  22319. showError(e)
  22320. }
  22321. }
  22322. /**
  22323. * 移动状态切换
  22324. */
  22325. function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
  22326. try {
  22327. editor.body.style.cursor =
  22328. dir == 'h' ? 'col-resize' : dir == 'v' ? 'row-resize' : 'text'
  22329. if (browser.ie) {
  22330. if (dir && !mousedown && !getUETableBySelected(editor)) {
  22331. getDragLine(editor, editor.document)
  22332. showDragLineAt(dir, cell)
  22333. } else {
  22334. hideDragLine(editor)
  22335. }
  22336. }
  22337. onBorder = draggable
  22338. } catch (e) {
  22339. showError(e)
  22340. }
  22341. }
  22342. /**
  22343. * 获取与UETable相关的resize line
  22344. * @param uetable UETable对象
  22345. */
  22346. function getResizeLineByUETable() {
  22347. var lineId = '_UETableResizeLine',
  22348. line = this.document.getElementById(lineId)
  22349. if (!line) {
  22350. line = this.document.createElement('div')
  22351. line.id = lineId
  22352. line.contnetEditable = false
  22353. line.setAttribute('unselectable', 'on')
  22354. var styles = {
  22355. width: 2 * cellBorderWidth + 1 + 'px',
  22356. position: 'absolute',
  22357. 'z-index': 100000,
  22358. cursor: 'col-resize',
  22359. background: 'red',
  22360. display: 'none'
  22361. }
  22362. //切换状态
  22363. line.onmouseout = function () {
  22364. this.style.display = 'none'
  22365. }
  22366. utils.extend(line.style, styles)
  22367. this.document.body.appendChild(line)
  22368. }
  22369. return line
  22370. }
  22371. /**
  22372. * 更新resize-line
  22373. */
  22374. function updateResizeLine(cell, uetable) {
  22375. var line = getResizeLineByUETable.call(this),
  22376. table = uetable.table,
  22377. styles = {
  22378. top: domUtils.getXY(table).y + 'px',
  22379. left:
  22380. domUtils.getXY(cell).x + cell.offsetWidth - cellBorderWidth + 'px',
  22381. display: 'block',
  22382. height: table.offsetHeight + 'px'
  22383. }
  22384. utils.extend(line.style, styles)
  22385. }
  22386. /**
  22387. * 显示resize-line
  22388. */
  22389. function showResizeLine(cell) {
  22390. var uetable = getUETable(cell)
  22391. updateResizeLine.call(this, cell, uetable)
  22392. }
  22393. /**
  22394. * 获取鼠标与当前单元格的相对位置
  22395. * @param ele
  22396. * @param mousePos
  22397. */
  22398. function getRelation(ele, mousePos) {
  22399. var elePos = domUtils.getXY(ele)
  22400. if (!elePos) {
  22401. return ''
  22402. }
  22403. if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
  22404. return 'h'
  22405. }
  22406. if (mousePos.x - elePos.x < cellBorderWidth) {
  22407. return 'h1'
  22408. }
  22409. if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
  22410. return 'v'
  22411. }
  22412. if (mousePos.y - elePos.y < cellBorderWidth) {
  22413. return 'v1'
  22414. }
  22415. return ''
  22416. }
  22417. function mouseDownEvent(type, evt) {
  22418. if (isEditorDisabled()) {
  22419. return
  22420. }
  22421. userActionStatus = {
  22422. x: evt.clientX,
  22423. y: evt.clientY
  22424. }
  22425. //右键菜单单独处理
  22426. if (evt.button == 2) {
  22427. var ut = getUETableBySelected(me),
  22428. flag = false
  22429. if (ut) {
  22430. var td = getTargetTd(me, evt)
  22431. utils.each(ut.selectedTds, function (ti) {
  22432. if (ti === td) {
  22433. flag = true
  22434. }
  22435. })
  22436. if (!flag) {
  22437. removeSelectedClass(domUtils.getElementsByTagName(me.body, 'th td'))
  22438. ut.clearSelected()
  22439. } else {
  22440. td = ut.selectedTds[0]
  22441. setTimeout(function () {
  22442. me.selection.getRange().setStart(td, 0).setCursor(false, true)
  22443. }, 0)
  22444. }
  22445. }
  22446. } else {
  22447. tableClickHander(evt)
  22448. }
  22449. }
  22450. //清除表格的计时器
  22451. function clearTableTimer() {
  22452. tabTimer && clearTimeout(tabTimer)
  22453. tabTimer = null
  22454. }
  22455. //双击收缩
  22456. function tableDbclickHandler(evt) {
  22457. singleClickState = 0
  22458. evt = evt || me.window.event
  22459. var target = getParentTdOrTh(evt.target || evt.srcElement)
  22460. if (target) {
  22461. var h
  22462. if ((h = getRelation(target, mouseCoords(evt)))) {
  22463. hideDragLine(me)
  22464. if (h == 'h1') {
  22465. h = 'h'
  22466. if (
  22467. inTableSide(
  22468. domUtils.findParentByTagName(target, 'table'),
  22469. target,
  22470. evt
  22471. )
  22472. ) {
  22473. me.execCommand('adaptbywindow')
  22474. } else {
  22475. target = getUETable(target).getPreviewCell(target)
  22476. if (target) {
  22477. var rng = me.selection.getRange()
  22478. rng.selectNodeContents(target).setCursor(true, true)
  22479. }
  22480. }
  22481. }
  22482. if (h == 'h') {
  22483. var ut = getUETable(target),
  22484. table = ut.table,
  22485. cells = getCellsByMoveBorder(target, table, true)
  22486. cells = extractArray(cells, 'left')
  22487. ut.width = ut.offsetWidth
  22488. var oldWidth = [],
  22489. newWidth = []
  22490. utils.each(cells, function (cell) {
  22491. oldWidth.push(cell.offsetWidth)
  22492. })
  22493. utils.each(cells, function (cell) {
  22494. cell.removeAttribute('width')
  22495. })
  22496. window.setTimeout(function () {
  22497. //是否允许改变
  22498. var changeable = true
  22499. utils.each(cells, function (cell, index) {
  22500. var width = cell.offsetWidth
  22501. if (width > oldWidth[index]) {
  22502. changeable = false
  22503. return false
  22504. }
  22505. newWidth.push(width)
  22506. })
  22507. var change = changeable ? newWidth : oldWidth
  22508. utils.each(cells, function (cell, index) {
  22509. cell.width = change[index] - getTabcellSpace()
  22510. })
  22511. }, 0)
  22512. // minWidth -= cellMinWidth;
  22513. //
  22514. // table.removeAttribute("width");
  22515. // utils.each(cells, function (cell) {
  22516. // cell.style.width = "";
  22517. // cell.width -= minWidth;
  22518. // });
  22519. }
  22520. }
  22521. }
  22522. }
  22523. function tableClickHander(evt) {
  22524. removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td th'))
  22525. //trace:3113
  22526. //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值
  22527. utils.each(me.document.getElementsByTagName('table'), function (t) {
  22528. t.ueTable = null
  22529. })
  22530. startTd = getTargetTd(me, evt)
  22531. if (!startTd) return
  22532. var table = domUtils.findParentByTagName(startTd, 'table', true)
  22533. ut = getUETable(table)
  22534. ut && ut.clearSelected()
  22535. //判断当前鼠标状态
  22536. if (!onBorder) {
  22537. me.document.body.style.webkitUserSelect = ''
  22538. mousedown = true
  22539. me.addListener('mouseover', mouseOverEvent)
  22540. } else {
  22541. //边框上的动作处理
  22542. borderActionHandler(evt)
  22543. }
  22544. }
  22545. //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响
  22546. function borderActionHandler(evt) {
  22547. if (browser.ie) {
  22548. evt = reconstruct(evt)
  22549. }
  22550. clearTableDragTimer()
  22551. //是否正在等待resize的缓冲中
  22552. isInResizeBuffer = true
  22553. tableDragTimer = setTimeout(function () {
  22554. tableBorderDrag(evt)
  22555. }, dblclickTime)
  22556. }
  22557. function extractArray(originArr, key) {
  22558. var result = [],
  22559. tmp = null
  22560. for (var i = 0, len = originArr.length; i < len; i++) {
  22561. tmp = originArr[i][key]
  22562. if (tmp) {
  22563. result.push(tmp)
  22564. }
  22565. }
  22566. return result
  22567. }
  22568. function clearTableDragTimer() {
  22569. tableDragTimer && clearTimeout(tableDragTimer)
  22570. tableDragTimer = null
  22571. }
  22572. function reconstruct(obj) {
  22573. var attrs = [
  22574. 'pageX',
  22575. 'pageY',
  22576. 'clientX',
  22577. 'clientY',
  22578. 'srcElement',
  22579. 'target'
  22580. ],
  22581. newObj = {}
  22582. if (obj) {
  22583. for (var i = 0, key, val; (key = attrs[i]); i++) {
  22584. val = obj[key]
  22585. val && (newObj[key] = val)
  22586. }
  22587. }
  22588. return newObj
  22589. }
  22590. //边框拖动
  22591. function tableBorderDrag(evt) {
  22592. isInResizeBuffer = false
  22593. startTd = evt.target || evt.srcElement
  22594. if (!startTd) return
  22595. var state = getRelation(startTd, mouseCoords(evt))
  22596. if (/\d/.test(state)) {
  22597. state = state.replace(/\d/, '')
  22598. startTd = getUETable(startTd).getPreviewCell(startTd, state == 'v')
  22599. }
  22600. hideDragLine(me)
  22601. getDragLine(me, me.document)
  22602. me.fireEvent('saveScene')
  22603. showDragLineAt(state, startTd)
  22604. mousedown = true
  22605. //拖动开始
  22606. onDrag = state
  22607. dragTd = startTd
  22608. }
  22609. function mouseUpEvent(type, evt) {
  22610. if (isEditorDisabled()) {
  22611. return
  22612. }
  22613. clearTableDragTimer()
  22614. isInResizeBuffer = false
  22615. if (onBorder) {
  22616. singleClickState = ++singleClickState % 3
  22617. userActionStatus = {
  22618. x: evt.clientX,
  22619. y: evt.clientY
  22620. }
  22621. tableResizeTimer = setTimeout(function () {
  22622. singleClickState > 0 && singleClickState--
  22623. }, dblclickTime)
  22624. if (singleClickState === 2) {
  22625. singleClickState = 0
  22626. tableDbclickHandler(evt)
  22627. return
  22628. }
  22629. }
  22630. if (evt.button == 2) return
  22631. var me = this
  22632. //清除表格上原生跨选问题
  22633. var range = me.selection.getRange(),
  22634. start = domUtils.findParentByTagName(
  22635. range.startContainer,
  22636. 'table',
  22637. true
  22638. ),
  22639. end = domUtils.findParentByTagName(range.endContainer, 'table', true)
  22640. if (start || end) {
  22641. if (start === end) {
  22642. start = domUtils.findParentByTagName(
  22643. range.startContainer,
  22644. ['td', 'th', 'caption'],
  22645. true
  22646. )
  22647. end = domUtils.findParentByTagName(
  22648. range.endContainer,
  22649. ['td', 'th', 'caption'],
  22650. true
  22651. )
  22652. if (start !== end) {
  22653. me.selection.clearRange()
  22654. }
  22655. } else {
  22656. me.selection.clearRange()
  22657. }
  22658. }
  22659. mousedown = false
  22660. me.document.body.style.webkitUserSelect = ''
  22661. //拖拽状态下的mouseUP
  22662. if (onDrag && dragTd) {
  22663. me.selection
  22664. .getNative()
  22665. [browser.ie9below ? 'empty' : 'removeAllRanges']()
  22666. singleClickState = 0
  22667. dragLine = me.document.getElementById('ue_tableDragLine')
  22668. // trace 3973
  22669. if (dragLine) {
  22670. var dragTdPos = domUtils.getXY(dragTd),
  22671. dragLinePos = domUtils.getXY(dragLine)
  22672. switch (onDrag) {
  22673. case 'h':
  22674. changeColWidth(dragTd, dragLinePos.x - dragTdPos.x)
  22675. break
  22676. case 'v':
  22677. changeRowHeight(
  22678. dragTd,
  22679. dragLinePos.y - dragTdPos.y - dragTd.offsetHeight
  22680. )
  22681. break
  22682. default:
  22683. }
  22684. onDrag = ''
  22685. dragTd = null
  22686. hideDragLine(me)
  22687. me.fireEvent('saveScene')
  22688. return
  22689. }
  22690. }
  22691. //正常状态下的mouseup
  22692. if (!startTd) {
  22693. var target = domUtils.findParentByTagName(
  22694. evt.target || evt.srcElement,
  22695. 'td',
  22696. true
  22697. )
  22698. if (!target)
  22699. target = domUtils.findParentByTagName(
  22700. evt.target || evt.srcElement,
  22701. 'th',
  22702. true
  22703. )
  22704. if (target && (target.tagName == 'TD' || target.tagName == 'TH')) {
  22705. if (me.fireEvent('excludetable', target) === true) return
  22706. range = new dom.Range(me.document)
  22707. range.setStart(target, 0).setCursor(false, true)
  22708. }
  22709. } else {
  22710. var ut = getUETable(startTd),
  22711. cell = ut ? ut.selectedTds[0] : null
  22712. if (cell) {
  22713. range = new dom.Range(me.document)
  22714. if (domUtils.isEmptyBlock(cell)) {
  22715. range.setStart(cell, 0).setCursor(false, true)
  22716. } else {
  22717. range
  22718. .selectNodeContents(cell)
  22719. .shrinkBoundary()
  22720. .setCursor(false, true)
  22721. }
  22722. } else {
  22723. range = me.selection.getRange().shrinkBoundary()
  22724. if (!range.collapsed) {
  22725. var start = domUtils.findParentByTagName(
  22726. range.startContainer,
  22727. ['td', 'th'],
  22728. true
  22729. ),
  22730. end = domUtils.findParentByTagName(
  22731. range.endContainer,
  22732. ['td', 'th'],
  22733. true
  22734. )
  22735. //在table里边的不能清除
  22736. if (
  22737. (start && !end) ||
  22738. (!start && end) ||
  22739. (start && end && start !== end)
  22740. ) {
  22741. range.setCursor(false, true)
  22742. }
  22743. }
  22744. }
  22745. startTd = null
  22746. me.removeListener('mouseover', mouseOverEvent)
  22747. }
  22748. me._selectionChange(250, evt)
  22749. }
  22750. function mouseOverEvent(type, evt) {
  22751. if (isEditorDisabled()) {
  22752. return
  22753. }
  22754. var me = this,
  22755. tar = evt.target || evt.srcElement
  22756. currentTd =
  22757. domUtils.findParentByTagName(tar, 'td', true) ||
  22758. domUtils.findParentByTagName(tar, 'th', true)
  22759. //需要判断两个TD是否位于同一个表格内
  22760. if (
  22761. startTd &&
  22762. currentTd &&
  22763. ((startTd.tagName == 'TD' && currentTd.tagName == 'TD') ||
  22764. (startTd.tagName == 'TH' && currentTd.tagName == 'TH')) &&
  22765. domUtils.findParentByTagName(startTd, 'table') ==
  22766. domUtils.findParentByTagName(currentTd, 'table')
  22767. ) {
  22768. var ut = getUETable(currentTd)
  22769. if (startTd != currentTd) {
  22770. me.document.body.style.webkitUserSelect = 'none'
  22771. me.selection
  22772. .getNative()
  22773. [browser.ie9below ? 'empty' : 'removeAllRanges']()
  22774. var range = ut.getCellsRange(startTd, currentTd)
  22775. ut.setSelected(range)
  22776. } else {
  22777. me.document.body.style.webkitUserSelect = ''
  22778. ut.clearSelected()
  22779. }
  22780. }
  22781. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
  22782. }
  22783. function setCellHeight(cell, height, backHeight) {
  22784. var lineHight = parseInt(
  22785. domUtils.getComputedStyle(cell, 'line-height'),
  22786. 10
  22787. ),
  22788. tmpHeight = backHeight + height
  22789. height = tmpHeight < lineHight ? lineHight : tmpHeight
  22790. if (cell.style.height) cell.style.height = ''
  22791. cell.rowSpan == 1
  22792. ? cell.setAttribute('height', height)
  22793. : cell.removeAttribute && cell.removeAttribute('height')
  22794. }
  22795. function getWidth(cell) {
  22796. if (!cell) return 0
  22797. return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
  22798. }
  22799. function changeColWidth(cell, changeValue) {
  22800. var ut = getUETable(cell)
  22801. if (ut) {
  22802. //根据当前移动的边框获取相关的单元格
  22803. var table = ut.table,
  22804. cells = getCellsByMoveBorder(cell, table)
  22805. table.style.width = ''
  22806. table.removeAttribute('width')
  22807. //修正改变量
  22808. changeValue = correctChangeValue(changeValue, cell, cells)
  22809. if (cell.nextSibling) {
  22810. var i = 0
  22811. utils.each(cells, function (cellGroup) {
  22812. cellGroup.left.width = +cellGroup.left.width + changeValue
  22813. cellGroup.right &&
  22814. (cellGroup.right.width = +cellGroup.right.width - changeValue)
  22815. })
  22816. } else {
  22817. utils.each(cells, function (cellGroup) {
  22818. cellGroup.left.width -= -changeValue
  22819. })
  22820. }
  22821. }
  22822. }
  22823. function isEditorDisabled() {
  22824. return me.body.contentEditable === 'false'
  22825. }
  22826. function changeRowHeight(td, changeValue) {
  22827. if (Math.abs(changeValue) < 10) return
  22828. var ut = getUETable(td)
  22829. if (ut) {
  22830. var cells = ut.getSameEndPosCells(td, 'y'),
  22831. //备份需要连带变化的td的原始高度,否则后期无法获取正确的值
  22832. backHeight = cells[0] ? cells[0].offsetHeight : 0
  22833. for (var i = 0, cell; (cell = cells[i++]); ) {
  22834. setCellHeight(cell, changeValue, backHeight)
  22835. }
  22836. }
  22837. }
  22838. /**
  22839. * 获取调整单元格大小的相关单元格
  22840. * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格
  22841. */
  22842. function getCellsByMoveBorder(cell, table, isContainMergeCell) {
  22843. if (!table) {
  22844. table = domUtils.findParentByTagName(cell, 'table')
  22845. }
  22846. if (!table) {
  22847. return null
  22848. }
  22849. //获取到该单元格所在行的序列号
  22850. var index = domUtils.getNodeIndex(cell),
  22851. temp = cell,
  22852. rows = table.rows,
  22853. colIndex = 0
  22854. while (temp) {
  22855. //获取到当前单元格在未发生单元格合并时的序列
  22856. if (temp.nodeType === 1) {
  22857. colIndex += temp.colSpan || 1
  22858. }
  22859. temp = temp.previousSibling
  22860. }
  22861. temp = null
  22862. //记录想关的单元格
  22863. var borderCells = []
  22864. utils.each(rows, function (tabRow) {
  22865. var cells = tabRow.cells,
  22866. currIndex = 0
  22867. utils.each(cells, function (tabCell) {
  22868. currIndex += tabCell.colSpan || 1
  22869. if (currIndex === colIndex) {
  22870. borderCells.push({
  22871. left: tabCell,
  22872. right: tabCell.nextSibling || null
  22873. })
  22874. return false
  22875. } else if (currIndex > colIndex) {
  22876. if (isContainMergeCell) {
  22877. borderCells.push({
  22878. left: tabCell
  22879. })
  22880. }
  22881. return false
  22882. }
  22883. })
  22884. })
  22885. return borderCells
  22886. }
  22887. /**
  22888. * 通过给定的单元格集合获取最小的单元格width
  22889. */
  22890. function getMinWidthByTableCells(cells) {
  22891. var minWidth = Number.MAX_VALUE
  22892. for (var i = 0, curCell; (curCell = cells[i]); i++) {
  22893. minWidth = Math.min(
  22894. minWidth,
  22895. curCell.width || getTableCellWidth(curCell)
  22896. )
  22897. }
  22898. return minWidth
  22899. }
  22900. function correctChangeValue(changeValue, relatedCell, cells) {
  22901. //为单元格的paading预留空间
  22902. changeValue -= getTabcellSpace()
  22903. if (changeValue < 0) {
  22904. return 0
  22905. }
  22906. changeValue -= getTableCellWidth(relatedCell)
  22907. //确定方向
  22908. var direction = changeValue < 0 ? 'left' : 'right'
  22909. changeValue = Math.abs(changeValue)
  22910. //只关心非最后一个单元格就可以
  22911. utils.each(cells, function (cellGroup) {
  22912. var curCell = cellGroup[direction]
  22913. //为单元格保留最小空间
  22914. if (curCell) {
  22915. changeValue = Math.min(
  22916. changeValue,
  22917. getTableCellWidth(curCell) - cellMinWidth
  22918. )
  22919. }
  22920. })
  22921. //修正越界
  22922. changeValue = changeValue < 0 ? 0 : changeValue
  22923. return direction === 'left' ? -changeValue : changeValue
  22924. }
  22925. function getTableCellWidth(cell) {
  22926. var width = 0,
  22927. //偏移纠正量
  22928. offset = 0,
  22929. width = cell.offsetWidth - getTabcellSpace()
  22930. //最后一个节点纠正一下
  22931. if (!cell.nextSibling) {
  22932. width -= getTableCellOffset(cell)
  22933. }
  22934. width = width < 0 ? 0 : width
  22935. try {
  22936. cell.width = width
  22937. } catch (e) {}
  22938. return width
  22939. }
  22940. /**
  22941. * 获取单元格所在表格的最末单元格的偏移量
  22942. */
  22943. function getTableCellOffset(cell) {
  22944. tab = domUtils.findParentByTagName(cell, 'table', false)
  22945. if (tab.offsetVal === undefined) {
  22946. var prev = cell.previousSibling
  22947. if (prev) {
  22948. //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立
  22949. tab.offsetVal =
  22950. cell.offsetWidth - prev.offsetWidth === UT.borderWidth
  22951. ? UT.borderWidth
  22952. : 0
  22953. } else {
  22954. tab.offsetVal = 0
  22955. }
  22956. }
  22957. return tab.offsetVal
  22958. }
  22959. function getTabcellSpace() {
  22960. if (UT.tabcellSpace === undefined) {
  22961. var cell = null,
  22962. tab = me.document.createElement('table'),
  22963. tbody = me.document.createElement('tbody'),
  22964. trow = me.document.createElement('tr'),
  22965. tabcell = me.document.createElement('td'),
  22966. mirror = null
  22967. tabcell.style.cssText = 'border: 0;'
  22968. tabcell.width = 1
  22969. trow.appendChild(tabcell)
  22970. trow.appendChild((mirror = tabcell.cloneNode(false)))
  22971. tbody.appendChild(trow)
  22972. tab.appendChild(tbody)
  22973. tab.style.cssText = 'visibility: hidden;'
  22974. me.body.appendChild(tab)
  22975. UT.paddingSpace = tabcell.offsetWidth - 1
  22976. var tmpTabWidth = tab.offsetWidth
  22977. tabcell.style.cssText = ''
  22978. mirror.style.cssText = ''
  22979. UT.borderWidth = (tab.offsetWidth - tmpTabWidth) / 3
  22980. UT.tabcellSpace = UT.paddingSpace + UT.borderWidth
  22981. me.body.removeChild(tab)
  22982. }
  22983. getTabcellSpace = function () {
  22984. return UT.tabcellSpace
  22985. }
  22986. return UT.tabcellSpace
  22987. }
  22988. function getDragLine(editor, doc) {
  22989. if (mousedown) return
  22990. dragLine = editor.document.createElement('div')
  22991. domUtils.setAttributes(dragLine, {
  22992. id: 'ue_tableDragLine',
  22993. unselectable: 'on',
  22994. contenteditable: false,
  22995. onresizestart: 'return false',
  22996. ondragstart: 'return false',
  22997. onselectstart: 'return false',
  22998. style:
  22999. 'background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)'
  23000. })
  23001. editor.body.appendChild(dragLine)
  23002. }
  23003. function hideDragLine(editor) {
  23004. if (mousedown) return
  23005. var line
  23006. while ((line = editor.document.getElementById('ue_tableDragLine'))) {
  23007. domUtils.remove(line)
  23008. }
  23009. }
  23010. /**
  23011. * 依据state(v|h)在cell位置显示横线
  23012. * @param state
  23013. * @param cell
  23014. */
  23015. function showDragLineAt(state, cell) {
  23016. if (!cell) return
  23017. var table = domUtils.findParentByTagName(cell, 'table'),
  23018. caption = table.getElementsByTagName('caption'),
  23019. width = table.offsetWidth,
  23020. height =
  23021. table.offsetHeight -
  23022. (caption.length > 0 ? caption[0].offsetHeight : 0),
  23023. tablePos = domUtils.getXY(table),
  23024. cellPos = domUtils.getXY(cell),
  23025. css
  23026. switch (state) {
  23027. case 'h':
  23028. css =
  23029. 'height:' +
  23030. height +
  23031. 'px;top:' +
  23032. (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) +
  23033. 'px;left:' +
  23034. (cellPos.x + cell.offsetWidth)
  23035. dragLine.style.cssText =
  23036. css +
  23037. 'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)'
  23038. break
  23039. case 'v':
  23040. css =
  23041. 'width:' +
  23042. width +
  23043. 'px;left:' +
  23044. tablePos.x +
  23045. 'px;top:' +
  23046. (cellPos.y + cell.offsetHeight)
  23047. //必须加上border:0和color:blue,否则低版ie不支持背景色显示
  23048. dragLine.style.cssText =
  23049. css +
  23050. 'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)'
  23051. break
  23052. default:
  23053. }
  23054. }
  23055. /**
  23056. * 当表格边框颜色为白色时设置为虚线,true为添加虚线
  23057. * @param editor
  23058. * @param flag
  23059. */
  23060. function switchBorderColor(editor, flag) {
  23061. var tableArr = domUtils.getElementsByTagName(editor.body, 'table'),
  23062. color
  23063. for (var i = 0, node; (node = tableArr[i++]); ) {
  23064. var td = domUtils.getElementsByTagName(node, 'td')
  23065. if (td[0]) {
  23066. if (flag) {
  23067. color = td[0].style.borderColor.replace(/\s/g, '')
  23068. if (/(#ffffff)|(rgb\(255,255,255\))/gi.test(color))
  23069. domUtils.addClass(node, 'noBorderTable')
  23070. } else {
  23071. domUtils.removeClasses(node, 'noBorderTable')
  23072. }
  23073. }
  23074. }
  23075. }
  23076. function getTableWidth(editor, needIEHack, defaultValue) {
  23077. var body = editor.body
  23078. return (
  23079. body.offsetWidth -
  23080. (needIEHack
  23081. ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2
  23082. : 0) -
  23083. defaultValue.tableBorder * 2 -
  23084. (editor.options.offsetWidth || 0)
  23085. )
  23086. }
  23087. /**
  23088. * 获取当前拖动的单元格
  23089. */
  23090. function getTargetTd(editor, evt) {
  23091. var target = domUtils.findParentByTagName(
  23092. evt.target || evt.srcElement,
  23093. ['td', 'th'],
  23094. true
  23095. ),
  23096. dir = null
  23097. if (!target) {
  23098. return null
  23099. }
  23100. dir = getRelation(target, mouseCoords(evt))
  23101. //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td
  23102. if (!target) {
  23103. return null
  23104. }
  23105. if (dir === 'h1' && target.previousSibling) {
  23106. var position = domUtils.getXY(target),
  23107. cellWidth = target.offsetWidth
  23108. if (Math.abs(position.x + cellWidth - evt.clientX) > cellWidth / 3) {
  23109. target = target.previousSibling
  23110. }
  23111. } else if (dir === 'v1' && target.parentNode.previousSibling) {
  23112. var position = domUtils.getXY(target),
  23113. cellHeight = target.offsetHeight
  23114. if (Math.abs(position.y + cellHeight - evt.clientY) > cellHeight / 3) {
  23115. target = target.parentNode.previousSibling.firstChild
  23116. }
  23117. }
  23118. //排除了非td内部以及用于代码高亮部分的td
  23119. return target && !(editor.fireEvent('excludetable', target) === true)
  23120. ? target
  23121. : null
  23122. }
  23123. }
  23124. // plugins/table.sort.js
  23125. /**
  23126. * Created with JetBrains PhpStorm.
  23127. * User: Jinqn
  23128. * Date: 13-10-12
  23129. * Time: 上午10:20
  23130. * To change this template use File | Settings | File Templates.
  23131. */
  23132. UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) {
  23133. var table = this.table,
  23134. rows = table.rows,
  23135. trArray = [],
  23136. flag = rows[0].cells[0].tagName === 'TH',
  23137. lastRowIndex = 0
  23138. if (this.selectedTds.length) {
  23139. var range = this.cellsRange,
  23140. len = range.endRowIndex + 1
  23141. for (var i = range.beginRowIndex; i < len; i++) {
  23142. trArray[i] = rows[i]
  23143. }
  23144. trArray.splice(0, range.beginRowIndex)
  23145. lastRowIndex =
  23146. range.endRowIndex + 1 === this.rowsNum ? 0 : range.endRowIndex + 1
  23147. } else {
  23148. for (var i = 0, len = rows.length; i < len; i++) {
  23149. trArray[i] = rows[i]
  23150. }
  23151. }
  23152. var Fn = {
  23153. reversecurrent: function (td1, td2) {
  23154. return 1
  23155. },
  23156. orderbyasc: function (td1, td2) {
  23157. var value1 = td1.innerText || td1.textContent,
  23158. value2 = td2.innerText || td2.textContent
  23159. return value1.localeCompare(value2)
  23160. },
  23161. reversebyasc: function (td1, td2) {
  23162. var value1 = td1.innerHTML,
  23163. value2 = td2.innerHTML
  23164. return value2.localeCompare(value1)
  23165. },
  23166. orderbynum: function (td1, td2) {
  23167. var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
  23168. value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
  23169. if (value1) value1 = +value1[0]
  23170. if (value2) value2 = +value2[0]
  23171. return (value1 || 0) - (value2 || 0)
  23172. },
  23173. reversebynum: function (td1, td2) {
  23174. var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
  23175. value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
  23176. if (value1) value1 = +value1[0]
  23177. if (value2) value2 = +value2[0]
  23178. return (value2 || 0) - (value1 || 0)
  23179. }
  23180. }
  23181. //对表格设置排序的标记data-sort-type
  23182. table.setAttribute(
  23183. 'data-sort-type',
  23184. compareFn && typeof compareFn === 'string' && Fn[compareFn]
  23185. ? compareFn
  23186. : ''
  23187. )
  23188. //th不参与排序
  23189. flag && trArray.splice(0, 1)
  23190. trArray = utils.sort(trArray, function (tr1, tr2) {
  23191. var result
  23192. if (compareFn && typeof compareFn === 'function') {
  23193. result = compareFn.call(
  23194. this,
  23195. tr1.cells[sortByCellIndex],
  23196. tr2.cells[sortByCellIndex]
  23197. )
  23198. } else if (compareFn && typeof compareFn === 'number') {
  23199. result = 1
  23200. } else if (compareFn && typeof compareFn === 'string' && Fn[compareFn]) {
  23201. result = Fn[compareFn].call(
  23202. this,
  23203. tr1.cells[sortByCellIndex],
  23204. tr2.cells[sortByCellIndex]
  23205. )
  23206. } else {
  23207. result = Fn['orderbyasc'].call(
  23208. this,
  23209. tr1.cells[sortByCellIndex],
  23210. tr2.cells[sortByCellIndex]
  23211. )
  23212. }
  23213. return result
  23214. })
  23215. var fragment = table.ownerDocument.createDocumentFragment()
  23216. for (var j = 0, len = trArray.length; j < len; j++) {
  23217. fragment.appendChild(trArray[j])
  23218. }
  23219. var tbody = table.getElementsByTagName('tbody')[0]
  23220. if (!lastRowIndex) {
  23221. tbody.appendChild(fragment)
  23222. } else {
  23223. tbody.insertBefore(
  23224. fragment,
  23225. rows[lastRowIndex - range.endRowIndex + range.beginRowIndex - 1]
  23226. )
  23227. }
  23228. }
  23229. UE.plugins['tablesort'] = function () {
  23230. var me = this,
  23231. UT = UE.UETable,
  23232. getUETable = function (tdOrTable) {
  23233. return UT.getUETable(tdOrTable)
  23234. },
  23235. getTableItemsByRange = function (editor) {
  23236. return UT.getTableItemsByRange(editor)
  23237. }
  23238. me.ready(function () {
  23239. //添加表格可排序的样式
  23240. utils.cssRule(
  23241. 'tablesort',
  23242. 'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' +
  23243. ' background-image:url(' +
  23244. me.options.themePath +
  23245. me.options.theme +
  23246. '/images/sortable.png);}',
  23247. me.document
  23248. )
  23249. //做单元格合并操作时,清除可排序标识
  23250. me.addListener('afterexeccommand', function (type, cmd) {
  23251. if (cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') {
  23252. this.execCommand('disablesort')
  23253. }
  23254. })
  23255. })
  23256. //表格排序
  23257. UE.commands['sorttable'] = {
  23258. queryCommandState: function () {
  23259. var me = this,
  23260. tableItems = getTableItemsByRange(me)
  23261. if (!tableItems.cell) return -1
  23262. var table = tableItems.table,
  23263. cells = table.getElementsByTagName('td')
  23264. for (var i = 0, cell; (cell = cells[i++]); ) {
  23265. if (cell.rowSpan != 1 || cell.colSpan != 1) return -1
  23266. }
  23267. return 0
  23268. },
  23269. execCommand: function (cmd, fn) {
  23270. var me = this,
  23271. range = me.selection.getRange(),
  23272. bk = range.createBookmark(true),
  23273. tableItems = getTableItemsByRange(me),
  23274. cell = tableItems.cell,
  23275. ut = getUETable(tableItems.table),
  23276. cellInfo = ut.getCellInfo(cell)
  23277. ut.sortTable(cellInfo.cellIndex, fn)
  23278. range.moveToBookmark(bk)
  23279. try {
  23280. range.select()
  23281. } catch (e) {}
  23282. }
  23283. }
  23284. //设置表格可排序,清除表格可排序
  23285. UE.commands['enablesort'] = UE.commands['disablesort'] = {
  23286. queryCommandState: function (cmd) {
  23287. var table = getTableItemsByRange(this).table
  23288. if (table && cmd == 'enablesort') {
  23289. var cells = domUtils.getElementsByTagName(table, 'th td')
  23290. for (var i = 0; i < cells.length; i++) {
  23291. if (
  23292. cells[i].getAttribute('colspan') > 1 ||
  23293. cells[i].getAttribute('rowspan') > 1
  23294. )
  23295. return -1
  23296. }
  23297. }
  23298. return !table
  23299. ? -1
  23300. : (cmd == 'enablesort') ^
  23301. (table.getAttribute('data-sort') != 'sortEnabled')
  23302. ? -1
  23303. : 0
  23304. },
  23305. execCommand: function (cmd) {
  23306. var table = getTableItemsByRange(this).table
  23307. table.setAttribute(
  23308. 'data-sort',
  23309. cmd == 'enablesort' ? 'sortEnabled' : 'sortDisabled'
  23310. )
  23311. cmd == 'enablesort'
  23312. ? domUtils.addClass(table, 'sortEnabled')
  23313. : domUtils.removeClasses(table, 'sortEnabled')
  23314. }
  23315. }
  23316. }
  23317. // plugins/contextmenu.js
  23318. ///import core
  23319. ///commands 右键菜单
  23320. ///commandsName ContextMenu
  23321. ///commandsTitle 右键菜单
  23322. /**
  23323. * 右键菜单
  23324. * @function
  23325. * @name baidu.editor.plugins.contextmenu
  23326. * @author zhanyi
  23327. */
  23328. UE.plugins['contextmenu'] = function () {
  23329. var me = this
  23330. me.setOpt('enableContextMenu', true)
  23331. if (me.getOpt('enableContextMenu') === false) {
  23332. return
  23333. }
  23334. var lang = me.getLang('contextMenu'),
  23335. menu,
  23336. items = me.options.contextMenu || [
  23337. { label: lang['selectall'], cmdName: 'selectall' },
  23338. {
  23339. label: lang.cleardoc,
  23340. cmdName: 'cleardoc',
  23341. exec: function () {
  23342. if (confirm(lang.confirmclear)) {
  23343. this.execCommand('cleardoc')
  23344. }
  23345. }
  23346. },
  23347. '-',
  23348. {
  23349. label: lang.unlink,
  23350. cmdName: 'unlink'
  23351. },
  23352. '-',
  23353. {
  23354. group: lang.paragraph,
  23355. icon: 'justifyjustify',
  23356. subMenu: [
  23357. {
  23358. label: lang.justifyleft,
  23359. cmdName: 'justify',
  23360. value: 'left'
  23361. },
  23362. {
  23363. label: lang.justifyright,
  23364. cmdName: 'justify',
  23365. value: 'right'
  23366. },
  23367. {
  23368. label: lang.justifycenter,
  23369. cmdName: 'justify',
  23370. value: 'center'
  23371. },
  23372. {
  23373. label: lang.justifyjustify,
  23374. cmdName: 'justify',
  23375. value: 'justify'
  23376. }
  23377. ]
  23378. },
  23379. '-',
  23380. {
  23381. group: lang.table,
  23382. icon: 'table',
  23383. subMenu: [
  23384. {
  23385. label: lang.inserttable,
  23386. cmdName: 'inserttable'
  23387. },
  23388. {
  23389. label: lang.deletetable,
  23390. cmdName: 'deletetable'
  23391. },
  23392. '-',
  23393. {
  23394. label: lang.deleterow,
  23395. cmdName: 'deleterow'
  23396. },
  23397. {
  23398. label: lang.deletecol,
  23399. cmdName: 'deletecol'
  23400. },
  23401. {
  23402. label: lang.insertcol,
  23403. cmdName: 'insertcol'
  23404. },
  23405. {
  23406. label: lang.insertcolnext,
  23407. cmdName: 'insertcolnext'
  23408. },
  23409. {
  23410. label: lang.insertrow,
  23411. cmdName: 'insertrow'
  23412. },
  23413. {
  23414. label: lang.insertrownext,
  23415. cmdName: 'insertrownext'
  23416. },
  23417. '-',
  23418. {
  23419. label: lang.insertcaption,
  23420. cmdName: 'insertcaption'
  23421. },
  23422. {
  23423. label: lang.deletecaption,
  23424. cmdName: 'deletecaption'
  23425. },
  23426. {
  23427. label: lang.inserttitle,
  23428. cmdName: 'inserttitle'
  23429. },
  23430. {
  23431. label: lang.deletetitle,
  23432. cmdName: 'deletetitle'
  23433. },
  23434. {
  23435. label: lang.inserttitlecol,
  23436. cmdName: 'inserttitlecol'
  23437. },
  23438. {
  23439. label: lang.deletetitlecol,
  23440. cmdName: 'deletetitlecol'
  23441. },
  23442. '-',
  23443. {
  23444. label: lang.mergecells,
  23445. cmdName: 'mergecells'
  23446. },
  23447. {
  23448. label: lang.mergeright,
  23449. cmdName: 'mergeright'
  23450. },
  23451. {
  23452. label: lang.mergedown,
  23453. cmdName: 'mergedown'
  23454. },
  23455. '-',
  23456. {
  23457. label: lang.splittorows,
  23458. cmdName: 'splittorows'
  23459. },
  23460. {
  23461. label: lang.splittocols,
  23462. cmdName: 'splittocols'
  23463. },
  23464. {
  23465. label: lang.splittocells,
  23466. cmdName: 'splittocells'
  23467. },
  23468. '-',
  23469. {
  23470. label: lang.averageDiseRow,
  23471. cmdName: 'averagedistributerow'
  23472. },
  23473. {
  23474. label: lang.averageDisCol,
  23475. cmdName: 'averagedistributecol'
  23476. },
  23477. '-',
  23478. {
  23479. label: lang.edittd,
  23480. cmdName: 'edittd',
  23481. exec: function () {
  23482. if (UE.ui['edittd']) {
  23483. new UE.ui['edittd'](this)
  23484. }
  23485. this.getDialog('edittd').open()
  23486. }
  23487. },
  23488. {
  23489. label: lang.edittable,
  23490. cmdName: 'edittable',
  23491. exec: function () {
  23492. if (UE.ui['edittable']) {
  23493. new UE.ui['edittable'](this)
  23494. }
  23495. this.getDialog('edittable').open()
  23496. }
  23497. },
  23498. {
  23499. label: lang.setbordervisible,
  23500. cmdName: 'setbordervisible'
  23501. }
  23502. ]
  23503. },
  23504. {
  23505. group: lang.tablesort,
  23506. icon: 'tablesort',
  23507. subMenu: [
  23508. {
  23509. label: lang.enablesort,
  23510. cmdName: 'enablesort'
  23511. },
  23512. {
  23513. label: lang.disablesort,
  23514. cmdName: 'disablesort'
  23515. },
  23516. '-',
  23517. {
  23518. label: lang.reversecurrent,
  23519. cmdName: 'sorttable',
  23520. value: 'reversecurrent'
  23521. },
  23522. {
  23523. label: lang.orderbyasc,
  23524. cmdName: 'sorttable',
  23525. value: 'orderbyasc'
  23526. },
  23527. {
  23528. label: lang.reversebyasc,
  23529. cmdName: 'sorttable',
  23530. value: 'reversebyasc'
  23531. },
  23532. {
  23533. label: lang.orderbynum,
  23534. cmdName: 'sorttable',
  23535. value: 'orderbynum'
  23536. },
  23537. {
  23538. label: lang.reversebynum,
  23539. cmdName: 'sorttable',
  23540. value: 'reversebynum'
  23541. }
  23542. ]
  23543. },
  23544. {
  23545. group: lang.borderbk,
  23546. icon: 'borderBack',
  23547. subMenu: [
  23548. {
  23549. label: lang.setcolor,
  23550. cmdName: 'interlacetable',
  23551. exec: function () {
  23552. this.execCommand('interlacetable')
  23553. }
  23554. },
  23555. {
  23556. label: lang.unsetcolor,
  23557. cmdName: 'uninterlacetable',
  23558. exec: function () {
  23559. this.execCommand('uninterlacetable')
  23560. }
  23561. },
  23562. {
  23563. label: lang.setbackground,
  23564. cmdName: 'settablebackground',
  23565. exec: function () {
  23566. this.execCommand('settablebackground', {
  23567. repeat: true,
  23568. colorList: ['#bbb', '#ccc']
  23569. })
  23570. }
  23571. },
  23572. {
  23573. label: lang.unsetbackground,
  23574. cmdName: 'cleartablebackground',
  23575. exec: function () {
  23576. this.execCommand('cleartablebackground')
  23577. }
  23578. },
  23579. {
  23580. label: lang.redandblue,
  23581. cmdName: 'settablebackground',
  23582. exec: function () {
  23583. this.execCommand('settablebackground', {
  23584. repeat: true,
  23585. colorList: ['red', 'blue']
  23586. })
  23587. }
  23588. },
  23589. {
  23590. label: lang.threecolorgradient,
  23591. cmdName: 'settablebackground',
  23592. exec: function () {
  23593. this.execCommand('settablebackground', {
  23594. repeat: true,
  23595. colorList: ['#aaa', '#bbb', '#ccc']
  23596. })
  23597. }
  23598. }
  23599. ]
  23600. },
  23601. {
  23602. group: lang.aligntd,
  23603. icon: 'aligntd',
  23604. subMenu: [
  23605. {
  23606. cmdName: 'cellalignment',
  23607. value: { align: 'left', vAlign: 'top' }
  23608. },
  23609. {
  23610. cmdName: 'cellalignment',
  23611. value: { align: 'center', vAlign: 'top' }
  23612. },
  23613. {
  23614. cmdName: 'cellalignment',
  23615. value: { align: 'right', vAlign: 'top' }
  23616. },
  23617. {
  23618. cmdName: 'cellalignment',
  23619. value: { align: 'left', vAlign: 'middle' }
  23620. },
  23621. {
  23622. cmdName: 'cellalignment',
  23623. value: { align: 'center', vAlign: 'middle' }
  23624. },
  23625. {
  23626. cmdName: 'cellalignment',
  23627. value: { align: 'right', vAlign: 'middle' }
  23628. },
  23629. {
  23630. cmdName: 'cellalignment',
  23631. value: { align: 'left', vAlign: 'bottom' }
  23632. },
  23633. {
  23634. cmdName: 'cellalignment',
  23635. value: { align: 'center', vAlign: 'bottom' }
  23636. },
  23637. {
  23638. cmdName: 'cellalignment',
  23639. value: { align: 'right', vAlign: 'bottom' }
  23640. }
  23641. ]
  23642. },
  23643. {
  23644. group: lang.aligntable,
  23645. icon: 'aligntable',
  23646. subMenu: [
  23647. {
  23648. cmdName: 'tablealignment',
  23649. className: 'left',
  23650. label: lang.tableleft,
  23651. value: 'left'
  23652. },
  23653. {
  23654. cmdName: 'tablealignment',
  23655. className: 'center',
  23656. label: lang.tablecenter,
  23657. value: 'center'
  23658. },
  23659. {
  23660. cmdName: 'tablealignment',
  23661. className: 'right',
  23662. label: lang.tableright,
  23663. value: 'right'
  23664. }
  23665. ]
  23666. },
  23667. '-',
  23668. {
  23669. label: lang.insertparagraphbefore,
  23670. cmdName: 'insertparagraph',
  23671. value: true
  23672. },
  23673. {
  23674. label: lang.insertparagraphafter,
  23675. cmdName: 'insertparagraph'
  23676. },
  23677. {
  23678. label: lang['copy'],
  23679. cmdName: 'copy'
  23680. },
  23681. {
  23682. label: lang['paste'],
  23683. cmdName: 'paste'
  23684. }
  23685. ]
  23686. if (!items.length) {
  23687. return
  23688. }
  23689. var uiUtils = UE.ui.uiUtils
  23690. me.addListener('contextmenu', function (type, evt) {
  23691. var offset = uiUtils.getViewportOffsetByEvent(evt)
  23692. me.fireEvent('beforeselectionchange')
  23693. if (menu) {
  23694. menu.destroy()
  23695. }
  23696. for (var i = 0, ti, contextItems = []; (ti = items[i]); i++) {
  23697. var last
  23698. ;(function (item) {
  23699. if (item == '-') {
  23700. if (
  23701. (last = contextItems[contextItems.length - 1]) &&
  23702. last !== '-'
  23703. ) {
  23704. contextItems.push('-')
  23705. }
  23706. } else if (item.hasOwnProperty('group')) {
  23707. for (var j = 0, cj, subMenu = []; (cj = item.subMenu[j]); j++) {
  23708. ;(function (subItem) {
  23709. if (subItem == '-') {
  23710. if ((last = subMenu[subMenu.length - 1]) && last !== '-') {
  23711. subMenu.push('-')
  23712. } else {
  23713. subMenu.splice(subMenu.length - 1)
  23714. }
  23715. } else {
  23716. if (
  23717. (me.commands[subItem.cmdName] ||
  23718. UE.commands[subItem.cmdName] ||
  23719. subItem.query) &&
  23720. (subItem.query
  23721. ? subItem.query()
  23722. : me.queryCommandState(subItem.cmdName)) > -1
  23723. ) {
  23724. subMenu.push({
  23725. label:
  23726. subItem.label ||
  23727. me.getLang(
  23728. 'contextMenu.' +
  23729. subItem.cmdName +
  23730. (subItem.value || '')
  23731. ) ||
  23732. '',
  23733. className:
  23734. 'edui-for-' +
  23735. subItem.cmdName +
  23736. (subItem.className
  23737. ? ' edui-for-' +
  23738. subItem.cmdName +
  23739. '-' +
  23740. subItem.className
  23741. : ''),
  23742. onclick: subItem.exec
  23743. ? function () {
  23744. subItem.exec.call(me)
  23745. }
  23746. : function () {
  23747. me.execCommand(subItem.cmdName, subItem.value)
  23748. }
  23749. })
  23750. }
  23751. }
  23752. })(cj)
  23753. }
  23754. if (subMenu.length) {
  23755. function getLabel() {
  23756. switch (item.icon) {
  23757. case 'table':
  23758. return me.getLang('contextMenu.table')
  23759. case 'justifyjustify':
  23760. return me.getLang('contextMenu.paragraph')
  23761. case 'aligntd':
  23762. return me.getLang('contextMenu.aligntd')
  23763. case 'aligntable':
  23764. return me.getLang('contextMenu.aligntable')
  23765. case 'tablesort':
  23766. return lang.tablesort
  23767. case 'borderBack':
  23768. return lang.borderbk
  23769. default:
  23770. return ''
  23771. }
  23772. }
  23773. contextItems.push({
  23774. //todo 修正成自动获取方式
  23775. label: getLabel(),
  23776. className: 'edui-for-' + item.icon,
  23777. subMenu: {
  23778. items: subMenu,
  23779. editor: me
  23780. }
  23781. })
  23782. }
  23783. } else {
  23784. //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法
  23785. if (
  23786. (me.commands[item.cmdName] ||
  23787. UE.commands[item.cmdName] ||
  23788. item.query) &&
  23789. (item.query
  23790. ? item.query.call(me)
  23791. : me.queryCommandState(item.cmdName)) > -1
  23792. ) {
  23793. contextItems.push({
  23794. label: item.label || me.getLang('contextMenu.' + item.cmdName),
  23795. className:
  23796. 'edui-for-' +
  23797. (item.icon ? item.icon : item.cmdName + (item.value || '')),
  23798. onclick: item.exec
  23799. ? function () {
  23800. item.exec.call(me)
  23801. }
  23802. : function () {
  23803. me.execCommand(item.cmdName, item.value)
  23804. }
  23805. })
  23806. }
  23807. }
  23808. })(ti)
  23809. }
  23810. if (contextItems[contextItems.length - 1] == '-') {
  23811. contextItems.pop()
  23812. }
  23813. menu = new UE.ui.Menu({
  23814. items: contextItems,
  23815. className: 'edui-contextmenu',
  23816. editor: me
  23817. })
  23818. menu.render()
  23819. menu.showAt(offset)
  23820. me.fireEvent('aftershowcontextmenu', menu)
  23821. domUtils.preventDefault(evt)
  23822. if (browser.ie) {
  23823. var ieRange
  23824. try {
  23825. ieRange = me.selection.getNative().createRange()
  23826. } catch (e) {
  23827. return
  23828. }
  23829. if (ieRange.item) {
  23830. var range = new dom.Range(me.document)
  23831. range.selectNode(ieRange.item(0)).select(true, true)
  23832. }
  23833. }
  23834. })
  23835. // 添加复制的flash按钮
  23836. me.addListener('aftershowcontextmenu', function (type, menu) {
  23837. if (me.zeroclipboard) {
  23838. var items = menu.items
  23839. for (var key in items) {
  23840. if (items[key].className == 'edui-for-copy') {
  23841. me.zeroclipboard.clip(items[key].getDom())
  23842. }
  23843. }
  23844. }
  23845. })
  23846. }
  23847. // plugins/shortcutmenu.js
  23848. ///import core
  23849. ///commands 弹出菜单
  23850. // commandsName popupmenu
  23851. ///commandsTitle 弹出菜单
  23852. /**
  23853. * 弹出菜单
  23854. * @function
  23855. * @name baidu.editor.plugins.popupmenu
  23856. * @author xuheng
  23857. */
  23858. UE.plugins['shortcutmenu'] = function () {
  23859. var me = this,
  23860. menu,
  23861. items = me.options.shortcutMenu || []
  23862. if (!items.length) {
  23863. return
  23864. }
  23865. me.addListener('contextmenu mouseup', function (type, e) {
  23866. var me = this,
  23867. customEvt = {
  23868. type: type,
  23869. target: e.target || e.srcElement,
  23870. screenX: e.screenX,
  23871. screenY: e.screenY,
  23872. clientX: e.clientX,
  23873. clientY: e.clientY
  23874. }
  23875. setTimeout(function () {
  23876. var rng = me.selection.getRange()
  23877. if (rng.collapsed === false || type == 'contextmenu') {
  23878. if (!menu) {
  23879. menu = new baidu.editor.ui.ShortCutMenu({
  23880. editor: me,
  23881. items: items,
  23882. theme: me.options.theme,
  23883. className: 'edui-shortcutmenu'
  23884. })
  23885. menu.render()
  23886. me.fireEvent('afterrendershortcutmenu', menu)
  23887. }
  23888. menu.show(customEvt, !!UE.plugins['contextmenu'])
  23889. }
  23890. })
  23891. if (type == 'contextmenu') {
  23892. domUtils.preventDefault(e)
  23893. if (browser.ie9below) {
  23894. var ieRange
  23895. try {
  23896. ieRange = me.selection.getNative().createRange()
  23897. } catch (e) {
  23898. return
  23899. }
  23900. if (ieRange.item) {
  23901. var range = new dom.Range(me.document)
  23902. range.selectNode(ieRange.item(0)).select(true, true)
  23903. }
  23904. }
  23905. }
  23906. })
  23907. me.addListener('keydown', function (type) {
  23908. if (type == 'keydown') {
  23909. menu && !menu.isHidden && menu.hide()
  23910. }
  23911. })
  23912. }
  23913. // plugins/basestyle.js
  23914. /**
  23915. * B、I、sub、super命令支持
  23916. * @file
  23917. * @since 1.2.6.1
  23918. */
  23919. UE.plugins['basestyle'] = function () {
  23920. /**
  23921. * 字体加粗
  23922. * @command bold
  23923. * @param { String } cmd 命令字符串
  23924. * @remind 对已加粗的文本内容执行该命令, 将取消加粗
  23925. * @method execCommand
  23926. * @example
  23927. * ```javascript
  23928. * //editor是编辑器实例
  23929. * //对当前选中的文本内容执行加粗操作
  23930. * //第一次执行, 文本内容加粗
  23931. * editor.execCommand( 'bold' );
  23932. *
  23933. * //第二次执行, 文本内容取消加粗
  23934. * editor.execCommand( 'bold' );
  23935. * ```
  23936. */
  23937. /**
  23938. * 字体倾斜
  23939. * @command italic
  23940. * @method execCommand
  23941. * @param { String } cmd 命令字符串
  23942. * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
  23943. * @example
  23944. * ```javascript
  23945. * //editor是编辑器实例
  23946. * //对当前选中的文本内容执行斜体操作
  23947. * //第一次操作, 文本内容将变成斜体
  23948. * editor.execCommand( 'italic' );
  23949. *
  23950. * //再次对同一文本内容执行, 则文本内容将恢复正常
  23951. * editor.execCommand( 'italic' );
  23952. * ```
  23953. */
  23954. /**
  23955. * 下标文本,与“superscript”命令互斥
  23956. * @command subscript
  23957. * @method execCommand
  23958. * @remind 把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
  23959. * @param { String } cmd 命令字符串
  23960. * @example
  23961. * ```javascript
  23962. * //editor是编辑器实例
  23963. * //对当前选中的文本内容执行下标操作
  23964. * //第一次操作, 文本内容将变成下标文本
  23965. * editor.execCommand( 'subscript' );
  23966. *
  23967. * //再次对同一文本内容执行, 则文本内容将恢复正常
  23968. * editor.execCommand( 'subscript' );
  23969. * ```
  23970. */
  23971. /**
  23972. * 上标文本,与“subscript”命令互斥
  23973. * @command superscript
  23974. * @method execCommand
  23975. * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
  23976. * @param { String } cmd 命令字符串
  23977. * @example
  23978. * ```javascript
  23979. * //editor是编辑器实例
  23980. * //对当前选中的文本内容执行上标操作
  23981. * //第一次操作, 文本内容将变成上标文本
  23982. * editor.execCommand( 'superscript' );
  23983. *
  23984. * //再次对同一文本内容执行, 则文本内容将恢复正常
  23985. * editor.execCommand( 'superscript' );
  23986. * ```
  23987. */
  23988. var basestyles = {
  23989. bold: ['strong', 'b'],
  23990. italic: ['em', 'i'],
  23991. subscript: ['sub'],
  23992. superscript: ['sup']
  23993. },
  23994. getObj = function (editor, tagNames) {
  23995. return domUtils.filterNodeList(
  23996. editor.selection.getStartElementPath(),
  23997. tagNames
  23998. )
  23999. },
  24000. me = this
  24001. //添加快捷键
  24002. me.addshortcutkey({
  24003. Bold: 'ctrl+66', //^B
  24004. Italic: 'ctrl+73', //^I
  24005. Underline: 'ctrl+85' //^U
  24006. })
  24007. me.addInputRule(function (root) {
  24008. utils.each(root.getNodesByTagName('b i'), function (node) {
  24009. switch (node.tagName) {
  24010. case 'b':
  24011. node.tagName = 'strong'
  24012. break
  24013. case 'i':
  24014. node.tagName = 'em'
  24015. }
  24016. })
  24017. })
  24018. for (var style in basestyles) {
  24019. ;(function (cmd, tagNames) {
  24020. me.commands[cmd] = {
  24021. execCommand: function (cmdName) {
  24022. var range = me.selection.getRange(),
  24023. obj = getObj(this, tagNames)
  24024. if (range.collapsed) {
  24025. if (obj) {
  24026. var tmpText = me.document.createTextNode('')
  24027. range.insertNode(tmpText).removeInlineStyle(tagNames)
  24028. range.setStartBefore(tmpText)
  24029. domUtils.remove(tmpText)
  24030. } else {
  24031. var tmpNode = range.document.createElement(tagNames[0])
  24032. if (cmdName == 'superscript' || cmdName == 'subscript') {
  24033. tmpText = me.document.createTextNode('')
  24034. range
  24035. .insertNode(tmpText)
  24036. .removeInlineStyle(['sub', 'sup'])
  24037. .setStartBefore(tmpText)
  24038. .collapse(true)
  24039. }
  24040. range.insertNode(tmpNode).setStart(tmpNode, 0)
  24041. }
  24042. range.collapse(true)
  24043. } else {
  24044. if (cmdName == 'superscript' || cmdName == 'subscript') {
  24045. if (!obj || obj.tagName.toLowerCase() != cmdName) {
  24046. range.removeInlineStyle(['sub', 'sup'])
  24047. }
  24048. }
  24049. obj
  24050. ? range.removeInlineStyle(tagNames)
  24051. : range.applyInlineStyle(tagNames[0])
  24052. }
  24053. range.select()
  24054. },
  24055. queryCommandState: function () {
  24056. return getObj(this, tagNames) ? 1 : 0
  24057. }
  24058. }
  24059. })(style, basestyles[style])
  24060. }
  24061. }
  24062. // plugins/elementpath.js
  24063. /**
  24064. * 选取路径命令
  24065. * @file
  24066. */
  24067. UE.plugins['elementpath'] = function () {
  24068. var currentLevel,
  24069. tagNames,
  24070. me = this
  24071. me.setOpt('elementPathEnabled', true)
  24072. if (!me.options.elementPathEnabled) {
  24073. return
  24074. }
  24075. me.commands['elementpath'] = {
  24076. execCommand: function (cmdName, level) {
  24077. var start = tagNames[level],
  24078. range = me.selection.getRange()
  24079. currentLevel = level * 1
  24080. range.selectNode(start).select()
  24081. },
  24082. queryCommandValue: function () {
  24083. //产生一个副本,不能修改原来的startElementPath;
  24084. var parents = [].concat(this.selection.getStartElementPath()).reverse(),
  24085. names = []
  24086. tagNames = parents
  24087. for (var i = 0, ci; (ci = parents[i]); i++) {
  24088. if (ci.nodeType == 3) {
  24089. continue
  24090. }
  24091. var name = ci.tagName.toLowerCase()
  24092. if (name == 'img' && ci.getAttribute('anchorname')) {
  24093. name = 'anchor'
  24094. }
  24095. names[i] = name
  24096. if (currentLevel == i) {
  24097. currentLevel = -1
  24098. break
  24099. }
  24100. }
  24101. return names
  24102. }
  24103. }
  24104. }
  24105. // plugins/formatmatch.js
  24106. /**
  24107. * 格式刷,只格式inline的
  24108. * @file
  24109. * @since 1.2.6.1
  24110. */
  24111. /**
  24112. * 格式刷
  24113. * @command formatmatch
  24114. * @method execCommand
  24115. * @remind 该操作不能复制段落格式
  24116. * @param { String } cmd 命令字符串
  24117. * @example
  24118. * ```javascript
  24119. * //editor是编辑器实例
  24120. * //获取格式刷
  24121. * editor.execCommand( 'formatmatch' );
  24122. * ```
  24123. */
  24124. UE.plugins['formatmatch'] = function () {
  24125. var me = this,
  24126. list = [],
  24127. img,
  24128. flag = 0
  24129. me.addListener('reset', function () {
  24130. list = []
  24131. flag = 0
  24132. })
  24133. function addList(type, evt) {
  24134. if (browser.webkit) {
  24135. var target = evt.target.tagName == 'IMG' ? evt.target : null
  24136. }
  24137. function addFormat(range) {
  24138. if (text) {
  24139. range.selectNode(text)
  24140. }
  24141. return range.applyInlineStyle(list[list.length - 1].tagName, null, list)
  24142. }
  24143. me.undoManger && me.undoManger.save()
  24144. var range = me.selection.getRange(),
  24145. imgT = target || range.getClosedNode()
  24146. if (img && imgT && imgT.tagName == 'IMG') {
  24147. //trace:964
  24148. imgT.style.cssText +=
  24149. ';float:' +
  24150. (img.style.cssFloat || img.style.styleFloat || 'none') +
  24151. ';display:' +
  24152. (img.style.display || 'inline')
  24153. img = null
  24154. } else {
  24155. if (!img) {
  24156. var collapsed = range.collapsed
  24157. if (collapsed) {
  24158. var text = me.document.createTextNode('match')
  24159. range.insertNode(text).select()
  24160. }
  24161. me.__hasEnterExecCommand = true
  24162. //不能把block上的属性干掉
  24163. //trace:1553
  24164. var removeFormatAttributes = me.options.removeFormatAttributes
  24165. me.options.removeFormatAttributes = ''
  24166. me.execCommand('removeformat')
  24167. me.options.removeFormatAttributes = removeFormatAttributes
  24168. me.__hasEnterExecCommand = false
  24169. //trace:969
  24170. range = me.selection.getRange()
  24171. if (list.length) {
  24172. addFormat(range)
  24173. }
  24174. if (text) {
  24175. range.setStartBefore(text).collapse(true)
  24176. }
  24177. range.select()
  24178. text && domUtils.remove(text)
  24179. }
  24180. }
  24181. me.undoManger && me.undoManger.save()
  24182. me.removeListener('mouseup', addList)
  24183. flag = 0
  24184. }
  24185. me.commands['formatmatch'] = {
  24186. execCommand: function (cmdName) {
  24187. if (flag) {
  24188. flag = 0
  24189. list = []
  24190. me.removeListener('mouseup', addList)
  24191. return
  24192. }
  24193. var range = me.selection.getRange()
  24194. img = range.getClosedNode()
  24195. if (!img || img.tagName != 'IMG') {
  24196. range.collapse(true).shrinkBoundary()
  24197. var start = range.startContainer
  24198. list = domUtils.findParents(start, true, function (node) {
  24199. return !domUtils.isBlockElm(node) && node.nodeType == 1
  24200. })
  24201. //a不能加入格式刷, 并且克隆节点
  24202. for (var i = 0, ci; (ci = list[i]); i++) {
  24203. if (ci.tagName == 'A') {
  24204. list.splice(i, 1)
  24205. break
  24206. }
  24207. }
  24208. }
  24209. me.addListener('mouseup', addList)
  24210. flag = 1
  24211. },
  24212. queryCommandState: function () {
  24213. return flag
  24214. },
  24215. notNeedUndo: 1
  24216. }
  24217. }
  24218. // plugins/searchreplace.js
  24219. ///import core
  24220. ///commands 查找替换
  24221. ///commandsName SearchReplace
  24222. ///commandsTitle 查询替换
  24223. ///commandsDialog dialogs\searchreplace
  24224. /**
  24225. * @description 查找替换
  24226. * @author zhanyi
  24227. */
  24228. UE.plugin.register('searchreplace', function () {
  24229. var me = this
  24230. var _blockElm = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 }
  24231. function findTextInString(textContent, opt, currentIndex) {
  24232. var str = opt.searchStr
  24233. if (opt.dir == -1) {
  24234. textContent = textContent.split('').reverse().join('')
  24235. str = str.split('').reverse().join('')
  24236. currentIndex = textContent.length - currentIndex
  24237. }
  24238. var reg = new RegExp(str, 'g' + (opt.casesensitive ? '' : 'i')),
  24239. match
  24240. while ((match = reg.exec(textContent))) {
  24241. if (match.index >= currentIndex) {
  24242. return opt.dir == -1
  24243. ? textContent.length - match.index - opt.searchStr.length
  24244. : match.index
  24245. }
  24246. }
  24247. return -1
  24248. }
  24249. function findTextBlockElm(node, currentIndex, opt) {
  24250. var textContent,
  24251. index,
  24252. methodName =
  24253. opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode'
  24254. if (domUtils.isBody(node)) {
  24255. node = node.firstChild
  24256. }
  24257. var first = 1
  24258. while (node) {
  24259. textContent =
  24260. node.nodeType == 3
  24261. ? node.nodeValue
  24262. : node[browser.ie ? 'innerText' : 'textContent']
  24263. index = findTextInString(textContent, opt, currentIndex)
  24264. first = 0
  24265. if (index != -1) {
  24266. return {
  24267. node: node,
  24268. index: index
  24269. }
  24270. }
  24271. node = domUtils[methodName](node)
  24272. while (node && _blockElm[node.nodeName.toLowerCase()]) {
  24273. node = domUtils[methodName](node, true)
  24274. }
  24275. if (node) {
  24276. currentIndex =
  24277. opt.dir == -1
  24278. ? (node.nodeType == 3
  24279. ? node.nodeValue
  24280. : node[browser.ie ? 'innerText' : 'textContent']
  24281. ).length
  24282. : 0
  24283. }
  24284. }
  24285. }
  24286. function findNTextInBlockElm(node, index, str) {
  24287. var currentIndex = 0,
  24288. currentNode = node.firstChild,
  24289. currentNodeLength = 0,
  24290. result
  24291. while (currentNode) {
  24292. if (currentNode.nodeType == 3) {
  24293. currentNodeLength = currentNode.nodeValue.replace(
  24294. /(^[\t\r\n]+)|([\t\r\n]+$)/,
  24295. ''
  24296. ).length
  24297. currentIndex += currentNodeLength
  24298. if (currentIndex >= index) {
  24299. return {
  24300. node: currentNode,
  24301. index: currentNodeLength - (currentIndex - index)
  24302. }
  24303. }
  24304. } else if (!dtd.$empty[currentNode.tagName]) {
  24305. currentNodeLength = currentNode[
  24306. browser.ie ? 'innerText' : 'textContent'
  24307. ].replace(/(^[\t\r\n]+)|([\t\r\n]+$)/, '').length
  24308. currentIndex += currentNodeLength
  24309. if (currentIndex >= index) {
  24310. result = findNTextInBlockElm(
  24311. currentNode,
  24312. currentNodeLength - (currentIndex - index),
  24313. str
  24314. )
  24315. if (result) {
  24316. return result
  24317. }
  24318. }
  24319. }
  24320. currentNode = domUtils.getNextDomNode(currentNode)
  24321. }
  24322. }
  24323. function searchReplace(me, opt) {
  24324. var rng = me.selection.getRange(),
  24325. startBlockNode,
  24326. searchStr = opt.searchStr,
  24327. span = me.document.createElement('span')
  24328. span.innerHTML = '$$ueditor_searchreplace_key$$'
  24329. rng.shrinkBoundary(true)
  24330. //判断是不是第一次选中
  24331. if (!rng.collapsed) {
  24332. rng.select()
  24333. var rngText = me.selection.getText()
  24334. if (
  24335. new RegExp(
  24336. '^' + opt.searchStr + '$',
  24337. opt.casesensitive ? '' : 'i'
  24338. ).test(rngText)
  24339. ) {
  24340. if (opt.replaceStr != undefined) {
  24341. replaceText(rng, opt.replaceStr)
  24342. rng.select()
  24343. return true
  24344. } else {
  24345. rng.collapse(opt.dir == -1)
  24346. }
  24347. }
  24348. }
  24349. rng.insertNode(span)
  24350. rng.enlargeToBlockElm(true)
  24351. startBlockNode = rng.startContainer
  24352. var currentIndex = startBlockNode[
  24353. browser.ie ? 'innerText' : 'textContent'
  24354. ].indexOf('$$ueditor_searchreplace_key$$')
  24355. rng.setStartBefore(span)
  24356. domUtils.remove(span)
  24357. var result = findTextBlockElm(startBlockNode, currentIndex, opt)
  24358. if (result) {
  24359. var rngStart = findNTextInBlockElm(result.node, result.index, searchStr)
  24360. var rngEnd = findNTextInBlockElm(
  24361. result.node,
  24362. result.index + searchStr.length,
  24363. searchStr
  24364. )
  24365. rng
  24366. .setStart(rngStart.node, rngStart.index)
  24367. .setEnd(rngEnd.node, rngEnd.index)
  24368. if (opt.replaceStr !== undefined) {
  24369. replaceText(rng, opt.replaceStr)
  24370. }
  24371. rng.select()
  24372. return true
  24373. } else {
  24374. rng.setCursor()
  24375. }
  24376. }
  24377. function replaceText(rng, str) {
  24378. str = me.document.createTextNode(str)
  24379. rng.deleteContents().insertNode(str)
  24380. }
  24381. return {
  24382. commands: {
  24383. searchreplace: {
  24384. execCommand: function (cmdName, opt) {
  24385. utils.extend(
  24386. opt,
  24387. {
  24388. all: false,
  24389. casesensitive: false,
  24390. dir: 1
  24391. },
  24392. true
  24393. )
  24394. var num = 0
  24395. if (opt.all) {
  24396. var rng = me.selection.getRange(),
  24397. first = me.body.firstChild
  24398. if (first && first.nodeType == 1) {
  24399. rng.setStart(first, 0)
  24400. rng.shrinkBoundary(true)
  24401. } else if (first.nodeType == 3) {
  24402. rng.setStartBefore(first)
  24403. }
  24404. rng.collapse(true).select(true)
  24405. if (opt.replaceStr !== undefined) {
  24406. me.fireEvent('saveScene')
  24407. }
  24408. while (searchReplace(this, opt)) {
  24409. num++
  24410. }
  24411. if (num) {
  24412. me.fireEvent('saveScene')
  24413. }
  24414. } else {
  24415. if (opt.replaceStr !== undefined) {
  24416. me.fireEvent('saveScene')
  24417. }
  24418. if (searchReplace(this, opt)) {
  24419. num++
  24420. }
  24421. if (num) {
  24422. me.fireEvent('saveScene')
  24423. }
  24424. }
  24425. return num
  24426. },
  24427. notNeedUndo: 1
  24428. }
  24429. }
  24430. }
  24431. })
  24432. // plugins/customstyle.js
  24433. /**
  24434. * 自定义样式
  24435. * @file
  24436. * @since 1.2.6.1
  24437. */
  24438. /**
  24439. * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。
  24440. * @command customstyle
  24441. * @method execCommand
  24442. * @param { String } cmd 命令字符串
  24443. * @example
  24444. * ```javascript
  24445. * editor.execCommand( 'customstyle' );
  24446. * ```
  24447. */
  24448. UE.plugins['customstyle'] = function () {
  24449. var me = this
  24450. me.setOpt({
  24451. customstyle: [
  24452. {
  24453. tag: 'h1',
  24454. name: 'tc',
  24455. style:
  24456. 'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'
  24457. },
  24458. {
  24459. tag: 'h1',
  24460. name: 'tl',
  24461. style:
  24462. 'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'
  24463. },
  24464. {
  24465. tag: 'span',
  24466. name: 'im',
  24467. style:
  24468. 'font-size:16px;font-style:italic;font-weight:bold;line-height:18px;'
  24469. },
  24470. {
  24471. tag: 'span',
  24472. name: 'hi',
  24473. style:
  24474. 'font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'
  24475. }
  24476. ]
  24477. })
  24478. me.commands['customstyle'] = {
  24479. execCommand: function (cmdName, obj) {
  24480. var me = this,
  24481. tagName = obj.tag,
  24482. node = domUtils.findParent(
  24483. me.selection.getStart(),
  24484. function (node) {
  24485. return node.getAttribute('label')
  24486. },
  24487. true
  24488. ),
  24489. range,
  24490. bk,
  24491. tmpObj = {}
  24492. for (var p in obj) {
  24493. if (obj[p] !== undefined) tmpObj[p] = obj[p]
  24494. }
  24495. delete tmpObj.tag
  24496. if (node && node.getAttribute('label') == obj.label) {
  24497. range = this.selection.getRange()
  24498. bk = range.createBookmark()
  24499. if (range.collapsed) {
  24500. //trace:1732 删掉自定义标签,要有p来回填站位
  24501. if (dtd.$block[node.tagName]) {
  24502. var fillNode = me.document.createElement('p')
  24503. domUtils.moveChild(node, fillNode)
  24504. node.parentNode.insertBefore(fillNode, node)
  24505. domUtils.remove(node)
  24506. } else {
  24507. domUtils.remove(node, true)
  24508. }
  24509. } else {
  24510. var common = domUtils.getCommonAncestor(bk.start, bk.end),
  24511. nodes = domUtils.getElementsByTagName(common, tagName)
  24512. if (new RegExp(tagName, 'i').test(common.tagName)) {
  24513. nodes.push(common)
  24514. }
  24515. for (var i = 0, ni; (ni = nodes[i++]); ) {
  24516. if (ni.getAttribute('label') == obj.label) {
  24517. var ps = domUtils.getPosition(ni, bk.start),
  24518. pe = domUtils.getPosition(ni, bk.end)
  24519. if (
  24520. (ps & domUtils.POSITION_FOLLOWING ||
  24521. ps & domUtils.POSITION_CONTAINS) &&
  24522. (pe & domUtils.POSITION_PRECEDING ||
  24523. pe & domUtils.POSITION_CONTAINS)
  24524. )
  24525. if (dtd.$block[tagName]) {
  24526. var fillNode = me.document.createElement('p')
  24527. domUtils.moveChild(ni, fillNode)
  24528. ni.parentNode.insertBefore(fillNode, ni)
  24529. }
  24530. domUtils.remove(ni, true)
  24531. }
  24532. }
  24533. node = domUtils.findParent(
  24534. common,
  24535. function (node) {
  24536. return node.getAttribute('label') == obj.label
  24537. },
  24538. true
  24539. )
  24540. if (node) {
  24541. domUtils.remove(node, true)
  24542. }
  24543. }
  24544. range.moveToBookmark(bk).select()
  24545. } else {
  24546. if (dtd.$block[tagName]) {
  24547. this.execCommand('paragraph', tagName, tmpObj, 'customstyle')
  24548. range = me.selection.getRange()
  24549. if (!range.collapsed) {
  24550. range.collapse()
  24551. node = domUtils.findParent(
  24552. me.selection.getStart(),
  24553. function (node) {
  24554. return node.getAttribute('label') == obj.label
  24555. },
  24556. true
  24557. )
  24558. var pNode = me.document.createElement('p')
  24559. domUtils.insertAfter(node, pNode)
  24560. domUtils.fillNode(me.document, pNode)
  24561. range.setStart(pNode, 0).setCursor()
  24562. }
  24563. } else {
  24564. range = me.selection.getRange()
  24565. if (range.collapsed) {
  24566. node = me.document.createElement(tagName)
  24567. domUtils.setAttributes(node, tmpObj)
  24568. range.insertNode(node).setStart(node, 0).setCursor()
  24569. return
  24570. }
  24571. bk = range.createBookmark()
  24572. range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select()
  24573. }
  24574. }
  24575. },
  24576. queryCommandValue: function () {
  24577. var parent = domUtils.filterNodeList(
  24578. this.selection.getStartElementPath(),
  24579. function (node) {
  24580. return node.getAttribute('label')
  24581. }
  24582. )
  24583. return parent ? parent.getAttribute('label') : ''
  24584. }
  24585. }
  24586. //当去掉customstyle是,如果是块元素,用p代替
  24587. me.addListener('keyup', function (type, evt) {
  24588. var keyCode = evt.keyCode || evt.which
  24589. if (keyCode == 32 || keyCode == 13) {
  24590. var range = me.selection.getRange()
  24591. if (range.collapsed) {
  24592. var node = domUtils.findParent(
  24593. me.selection.getStart(),
  24594. function (node) {
  24595. return node.getAttribute('label')
  24596. },
  24597. true
  24598. )
  24599. if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
  24600. var p = me.document.createElement('p')
  24601. domUtils.insertAfter(node, p)
  24602. domUtils.fillNode(me.document, p)
  24603. domUtils.remove(node)
  24604. range.setStart(p, 0).setCursor()
  24605. }
  24606. }
  24607. }
  24608. })
  24609. }
  24610. // plugins/catchremoteimage.js
  24611. ///import core
  24612. ///commands 远程图片抓取
  24613. ///commandsName catchRemoteImage,catchremoteimageenable
  24614. ///commandsTitle 远程图片抓取
  24615. /**
  24616. * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片
  24617. */
  24618. UE.plugins['catchremoteimage'] = function () {
  24619. var me = this,
  24620. ajax = UE.ajax
  24621. /* 设置默认值 */
  24622. if (me.options.catchRemoteImageEnable === false) return
  24623. me.setOpt({
  24624. catchRemoteImageEnable: false
  24625. })
  24626. me.addListener('afterpaste', function () {
  24627. me.fireEvent('catchRemoteImage')
  24628. })
  24629. me.addListener('catchRemoteImage', function () {
  24630. var catcherLocalDomain = me.getOpt('catcherLocalDomain'),
  24631. catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')),
  24632. catcherUrlPrefix = me.getOpt('catcherUrlPrefix'),
  24633. catcherFieldName = me.getOpt('catcherFieldName')
  24634. var remoteImages = [],
  24635. imgs = domUtils.getElementsByTagName(me.document, 'img'),
  24636. test = function (src, urls) {
  24637. if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {
  24638. return true
  24639. }
  24640. if (urls) {
  24641. for (var j = 0, url; (url = urls[j++]); ) {
  24642. if (src.indexOf(url) !== -1) {
  24643. return true
  24644. }
  24645. }
  24646. }
  24647. return false
  24648. }
  24649. for (var i = 0, ci; (ci = imgs[i++]); ) {
  24650. if (ci.getAttribute('word_img')) {
  24651. continue
  24652. }
  24653. var src = ci.getAttribute('_src') || ci.src || ''
  24654. if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
  24655. remoteImages.push(src)
  24656. }
  24657. }
  24658. if (remoteImages.length) {
  24659. catchremoteimage(remoteImages, {
  24660. //成功抓取
  24661. success: function (r) {
  24662. try {
  24663. var info =
  24664. r.state !== undefined ? r : eval('(' + r.responseText + ')')
  24665. } catch (e) {
  24666. return
  24667. }
  24668. /* 获取源路径和新路径 */
  24669. var i,
  24670. j,
  24671. ci,
  24672. cj,
  24673. oldSrc,
  24674. newSrc,
  24675. list = info.list
  24676. for (i = 0; (ci = imgs[i++]); ) {
  24677. oldSrc = ci.getAttribute('_src') || ci.src || ''
  24678. for (j = 0; (cj = list[j++]); ) {
  24679. if (oldSrc == cj.source && cj.state == 'SUCCESS') {
  24680. //抓取失败时不做替换处理
  24681. newSrc = catcherUrlPrefix + cj.url
  24682. domUtils.setAttributes(ci, {
  24683. src: newSrc,
  24684. _src: newSrc
  24685. })
  24686. break
  24687. }
  24688. }
  24689. }
  24690. me.fireEvent('catchremotesuccess')
  24691. },
  24692. //回调失败,本次请求超时
  24693. error: function () {
  24694. me.fireEvent('catchremoteerror')
  24695. }
  24696. })
  24697. }
  24698. function catchremoteimage(imgs, callbacks) {
  24699. var params =
  24700. utils.serializeParam(me.queryCommandValue('serverparam')) || '',
  24701. url = utils.formatUrl(
  24702. catcherActionUrl +
  24703. (catcherActionUrl.indexOf('?') == -1 ? '?' : '&') +
  24704. params
  24705. ),
  24706. isJsonp = utils.isCrossDomainUrl(url),
  24707. opt = {
  24708. method: 'POST',
  24709. dataType: isJsonp ? 'jsonp' : '',
  24710. timeout: 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值
  24711. onsuccess: callbacks['success'],
  24712. onerror: callbacks['error']
  24713. }
  24714. opt[catcherFieldName] = imgs
  24715. ajax.request(url, opt)
  24716. }
  24717. })
  24718. }
  24719. // plugins/snapscreen.js
  24720. /**
  24721. * 截屏插件,为UEditor提供插入支持
  24722. * @file
  24723. * @since 1.4.2
  24724. */
  24725. UE.plugin.register('snapscreen', function () {
  24726. var me = this
  24727. var snapplugin
  24728. function getLocation(url) {
  24729. var search,
  24730. a = document.createElement('a'),
  24731. params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
  24732. a.href = url
  24733. if (browser.ie) {
  24734. a.href = a.href
  24735. }
  24736. search = a.search
  24737. if (params) {
  24738. search = search + (search.indexOf('?') == -1 ? '?' : '&') + params
  24739. search = search.replace(/[&]+/gi, '&')
  24740. }
  24741. return {
  24742. port: a.port,
  24743. hostname: a.hostname,
  24744. path: a.pathname + search || +a.hash
  24745. }
  24746. }
  24747. return {
  24748. commands: {
  24749. /**
  24750. * 字体背景颜色
  24751. * @command snapscreen
  24752. * @method execCommand
  24753. * @param { String } cmd 命令字符串
  24754. * @example
  24755. * ```javascript
  24756. * editor.execCommand('snapscreen');
  24757. * ```
  24758. */
  24759. snapscreen: {
  24760. execCommand: function (cmd) {
  24761. var url, local, res
  24762. var lang = me.getLang('snapScreen_plugin')
  24763. if (!snapplugin) {
  24764. var container = me.container
  24765. var doc = me.container.ownerDocument || me.container.document
  24766. snapplugin = doc.createElement('object')
  24767. try {
  24768. snapplugin.type = 'application/x-pluginbaidusnap'
  24769. } catch (e) {
  24770. return
  24771. }
  24772. snapplugin.style.cssText =
  24773. 'position:absolute;left:-9999px;width:0;height:0;'
  24774. snapplugin.setAttribute('width', '0')
  24775. snapplugin.setAttribute('height', '0')
  24776. container.appendChild(snapplugin)
  24777. }
  24778. function onSuccess(rs) {
  24779. try {
  24780. rs = eval('(' + rs + ')')
  24781. if (rs.state == 'SUCCESS') {
  24782. var opt = me.options
  24783. me.execCommand('insertimage', {
  24784. src: opt.snapscreenUrlPrefix + rs.url,
  24785. _src: opt.snapscreenUrlPrefix + rs.url,
  24786. alt: rs.title || '',
  24787. floatStyle: opt.snapscreenImgAlign
  24788. })
  24789. } else {
  24790. alert(rs.state)
  24791. }
  24792. } catch (e) {
  24793. alert(lang.callBackErrorMsg)
  24794. }
  24795. }
  24796. url = me.getActionUrl(me.getOpt('snapscreenActionName'))
  24797. local = getLocation(url)
  24798. setTimeout(function () {
  24799. try {
  24800. res = snapplugin.saveSnapshot(
  24801. local.hostname,
  24802. local.path,
  24803. local.port
  24804. )
  24805. } catch (e) {
  24806. me.ui._dialogs['snapscreenDialog'].open()
  24807. return
  24808. }
  24809. onSuccess(res)
  24810. }, 50)
  24811. },
  24812. queryCommandState: function () {
  24813. return navigator.userAgent.indexOf('Windows', 0) != -1 ? 0 : -1
  24814. }
  24815. }
  24816. }
  24817. }
  24818. })
  24819. // plugins/insertparagraph.js
  24820. /**
  24821. * 插入段落
  24822. * @file
  24823. * @since 1.2.6.1
  24824. */
  24825. /**
  24826. * 插入段落
  24827. * @command insertparagraph
  24828. * @method execCommand
  24829. * @param { String } cmd 命令字符串
  24830. * @example
  24831. * ```javascript
  24832. * //editor是编辑器实例
  24833. * editor.execCommand( 'insertparagraph' );
  24834. * ```
  24835. */
  24836. UE.commands['insertparagraph'] = {
  24837. execCommand: function (cmdName, front) {
  24838. var me = this,
  24839. range = me.selection.getRange(),
  24840. start = range.startContainer,
  24841. tmpNode
  24842. while (start) {
  24843. if (domUtils.isBody(start)) {
  24844. break
  24845. }
  24846. tmpNode = start
  24847. start = start.parentNode
  24848. }
  24849. if (tmpNode) {
  24850. var p = me.document.createElement('p')
  24851. if (front) {
  24852. tmpNode.parentNode.insertBefore(p, tmpNode)
  24853. } else {
  24854. tmpNode.parentNode.insertBefore(p, tmpNode.nextSibling)
  24855. }
  24856. domUtils.fillNode(me.document, p)
  24857. range.setStart(p, 0).setCursor(false, true)
  24858. }
  24859. }
  24860. }
  24861. // plugins/webapp.js
  24862. /**
  24863. * 百度应用
  24864. * @file
  24865. * @since 1.2.6.1
  24866. */
  24867. /**
  24868. * 插入百度应用
  24869. * @command webapp
  24870. * @method execCommand
  24871. * @remind 需要百度APPKey
  24872. * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
  24873. * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
  24874. * height=>应用容器高度,logo=>应用logo,url=>应用地址
  24875. * @example
  24876. * ```javascript
  24877. * //editor是编辑器实例
  24878. * //在编辑器里插入一个“植物大战僵尸”的APP
  24879. * editor.execCommand( 'webapp' , {
  24880. * title: '植物大战僵尸',
  24881. * width: 560,
  24882. * height: 465,
  24883. * logo: '应用展示的图片',
  24884. * url: '百度应用的地址'
  24885. * } );
  24886. * ```
  24887. */
  24888. //UE.plugins['webapp'] = function () {
  24889. // var me = this;
  24890. // function createInsertStr( obj, toIframe, addParagraph ) {
  24891. // return !toIframe ?
  24892. // (addParagraph ? '<p>' : '') + '<img title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '"' +
  24893. // ' src="' + me.options.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif" style="background:url(' + obj.logo+') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' + obj.url + '" />' +
  24894. // (addParagraph ? '</p>' : '')
  24895. // :
  24896. // '<iframe class="edui-faked-webapp" title="'+obj.title+'" width="' + obj.width + '" height="' + obj.height + '" scrolling="no" frameborder="0" src="' + obj.url + '" logo_url = '+obj.logo+'></iframe>';
  24897. // }
  24898. //
  24899. // function switchImgAndIframe( img2frame ) {
  24900. // var tmpdiv,
  24901. // nodes = domUtils.getElementsByTagName( me.document, !img2frame ? "iframe" : "img" );
  24902. // for ( var i = 0, node; node = nodes[i++]; ) {
  24903. // if ( node.className != "edui-faked-webapp" ){
  24904. // continue;
  24905. // }
  24906. // tmpdiv = me.document.createElement( "div" );
  24907. // tmpdiv.innerHTML = createInsertStr( img2frame ? {url:node.getAttribute( "_url" ), width:node.width, height:node.height,title:node.title,logo:node.style.backgroundImage.replace("url(","").replace(")","")} : {url:node.getAttribute( "src", 2 ),title:node.title, width:node.width, height:node.height,logo:node.getAttribute("logo_url")}, img2frame ? true : false,false );
  24908. // node.parentNode.replaceChild( tmpdiv.firstChild, node );
  24909. // }
  24910. // }
  24911. //
  24912. // me.addListener( "beforegetcontent", function () {
  24913. // switchImgAndIframe( true );
  24914. // } );
  24915. // me.addListener( 'aftersetcontent', function () {
  24916. // switchImgAndIframe( false );
  24917. // } );
  24918. // me.addListener( 'aftergetcontent', function ( cmdName ) {
  24919. // if ( cmdName == 'aftergetcontent' && me.queryCommandState( 'source' ) ){
  24920. // return;
  24921. // }
  24922. // switchImgAndIframe( false );
  24923. // } );
  24924. //
  24925. // me.commands['webapp'] = {
  24926. // execCommand:function ( cmd, obj ) {
  24927. // me.execCommand( "inserthtml", createInsertStr( obj, false,true ) );
  24928. // }
  24929. // };
  24930. //};
  24931. UE.plugin.register('webapp', function () {
  24932. var me = this
  24933. function createInsertStr(obj, toEmbed) {
  24934. return !toEmbed
  24935. ? '<img title="' +
  24936. obj.title +
  24937. '" width="' +
  24938. obj.width +
  24939. '" height="' +
  24940. obj.height +
  24941. '"' +
  24942. ' src="' +
  24943. me.options.UEDITOR_HOME_URL +
  24944. 'themes/default/images/spacer.gif" _logo_url="' +
  24945. obj.logo +
  24946. '" style="background:url(' +
  24947. obj.logo +
  24948. ') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' +
  24949. obj.url +
  24950. '" ' +
  24951. (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
  24952. (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
  24953. '/>'
  24954. : '<iframe class="edui-faked-webapp" title="' +
  24955. obj.title +
  24956. '" ' +
  24957. (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
  24958. (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
  24959. 'width="' +
  24960. obj.width +
  24961. '" height="' +
  24962. obj.height +
  24963. '" scrolling="no" frameborder="0" src="' +
  24964. obj.url +
  24965. '" logo_url = "' +
  24966. obj.logo +
  24967. '"></iframe>'
  24968. }
  24969. return {
  24970. outputRule: function (root) {
  24971. utils.each(root.getNodesByTagName('img'), function (node) {
  24972. var html
  24973. if (node.getAttr('class') == 'edui-faked-webapp') {
  24974. html = createInsertStr(
  24975. {
  24976. title: node.getAttr('title'),
  24977. width: node.getAttr('width'),
  24978. height: node.getAttr('height'),
  24979. align: node.getAttr('align'),
  24980. cssfloat: node.getStyle('float'),
  24981. url: node.getAttr('_url'),
  24982. logo: node.getAttr('_logo_url')
  24983. },
  24984. true
  24985. )
  24986. var embed = UE.uNode.createElement(html)
  24987. node.parentNode.replaceChild(embed, node)
  24988. }
  24989. })
  24990. },
  24991. inputRule: function (root) {
  24992. utils.each(root.getNodesByTagName('iframe'), function (node) {
  24993. if (node.getAttr('class') == 'edui-faked-webapp') {
  24994. var img = UE.uNode.createElement(
  24995. createInsertStr({
  24996. title: node.getAttr('title'),
  24997. width: node.getAttr('width'),
  24998. height: node.getAttr('height'),
  24999. align: node.getAttr('align'),
  25000. cssfloat: node.getStyle('float'),
  25001. url: node.getAttr('src'),
  25002. logo: node.getAttr('logo_url')
  25003. })
  25004. )
  25005. node.parentNode.replaceChild(img, node)
  25006. }
  25007. })
  25008. },
  25009. commands: {
  25010. /**
  25011. * 插入百度应用
  25012. * @command webapp
  25013. * @method execCommand
  25014. * @remind 需要百度APPKey
  25015. * @remind 百度应用主页: <a href="http://app.baidu.com/" target="_blank">http://app.baidu.com/</a>
  25016. * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
  25017. * height=>应用容器高度,logo=>应用logo,url=>应用地址
  25018. * @example
  25019. * ```javascript
  25020. * //editor是编辑器实例
  25021. * //在编辑器里插入一个“植物大战僵尸”的APP
  25022. * editor.execCommand( 'webapp' , {
  25023. * title: '植物大战僵尸',
  25024. * width: 560,
  25025. * height: 465,
  25026. * logo: '应用展示的图片',
  25027. * url: '百度应用的地址'
  25028. * } );
  25029. * ```
  25030. */
  25031. webapp: {
  25032. execCommand: function (cmd, obj) {
  25033. var me = this,
  25034. str = createInsertStr(
  25035. utils.extend(obj, {
  25036. align: 'none'
  25037. }),
  25038. false
  25039. )
  25040. me.execCommand('inserthtml', str)
  25041. },
  25042. queryCommandState: function () {
  25043. var me = this,
  25044. img = me.selection.getRange().getClosedNode(),
  25045. flag = img && img.className == 'edui-faked-webapp'
  25046. return flag ? 1 : 0
  25047. }
  25048. }
  25049. }
  25050. }
  25051. })
  25052. // plugins/template.js
  25053. ///import core
  25054. ///import plugins\inserthtml.js
  25055. ///import plugins\cleardoc.js
  25056. ///commands 模板
  25057. ///commandsName template
  25058. ///commandsTitle 模板
  25059. ///commandsDialog dialogs\template
  25060. UE.plugins['template'] = function () {
  25061. UE.commands['template'] = {
  25062. execCommand: function (cmd, obj) {
  25063. obj.html && this.execCommand('inserthtml', obj.html)
  25064. }
  25065. }
  25066. this.addListener('click', function (type, evt) {
  25067. var el = evt.target || evt.srcElement,
  25068. range = this.selection.getRange()
  25069. var tnode = domUtils.findParent(
  25070. el,
  25071. function (node) {
  25072. if (node.className && domUtils.hasClass(node, 'ue_t')) {
  25073. return node
  25074. }
  25075. },
  25076. true
  25077. )
  25078. tnode && range.selectNode(tnode).shrinkBoundary().select()
  25079. })
  25080. this.addListener('keydown', function (type, evt) {
  25081. var range = this.selection.getRange()
  25082. if (!range.collapsed) {
  25083. if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  25084. var tnode = domUtils.findParent(
  25085. range.startContainer,
  25086. function (node) {
  25087. if (node.className && domUtils.hasClass(node, 'ue_t')) {
  25088. return node
  25089. }
  25090. },
  25091. true
  25092. )
  25093. if (tnode) {
  25094. domUtils.removeClasses(tnode, ['ue_t'])
  25095. }
  25096. }
  25097. }
  25098. })
  25099. }
  25100. // plugins/music.js
  25101. /**
  25102. * 插入音乐命令
  25103. * @file
  25104. */
  25105. UE.plugin.register('music', function () {
  25106. var me = this
  25107. function creatInsertStr(url, width, height, align, cssfloat, toEmbed) {
  25108. return !toEmbed
  25109. ? '<img ' +
  25110. (align && !cssfloat ? 'align="' + align + '"' : '') +
  25111. (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
  25112. ' width="' +
  25113. width +
  25114. '" height="' +
  25115. height +
  25116. '" _url="' +
  25117. url +
  25118. '" class="edui-faked-music"' +
  25119. ' src="' +
  25120. me.options.langPath +
  25121. me.options.lang +
  25122. '/images/music.png" />'
  25123. : '<embed type="application/x-shockwave-flash" class="edui-faked-music" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
  25124. ' src="' +
  25125. url +
  25126. '" width="' +
  25127. width +
  25128. '" height="' +
  25129. height +
  25130. '" ' +
  25131. (align && !cssfloat ? 'align="' + align + '"' : '') +
  25132. (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
  25133. ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >'
  25134. }
  25135. return {
  25136. outputRule: function (root) {
  25137. utils.each(root.getNodesByTagName('img'), function (node) {
  25138. var html
  25139. if (node.getAttr('class') == 'edui-faked-music') {
  25140. var cssfloat = node.getStyle('float')
  25141. var align = node.getAttr('align')
  25142. html = creatInsertStr(
  25143. node.getAttr('_url'),
  25144. node.getAttr('width'),
  25145. node.getAttr('height'),
  25146. align,
  25147. cssfloat,
  25148. true
  25149. )
  25150. var embed = UE.uNode.createElement(html)
  25151. node.parentNode.replaceChild(embed, node)
  25152. }
  25153. })
  25154. },
  25155. inputRule: function (root) {
  25156. utils.each(root.getNodesByTagName('embed'), function (node) {
  25157. if (node.getAttr('class') == 'edui-faked-music') {
  25158. var cssfloat = node.getStyle('float')
  25159. var align = node.getAttr('align')
  25160. html = creatInsertStr(
  25161. node.getAttr('src'),
  25162. node.getAttr('width'),
  25163. node.getAttr('height'),
  25164. align,
  25165. cssfloat,
  25166. false
  25167. )
  25168. var img = UE.uNode.createElement(html)
  25169. node.parentNode.replaceChild(img, node)
  25170. }
  25171. })
  25172. },
  25173. commands: {
  25174. /**
  25175. * 插入音乐
  25176. * @command music
  25177. * @method execCommand
  25178. * @param { Object } musicOptions 插入音乐的参数项, 支持的key有: url=>音乐地址;
  25179. * width=>音乐容器宽度;height=>音乐容器高度;align=>音乐文件的对齐方式, 可选值有: left, center, right, none
  25180. * @example
  25181. * ```javascript
  25182. * //editor是编辑器实例
  25183. * //在编辑器里插入一个“植物大战僵尸”的APP
  25184. * editor.execCommand( 'music' , {
  25185. * width: 400,
  25186. * height: 95,
  25187. * align: "center",
  25188. * url: "音乐地址"
  25189. * } );
  25190. * ```
  25191. */
  25192. music: {
  25193. execCommand: function (cmd, musicObj) {
  25194. var me = this,
  25195. str = creatInsertStr(
  25196. musicObj.url,
  25197. musicObj.width || 400,
  25198. musicObj.height || 95,
  25199. 'none',
  25200. false
  25201. )
  25202. me.execCommand('inserthtml', str)
  25203. },
  25204. queryCommandState: function () {
  25205. var me = this,
  25206. img = me.selection.getRange().getClosedNode(),
  25207. flag = img && img.className == 'edui-faked-music'
  25208. return flag ? 1 : 0
  25209. }
  25210. }
  25211. }
  25212. }
  25213. })
  25214. // plugins/autoupload.js
  25215. /**
  25216. * @description
  25217. * 1.拖放文件到编辑区域,自动上传并插入到选区
  25218. * 2.插入粘贴板的图片,自动上传并插入到选区
  25219. * @author Jinqn
  25220. * @date 2013-10-14
  25221. */
  25222. UE.plugin.register('autoupload', function () {
  25223. function sendAndInsertFile(file, editor) {
  25224. var me = editor
  25225. //模拟数据
  25226. var fieldName,
  25227. urlPrefix,
  25228. maxSize,
  25229. allowFiles,
  25230. actionUrl,
  25231. loadingHtml,
  25232. errorHandler,
  25233. successHandler,
  25234. filetype = /image\/\w+/i.test(file.type) ? 'image' : 'file',
  25235. loadingId = 'loading_' + (+new Date()).toString(36)
  25236. fieldName = me.getOpt(filetype + 'FieldName')
  25237. urlPrefix = me.getOpt(filetype + 'UrlPrefix')
  25238. maxSize = me.getOpt(filetype + 'MaxSize')
  25239. allowFiles = me.getOpt(filetype + 'AllowFiles')
  25240. actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName'))
  25241. errorHandler = function (title) {
  25242. var loader = me.document.getElementById(loadingId)
  25243. loader && domUtils.remove(loader)
  25244. me.fireEvent('showmessage', {
  25245. id: loadingId,
  25246. content: title,
  25247. type: 'error',
  25248. timeout: 4000
  25249. })
  25250. }
  25251. if (filetype == 'image') {
  25252. loadingHtml =
  25253. '<img class="loadingclass" id="' +
  25254. loadingId +
  25255. '" src="' +
  25256. me.options.themePath +
  25257. me.options.theme +
  25258. '/images/spacer.gif" title="' +
  25259. (me.getLang('autoupload.loading') || '') +
  25260. '" >'
  25261. successHandler = function (data) {
  25262. var link = urlPrefix + data.url,
  25263. loader = me.document.getElementById(loadingId)
  25264. if (loader) {
  25265. loader.setAttribute('src', link)
  25266. loader.setAttribute('_src', link)
  25267. loader.setAttribute('title', data.title || '')
  25268. loader.setAttribute('alt', data.original || '')
  25269. loader.removeAttribute('id')
  25270. domUtils.removeClasses(loader, 'loadingclass')
  25271. }
  25272. }
  25273. } else {
  25274. loadingHtml =
  25275. '<p>' +
  25276. '<img class="loadingclass" id="' +
  25277. loadingId +
  25278. '" src="' +
  25279. me.options.themePath +
  25280. me.options.theme +
  25281. '/images/spacer.gif" title="' +
  25282. (me.getLang('autoupload.loading') || '') +
  25283. '" >' +
  25284. '</p>'
  25285. successHandler = function (data) {
  25286. var link = urlPrefix + data.url,
  25287. loader = me.document.getElementById(loadingId)
  25288. var rng = me.selection.getRange(),
  25289. bk = rng.createBookmark()
  25290. rng.selectNode(loader).select()
  25291. me.execCommand('insertfile', { url: link })
  25292. rng.moveToBookmark(bk).select()
  25293. }
  25294. }
  25295. /* 插入loading的占位符 */
  25296. me.execCommand('inserthtml', loadingHtml)
  25297. /* 判断后端配置是否没有加载成功 */
  25298. if (!me.getOpt(filetype + 'ActionName')) {
  25299. errorHandler(me.getLang('autoupload.errorLoadConfig'))
  25300. return
  25301. }
  25302. /* 判断文件大小是否超出限制 */
  25303. if (file.size > maxSize) {
  25304. errorHandler(me.getLang('autoupload.exceedSizeError'))
  25305. return
  25306. }
  25307. /* 判断文件格式是否超出允许 */
  25308. var fileext = file.name
  25309. ? file.name.substr(file.name.lastIndexOf('.'))
  25310. : ''
  25311. if (
  25312. (fileext && filetype != 'image') ||
  25313. (allowFiles &&
  25314. (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') ==
  25315. -1)
  25316. ) {
  25317. errorHandler(me.getLang('autoupload.exceedTypeError'))
  25318. return
  25319. }
  25320. /* 创建Ajax并提交 */
  25321. var xhr = new XMLHttpRequest(),
  25322. fd = new FormData(),
  25323. params =
  25324. utils.serializeParam(me.queryCommandValue('serverparam')) || '',
  25325. url = utils.formatUrl(
  25326. actionUrl + (actionUrl.indexOf('?') == -1 ? '?' : '&') + params
  25327. )
  25328. fd.append(
  25329. fieldName,
  25330. file,
  25331. file.name || 'blob.' + file.type.substr('image/'.length)
  25332. )
  25333. fd.append('type', 'ajax')
  25334. xhr.open('post', url, true)
  25335. xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
  25336. xhr.addEventListener('load', function (e) {
  25337. try {
  25338. var json = new Function('return ' + utils.trim(e.target.response))()
  25339. if (json.state == 'SUCCESS' && json.url) {
  25340. successHandler(json)
  25341. } else {
  25342. errorHandler(json.state)
  25343. }
  25344. } catch (er) {
  25345. errorHandler(me.getLang('autoupload.loadError'))
  25346. }
  25347. })
  25348. xhr.send(fd)
  25349. }
  25350. function getPasteImage(e) {
  25351. return e.clipboardData &&
  25352. e.clipboardData.items &&
  25353. e.clipboardData.items.length == 1 &&
  25354. /^image\//.test(e.clipboardData.items[0].type)
  25355. ? e.clipboardData.items
  25356. : null
  25357. }
  25358. function getDropImage(e) {
  25359. return e.dataTransfer && e.dataTransfer.files
  25360. ? e.dataTransfer.files
  25361. : null
  25362. }
  25363. return {
  25364. outputRule: function (root) {
  25365. utils.each(root.getNodesByTagName('img'), function (n) {
  25366. if (
  25367. /\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))
  25368. ) {
  25369. n.parentNode.removeChild(n)
  25370. }
  25371. })
  25372. utils.each(root.getNodesByTagName('p'), function (n) {
  25373. if (/\bloadpara\b/.test(n.getAttr('class'))) {
  25374. n.parentNode.removeChild(n)
  25375. }
  25376. })
  25377. },
  25378. bindEvents: {
  25379. //插入粘贴板的图片,拖放插入图片
  25380. ready: function (e) {
  25381. var me = this
  25382. if (window.FormData && window.FileReader) {
  25383. domUtils.on(me.body, 'paste drop', function (e) {
  25384. var hasImg = false,
  25385. items
  25386. //获取粘贴板文件列表或者拖放文件列表
  25387. items = e.type == 'paste' ? getPasteImage(e) : getDropImage(e)
  25388. if (items) {
  25389. var len = items.length,
  25390. file
  25391. while (len--) {
  25392. file = items[len]
  25393. if (file.getAsFile) file = file.getAsFile()
  25394. if (file && file.size > 0) {
  25395. sendAndInsertFile(file, me)
  25396. hasImg = true
  25397. }
  25398. }
  25399. hasImg && e.preventDefault()
  25400. }
  25401. })
  25402. //取消拖放图片时出现的文字光标位置提示
  25403. domUtils.on(me.body, 'dragover', function (e) {
  25404. if (e.dataTransfer.types[0] == 'Files') {
  25405. e.preventDefault()
  25406. }
  25407. })
  25408. //设置loading的样式
  25409. utils.cssRule(
  25410. 'loading',
  25411. ".loadingclass{display:inline-block;cursor:default;background: url('" +
  25412. this.options.themePath +
  25413. this.options.theme +
  25414. "/images/loading.gif') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n" +
  25415. ".loaderrorclass{display:inline-block;cursor:default;background: url('" +
  25416. this.options.themePath +
  25417. this.options.theme +
  25418. "/images/loaderror.png') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;" +
  25419. '}',
  25420. this.document
  25421. )
  25422. }
  25423. }
  25424. }
  25425. }
  25426. })
  25427. // plugins/autosave.js
  25428. UE.plugin.register('autosave', function () {
  25429. var me = this,
  25430. //无限循环保护
  25431. lastSaveTime = new Date(),
  25432. //最小保存间隔时间
  25433. MIN_TIME = 20,
  25434. //auto save key
  25435. saveKey = null
  25436. function save(editor) {
  25437. var saveData
  25438. if (new Date() - lastSaveTime < MIN_TIME) {
  25439. return
  25440. }
  25441. if (!editor.hasContents()) {
  25442. //这里不能调用命令来删除, 会造成事件死循环
  25443. saveKey && me.removePreferences(saveKey)
  25444. return
  25445. }
  25446. lastSaveTime = new Date()
  25447. editor._saveFlag = null
  25448. saveData = me.body.innerHTML
  25449. if (
  25450. editor.fireEvent('beforeautosave', {
  25451. content: saveData
  25452. }) === false
  25453. ) {
  25454. return
  25455. }
  25456. me.setPreferences(saveKey, saveData)
  25457. editor.fireEvent('afterautosave', {
  25458. content: saveData
  25459. })
  25460. }
  25461. return {
  25462. defaultOptions: {
  25463. //默认间隔时间
  25464. saveInterval: 500,
  25465. enableAutoSave: true // HaoChuan9421
  25466. },
  25467. bindEvents: {
  25468. ready: function () {
  25469. var _suffix = '-drafts-data',
  25470. key = null
  25471. if (me.key) {
  25472. key = me.key + _suffix
  25473. } else {
  25474. key = (me.container.parentNode.id || 'ue-common') + _suffix
  25475. }
  25476. //页面地址+编辑器ID 保持唯一
  25477. saveKey =
  25478. (location.protocol + location.host + location.pathname).replace(
  25479. /[.:\/]/g,
  25480. '_'
  25481. ) + key
  25482. },
  25483. contentchange: function () {
  25484. // HaoChuan9421
  25485. if (!me.getOpt('enableAutoSave')) {
  25486. return
  25487. }
  25488. if (!saveKey) {
  25489. return
  25490. }
  25491. if (me._saveFlag) {
  25492. window.clearTimeout(me._saveFlag)
  25493. }
  25494. if (me.options.saveInterval > 0) {
  25495. me._saveFlag = window.setTimeout(function () {
  25496. save(me)
  25497. }, me.options.saveInterval)
  25498. } else {
  25499. save(me)
  25500. }
  25501. }
  25502. },
  25503. commands: {
  25504. clearlocaldata: {
  25505. execCommand: function (cmd, name) {
  25506. if (saveKey && me.getPreferences(saveKey)) {
  25507. me.removePreferences(saveKey)
  25508. }
  25509. },
  25510. notNeedUndo: true,
  25511. ignoreContentChange: true
  25512. },
  25513. getlocaldata: {
  25514. execCommand: function (cmd, name) {
  25515. return saveKey ? me.getPreferences(saveKey) || '' : ''
  25516. },
  25517. notNeedUndo: true,
  25518. ignoreContentChange: true
  25519. },
  25520. drafts: {
  25521. execCommand: function (cmd, name) {
  25522. if (saveKey) {
  25523. me.body.innerHTML =
  25524. me.getPreferences(saveKey) || '<p>' + domUtils.fillHtml + '</p>'
  25525. me.focus(true)
  25526. }
  25527. },
  25528. queryCommandState: function () {
  25529. return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1
  25530. },
  25531. notNeedUndo: true,
  25532. ignoreContentChange: true
  25533. }
  25534. }
  25535. }
  25536. })
  25537. // plugins/charts.js
  25538. UE.plugin.register('charts', function () {
  25539. var me = this
  25540. return {
  25541. bindEvents: {
  25542. chartserror: function () {}
  25543. },
  25544. commands: {
  25545. charts: {
  25546. execCommand: function (cmd, data) {
  25547. var tableNode = domUtils.findParentByTagName(
  25548. this.selection.getRange().startContainer,
  25549. 'table',
  25550. true
  25551. ),
  25552. flagText = [],
  25553. config = {}
  25554. if (!tableNode) {
  25555. return false
  25556. }
  25557. if (!validData(tableNode)) {
  25558. me.fireEvent('chartserror')
  25559. return false
  25560. }
  25561. config.title = data.title || ''
  25562. config.subTitle = data.subTitle || ''
  25563. config.xTitle = data.xTitle || ''
  25564. config.yTitle = data.yTitle || ''
  25565. config.suffix = data.suffix || ''
  25566. config.tip = data.tip || ''
  25567. //数据对齐方式
  25568. config.dataFormat = data.tableDataFormat || ''
  25569. //图表类型
  25570. config.chartType = data.chartType || 0
  25571. for (var key in config) {
  25572. if (!config.hasOwnProperty(key)) {
  25573. continue
  25574. }
  25575. flagText.push(key + ':' + config[key])
  25576. }
  25577. tableNode.setAttribute('data-chart', flagText.join(';'))
  25578. domUtils.addClass(tableNode, 'edui-charts-table')
  25579. },
  25580. queryCommandState: function (cmd, name) {
  25581. var tableNode = domUtils.findParentByTagName(
  25582. this.selection.getRange().startContainer,
  25583. 'table',
  25584. true
  25585. )
  25586. return tableNode && validData(tableNode) ? 0 : -1
  25587. }
  25588. }
  25589. },
  25590. inputRule: function (root) {
  25591. utils.each(root.getNodesByTagName('table'), function (tableNode) {
  25592. if (tableNode.getAttr('data-chart') !== undefined) {
  25593. tableNode.setAttr('style')
  25594. }
  25595. })
  25596. },
  25597. outputRule: function (root) {
  25598. utils.each(root.getNodesByTagName('table'), function (tableNode) {
  25599. if (tableNode.getAttr('data-chart') !== undefined) {
  25600. tableNode.setAttr('style', 'display: none;')
  25601. }
  25602. })
  25603. }
  25604. }
  25605. function validData(table) {
  25606. var firstRows = null,
  25607. cellCount = 0
  25608. //行数不够
  25609. if (table.rows.length < 2) {
  25610. return false
  25611. }
  25612. //列数不够
  25613. if (table.rows[0].cells.length < 2) {
  25614. return false
  25615. }
  25616. //第一行所有cell必须是th
  25617. firstRows = table.rows[0].cells
  25618. cellCount = firstRows.length
  25619. for (var i = 0, cell; (cell = firstRows[i]); i++) {
  25620. if (cell.tagName.toLowerCase() !== 'th') {
  25621. return false
  25622. }
  25623. }
  25624. for (var i = 1, row; (row = table.rows[i]); i++) {
  25625. //每行单元格数不匹配, 返回false
  25626. if (row.cells.length != cellCount) {
  25627. return false
  25628. }
  25629. //第一列不是th也返回false
  25630. if (row.cells[0].tagName.toLowerCase() !== 'th') {
  25631. return false
  25632. }
  25633. for (var j = 1, cell; (cell = row.cells[j]); j++) {
  25634. var value = utils.trim(cell.innerText || cell.textContent || '')
  25635. value = value
  25636. .replace(new RegExp(UE.dom.domUtils.fillChar, 'g'), '')
  25637. .replace(/^\s+|\s+$/g, '')
  25638. //必须是数字
  25639. if (!/^\d*\.?\d+$/.test(value)) {
  25640. return false
  25641. }
  25642. }
  25643. }
  25644. return true
  25645. }
  25646. })
  25647. // plugins/section.js
  25648. /**
  25649. * 目录大纲支持插件
  25650. * @file
  25651. * @since 1.3.0
  25652. */
  25653. UE.plugin.register('section', function () {
  25654. /* 目录节点对象 */
  25655. function Section(option) {
  25656. this.tag = ''
  25657. ;(this.level = -1), (this.dom = null)
  25658. this.nextSection = null
  25659. this.previousSection = null
  25660. this.parentSection = null
  25661. this.startAddress = []
  25662. this.endAddress = []
  25663. this.children = []
  25664. }
  25665. function getSection(option) {
  25666. var section = new Section()
  25667. return utils.extend(section, option)
  25668. }
  25669. function getNodeFromAddress(startAddress, root) {
  25670. var current = root
  25671. for (var i = 0; i < startAddress.length; i++) {
  25672. if (!current.childNodes) return null
  25673. current = current.childNodes[startAddress[i]]
  25674. }
  25675. return current
  25676. }
  25677. var me = this
  25678. return {
  25679. bindMultiEvents: {
  25680. type: 'aftersetcontent afterscencerestore',
  25681. handler: function () {
  25682. me.fireEvent('updateSections')
  25683. }
  25684. },
  25685. bindEvents: {
  25686. /* 初始化、拖拽、粘贴、执行setcontent之后 */
  25687. ready: function () {
  25688. me.fireEvent('updateSections')
  25689. domUtils.on(me.body, 'drop paste', function () {
  25690. me.fireEvent('updateSections')
  25691. })
  25692. },
  25693. /* 执行paragraph命令之后 */
  25694. afterexeccommand: function (type, cmd) {
  25695. if (cmd == 'paragraph') {
  25696. me.fireEvent('updateSections')
  25697. }
  25698. },
  25699. /* 部分键盘操作,触发updateSections事件 */
  25700. keyup: function (type, e) {
  25701. var me = this,
  25702. range = me.selection.getRange()
  25703. if (range.collapsed != true) {
  25704. me.fireEvent('updateSections')
  25705. } else {
  25706. var keyCode = e.keyCode || e.which
  25707. if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
  25708. me.fireEvent('updateSections')
  25709. }
  25710. }
  25711. }
  25712. },
  25713. commands: {
  25714. getsections: {
  25715. execCommand: function (cmd, levels) {
  25716. var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
  25717. for (var i = 0; i < levelFn.length; i++) {
  25718. if (typeof levelFn[i] == 'string') {
  25719. levelFn[i] = (function (fn) {
  25720. return function (node) {
  25721. return node.tagName == fn.toUpperCase()
  25722. }
  25723. })(levelFn[i])
  25724. } else if (typeof levelFn[i] != 'function') {
  25725. levelFn[i] = function (node) {
  25726. return null
  25727. }
  25728. }
  25729. }
  25730. function getSectionLevel(node) {
  25731. for (var i = 0; i < levelFn.length; i++) {
  25732. if (levelFn[i](node)) return i
  25733. }
  25734. return -1
  25735. }
  25736. var me = this,
  25737. Directory = getSection({ level: -1, title: 'root' }),
  25738. previous = Directory
  25739. function traversal(node, Directory) {
  25740. var level,
  25741. tmpSection = null,
  25742. parent,
  25743. child,
  25744. children = node.childNodes
  25745. for (var i = 0, len = children.length; i < len; i++) {
  25746. child = children[i]
  25747. level = getSectionLevel(child)
  25748. if (level >= 0) {
  25749. var address = me.selection
  25750. .getRange()
  25751. .selectNode(child)
  25752. .createAddress(true).startAddress,
  25753. current = getSection({
  25754. tag: child.tagName,
  25755. title: child.innerText || child.textContent || '',
  25756. level: level,
  25757. dom: child,
  25758. startAddress: utils.clone(address, []),
  25759. endAddress: utils.clone(address, []),
  25760. children: []
  25761. })
  25762. previous.nextSection = current
  25763. current.previousSection = previous
  25764. parent = previous
  25765. while (level <= parent.level) {
  25766. parent = parent.parentSection
  25767. }
  25768. current.parentSection = parent
  25769. parent.children.push(current)
  25770. tmpSection = previous = current
  25771. } else {
  25772. child.nodeType === 1 && traversal(child, Directory)
  25773. tmpSection &&
  25774. tmpSection.endAddress[tmpSection.endAddress.length - 1]++
  25775. }
  25776. }
  25777. }
  25778. traversal(me.body, Directory)
  25779. return Directory
  25780. },
  25781. notNeedUndo: true
  25782. },
  25783. movesection: {
  25784. execCommand: function (cmd, sourceSection, targetSection, isAfter) {
  25785. var me = this,
  25786. targetAddress,
  25787. target
  25788. if (!sourceSection || !targetSection || targetSection.level == -1)
  25789. return
  25790. targetAddress = isAfter
  25791. ? targetSection.endAddress
  25792. : targetSection.startAddress
  25793. target = getNodeFromAddress(targetAddress, me.body)
  25794. /* 判断目标地址是否被源章节包含 */
  25795. if (
  25796. !targetAddress ||
  25797. !target ||
  25798. isContainsAddress(
  25799. sourceSection.startAddress,
  25800. sourceSection.endAddress,
  25801. targetAddress
  25802. )
  25803. )
  25804. return
  25805. var startNode = getNodeFromAddress(
  25806. sourceSection.startAddress,
  25807. me.body
  25808. ),
  25809. endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
  25810. current,
  25811. nextNode
  25812. if (isAfter) {
  25813. current = endNode
  25814. while (
  25815. current &&
  25816. !(
  25817. domUtils.getPosition(startNode, current) &
  25818. domUtils.POSITION_FOLLOWING
  25819. )
  25820. ) {
  25821. nextNode = current.previousSibling
  25822. domUtils.insertAfter(target, current)
  25823. if (current == startNode) break
  25824. current = nextNode
  25825. }
  25826. } else {
  25827. current = startNode
  25828. while (
  25829. current &&
  25830. !(
  25831. domUtils.getPosition(current, endNode) &
  25832. domUtils.POSITION_FOLLOWING
  25833. )
  25834. ) {
  25835. nextNode = current.nextSibling
  25836. target.parentNode.insertBefore(current, target)
  25837. if (current == endNode) break
  25838. current = nextNode
  25839. }
  25840. }
  25841. me.fireEvent('updateSections')
  25842. /* 获取地址的包含关系 */
  25843. function isContainsAddress(
  25844. startAddress,
  25845. endAddress,
  25846. addressTarget
  25847. ) {
  25848. var isAfterStartAddress = false,
  25849. isBeforeEndAddress = false
  25850. for (var i = 0; i < startAddress.length; i++) {
  25851. if (i >= addressTarget.length) break
  25852. if (addressTarget[i] > startAddress[i]) {
  25853. isAfterStartAddress = true
  25854. break
  25855. } else if (addressTarget[i] < startAddress[i]) {
  25856. break
  25857. }
  25858. }
  25859. for (var i = 0; i < endAddress.length; i++) {
  25860. if (i >= addressTarget.length) break
  25861. if (addressTarget[i] < startAddress[i]) {
  25862. isBeforeEndAddress = true
  25863. break
  25864. } else if (addressTarget[i] > startAddress[i]) {
  25865. break
  25866. }
  25867. }
  25868. return isAfterStartAddress && isBeforeEndAddress
  25869. }
  25870. }
  25871. },
  25872. deletesection: {
  25873. execCommand: function (cmd, section, keepChildren) {
  25874. var me = this
  25875. if (!section) return
  25876. function getNodeFromAddress(startAddress) {
  25877. var current = me.body
  25878. for (var i = 0; i < startAddress.length; i++) {
  25879. if (!current.childNodes) return null
  25880. current = current.childNodes[startAddress[i]]
  25881. }
  25882. return current
  25883. }
  25884. var startNode = getNodeFromAddress(section.startAddress),
  25885. endNode = getNodeFromAddress(section.endAddress),
  25886. current = startNode,
  25887. nextNode
  25888. if (!keepChildren) {
  25889. while (
  25890. current &&
  25891. domUtils.inDoc(endNode, me.document) &&
  25892. !(
  25893. domUtils.getPosition(current, endNode) &
  25894. domUtils.POSITION_FOLLOWING
  25895. )
  25896. ) {
  25897. nextNode = current.nextSibling
  25898. domUtils.remove(current)
  25899. current = nextNode
  25900. }
  25901. } else {
  25902. domUtils.remove(current)
  25903. }
  25904. me.fireEvent('updateSections')
  25905. }
  25906. },
  25907. selectsection: {
  25908. execCommand: function (cmd, section) {
  25909. if (!section && !section.dom) return false
  25910. var me = this,
  25911. range = me.selection.getRange(),
  25912. address = {
  25913. startAddress: utils.clone(section.startAddress, []),
  25914. endAddress: utils.clone(section.endAddress, [])
  25915. }
  25916. address.endAddress[address.endAddress.length - 1]++
  25917. range.moveToAddress(address).select().scrollToView()
  25918. return true
  25919. },
  25920. notNeedUndo: true
  25921. },
  25922. scrolltosection: {
  25923. execCommand: function (cmd, section) {
  25924. if (!section && !section.dom) return false
  25925. var me = this,
  25926. range = me.selection.getRange(),
  25927. address = {
  25928. startAddress: section.startAddress,
  25929. endAddress: section.endAddress
  25930. }
  25931. address.endAddress[address.endAddress.length - 1]++
  25932. range.moveToAddress(address).scrollToView()
  25933. return true
  25934. },
  25935. notNeedUndo: true
  25936. }
  25937. }
  25938. }
  25939. })
  25940. // plugins/simpleupload.js
  25941. /**
  25942. * @description
  25943. * 简单上传:点击按钮,直接选择文件上传。
  25944. * 原 UEditor 作者使用了 form 表单 + iframe 的方式上传
  25945. * 但由于同源策略的限制,父页面无法访问跨域的 iframe 内容
  25946. * 导致无法获取接口返回的数据,使得单图上传无法在跨域的情况下使用
  25947. * 这里改为普通的XHR上传,兼容到IE10+
  25948. * @author HaoChuan9421 <hc199421@gmail.com>
  25949. * @date 2018-12-20
  25950. */
  25951. UE.plugin.register('simpleupload', function () {
  25952. var me = this,
  25953. containerBtn,
  25954. timestrap = (+new Date()).toString(36)
  25955. function initUploadBtn() {
  25956. var w = containerBtn.offsetWidth || 20,
  25957. h = containerBtn.offsetHeight || 20,
  25958. btnStyle =
  25959. 'display:block;width:' +
  25960. w +
  25961. 'px;height:' +
  25962. h +
  25963. 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;'
  25964. var form = document.createElement('form')
  25965. var input = document.createElement('input')
  25966. form.id = 'edui_form_' + timestrap
  25967. form.enctype = 'multipart/form-data'
  25968. form.style = btnStyle
  25969. input.id = 'edui_input_' + timestrap
  25970. input.type = 'file'
  25971. input.accept = 'image/*'
  25972. input.name = me.options.imageFieldName
  25973. input.style = btnStyle
  25974. form.appendChild(input)
  25975. containerBtn.appendChild(form)
  25976. input.addEventListener('change', function (event) {
  25977. if (!input.value) return
  25978. var loadingId = 'loading_' + (+new Date()).toString(36)
  25979. var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'))
  25980. var params =
  25981. utils.serializeParam(me.queryCommandValue('serverparam')) || ''
  25982. var action = utils.formatUrl(
  25983. imageActionUrl +
  25984. (imageActionUrl.indexOf('?') == -1 ? '?' : '&') +
  25985. params
  25986. )
  25987. var allowFiles = me.getOpt('imageAllowFiles')
  25988. console.log(action, 'action', 'allowFiles', allowFiles)
  25989. me.focus()
  25990. me.execCommand(
  25991. 'inserthtml',
  25992. '<img class="loadingclass" id="' +
  25993. loadingId +
  25994. '" src="' +
  25995. me.options.themePath +
  25996. me.options.theme +
  25997. '/images/spacer.gif" title="' +
  25998. (me.getLang('simpleupload.loading') || '') +
  25999. '" >'
  26000. )
  26001. function showErrorLoader(title) {
  26002. if (loadingId) {
  26003. var loader = me.document.getElementById(loadingId)
  26004. loader && domUtils.remove(loader)
  26005. me.fireEvent('showmessage', {
  26006. id: loadingId,
  26007. content: title,
  26008. type: 'error',
  26009. timeout: 4000
  26010. })
  26011. }
  26012. }
  26013. /* 判断后端配置是否没有加载成功 */
  26014. if (!me.getOpt('imageActionName')) {
  26015. showErrorLoader(me.getLang('autoupload.errorLoadConfig'))
  26016. return
  26017. }
  26018. // 判断文件格式是否错误
  26019. var filename = input.value,
  26020. fileext = filename ? filename.substr(filename.lastIndexOf('.')) : ''
  26021. if (
  26022. !fileext ||
  26023. (allowFiles &&
  26024. (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') ==
  26025. -1)
  26026. ) {
  26027. showErrorLoader(me.getLang('simpleupload.exceedTypeError'))
  26028. return
  26029. }
  26030. console.log('filename', input.value)
  26031. console.log('准备上传action', action, input)
  26032. let formData = new FormData()
  26033. let files = input.files[0]
  26034. formData.append('file', files)
  26035. var xhr = new XMLHttpRequest()
  26036. // xhr.setRequestHeader('Content-Type', 'multipart/form-data')
  26037. xhr.open('post', action, true)
  26038. xhr.send(formData)
  26039. if (
  26040. me.options.headers &&
  26041. Object.prototype.toString.apply(me.options.headers) ===
  26042. '[object Object]'
  26043. ) {
  26044. for (var key in me.options.headers) {
  26045. xhr.setRequestHeader(key, me.options.headers[key])
  26046. }
  26047. }
  26048. xhr.onload = function () {
  26049. if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
  26050. var res = JSON.parse(xhr.responseText)
  26051. var link = res.data.url
  26052. if (res.code == 200 && res.data.url) {
  26053. loader = me.document.getElementById(loadingId)
  26054. loader.setAttribute('src', link)
  26055. loader.setAttribute('_src', link)
  26056. loader.setAttribute('title', res.title || '')
  26057. loader.setAttribute('alt', res.original || '')
  26058. loader.removeAttribute('id')
  26059. domUtils.removeClasses(loader, 'loadingclass')
  26060. me.fireEvent('contentchange')
  26061. } else {
  26062. showErrorLoader(res.state)
  26063. }
  26064. } else {
  26065. console.log('请求失败')
  26066. showErrorLoader(me.getLang('simpleupload.loadError'))
  26067. }
  26068. }
  26069. xhr.onerror = function () {
  26070. console.log('请求失败onerror')
  26071. showErrorLoader(me.getLang('simpleupload.loadError'))
  26072. }
  26073. form.reset()
  26074. })
  26075. }
  26076. return {
  26077. bindEvents: {
  26078. ready: function () {
  26079. //设置loading的样式
  26080. utils.cssRule(
  26081. 'loading',
  26082. ".loadingclass{display:inline-block;cursor:default;background: url('" +
  26083. this.options.themePath +
  26084. this.options.theme +
  26085. "/images/loading.gif') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n" +
  26086. ".loaderrorclass{display:inline-block;cursor:default;background: url('" +
  26087. this.options.themePath +
  26088. this.options.theme +
  26089. "/images/loaderror.png') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;" +
  26090. '}',
  26091. this.document
  26092. )
  26093. },
  26094. /* 初始化简单上传按钮 */
  26095. simpleuploadbtnready: function (type, container) {
  26096. containerBtn = container
  26097. me.afterConfigReady(initUploadBtn)
  26098. }
  26099. },
  26100. outputRule: function (root) {
  26101. utils.each(root.getNodesByTagName('img'), function (n) {
  26102. if (
  26103. /\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))
  26104. ) {
  26105. n.parentNode.removeChild(n)
  26106. }
  26107. })
  26108. }
  26109. }
  26110. })
  26111. // plugins/serverparam.js
  26112. /**
  26113. * 服务器提交的额外参数列表设置插件
  26114. * @file
  26115. * @since 1.2.6.1
  26116. */
  26117. UE.plugin.register('serverparam', function () {
  26118. var me = this,
  26119. serverParam = {}
  26120. return {
  26121. commands: {
  26122. /**
  26123. * 修改服务器提交的额外参数列表,清除所有项
  26124. * @command serverparam
  26125. * @method execCommand
  26126. * @param { String } cmd 命令字符串
  26127. * @example
  26128. * ```javascript
  26129. * editor.execCommand('serverparam');
  26130. * editor.queryCommandValue('serverparam'); //返回空
  26131. * ```
  26132. */
  26133. /**
  26134. * 修改服务器提交的额外参数列表,删除指定项
  26135. * @command serverparam
  26136. * @method execCommand
  26137. * @param { String } cmd 命令字符串
  26138. * @param { String } key 要清除的属性
  26139. * @example
  26140. * ```javascript
  26141. * editor.execCommand('serverparam', 'name'); //删除属性name
  26142. * ```
  26143. */
  26144. /**
  26145. * 修改服务器提交的额外参数列表,使用键值添加项
  26146. * @command serverparam
  26147. * @method execCommand
  26148. * @param { String } cmd 命令字符串
  26149. * @param { String } key 要添加的属性
  26150. * @param { String } value 要添加属性的值
  26151. * @example
  26152. * ```javascript
  26153. * editor.execCommand('serverparam', 'name', 'hello');
  26154. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  26155. * ```
  26156. */
  26157. /**
  26158. * 修改服务器提交的额外参数列表,传入键值对对象添加多项
  26159. * @command serverparam
  26160. * @method execCommand
  26161. * @param { String } cmd 命令字符串
  26162. * @param { Object } key 传入的键值对对象
  26163. * @example
  26164. * ```javascript
  26165. * editor.execCommand('serverparam', {'name': 'hello'});
  26166. * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
  26167. * ```
  26168. */
  26169. /**
  26170. * 修改服务器提交的额外参数列表,使用自定义函数添加多项
  26171. * @command serverparam
  26172. * @method execCommand
  26173. * @param { String } cmd 命令字符串
  26174. * @param { Function } key 自定义获取参数的函数
  26175. * @example
  26176. * ```javascript
  26177. * editor.execCommand('serverparam', function(editor){
  26178. * return {'key': 'value'};
  26179. * });
  26180. * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'}
  26181. * ```
  26182. */
  26183. /**
  26184. * 获取服务器提交的额外参数列表
  26185. * @command serverparam
  26186. * @method queryCommandValue
  26187. * @param { String } cmd 命令字符串
  26188. * @example
  26189. * ```javascript
  26190. * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'}
  26191. * ```
  26192. */
  26193. serverparam: {
  26194. execCommand: function (cmd, key, value) {
  26195. if (key === undefined || key === null) {
  26196. //不传参数,清空列表
  26197. serverParam = {}
  26198. } else if (utils.isString(key)) {
  26199. //传入键值
  26200. if (value === undefined || value === null) {
  26201. delete serverParam[key]
  26202. } else {
  26203. serverParam[key] = value
  26204. }
  26205. } else if (utils.isObject(key)) {
  26206. //传入对象,覆盖列表项
  26207. utils.extend(serverParam, key, true)
  26208. } else if (utils.isFunction(key)) {
  26209. //传入函数,添加列表项
  26210. utils.extend(serverParam, key(), true)
  26211. }
  26212. },
  26213. queryCommandValue: function () {
  26214. return serverParam || {}
  26215. }
  26216. }
  26217. }
  26218. }
  26219. })
  26220. // plugins/insertfile.js
  26221. /**
  26222. * 插入附件
  26223. */
  26224. UE.plugin.register('insertfile', function () {
  26225. var me = this
  26226. function getFileIcon(url) {
  26227. var ext = url.substr(url.lastIndexOf('.') + 1).toLowerCase(),
  26228. maps = {
  26229. rar: 'icon_rar.gif',
  26230. zip: 'icon_rar.gif',
  26231. tar: 'icon_rar.gif',
  26232. gz: 'icon_rar.gif',
  26233. bz2: 'icon_rar.gif',
  26234. doc: 'icon_doc.gif',
  26235. docx: 'icon_doc.gif',
  26236. pdf: 'icon_pdf.gif',
  26237. mp3: 'icon_mp3.gif',
  26238. xls: 'icon_xls.gif',
  26239. chm: 'icon_chm.gif',
  26240. ppt: 'icon_ppt.gif',
  26241. pptx: 'icon_ppt.gif',
  26242. avi: 'icon_mv.gif',
  26243. rmvb: 'icon_mv.gif',
  26244. wmv: 'icon_mv.gif',
  26245. flv: 'icon_mv.gif',
  26246. swf: 'icon_mv.gif',
  26247. rm: 'icon_mv.gif',
  26248. exe: 'icon_exe.gif',
  26249. psd: 'icon_psd.gif',
  26250. txt: 'icon_txt.gif',
  26251. jpg: 'icon_jpg.gif',
  26252. png: 'icon_jpg.gif',
  26253. jpeg: 'icon_jpg.gif',
  26254. gif: 'icon_jpg.gif',
  26255. ico: 'icon_jpg.gif',
  26256. bmp: 'icon_jpg.gif'
  26257. }
  26258. return maps[ext] ? maps[ext] : maps['txt']
  26259. }
  26260. return {
  26261. commands: {
  26262. insertfile: {
  26263. execCommand: function (command, filelist) {
  26264. filelist = utils.isArray(filelist) ? filelist : [filelist]
  26265. var i,
  26266. item,
  26267. icon,
  26268. title,
  26269. html = '',
  26270. URL = me.getOpt('UEDITOR_HOME_URL'),
  26271. iconDir =
  26272. URL +
  26273. (URL.substr(URL.length - 1) == '/' ? '' : '/') +
  26274. 'dialogs/attachment/fileTypeImages/'
  26275. for (i = 0; i < filelist.length; i++) {
  26276. item = filelist[i]
  26277. icon = iconDir + getFileIcon(item.url)
  26278. title =
  26279. item.title || item.url.substr(item.url.lastIndexOf('/') + 1)
  26280. html +=
  26281. '<p style="line-height: 16px;">' +
  26282. '<img style="vertical-align: middle; margin-right: 2px;" src="' +
  26283. icon +
  26284. '" _src="' +
  26285. icon +
  26286. '" />' +
  26287. '<a style="font-size:12px; color:#0066cc;" href="' +
  26288. item.url +
  26289. '" title="' +
  26290. title +
  26291. '">' +
  26292. title +
  26293. '</a>' +
  26294. '</p>'
  26295. }
  26296. me.execCommand('insertHtml', html)
  26297. }
  26298. }
  26299. }
  26300. }
  26301. })
  26302. // plugins/xssFilter.js
  26303. /**
  26304. * @file xssFilter.js
  26305. * @desc xss过滤器
  26306. * @author robbenmu
  26307. */
  26308. UE.plugins.xssFilter = function () {
  26309. var config = UEDITOR_CONFIG
  26310. var whitList = config.whitList
  26311. function filter(node) {
  26312. var tagName = node.tagName
  26313. var attrs = node.attrs
  26314. if (!whitList.hasOwnProperty(tagName)) {
  26315. node.parentNode.removeChild(node)
  26316. return false
  26317. }
  26318. UE.utils.each(attrs, function (val, key) {
  26319. if (whitList[tagName].indexOf(key) === -1) {
  26320. node.setAttr(key)
  26321. }
  26322. })
  26323. }
  26324. // 添加inserthtml\paste等操作用的过滤规则
  26325. if (whitList && config.xssFilterRules) {
  26326. this.options.filterRules = (function () {
  26327. var result = {}
  26328. UE.utils.each(whitList, function (val, key) {
  26329. result[key] = function (node) {
  26330. return filter(node)
  26331. }
  26332. })
  26333. return result
  26334. })()
  26335. }
  26336. var tagList = []
  26337. UE.utils.each(whitList, function (val, key) {
  26338. tagList.push(key)
  26339. })
  26340. // 添加input过滤规则
  26341. //
  26342. if (whitList && config.inputXssFilter) {
  26343. this.addInputRule(function (root) {
  26344. root.traversal(function (node) {
  26345. if (node.type !== 'element') {
  26346. return false
  26347. }
  26348. filter(node)
  26349. })
  26350. })
  26351. }
  26352. // 添加output过滤规则
  26353. //
  26354. if (whitList && config.outputXssFilter) {
  26355. this.addOutputRule(function (root) {
  26356. root.traversal(function (node) {
  26357. if (node.type !== 'element') {
  26358. return false
  26359. }
  26360. filter(node)
  26361. })
  26362. })
  26363. }
  26364. }
  26365. // ui/ui.js
  26366. var baidu = baidu || {}
  26367. baidu.editor = baidu.editor || {}
  26368. UE.ui = baidu.editor.ui = {}
  26369. // ui/uiutils.js
  26370. ;(function () {
  26371. var browser = baidu.editor.browser,
  26372. domUtils = baidu.editor.dom.domUtils
  26373. var magic = '$EDITORUI'
  26374. var root = (window[magic] = {})
  26375. var uidMagic = 'ID' + magic
  26376. var uidCount = 0
  26377. var uiUtils = (baidu.editor.ui.uiUtils = {
  26378. uid: function (obj) {
  26379. return obj ? obj[uidMagic] || (obj[uidMagic] = ++uidCount) : ++uidCount
  26380. },
  26381. hook: function (fn, callback) {
  26382. var dg
  26383. if (fn && fn._callbacks) {
  26384. dg = fn
  26385. } else {
  26386. dg = function () {
  26387. var q
  26388. if (fn) {
  26389. q = fn.apply(this, arguments)
  26390. }
  26391. var callbacks = dg._callbacks
  26392. var k = callbacks.length
  26393. while (k--) {
  26394. var r = callbacks[k].apply(this, arguments)
  26395. if (q === undefined) {
  26396. q = r
  26397. }
  26398. }
  26399. return q
  26400. }
  26401. dg._callbacks = []
  26402. }
  26403. dg._callbacks.push(callback)
  26404. return dg
  26405. },
  26406. createElementByHtml: function (html) {
  26407. var el = document.createElement('div')
  26408. el.innerHTML = html
  26409. el = el.firstChild
  26410. el.parentNode.removeChild(el)
  26411. return el
  26412. },
  26413. getViewportElement: function () {
  26414. return browser.ie && browser.quirks
  26415. ? document.body
  26416. : document.documentElement
  26417. },
  26418. getClientRect: function (element) {
  26419. var bcr
  26420. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  26421. try {
  26422. bcr = element.getBoundingClientRect()
  26423. } catch (e) {
  26424. bcr = { left: 0, top: 0, height: 0, width: 0 }
  26425. }
  26426. var rect = {
  26427. left: Math.round(bcr.left),
  26428. top: Math.round(bcr.top),
  26429. height: Math.round(bcr.bottom - bcr.top),
  26430. width: Math.round(bcr.right - bcr.left)
  26431. }
  26432. var doc
  26433. while (
  26434. (doc = element.ownerDocument) !== document &&
  26435. (element = domUtils.getWindow(doc).frameElement)
  26436. ) {
  26437. bcr = element.getBoundingClientRect()
  26438. rect.left += bcr.left
  26439. rect.top += bcr.top
  26440. }
  26441. rect.bottom = rect.top + rect.height
  26442. rect.right = rect.left + rect.width
  26443. return rect
  26444. },
  26445. getViewportRect: function () {
  26446. var viewportEl = uiUtils.getViewportElement()
  26447. var width = (window.innerWidth || viewportEl.clientWidth) | 0
  26448. var height = (window.innerHeight || viewportEl.clientHeight) | 0
  26449. return {
  26450. left: 0,
  26451. top: 0,
  26452. height: height,
  26453. width: width,
  26454. bottom: height,
  26455. right: width
  26456. }
  26457. },
  26458. setViewportOffset: function (element, offset) {
  26459. var rect
  26460. var fixedLayer = uiUtils.getFixedLayer()
  26461. if (element.parentNode === fixedLayer) {
  26462. element.style.left = offset.left + 'px'
  26463. element.style.top = offset.top + 'px'
  26464. } else {
  26465. domUtils.setViewportOffset(element, offset)
  26466. }
  26467. },
  26468. getEventOffset: function (evt) {
  26469. var el = evt.target || evt.srcElement
  26470. var rect = uiUtils.getClientRect(el)
  26471. var offset = uiUtils.getViewportOffsetByEvent(evt)
  26472. return {
  26473. left: offset.left - rect.left,
  26474. top: offset.top - rect.top
  26475. }
  26476. },
  26477. getViewportOffsetByEvent: function (evt) {
  26478. var el = evt.target || evt.srcElement
  26479. var frameEl = domUtils.getWindow(el).frameElement
  26480. var offset = {
  26481. left: evt.clientX,
  26482. top: evt.clientY
  26483. }
  26484. if (frameEl && el.ownerDocument !== document) {
  26485. var rect = uiUtils.getClientRect(frameEl)
  26486. offset.left += rect.left
  26487. offset.top += rect.top
  26488. }
  26489. return offset
  26490. },
  26491. setGlobal: function (id, obj) {
  26492. root[id] = obj
  26493. return magic + '["' + id + '"]'
  26494. },
  26495. unsetGlobal: function (id) {
  26496. delete root[id]
  26497. },
  26498. copyAttributes: function (tgt, src) {
  26499. var attributes = src.attributes
  26500. var k = attributes.length
  26501. while (k--) {
  26502. var attrNode = attributes[k]
  26503. if (
  26504. attrNode.nodeName != 'style' &&
  26505. attrNode.nodeName != 'class' &&
  26506. (!browser.ie || attrNode.specified)
  26507. ) {
  26508. tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue)
  26509. }
  26510. }
  26511. if (src.className) {
  26512. domUtils.addClass(tgt, src.className)
  26513. }
  26514. if (src.style.cssText) {
  26515. tgt.style.cssText += ';' + src.style.cssText
  26516. }
  26517. },
  26518. removeStyle: function (el, styleName) {
  26519. if (el.style.removeProperty) {
  26520. el.style.removeProperty(styleName)
  26521. } else if (el.style.removeAttribute) {
  26522. el.style.removeAttribute(styleName)
  26523. } else throw ''
  26524. },
  26525. contains: function (elA, elB) {
  26526. return (
  26527. elA &&
  26528. elB &&
  26529. (elA === elB
  26530. ? false
  26531. : elA.contains
  26532. ? elA.contains(elB)
  26533. : elA.compareDocumentPosition(elB) & 16)
  26534. )
  26535. },
  26536. startDrag: function (evt, callbacks, doc) {
  26537. var doc = doc || document
  26538. var startX = evt.clientX
  26539. var startY = evt.clientY
  26540. function handleMouseMove(evt) {
  26541. var x = evt.clientX - startX
  26542. var y = evt.clientY - startY
  26543. callbacks.ondragmove(x, y, evt)
  26544. if (evt.stopPropagation) {
  26545. evt.stopPropagation()
  26546. } else {
  26547. evt.cancelBubble = true
  26548. }
  26549. }
  26550. if (doc.addEventListener) {
  26551. function handleMouseUp(evt) {
  26552. doc.removeEventListener('mousemove', handleMouseMove, true)
  26553. doc.removeEventListener('mouseup', handleMouseUp, true)
  26554. window.removeEventListener('mouseup', handleMouseUp, true)
  26555. callbacks.ondragstop()
  26556. }
  26557. doc.addEventListener('mousemove', handleMouseMove, true)
  26558. doc.addEventListener('mouseup', handleMouseUp, true)
  26559. window.addEventListener('mouseup', handleMouseUp, true)
  26560. evt.preventDefault()
  26561. } else {
  26562. var elm = evt.srcElement
  26563. elm.setCapture()
  26564. function releaseCaptrue() {
  26565. elm.releaseCapture()
  26566. elm.detachEvent('onmousemove', handleMouseMove)
  26567. elm.detachEvent('onmouseup', releaseCaptrue)
  26568. elm.detachEvent('onlosecaptrue', releaseCaptrue)
  26569. callbacks.ondragstop()
  26570. }
  26571. elm.attachEvent('onmousemove', handleMouseMove)
  26572. elm.attachEvent('onmouseup', releaseCaptrue)
  26573. elm.attachEvent('onlosecaptrue', releaseCaptrue)
  26574. evt.returnValue = false
  26575. }
  26576. callbacks.ondragstart()
  26577. },
  26578. getFixedLayer: function () {
  26579. var layer = document.getElementById('edui_fixedlayer')
  26580. if (layer == null) {
  26581. layer = document.createElement('div')
  26582. layer.id = 'edui_fixedlayer'
  26583. document.body.appendChild(layer)
  26584. if (browser.ie && browser.version <= 8) {
  26585. layer.style.position = 'absolute'
  26586. bindFixedLayer()
  26587. setTimeout(updateFixedOffset)
  26588. } else {
  26589. layer.style.position = 'fixed'
  26590. }
  26591. layer.style.left = '0'
  26592. layer.style.top = '0'
  26593. layer.style.width = '0'
  26594. layer.style.height = '0'
  26595. layer.style.zIndex = '3005'
  26596. }
  26597. return layer
  26598. },
  26599. makeUnselectable: function (element) {
  26600. if (browser.opera || (browser.ie && browser.version < 9)) {
  26601. element.unselectable = 'on'
  26602. if (element.hasChildNodes()) {
  26603. for (var i = 0; i < element.childNodes.length; i++) {
  26604. if (element.childNodes[i].nodeType == 1) {
  26605. uiUtils.makeUnselectable(element.childNodes[i])
  26606. }
  26607. }
  26608. }
  26609. } else {
  26610. if (element.style.MozUserSelect !== undefined) {
  26611. element.style.MozUserSelect = 'none'
  26612. } else if (element.style.WebkitUserSelect !== undefined) {
  26613. element.style.WebkitUserSelect = 'none'
  26614. } else if (element.style.KhtmlUserSelect !== undefined) {
  26615. element.style.KhtmlUserSelect = 'none'
  26616. }
  26617. }
  26618. }
  26619. })
  26620. function updateFixedOffset() {
  26621. var layer = document.getElementById('edui_fixedlayer')
  26622. uiUtils.setViewportOffset(layer, {
  26623. left: 0,
  26624. top: 0
  26625. })
  26626. // layer.style.display = 'none';
  26627. // layer.style.display = 'block';
  26628. //#trace: 1354
  26629. // setTimeout(updateFixedOffset);
  26630. }
  26631. function bindFixedLayer(adjOffset) {
  26632. domUtils.on(window, 'scroll', updateFixedOffset)
  26633. domUtils.on(
  26634. window,
  26635. 'resize',
  26636. baidu.editor.utils.defer(updateFixedOffset, 0, true)
  26637. )
  26638. }
  26639. })()
  26640. // ui/uibase.js
  26641. ;(function () {
  26642. var utils = baidu.editor.utils,
  26643. uiUtils = baidu.editor.ui.uiUtils,
  26644. EventBase = baidu.editor.EventBase,
  26645. UIBase = (baidu.editor.ui.UIBase = function () {})
  26646. UIBase.prototype = {
  26647. className: '',
  26648. uiName: '',
  26649. initOptions: function (options) {
  26650. var me = this
  26651. for (var k in options) {
  26652. me[k] = options[k]
  26653. }
  26654. this.id = this.id || 'edui' + uiUtils.uid()
  26655. },
  26656. initUIBase: function () {
  26657. this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this))
  26658. },
  26659. render: function (holder) {
  26660. var html = this.renderHtml()
  26661. var el = uiUtils.createElementByHtml(html)
  26662. //by xuheng 给每个node添加class
  26663. var list = domUtils.getElementsByTagName(el, '*')
  26664. var theme = 'edui-' + (this.theme || this.editor.options.theme)
  26665. var layer = document.getElementById('edui_fixedlayer')
  26666. for (var i = 0, node; (node = list[i++]); ) {
  26667. domUtils.addClass(node, theme)
  26668. }
  26669. domUtils.addClass(el, theme)
  26670. if (layer) {
  26671. layer.className = ''
  26672. domUtils.addClass(layer, theme)
  26673. }
  26674. var seatEl = this.getDom()
  26675. if (seatEl != null) {
  26676. seatEl.parentNode.replaceChild(el, seatEl)
  26677. uiUtils.copyAttributes(el, seatEl)
  26678. } else {
  26679. if (typeof holder == 'string') {
  26680. holder = document.getElementById(holder)
  26681. }
  26682. holder = holder || uiUtils.getFixedLayer()
  26683. domUtils.addClass(holder, theme)
  26684. holder.appendChild(el)
  26685. }
  26686. this.postRender()
  26687. },
  26688. getDom: function (name) {
  26689. if (!name) {
  26690. return document.getElementById(this.id)
  26691. } else {
  26692. return document.getElementById(this.id + '_' + name)
  26693. }
  26694. },
  26695. postRender: function () {
  26696. this.fireEvent('postrender')
  26697. },
  26698. getHtmlTpl: function () {
  26699. return ''
  26700. },
  26701. formatHtml: function (tpl) {
  26702. var prefix = 'edui-' + this.uiName
  26703. return tpl
  26704. .replace(/##/g, this.id)
  26705. .replace(/%%-/g, this.uiName ? prefix + '-' : '')
  26706. .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
  26707. .replace(/\$\$/g, this._globalKey)
  26708. },
  26709. renderHtml: function () {
  26710. return this.formatHtml(this.getHtmlTpl())
  26711. },
  26712. dispose: function () {
  26713. var box = this.getDom()
  26714. if (box) baidu.editor.dom.domUtils.remove(box)
  26715. uiUtils.unsetGlobal(this.id)
  26716. }
  26717. }
  26718. utils.inherits(UIBase, EventBase)
  26719. })()
  26720. // ui/separator.js
  26721. ;(function () {
  26722. var utils = baidu.editor.utils,
  26723. UIBase = baidu.editor.ui.UIBase,
  26724. Separator = (baidu.editor.ui.Separator = function (options) {
  26725. this.initOptions(options)
  26726. this.initSeparator()
  26727. })
  26728. Separator.prototype = {
  26729. uiName: 'separator',
  26730. initSeparator: function () {
  26731. this.initUIBase()
  26732. },
  26733. getHtmlTpl: function () {
  26734. return '<div id="##" class="edui-box %%"></div>'
  26735. }
  26736. }
  26737. utils.inherits(Separator, UIBase)
  26738. })()
  26739. // ui/mask.js
  26740. ///import core
  26741. ///import uicore
  26742. ;(function () {
  26743. var utils = baidu.editor.utils,
  26744. domUtils = baidu.editor.dom.domUtils,
  26745. UIBase = baidu.editor.ui.UIBase,
  26746. uiUtils = baidu.editor.ui.uiUtils
  26747. var Mask = (baidu.editor.ui.Mask = function (options) {
  26748. this.initOptions(options)
  26749. this.initUIBase()
  26750. })
  26751. Mask.prototype = {
  26752. getHtmlTpl: function () {
  26753. return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>'
  26754. },
  26755. postRender: function () {
  26756. var me = this
  26757. domUtils.on(window, 'resize', function () {
  26758. setTimeout(function () {
  26759. if (!me.isHidden()) {
  26760. me._fill()
  26761. }
  26762. })
  26763. })
  26764. },
  26765. show: function (zIndex) {
  26766. this._fill()
  26767. this.getDom().style.display = ''
  26768. this.getDom().style.zIndex = zIndex
  26769. },
  26770. hide: function () {
  26771. this.getDom().style.display = 'none'
  26772. this.getDom().style.zIndex = ''
  26773. },
  26774. isHidden: function () {
  26775. return this.getDom().style.display == 'none'
  26776. },
  26777. _onMouseDown: function () {
  26778. return false
  26779. },
  26780. _onClick: function (e, target) {
  26781. this.fireEvent('click', e, target)
  26782. },
  26783. _fill: function () {
  26784. var el = this.getDom()
  26785. var vpRect = uiUtils.getViewportRect()
  26786. el.style.width = vpRect.width + 'px'
  26787. el.style.height = vpRect.height + 'px'
  26788. }
  26789. }
  26790. utils.inherits(Mask, UIBase)
  26791. })()
  26792. // ui/popup.js
  26793. ///import core
  26794. ///import uicore
  26795. ;(function () {
  26796. var utils = baidu.editor.utils,
  26797. uiUtils = baidu.editor.ui.uiUtils,
  26798. domUtils = baidu.editor.dom.domUtils,
  26799. UIBase = baidu.editor.ui.UIBase,
  26800. Popup = (baidu.editor.ui.Popup = function (options) {
  26801. this.initOptions(options)
  26802. this.initPopup()
  26803. })
  26804. var allPopups = []
  26805. function closeAllPopup(evt, el) {
  26806. for (var i = 0; i < allPopups.length; i++) {
  26807. var pop = allPopups[i]
  26808. if (!pop.isHidden()) {
  26809. if (pop.queryAutoHide(el) !== false) {
  26810. if (
  26811. evt &&
  26812. /scroll/gi.test(evt.type) &&
  26813. pop.className == 'edui-wordpastepop'
  26814. )
  26815. return
  26816. pop.hide()
  26817. }
  26818. }
  26819. }
  26820. if (allPopups.length) pop.editor.fireEvent('afterhidepop')
  26821. }
  26822. Popup.postHide = closeAllPopup
  26823. var ANCHOR_CLASSES = [
  26824. 'edui-anchor-topleft',
  26825. 'edui-anchor-topright',
  26826. 'edui-anchor-bottomleft',
  26827. 'edui-anchor-bottomright'
  26828. ]
  26829. Popup.prototype = {
  26830. SHADOW_RADIUS: 5,
  26831. content: null,
  26832. _hidden: false,
  26833. autoRender: true,
  26834. canSideLeft: true,
  26835. canSideUp: true,
  26836. initPopup: function () {
  26837. this.initUIBase()
  26838. allPopups.push(this)
  26839. },
  26840. getHtmlTpl: function () {
  26841. return (
  26842. '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
  26843. ' <div id="##_body" class="edui-popup-body">' +
  26844. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  26845. ' <div class="edui-shadow"></div>' +
  26846. ' <div id="##_content" class="edui-popup-content">' +
  26847. this.getContentHtmlTpl() +
  26848. ' </div>' +
  26849. ' </div>' +
  26850. '</div>'
  26851. )
  26852. },
  26853. getContentHtmlTpl: function () {
  26854. if (this.content) {
  26855. if (typeof this.content == 'string') {
  26856. return this.content
  26857. }
  26858. return this.content.renderHtml()
  26859. } else {
  26860. return ''
  26861. }
  26862. },
  26863. _UIBase_postRender: UIBase.prototype.postRender,
  26864. postRender: function () {
  26865. if (this.content instanceof UIBase) {
  26866. this.content.postRender()
  26867. }
  26868. //捕获鼠标滚轮
  26869. if (this.captureWheel && !this.captured) {
  26870. this.captured = true
  26871. var winHeight =
  26872. (document.documentElement.clientHeight ||
  26873. document.body.clientHeight) - 80,
  26874. _height = this.getDom().offsetHeight,
  26875. _top = uiUtils.getClientRect(this.combox.getDom()).top,
  26876. content = this.getDom('content'),
  26877. ifr = this.getDom('body').getElementsByTagName('iframe'),
  26878. me = this
  26879. ifr.length && (ifr = ifr[0])
  26880. while (_top + _height > winHeight) {
  26881. _height -= 30
  26882. }
  26883. content.style.height = _height + 'px'
  26884. //同步更改iframe高度
  26885. ifr && (ifr.style.height = _height + 'px')
  26886. //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
  26887. if (window.XMLHttpRequest) {
  26888. domUtils.on(
  26889. content,
  26890. 'onmousewheel' in document.body ? 'mousewheel' : 'DOMMouseScroll',
  26891. function (e) {
  26892. if (e.preventDefault) {
  26893. e.preventDefault()
  26894. } else {
  26895. e.returnValue = false
  26896. }
  26897. if (e.wheelDelta) {
  26898. content.scrollTop -= (e.wheelDelta / 120) * 60
  26899. } else {
  26900. content.scrollTop -= (e.detail / -3) * 60
  26901. }
  26902. }
  26903. )
  26904. } else {
  26905. //ie6
  26906. domUtils.on(this.getDom(), 'mousewheel', function (e) {
  26907. e.returnValue = false
  26908. me.getDom('content').scrollTop -= (e.wheelDelta / 120) * 60
  26909. })
  26910. }
  26911. }
  26912. this.fireEvent('postRenderAfter')
  26913. this.hide(true)
  26914. this._UIBase_postRender()
  26915. },
  26916. _doAutoRender: function () {
  26917. if (!this.getDom() && this.autoRender) {
  26918. this.render()
  26919. }
  26920. },
  26921. mesureSize: function () {
  26922. var box = this.getDom('content')
  26923. return uiUtils.getClientRect(box)
  26924. },
  26925. fitSize: function () {
  26926. if (this.captureWheel && this.sized) {
  26927. return this.__size
  26928. }
  26929. this.sized = true
  26930. var popBodyEl = this.getDom('body')
  26931. popBodyEl.style.width = ''
  26932. popBodyEl.style.height = ''
  26933. var size = this.mesureSize()
  26934. if (this.captureWheel) {
  26935. popBodyEl.style.width = -(-20 - size.width) + 'px'
  26936. var height = parseInt(this.getDom('content').style.height, 10)
  26937. !window.isNaN(height) && (size.height = height)
  26938. } else {
  26939. popBodyEl.style.width = size.width + 'px'
  26940. }
  26941. popBodyEl.style.height = size.height + 'px'
  26942. this.__size = size
  26943. this.captureWheel && (this.getDom('content').style.overflow = 'auto')
  26944. return size
  26945. },
  26946. showAnchor: function (element, hoz) {
  26947. this.showAnchorRect(uiUtils.getClientRect(element), hoz)
  26948. },
  26949. showAnchorRect: function (rect, hoz, adj) {
  26950. this._doAutoRender()
  26951. var vpRect = uiUtils.getViewportRect()
  26952. this.getDom().style.visibility = 'hidden'
  26953. this._show()
  26954. var popSize = this.fitSize()
  26955. var sideLeft, sideUp, left, top
  26956. if (hoz) {
  26957. sideLeft =
  26958. this.canSideLeft &&
  26959. rect.right + popSize.width > vpRect.right &&
  26960. rect.left > popSize.width
  26961. sideUp =
  26962. this.canSideUp &&
  26963. rect.top + popSize.height > vpRect.bottom &&
  26964. rect.bottom > popSize.height
  26965. left = sideLeft ? rect.left - popSize.width : rect.right
  26966. top = sideUp ? rect.bottom - popSize.height : rect.top
  26967. } else {
  26968. sideLeft =
  26969. this.canSideLeft &&
  26970. rect.right + popSize.width > vpRect.right &&
  26971. rect.left > popSize.width
  26972. sideUp =
  26973. this.canSideUp &&
  26974. rect.top + popSize.height > vpRect.bottom &&
  26975. rect.bottom > popSize.height
  26976. left = sideLeft ? rect.right - popSize.width : rect.left
  26977. top = sideUp ? rect.top - popSize.height : rect.bottom
  26978. }
  26979. var popEl = this.getDom()
  26980. uiUtils.setViewportOffset(popEl, {
  26981. left: left,
  26982. top: top
  26983. })
  26984. domUtils.removeClasses(popEl, ANCHOR_CLASSES)
  26985. popEl.className +=
  26986. ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)]
  26987. if (this.editor) {
  26988. popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10
  26989. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex =
  26990. popEl.style.zIndex - 1
  26991. }
  26992. this.getDom().style.visibility = 'visible'
  26993. },
  26994. showAt: function (offset) {
  26995. var left = offset.left
  26996. var top = offset.top
  26997. var rect = {
  26998. left: left,
  26999. top: top,
  27000. right: left,
  27001. bottom: top,
  27002. height: 0,
  27003. width: 0
  27004. }
  27005. this.showAnchorRect(rect, false, true)
  27006. },
  27007. _show: function () {
  27008. if (this._hidden) {
  27009. var box = this.getDom()
  27010. box.style.display = ''
  27011. this._hidden = false
  27012. // if (box.setActive) {
  27013. // box.setActive();
  27014. // }
  27015. this.fireEvent('show')
  27016. }
  27017. },
  27018. isHidden: function () {
  27019. return this._hidden
  27020. },
  27021. show: function () {
  27022. this._doAutoRender()
  27023. this._show()
  27024. },
  27025. hide: function (notNofity) {
  27026. if (!this._hidden && this.getDom()) {
  27027. this.getDom().style.display = 'none'
  27028. this._hidden = true
  27029. if (!notNofity) {
  27030. this.fireEvent('hide')
  27031. }
  27032. }
  27033. },
  27034. queryAutoHide: function (el) {
  27035. return !el || !uiUtils.contains(this.getDom(), el)
  27036. }
  27037. }
  27038. utils.inherits(Popup, UIBase)
  27039. domUtils.on(document, 'mousedown', function (evt) {
  27040. var el = evt.target || evt.srcElement
  27041. closeAllPopup(evt, el)
  27042. })
  27043. domUtils.on(window, 'scroll', function (evt, el) {
  27044. closeAllPopup(evt, el)
  27045. })
  27046. })()
  27047. // ui/colorpicker.js
  27048. ///import core
  27049. ///import uicore
  27050. ;(function () {
  27051. var utils = baidu.editor.utils,
  27052. UIBase = baidu.editor.ui.UIBase,
  27053. ColorPicker = (baidu.editor.ui.ColorPicker = function (options) {
  27054. this.initOptions(options)
  27055. this.noColorText = this.noColorText || this.editor.getLang('clearColor')
  27056. this.initUIBase()
  27057. })
  27058. ColorPicker.prototype = {
  27059. getHtmlTpl: function () {
  27060. return genColorPicker(this.noColorText, this.editor)
  27061. },
  27062. _onTableClick: function (evt) {
  27063. var tgt = evt.target || evt.srcElement
  27064. var color = tgt.getAttribute('data-color')
  27065. if (color) {
  27066. this.fireEvent('pickcolor', color)
  27067. }
  27068. },
  27069. _onTableOver: function (evt) {
  27070. var tgt = evt.target || evt.srcElement
  27071. var color = tgt.getAttribute('data-color')
  27072. if (color) {
  27073. this.getDom('preview').style.backgroundColor = color
  27074. }
  27075. },
  27076. _onTableOut: function () {
  27077. this.getDom('preview').style.backgroundColor = ''
  27078. },
  27079. _onPickNoColor: function () {
  27080. this.fireEvent('picknocolor')
  27081. }
  27082. }
  27083. utils.inherits(ColorPicker, UIBase)
  27084. var COLORS = (
  27085. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  27086. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  27087. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  27088. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  27089. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  27090. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  27091. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,'
  27092. ).split(',')
  27093. function genColorPicker(noColorText, editor) {
  27094. var html =
  27095. '<div id="##" class="edui-colorpicker %%">' +
  27096. '<div class="edui-colorpicker-topbar edui-clearfix">' +
  27097. '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
  27098. '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">' +
  27099. noColorText +
  27100. '</div>' +
  27101. '</div>' +
  27102. '<table class="edui-box" style="border-collapse: collapse;" onmouseover="$$._onTableOver(event, this);" onmouseout="$$._onTableOut(event, this);" onclick="return $$._onTableClick(event, this);" cellspacing="0" cellpadding="0">' +
  27103. '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;padding-top: 2px"><td colspan="10">' +
  27104. editor.getLang('themeColor') +
  27105. '</td> </tr>' +
  27106. '<tr class="edui-colorpicker-tablefirstrow" >'
  27107. for (var i = 0; i < COLORS.length; i++) {
  27108. if (i && i % 10 === 0) {
  27109. html +=
  27110. '</tr>' +
  27111. (i == 60
  27112. ? '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;"><td colspan="10">' +
  27113. editor.getLang('standardColor') +
  27114. '</td></tr>'
  27115. : '') +
  27116. '<tr' +
  27117. (i == 60 ? ' class="edui-colorpicker-tablefirstrow"' : '') +
  27118. '>'
  27119. }
  27120. html +=
  27121. i < 70
  27122. ? '<td style="padding: 0 2px;"><a hidefocus title="' +
  27123. COLORS[i] +
  27124. '" onclick="return false;" href="javascript:" unselectable="on" class="edui-box edui-colorpicker-colorcell"' +
  27125. ' data-color="#' +
  27126. COLORS[i] +
  27127. '"' +
  27128. ' style="background-color:#' +
  27129. COLORS[i] +
  27130. ';border:solid #ccc;' +
  27131. (i < 10 || i >= 60
  27132. ? 'border-width:1px;'
  27133. : i >= 10 && i < 20
  27134. ? 'border-width:1px 1px 0 1px;'
  27135. : 'border-width:0 1px 0 1px;') +
  27136. '"' +
  27137. '></a></td>'
  27138. : ''
  27139. }
  27140. html += '</tr></table></div>'
  27141. return html
  27142. }
  27143. })()
  27144. // ui/tablepicker.js
  27145. ///import core
  27146. ///import uicore
  27147. ;(function () {
  27148. var utils = baidu.editor.utils,
  27149. uiUtils = baidu.editor.ui.uiUtils,
  27150. UIBase = baidu.editor.ui.UIBase
  27151. var TablePicker = (baidu.editor.ui.TablePicker = function (options) {
  27152. this.initOptions(options)
  27153. this.initTablePicker()
  27154. })
  27155. TablePicker.prototype = {
  27156. defaultNumRows: 10,
  27157. defaultNumCols: 10,
  27158. maxNumRows: 20,
  27159. maxNumCols: 20,
  27160. numRows: 10,
  27161. numCols: 10,
  27162. lengthOfCellSide: 22,
  27163. initTablePicker: function () {
  27164. this.initUIBase()
  27165. },
  27166. getHtmlTpl: function () {
  27167. var me = this
  27168. return (
  27169. '<div id="##" class="edui-tablepicker %%">' +
  27170. '<div class="edui-tablepicker-body">' +
  27171. '<div class="edui-infoarea">' +
  27172. '<span id="##_label" class="edui-label"></span>' +
  27173. '</div>' +
  27174. '<div class="edui-pickarea"' +
  27175. ' onmousemove="$$._onMouseMove(event, this);"' +
  27176. ' onmouseover="$$._onMouseOver(event, this);"' +
  27177. ' onmouseout="$$._onMouseOut(event, this);"' +
  27178. ' onclick="$$._onClick(event, this);"' +
  27179. '>' +
  27180. '<div id="##_overlay" class="edui-overlay"></div>' +
  27181. '</div>' +
  27182. '</div>' +
  27183. '</div>'
  27184. )
  27185. },
  27186. _UIBase_render: UIBase.prototype.render,
  27187. render: function (holder) {
  27188. this._UIBase_render(holder)
  27189. this.getDom('label').innerHTML =
  27190. '0' +
  27191. this.editor.getLang('t_row') +
  27192. ' x 0' +
  27193. this.editor.getLang('t_col')
  27194. },
  27195. _track: function (numCols, numRows) {
  27196. var style = this.getDom('overlay').style
  27197. var sideLen = this.lengthOfCellSide
  27198. style.width = numCols * sideLen + 'px'
  27199. style.height = numRows * sideLen + 'px'
  27200. var label = this.getDom('label')
  27201. label.innerHTML =
  27202. numCols +
  27203. this.editor.getLang('t_col') +
  27204. ' x ' +
  27205. numRows +
  27206. this.editor.getLang('t_row')
  27207. this.numCols = numCols
  27208. this.numRows = numRows
  27209. },
  27210. _onMouseOver: function (evt, el) {
  27211. var rel = evt.relatedTarget || evt.fromElement
  27212. if (!uiUtils.contains(el, rel) && el !== rel) {
  27213. this.getDom('label').innerHTML =
  27214. '0' +
  27215. this.editor.getLang('t_col') +
  27216. ' x 0' +
  27217. this.editor.getLang('t_row')
  27218. this.getDom('overlay').style.visibility = ''
  27219. }
  27220. },
  27221. _onMouseOut: function (evt, el) {
  27222. var rel = evt.relatedTarget || evt.toElement
  27223. if (!uiUtils.contains(el, rel) && el !== rel) {
  27224. this.getDom('label').innerHTML =
  27225. '0' +
  27226. this.editor.getLang('t_col') +
  27227. ' x 0' +
  27228. this.editor.getLang('t_row')
  27229. this.getDom('overlay').style.visibility = 'hidden'
  27230. }
  27231. },
  27232. _onMouseMove: function (evt, el) {
  27233. var style = this.getDom('overlay').style
  27234. var offset = uiUtils.getEventOffset(evt)
  27235. var sideLen = this.lengthOfCellSide
  27236. var numCols = Math.ceil(offset.left / sideLen)
  27237. var numRows = Math.ceil(offset.top / sideLen)
  27238. this._track(numCols, numRows)
  27239. },
  27240. _onClick: function () {
  27241. this.fireEvent('picktable', this.numCols, this.numRows)
  27242. }
  27243. }
  27244. utils.inherits(TablePicker, UIBase)
  27245. })()
  27246. // ui/stateful.js
  27247. ;(function () {
  27248. var browser = baidu.editor.browser,
  27249. domUtils = baidu.editor.dom.domUtils,
  27250. uiUtils = baidu.editor.ui.uiUtils
  27251. var TPL_STATEFUL =
  27252. 'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
  27253. ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
  27254. (browser.ie
  27255. ? ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
  27256. ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"'
  27257. : ' onmouseover="$$.Stateful_onMouseOver(event, this);"' +
  27258. ' onmouseout="$$.Stateful_onMouseOut(event, this);"')
  27259. baidu.editor.ui.Stateful = {
  27260. alwalysHoverable: false,
  27261. target: null, //目标元素和this指向dom不一样
  27262. Stateful_init: function () {
  27263. this._Stateful_dGetHtmlTpl = this.getHtmlTpl
  27264. this.getHtmlTpl = this.Stateful_getHtmlTpl
  27265. },
  27266. Stateful_getHtmlTpl: function () {
  27267. var tpl = this._Stateful_dGetHtmlTpl()
  27268. // 使用function避免$转义
  27269. return tpl.replace(/stateful/g, function () {
  27270. return TPL_STATEFUL
  27271. })
  27272. },
  27273. Stateful_onMouseEnter: function (evt, el) {
  27274. this.target = el
  27275. if (!this.isDisabled() || this.alwalysHoverable) {
  27276. this.addState('hover')
  27277. this.fireEvent('over')
  27278. }
  27279. },
  27280. Stateful_onMouseLeave: function (evt, el) {
  27281. if (!this.isDisabled() || this.alwalysHoverable) {
  27282. this.removeState('hover')
  27283. this.removeState('active')
  27284. this.fireEvent('out')
  27285. }
  27286. },
  27287. Stateful_onMouseOver: function (evt, el) {
  27288. var rel = evt.relatedTarget
  27289. if (!uiUtils.contains(el, rel) && el !== rel) {
  27290. this.Stateful_onMouseEnter(evt, el)
  27291. }
  27292. },
  27293. Stateful_onMouseOut: function (evt, el) {
  27294. var rel = evt.relatedTarget
  27295. if (!uiUtils.contains(el, rel) && el !== rel) {
  27296. this.Stateful_onMouseLeave(evt, el)
  27297. }
  27298. },
  27299. Stateful_onMouseDown: function (evt, el) {
  27300. if (!this.isDisabled()) {
  27301. this.addState('active')
  27302. }
  27303. },
  27304. Stateful_onMouseUp: function (evt, el) {
  27305. if (!this.isDisabled()) {
  27306. this.removeState('active')
  27307. }
  27308. },
  27309. Stateful_postRender: function () {
  27310. if (this.disabled && !this.hasState('disabled')) {
  27311. this.addState('disabled')
  27312. }
  27313. },
  27314. hasState: function (state) {
  27315. return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state)
  27316. },
  27317. addState: function (state) {
  27318. if (!this.hasState(state)) {
  27319. this.getStateDom().className += ' edui-state-' + state
  27320. }
  27321. },
  27322. removeState: function (state) {
  27323. if (this.hasState(state)) {
  27324. domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state])
  27325. }
  27326. },
  27327. getStateDom: function () {
  27328. return this.getDom('state')
  27329. },
  27330. isChecked: function () {
  27331. return this.hasState('checked')
  27332. },
  27333. setChecked: function (checked) {
  27334. if (!this.isDisabled() && checked) {
  27335. this.addState('checked')
  27336. } else {
  27337. this.removeState('checked')
  27338. }
  27339. },
  27340. isDisabled: function () {
  27341. return this.hasState('disabled')
  27342. },
  27343. setDisabled: function (disabled) {
  27344. if (disabled) {
  27345. this.removeState('hover')
  27346. this.removeState('checked')
  27347. this.removeState('active')
  27348. this.addState('disabled')
  27349. } else {
  27350. this.removeState('disabled')
  27351. }
  27352. }
  27353. }
  27354. })()
  27355. // ui/button.js
  27356. ///import core
  27357. ///import uicore
  27358. ///import ui/stateful.js
  27359. ;(function () {
  27360. var utils = baidu.editor.utils,
  27361. UIBase = baidu.editor.ui.UIBase,
  27362. Stateful = baidu.editor.ui.Stateful,
  27363. Button = (baidu.editor.ui.Button = function (options) {
  27364. if (options.name) {
  27365. var btnName = options.name
  27366. var cssRules = options.cssRules
  27367. if (!options.className) {
  27368. options.className = 'edui-for-' + btnName
  27369. }
  27370. options.cssRules =
  27371. '.edui-default .edui-for-' +
  27372. btnName +
  27373. ' .edui-icon {' +
  27374. cssRules +
  27375. '}'
  27376. }
  27377. this.initOptions(options)
  27378. this.initButton()
  27379. })
  27380. Button.prototype = {
  27381. uiName: 'button',
  27382. label: '',
  27383. title: '',
  27384. showIcon: true,
  27385. showText: true,
  27386. cssRules: '',
  27387. initButton: function () {
  27388. this.initUIBase()
  27389. this.Stateful_init()
  27390. if (this.cssRules) {
  27391. utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules)
  27392. }
  27393. },
  27394. getHtmlTpl: function () {
  27395. return (
  27396. '<div id="##" class="edui-box %%">' +
  27397. '<div id="##_state" stateful>' +
  27398. '<div class="%%-wrap"><div id="##_body" unselectable="on" ' +
  27399. (this.title ? 'title="' + this.title + '"' : '') +
  27400. ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
  27401. (this.showIcon ? '<div class="edui-box edui-icon"></div>' : '') +
  27402. (this.showText
  27403. ? '<div class="edui-box edui-label">' + this.label + '</div>'
  27404. : '') +
  27405. '</div>' +
  27406. '</div>' +
  27407. '</div></div>'
  27408. )
  27409. },
  27410. postRender: function () {
  27411. this.Stateful_postRender()
  27412. this.setDisabled(this.disabled)
  27413. },
  27414. _onMouseDown: function (e) {
  27415. var target = e.target || e.srcElement,
  27416. tagName = target && target.tagName && target.tagName.toLowerCase()
  27417. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  27418. return false
  27419. }
  27420. },
  27421. _onClick: function () {
  27422. if (!this.isDisabled()) {
  27423. this.fireEvent('click')
  27424. }
  27425. },
  27426. setTitle: function (text) {
  27427. var label = this.getDom('label')
  27428. label.innerHTML = text
  27429. }
  27430. }
  27431. utils.inherits(Button, UIBase)
  27432. utils.extend(Button.prototype, Stateful)
  27433. })()
  27434. // ui/splitbutton.js
  27435. ///import core
  27436. ///import uicore
  27437. ///import ui/stateful.js
  27438. ;(function () {
  27439. var utils = baidu.editor.utils,
  27440. uiUtils = baidu.editor.ui.uiUtils,
  27441. domUtils = baidu.editor.dom.domUtils,
  27442. UIBase = baidu.editor.ui.UIBase,
  27443. Stateful = baidu.editor.ui.Stateful,
  27444. SplitButton = (baidu.editor.ui.SplitButton = function (options) {
  27445. this.initOptions(options)
  27446. this.initSplitButton()
  27447. })
  27448. SplitButton.prototype = {
  27449. popup: null,
  27450. uiName: 'splitbutton',
  27451. title: '',
  27452. initSplitButton: function () {
  27453. this.initUIBase()
  27454. this.Stateful_init()
  27455. var me = this
  27456. if (this.popup != null) {
  27457. var popup = this.popup
  27458. this.popup = null
  27459. this.setPopup(popup)
  27460. }
  27461. },
  27462. _UIBase_postRender: UIBase.prototype.postRender,
  27463. postRender: function () {
  27464. this.Stateful_postRender()
  27465. this._UIBase_postRender()
  27466. },
  27467. setPopup: function (popup) {
  27468. if (this.popup === popup) return
  27469. if (this.popup != null) {
  27470. this.popup.dispose()
  27471. }
  27472. popup.addListener('show', utils.bind(this._onPopupShow, this))
  27473. popup.addListener('hide', utils.bind(this._onPopupHide, this))
  27474. popup.addListener(
  27475. 'postrender',
  27476. utils.bind(function () {
  27477. popup
  27478. .getDom('body')
  27479. .appendChild(
  27480. uiUtils.createElementByHtml(
  27481. '<div id="' +
  27482. this.popup.id +
  27483. '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
  27484. (uiUtils.getClientRect(this.getDom()).width + 20) +
  27485. 'px"></div>'
  27486. )
  27487. )
  27488. popup.getDom().className += ' ' + this.className
  27489. }, this)
  27490. )
  27491. this.popup = popup
  27492. },
  27493. _onPopupShow: function () {
  27494. this.addState('opened')
  27495. },
  27496. _onPopupHide: function () {
  27497. this.removeState('opened')
  27498. },
  27499. getHtmlTpl: function () {
  27500. return (
  27501. '<div id="##" class="edui-box %%">' +
  27502. '<div ' +
  27503. (this.title ? 'title="' + this.title + '"' : '') +
  27504. ' id="##_state" stateful><div class="%%-body">' +
  27505. '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
  27506. '<div class="edui-box edui-icon"></div>' +
  27507. '</div>' +
  27508. '<div class="edui-box edui-splitborder"></div>' +
  27509. '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
  27510. '</div></div></div>'
  27511. )
  27512. },
  27513. showPopup: function () {
  27514. // 当popup往上弹出的时候,做特殊处理
  27515. var rect = uiUtils.getClientRect(this.getDom())
  27516. rect.top -= this.popup.SHADOW_RADIUS
  27517. rect.height += this.popup.SHADOW_RADIUS
  27518. this.popup.showAnchorRect(rect)
  27519. },
  27520. _onArrowClick: function (event, el) {
  27521. if (!this.isDisabled()) {
  27522. this.showPopup()
  27523. }
  27524. },
  27525. _onButtonClick: function () {
  27526. if (!this.isDisabled()) {
  27527. this.fireEvent('buttonclick')
  27528. }
  27529. }
  27530. }
  27531. utils.inherits(SplitButton, UIBase)
  27532. utils.extend(SplitButton.prototype, Stateful, true)
  27533. })()
  27534. // ui/colorbutton.js
  27535. ///import core
  27536. ///import uicore
  27537. ///import ui/colorpicker.js
  27538. ///import ui/popup.js
  27539. ///import ui/splitbutton.js
  27540. ;(function () {
  27541. var utils = baidu.editor.utils,
  27542. uiUtils = baidu.editor.ui.uiUtils,
  27543. ColorPicker = baidu.editor.ui.ColorPicker,
  27544. Popup = baidu.editor.ui.Popup,
  27545. SplitButton = baidu.editor.ui.SplitButton,
  27546. ColorButton = (baidu.editor.ui.ColorButton = function (options) {
  27547. this.initOptions(options)
  27548. this.initColorButton()
  27549. })
  27550. ColorButton.prototype = {
  27551. initColorButton: function () {
  27552. var me = this
  27553. this.popup = new Popup({
  27554. content: new ColorPicker({
  27555. noColorText: me.editor.getLang('clearColor'),
  27556. editor: me.editor,
  27557. onpickcolor: function (t, color) {
  27558. me._onPickColor(color)
  27559. },
  27560. onpicknocolor: function (t, color) {
  27561. me._onPickNoColor(color)
  27562. }
  27563. }),
  27564. editor: me.editor
  27565. })
  27566. this.initSplitButton()
  27567. },
  27568. _SplitButton_postRender: SplitButton.prototype.postRender,
  27569. postRender: function () {
  27570. this._SplitButton_postRender()
  27571. this.getDom('button_body').appendChild(
  27572. uiUtils.createElementByHtml(
  27573. '<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>'
  27574. )
  27575. )
  27576. this.getDom().className += ' edui-colorbutton'
  27577. },
  27578. setColor: function (color) {
  27579. this.getDom('colorlump').style.backgroundColor = color
  27580. this.color = color
  27581. },
  27582. _onPickColor: function (color) {
  27583. if (this.fireEvent('pickcolor', color) !== false) {
  27584. this.setColor(color)
  27585. this.popup.hide()
  27586. }
  27587. },
  27588. _onPickNoColor: function (color) {
  27589. if (this.fireEvent('picknocolor') !== false) {
  27590. this.popup.hide()
  27591. }
  27592. }
  27593. }
  27594. utils.inherits(ColorButton, SplitButton)
  27595. })()
  27596. // ui/tablebutton.js
  27597. ///import core
  27598. ///import uicore
  27599. ///import ui/popup.js
  27600. ///import ui/tablepicker.js
  27601. ///import ui/splitbutton.js
  27602. ;(function () {
  27603. var utils = baidu.editor.utils,
  27604. Popup = baidu.editor.ui.Popup,
  27605. TablePicker = baidu.editor.ui.TablePicker,
  27606. SplitButton = baidu.editor.ui.SplitButton,
  27607. TableButton = (baidu.editor.ui.TableButton = function (options) {
  27608. this.initOptions(options)
  27609. this.initTableButton()
  27610. })
  27611. TableButton.prototype = {
  27612. initTableButton: function () {
  27613. var me = this
  27614. this.popup = new Popup({
  27615. content: new TablePicker({
  27616. editor: me.editor,
  27617. onpicktable: function (t, numCols, numRows) {
  27618. me._onPickTable(numCols, numRows)
  27619. }
  27620. }),
  27621. editor: me.editor
  27622. })
  27623. this.initSplitButton()
  27624. },
  27625. _onPickTable: function (numCols, numRows) {
  27626. if (this.fireEvent('picktable', numCols, numRows) !== false) {
  27627. this.popup.hide()
  27628. }
  27629. }
  27630. }
  27631. utils.inherits(TableButton, SplitButton)
  27632. })()
  27633. // ui/autotypesetpicker.js
  27634. ///import core
  27635. ///import uicore
  27636. ;(function () {
  27637. var utils = baidu.editor.utils,
  27638. UIBase = baidu.editor.ui.UIBase
  27639. var AutoTypeSetPicker = (baidu.editor.ui.AutoTypeSetPicker = function (
  27640. options
  27641. ) {
  27642. this.initOptions(options)
  27643. this.initAutoTypeSetPicker()
  27644. })
  27645. AutoTypeSetPicker.prototype = {
  27646. initAutoTypeSetPicker: function () {
  27647. this.initUIBase()
  27648. },
  27649. getHtmlTpl: function () {
  27650. var me = this.editor,
  27651. opt = me.options.autotypeset,
  27652. lang = me.getLang('autoTypeSet')
  27653. var textAlignInputName = 'textAlignValue' + me.uid,
  27654. imageBlockInputName = 'imageBlockLineValue' + me.uid,
  27655. symbolConverInputName = 'symbolConverValue' + me.uid
  27656. return (
  27657. '<div id="##" class="edui-autotypesetpicker %%">' +
  27658. '<div class="edui-autotypesetpicker-body">' +
  27659. '<table >' +
  27660. '<tr><td nowrap><input type="checkbox" name="mergeEmptyline" ' +
  27661. (opt['mergeEmptyline'] ? 'checked' : '') +
  27662. '>' +
  27663. lang.mergeLine +
  27664. '</td><td colspan="2"><input type="checkbox" name="removeEmptyline" ' +
  27665. (opt['removeEmptyline'] ? 'checked' : '') +
  27666. '>' +
  27667. lang.delLine +
  27668. '</td></tr>' +
  27669. '<tr><td nowrap><input type="checkbox" name="removeClass" ' +
  27670. (opt['removeClass'] ? 'checked' : '') +
  27671. '>' +
  27672. lang.removeFormat +
  27673. '</td><td colspan="2"><input type="checkbox" name="indent" ' +
  27674. (opt['indent'] ? 'checked' : '') +
  27675. '>' +
  27676. lang.indent +
  27677. '</td></tr>' +
  27678. '<tr>' +
  27679. '<td nowrap><input type="checkbox" name="textAlign" ' +
  27680. (opt['textAlign'] ? 'checked' : '') +
  27681. '>' +
  27682. lang.alignment +
  27683. '</td>' +
  27684. '<td colspan="2" id="' +
  27685. textAlignInputName +
  27686. '">' +
  27687. '<input type="radio" name="' +
  27688. textAlignInputName +
  27689. '" value="left" ' +
  27690. (opt['textAlign'] && opt['textAlign'] == 'left' ? 'checked' : '') +
  27691. '>' +
  27692. me.getLang('justifyleft') +
  27693. '<input type="radio" name="' +
  27694. textAlignInputName +
  27695. '" value="center" ' +
  27696. (opt['textAlign'] && opt['textAlign'] == 'center' ? 'checked' : '') +
  27697. '>' +
  27698. me.getLang('justifycenter') +
  27699. '<input type="radio" name="' +
  27700. textAlignInputName +
  27701. '" value="right" ' +
  27702. (opt['textAlign'] && opt['textAlign'] == 'right' ? 'checked' : '') +
  27703. '>' +
  27704. me.getLang('justifyright') +
  27705. '</td>' +
  27706. '</tr>' +
  27707. '<tr>' +
  27708. '<td nowrap><input type="checkbox" name="imageBlockLine" ' +
  27709. (opt['imageBlockLine'] ? 'checked' : '') +
  27710. '>' +
  27711. lang.imageFloat +
  27712. '</td>' +
  27713. '<td nowrap id="' +
  27714. imageBlockInputName +
  27715. '">' +
  27716. '<input type="radio" name="' +
  27717. imageBlockInputName +
  27718. '" value="none" ' +
  27719. (opt['imageBlockLine'] && opt['imageBlockLine'] == 'none'
  27720. ? 'checked'
  27721. : '') +
  27722. '>' +
  27723. me.getLang('default') +
  27724. '<input type="radio" name="' +
  27725. imageBlockInputName +
  27726. '" value="left" ' +
  27727. (opt['imageBlockLine'] && opt['imageBlockLine'] == 'left'
  27728. ? 'checked'
  27729. : '') +
  27730. '>' +
  27731. me.getLang('justifyleft') +
  27732. '<input type="radio" name="' +
  27733. imageBlockInputName +
  27734. '" value="center" ' +
  27735. (opt['imageBlockLine'] && opt['imageBlockLine'] == 'center'
  27736. ? 'checked'
  27737. : '') +
  27738. '>' +
  27739. me.getLang('justifycenter') +
  27740. '<input type="radio" name="' +
  27741. imageBlockInputName +
  27742. '" value="right" ' +
  27743. (opt['imageBlockLine'] && opt['imageBlockLine'] == 'right'
  27744. ? 'checked'
  27745. : '') +
  27746. '>' +
  27747. me.getLang('justifyright') +
  27748. '</td>' +
  27749. '</tr>' +
  27750. '<tr><td nowrap><input type="checkbox" name="clearFontSize" ' +
  27751. (opt['clearFontSize'] ? 'checked' : '') +
  27752. '>' +
  27753. lang.removeFontsize +
  27754. '</td><td colspan="2"><input type="checkbox" name="clearFontFamily" ' +
  27755. (opt['clearFontFamily'] ? 'checked' : '') +
  27756. '>' +
  27757. lang.removeFontFamily +
  27758. '</td></tr>' +
  27759. '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' +
  27760. (opt['removeEmptyNode'] ? 'checked' : '') +
  27761. '>' +
  27762. lang.removeHtml +
  27763. '</td></tr>' +
  27764. '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' +
  27765. (opt['pasteFilter'] ? 'checked' : '') +
  27766. '>' +
  27767. lang.pasteFilter +
  27768. '</td></tr>' +
  27769. '<tr>' +
  27770. '<td nowrap><input type="checkbox" name="symbolConver" ' +
  27771. (opt['bdc2sb'] || opt['tobdc'] ? 'checked' : '') +
  27772. '>' +
  27773. lang.symbol +
  27774. '</td>' +
  27775. '<td id="' +
  27776. symbolConverInputName +
  27777. '">' +
  27778. '<input type="radio" name="bdc" value="bdc2sb" ' +
  27779. (opt['bdc2sb'] ? 'checked' : '') +
  27780. '>' +
  27781. lang.bdc2sb +
  27782. '<input type="radio" name="bdc" value="tobdc" ' +
  27783. (opt['tobdc'] ? 'checked' : '') +
  27784. '>' +
  27785. lang.tobdc +
  27786. '' +
  27787. '</td>' +
  27788. '<td nowrap align="right"><button >' +
  27789. lang.run +
  27790. '</button></td>' +
  27791. '</tr>' +
  27792. '</table>' +
  27793. '</div>' +
  27794. '</div>'
  27795. )
  27796. },
  27797. _UIBase_render: UIBase.prototype.render
  27798. }
  27799. utils.inherits(AutoTypeSetPicker, UIBase)
  27800. })()
  27801. // ui/autotypesetbutton.js
  27802. ///import core
  27803. ///import uicore
  27804. ///import ui/popup.js
  27805. ///import ui/autotypesetpicker.js
  27806. ///import ui/splitbutton.js
  27807. ;(function () {
  27808. var utils = baidu.editor.utils,
  27809. Popup = baidu.editor.ui.Popup,
  27810. AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
  27811. SplitButton = baidu.editor.ui.SplitButton,
  27812. AutoTypeSetButton = (baidu.editor.ui.AutoTypeSetButton = function (
  27813. options
  27814. ) {
  27815. this.initOptions(options)
  27816. this.initAutoTypeSetButton()
  27817. })
  27818. function getPara(me) {
  27819. var opt = {},
  27820. cont = me.getDom(),
  27821. editorId = me.editor.uid,
  27822. inputType = null,
  27823. attrName = null,
  27824. ipts = domUtils.getElementsByTagName(cont, 'input')
  27825. for (var i = ipts.length - 1, ipt; (ipt = ipts[i--]); ) {
  27826. inputType = ipt.getAttribute('type')
  27827. if (inputType == 'checkbox') {
  27828. attrName = ipt.getAttribute('name')
  27829. opt[attrName] && delete opt[attrName]
  27830. if (ipt.checked) {
  27831. var attrValue = document.getElementById(
  27832. attrName + 'Value' + editorId
  27833. )
  27834. if (attrValue) {
  27835. if (/input/gi.test(attrValue.tagName)) {
  27836. opt[attrName] = attrValue.value
  27837. } else {
  27838. var iptChilds = attrValue.getElementsByTagName('input')
  27839. for (
  27840. var j = iptChilds.length - 1, iptchild;
  27841. (iptchild = iptChilds[j--]);
  27842. ) {
  27843. if (iptchild.checked) {
  27844. opt[attrName] = iptchild.value
  27845. break
  27846. }
  27847. }
  27848. }
  27849. } else {
  27850. opt[attrName] = true
  27851. }
  27852. } else {
  27853. opt[attrName] = false
  27854. }
  27855. } else {
  27856. opt[ipt.getAttribute('value')] = ipt.checked
  27857. }
  27858. }
  27859. var selects = domUtils.getElementsByTagName(cont, 'select')
  27860. for (var i = 0, si; (si = selects[i++]); ) {
  27861. var attr = si.getAttribute('name')
  27862. opt[attr] = opt[attr] ? si.value : ''
  27863. }
  27864. utils.extend(me.editor.options.autotypeset, opt)
  27865. me.editor.setPreferences('autotypeset', opt)
  27866. }
  27867. AutoTypeSetButton.prototype = {
  27868. initAutoTypeSetButton: function () {
  27869. var me = this
  27870. this.popup = new Popup({
  27871. //传入配置参数
  27872. content: new AutoTypeSetPicker({ editor: me.editor }),
  27873. editor: me.editor,
  27874. hide: function () {
  27875. if (!this._hidden && this.getDom()) {
  27876. getPara(this)
  27877. this.getDom().style.display = 'none'
  27878. this._hidden = true
  27879. this.fireEvent('hide')
  27880. }
  27881. }
  27882. })
  27883. var flag = 0
  27884. this.popup.addListener('postRenderAfter', function () {
  27885. var popupUI = this
  27886. if (flag) return
  27887. var cont = this.getDom(),
  27888. btn = cont.getElementsByTagName('button')[0]
  27889. btn.onclick = function () {
  27890. getPara(popupUI)
  27891. me.editor.execCommand('autotypeset')
  27892. popupUI.hide()
  27893. }
  27894. domUtils.on(cont, 'click', function (e) {
  27895. var target = e.target || e.srcElement,
  27896. editorId = me.editor.uid
  27897. if (target && target.tagName == 'INPUT') {
  27898. // 点击图片浮动的checkbox,去除对应的radio
  27899. if (
  27900. target.name == 'imageBlockLine' ||
  27901. target.name == 'textAlign' ||
  27902. target.name == 'symbolConver'
  27903. ) {
  27904. var checked = target.checked,
  27905. radioTd = document.getElementById(
  27906. target.name + 'Value' + editorId
  27907. ),
  27908. radios = radioTd.getElementsByTagName('input'),
  27909. defalutSelect = {
  27910. imageBlockLine: 'none',
  27911. textAlign: 'left',
  27912. symbolConver: 'tobdc'
  27913. }
  27914. for (var i = 0; i < radios.length; i++) {
  27915. if (checked) {
  27916. if (radios[i].value == defalutSelect[target.name]) {
  27917. radios[i].checked = 'checked'
  27918. }
  27919. } else {
  27920. radios[i].checked = false
  27921. }
  27922. }
  27923. }
  27924. // 点击radio,选中对应的checkbox
  27925. if (
  27926. target.name == 'imageBlockLineValue' + editorId ||
  27927. target.name == 'textAlignValue' + editorId ||
  27928. target.name == 'bdc'
  27929. ) {
  27930. var checkboxs =
  27931. target.parentNode.previousSibling.getElementsByTagName(
  27932. 'input'
  27933. )
  27934. checkboxs && (checkboxs[0].checked = true)
  27935. }
  27936. getPara(popupUI)
  27937. }
  27938. })
  27939. flag = 1
  27940. })
  27941. this.initSplitButton()
  27942. }
  27943. }
  27944. utils.inherits(AutoTypeSetButton, SplitButton)
  27945. })()
  27946. // ui/cellalignpicker.js
  27947. ///import core
  27948. ///import uicore
  27949. ;(function () {
  27950. var utils = baidu.editor.utils,
  27951. Popup = baidu.editor.ui.Popup,
  27952. Stateful = baidu.editor.ui.Stateful,
  27953. UIBase = baidu.editor.ui.UIBase
  27954. /**
  27955. * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
  27956. * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
  27957. * @update 2013/4/2 hancong03@baidu.com
  27958. */
  27959. var CellAlignPicker = (baidu.editor.ui.CellAlignPicker = function (
  27960. options
  27961. ) {
  27962. this.initOptions(options)
  27963. this.initSelected()
  27964. this.initCellAlignPicker()
  27965. })
  27966. CellAlignPicker.prototype = {
  27967. //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
  27968. initSelected: function () {
  27969. var status = {
  27970. valign: {
  27971. top: 0,
  27972. middle: 1,
  27973. bottom: 2
  27974. },
  27975. align: {
  27976. left: 0,
  27977. center: 1,
  27978. right: 2
  27979. },
  27980. count: 3
  27981. },
  27982. result = -1
  27983. if (this.selected) {
  27984. this.selectedIndex =
  27985. status.valign[this.selected.valign] * status.count +
  27986. status.align[this.selected.align]
  27987. }
  27988. },
  27989. initCellAlignPicker: function () {
  27990. this.initUIBase()
  27991. this.Stateful_init()
  27992. },
  27993. getHtmlTpl: function () {
  27994. var alignType = ['left', 'center', 'right'],
  27995. COUNT = 9,
  27996. tempClassName = null,
  27997. tempIndex = -1,
  27998. tmpl = []
  27999. for (var i = 0; i < COUNT; i++) {
  28000. tempClassName =
  28001. this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : ''
  28002. tempIndex = i % 3
  28003. tempIndex === 0 && tmpl.push('<tr>')
  28004. tmpl.push(
  28005. '<td index="' +
  28006. i +
  28007. '" ' +
  28008. tempClassName +
  28009. ' stateful><div class="edui-icon edui-' +
  28010. alignType[tempIndex] +
  28011. '"></div></td>'
  28012. )
  28013. tempIndex === 2 && tmpl.push('</tr>')
  28014. }
  28015. return (
  28016. '<div id="##" class="edui-cellalignpicker %%">' +
  28017. '<div class="edui-cellalignpicker-body">' +
  28018. '<table onclick="$$._onClick(event);">' +
  28019. tmpl.join('') +
  28020. '</table>' +
  28021. '</div>' +
  28022. '</div>'
  28023. )
  28024. },
  28025. getStateDom: function () {
  28026. return this.target
  28027. },
  28028. _onClick: function (evt) {
  28029. var target = evt.target || evt.srcElement
  28030. if (/icon/.test(target.className)) {
  28031. this.items[target.parentNode.getAttribute('index')].onclick()
  28032. Popup.postHide(evt)
  28033. }
  28034. },
  28035. _UIBase_render: UIBase.prototype.render
  28036. }
  28037. utils.inherits(CellAlignPicker, UIBase)
  28038. utils.extend(CellAlignPicker.prototype, Stateful, true)
  28039. })()
  28040. // ui/pastepicker.js
  28041. ///import core
  28042. ///import uicore
  28043. ;(function () {
  28044. var utils = baidu.editor.utils,
  28045. Stateful = baidu.editor.ui.Stateful,
  28046. uiUtils = baidu.editor.ui.uiUtils,
  28047. UIBase = baidu.editor.ui.UIBase
  28048. var PastePicker = (baidu.editor.ui.PastePicker = function (options) {
  28049. this.initOptions(options)
  28050. this.initPastePicker()
  28051. })
  28052. PastePicker.prototype = {
  28053. initPastePicker: function () {
  28054. this.initUIBase()
  28055. this.Stateful_init()
  28056. },
  28057. getHtmlTpl: function () {
  28058. return (
  28059. '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
  28060. '<div class="edui-pastecontainer">' +
  28061. '<div class="edui-title">' +
  28062. this.editor.getLang('pasteOpt') +
  28063. '</div>' +
  28064. '<div class="edui-button">' +
  28065. '<div title="' +
  28066. this.editor.getLang('pasteSourceFormat') +
  28067. '" onclick="$$.format(false)" stateful>' +
  28068. '<div class="edui-richtxticon"></div></div>' +
  28069. '<div title="' +
  28070. this.editor.getLang('tagFormat') +
  28071. '" onclick="$$.format(2)" stateful>' +
  28072. '<div class="edui-tagicon"></div></div>' +
  28073. '<div title="' +
  28074. this.editor.getLang('pasteTextFormat') +
  28075. '" onclick="$$.format(true)" stateful>' +
  28076. '<div class="edui-plaintxticon"></div></div>' +
  28077. '</div>' +
  28078. '</div>' +
  28079. '</div>'
  28080. )
  28081. },
  28082. getStateDom: function () {
  28083. return this.target
  28084. },
  28085. format: function (param) {
  28086. this.editor.ui._isTransfer = true
  28087. this.editor.fireEvent('pasteTransfer', param)
  28088. },
  28089. _onClick: function (cur) {
  28090. var node = domUtils.getNextDomNode(cur),
  28091. screenHt = uiUtils.getViewportRect().height,
  28092. subPop = uiUtils.getClientRect(node)
  28093. if (subPop.top + subPop.height > screenHt)
  28094. node.style.top = -subPop.height - cur.offsetHeight + 'px'
  28095. else node.style.top = ''
  28096. if (/hidden/gi.test(domUtils.getComputedStyle(node, 'visibility'))) {
  28097. node.style.visibility = 'visible'
  28098. domUtils.addClass(cur, 'edui-state-opened')
  28099. } else {
  28100. node.style.visibility = 'hidden'
  28101. domUtils.removeClasses(cur, 'edui-state-opened')
  28102. }
  28103. },
  28104. _UIBase_render: UIBase.prototype.render
  28105. }
  28106. utils.inherits(PastePicker, UIBase)
  28107. utils.extend(PastePicker.prototype, Stateful, true)
  28108. })()
  28109. // ui/toolbar.js
  28110. ;(function () {
  28111. var utils = baidu.editor.utils,
  28112. uiUtils = baidu.editor.ui.uiUtils,
  28113. UIBase = baidu.editor.ui.UIBase,
  28114. Toolbar = (baidu.editor.ui.Toolbar = function (options) {
  28115. this.initOptions(options)
  28116. this.initToolbar()
  28117. })
  28118. Toolbar.prototype = {
  28119. items: null,
  28120. initToolbar: function () {
  28121. this.items = this.items || []
  28122. this.initUIBase()
  28123. },
  28124. add: function (item, index) {
  28125. if (index === undefined) {
  28126. this.items.push(item)
  28127. } else {
  28128. this.items.splice(index, 0, item)
  28129. }
  28130. },
  28131. getHtmlTpl: function () {
  28132. var buff = []
  28133. for (var i = 0; i < this.items.length; i++) {
  28134. buff[i] = this.items[i].renderHtml()
  28135. }
  28136. return (
  28137. '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
  28138. buff.join('') +
  28139. '</div>'
  28140. )
  28141. },
  28142. postRender: function () {
  28143. var box = this.getDom()
  28144. for (var i = 0; i < this.items.length; i++) {
  28145. this.items[i].postRender()
  28146. }
  28147. uiUtils.makeUnselectable(box)
  28148. },
  28149. _onMouseDown: function (e) {
  28150. var target = e.target || e.srcElement,
  28151. tagName = target && target.tagName && target.tagName.toLowerCase()
  28152. if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
  28153. return false
  28154. }
  28155. }
  28156. }
  28157. utils.inherits(Toolbar, UIBase)
  28158. })()
  28159. // ui/menu.js
  28160. ///import core
  28161. ///import uicore
  28162. ///import ui\popup.js
  28163. ///import ui\stateful.js
  28164. ;(function () {
  28165. var utils = baidu.editor.utils,
  28166. domUtils = baidu.editor.dom.domUtils,
  28167. uiUtils = baidu.editor.ui.uiUtils,
  28168. UIBase = baidu.editor.ui.UIBase,
  28169. Popup = baidu.editor.ui.Popup,
  28170. Stateful = baidu.editor.ui.Stateful,
  28171. CellAlignPicker = baidu.editor.ui.CellAlignPicker,
  28172. Menu = (baidu.editor.ui.Menu = function (options) {
  28173. this.initOptions(options)
  28174. this.initMenu()
  28175. })
  28176. var menuSeparator = {
  28177. renderHtml: function () {
  28178. return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>'
  28179. },
  28180. postRender: function () {},
  28181. queryAutoHide: function () {
  28182. return true
  28183. }
  28184. }
  28185. Menu.prototype = {
  28186. items: null,
  28187. uiName: 'menu',
  28188. initMenu: function () {
  28189. this.items = this.items || []
  28190. this.initPopup()
  28191. this.initItems()
  28192. },
  28193. initItems: function () {
  28194. for (var i = 0; i < this.items.length; i++) {
  28195. var item = this.items[i]
  28196. if (item == '-') {
  28197. this.items[i] = this.getSeparator()
  28198. } else if (!(item instanceof MenuItem)) {
  28199. item.editor = this.editor
  28200. item.theme = this.editor.options.theme
  28201. this.items[i] = this.createItem(item)
  28202. }
  28203. }
  28204. },
  28205. getSeparator: function () {
  28206. return menuSeparator
  28207. },
  28208. createItem: function (item) {
  28209. //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  28210. item.menu = this
  28211. return new MenuItem(item)
  28212. },
  28213. _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
  28214. getContentHtmlTpl: function () {
  28215. if (this.items.length == 0) {
  28216. return this._Popup_getContentHtmlTpl()
  28217. }
  28218. var buff = []
  28219. for (var i = 0; i < this.items.length; i++) {
  28220. var item = this.items[i]
  28221. buff[i] = item.renderHtml()
  28222. }
  28223. return '<div class="%%-body">' + buff.join('') + '</div>'
  28224. },
  28225. _Popup_postRender: Popup.prototype.postRender,
  28226. postRender: function () {
  28227. var me = this
  28228. for (var i = 0; i < this.items.length; i++) {
  28229. var item = this.items[i]
  28230. item.ownerMenu = this
  28231. item.postRender()
  28232. }
  28233. domUtils.on(this.getDom(), 'mouseover', function (evt) {
  28234. evt = evt || event
  28235. var rel = evt.relatedTarget || evt.fromElement
  28236. var el = me.getDom()
  28237. if (!uiUtils.contains(el, rel) && el !== rel) {
  28238. me.fireEvent('over')
  28239. }
  28240. })
  28241. this._Popup_postRender()
  28242. },
  28243. queryAutoHide: function (el) {
  28244. if (el) {
  28245. if (uiUtils.contains(this.getDom(), el)) {
  28246. return false
  28247. }
  28248. for (var i = 0; i < this.items.length; i++) {
  28249. var item = this.items[i]
  28250. if (item.queryAutoHide(el) === false) {
  28251. return false
  28252. }
  28253. }
  28254. }
  28255. },
  28256. clearItems: function () {
  28257. for (var i = 0; i < this.items.length; i++) {
  28258. var item = this.items[i]
  28259. clearTimeout(item._showingTimer)
  28260. clearTimeout(item._closingTimer)
  28261. if (item.subMenu) {
  28262. item.subMenu.destroy()
  28263. }
  28264. }
  28265. this.items = []
  28266. },
  28267. destroy: function () {
  28268. if (this.getDom()) {
  28269. domUtils.remove(this.getDom())
  28270. }
  28271. this.clearItems()
  28272. },
  28273. dispose: function () {
  28274. this.destroy()
  28275. }
  28276. }
  28277. utils.inherits(Menu, Popup)
  28278. /**
  28279. * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
  28280. * @type {Function}
  28281. */
  28282. var MenuItem = (baidu.editor.ui.MenuItem = function (options) {
  28283. this.initOptions(options)
  28284. this.initUIBase()
  28285. this.Stateful_init()
  28286. if (this.subMenu && !(this.subMenu instanceof Menu)) {
  28287. if (options.className && options.className.indexOf('aligntd') != -1) {
  28288. var me = this
  28289. //获取单元格对齐初始状态
  28290. this.subMenu.selected = this.editor.queryCommandValue('cellalignment')
  28291. this.subMenu = new Popup({
  28292. content: new CellAlignPicker(this.subMenu),
  28293. parentMenu: me,
  28294. editor: me.editor,
  28295. destroy: function () {
  28296. if (this.getDom()) {
  28297. domUtils.remove(this.getDom())
  28298. }
  28299. }
  28300. })
  28301. this.subMenu.addListener('postRenderAfter', function () {
  28302. domUtils.on(this.getDom(), 'mouseover', function () {
  28303. me.addState('opened')
  28304. })
  28305. })
  28306. } else {
  28307. this.subMenu = new Menu(this.subMenu)
  28308. }
  28309. }
  28310. })
  28311. MenuItem.prototype = {
  28312. label: '',
  28313. subMenu: null,
  28314. ownerMenu: null,
  28315. uiName: 'menuitem',
  28316. alwalysHoverable: true,
  28317. getHtmlTpl: function () {
  28318. return (
  28319. '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
  28320. '<div class="%%-body">' +
  28321. this.renderLabelHtml() +
  28322. '</div>' +
  28323. '</div>'
  28324. )
  28325. },
  28326. postRender: function () {
  28327. var me = this
  28328. this.addListener('over', function () {
  28329. me.ownerMenu.fireEvent('submenuover', me)
  28330. if (me.subMenu) {
  28331. me.delayShowSubMenu()
  28332. }
  28333. })
  28334. if (this.subMenu) {
  28335. this.getDom().className += ' edui-hassubmenu'
  28336. this.subMenu.render()
  28337. this.addListener('out', function () {
  28338. me.delayHideSubMenu()
  28339. })
  28340. this.subMenu.addListener('over', function () {
  28341. clearTimeout(me._closingTimer)
  28342. me._closingTimer = null
  28343. me.addState('opened')
  28344. })
  28345. this.ownerMenu.addListener('hide', function () {
  28346. me.hideSubMenu()
  28347. })
  28348. this.ownerMenu.addListener('submenuover', function (t, subMenu) {
  28349. if (subMenu !== me) {
  28350. me.delayHideSubMenu()
  28351. }
  28352. })
  28353. this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide
  28354. this.subMenu.queryAutoHide = function (el) {
  28355. if (el && uiUtils.contains(me.getDom(), el)) {
  28356. return false
  28357. }
  28358. return this._bakQueryAutoHide(el)
  28359. }
  28360. }
  28361. this.getDom().style.tabIndex = '-1'
  28362. uiUtils.makeUnselectable(this.getDom())
  28363. this.Stateful_postRender()
  28364. },
  28365. delayShowSubMenu: function () {
  28366. var me = this
  28367. if (!me.isDisabled()) {
  28368. me.addState('opened')
  28369. clearTimeout(me._showingTimer)
  28370. clearTimeout(me._closingTimer)
  28371. me._closingTimer = null
  28372. me._showingTimer = setTimeout(function () {
  28373. me.showSubMenu()
  28374. }, 250)
  28375. }
  28376. },
  28377. delayHideSubMenu: function () {
  28378. var me = this
  28379. if (!me.isDisabled()) {
  28380. me.removeState('opened')
  28381. clearTimeout(me._showingTimer)
  28382. if (!me._closingTimer) {
  28383. me._closingTimer = setTimeout(function () {
  28384. if (!me.hasState('opened')) {
  28385. me.hideSubMenu()
  28386. }
  28387. me._closingTimer = null
  28388. }, 400)
  28389. }
  28390. }
  28391. },
  28392. renderLabelHtml: function () {
  28393. return (
  28394. '<div class="edui-arrow"></div>' +
  28395. '<div class="edui-box edui-icon"></div>' +
  28396. '<div class="edui-box edui-label %%-label">' +
  28397. (this.label || '') +
  28398. '</div>'
  28399. )
  28400. },
  28401. getStateDom: function () {
  28402. return this.getDom()
  28403. },
  28404. queryAutoHide: function (el) {
  28405. if (this.subMenu && this.hasState('opened')) {
  28406. return this.subMenu.queryAutoHide(el)
  28407. }
  28408. },
  28409. _onClick: function (event, this_) {
  28410. if (this.hasState('disabled')) return
  28411. if (this.fireEvent('click', event, this_) !== false) {
  28412. if (this.subMenu) {
  28413. this.showSubMenu()
  28414. } else {
  28415. Popup.postHide(event)
  28416. }
  28417. }
  28418. },
  28419. showSubMenu: function () {
  28420. var rect = uiUtils.getClientRect(this.getDom())
  28421. rect.right -= 5
  28422. rect.left += 2
  28423. rect.width -= 7
  28424. rect.top -= 4
  28425. rect.bottom += 4
  28426. rect.height += 8
  28427. this.subMenu.showAnchorRect(rect, true, true)
  28428. },
  28429. hideSubMenu: function () {
  28430. this.subMenu.hide()
  28431. }
  28432. }
  28433. utils.inherits(MenuItem, UIBase)
  28434. utils.extend(MenuItem.prototype, Stateful, true)
  28435. })()
  28436. // ui/combox.js
  28437. ///import core
  28438. ///import uicore
  28439. ///import ui/menu.js
  28440. ///import ui/splitbutton.js
  28441. ;(function () {
  28442. // todo: menu和item提成通用list
  28443. var utils = baidu.editor.utils,
  28444. uiUtils = baidu.editor.ui.uiUtils,
  28445. Menu = baidu.editor.ui.Menu,
  28446. SplitButton = baidu.editor.ui.SplitButton,
  28447. Combox = (baidu.editor.ui.Combox = function (options) {
  28448. this.initOptions(options)
  28449. this.initCombox()
  28450. })
  28451. Combox.prototype = {
  28452. uiName: 'combox',
  28453. onbuttonclick: function () {
  28454. this.showPopup()
  28455. },
  28456. initCombox: function () {
  28457. var me = this
  28458. this.items = this.items || []
  28459. for (var i = 0; i < this.items.length; i++) {
  28460. var item = this.items[i]
  28461. item.uiName = 'listitem'
  28462. item.index = i
  28463. item.onclick = function () {
  28464. me.selectByIndex(this.index)
  28465. }
  28466. }
  28467. this.popup = new Menu({
  28468. items: this.items,
  28469. uiName: 'list',
  28470. editor: this.editor,
  28471. captureWheel: true,
  28472. combox: this
  28473. })
  28474. this.initSplitButton()
  28475. },
  28476. _SplitButton_postRender: SplitButton.prototype.postRender,
  28477. postRender: function () {
  28478. this._SplitButton_postRender()
  28479. this.setLabel(this.label || '')
  28480. this.setValue(this.initValue || '')
  28481. },
  28482. showPopup: function () {
  28483. var rect = uiUtils.getClientRect(this.getDom())
  28484. rect.top += 1
  28485. rect.bottom -= 1
  28486. rect.height -= 2
  28487. this.popup.showAnchorRect(rect)
  28488. },
  28489. getValue: function () {
  28490. return this.value
  28491. },
  28492. setValue: function (value) {
  28493. var index = this.indexByValue(value)
  28494. if (index != -1) {
  28495. this.selectedIndex = index
  28496. this.setLabel(this.items[index].label)
  28497. this.value = this.items[index].value
  28498. } else {
  28499. this.selectedIndex = -1
  28500. this.setLabel(this.getLabelForUnknowValue(value))
  28501. this.value = value
  28502. }
  28503. },
  28504. setLabel: function (label) {
  28505. this.getDom('button_body').innerHTML = label
  28506. this.label = label
  28507. },
  28508. getLabelForUnknowValue: function (value) {
  28509. return value
  28510. },
  28511. indexByValue: function (value) {
  28512. for (var i = 0; i < this.items.length; i++) {
  28513. if (value == this.items[i].value) {
  28514. return i
  28515. }
  28516. }
  28517. return -1
  28518. },
  28519. getItem: function (index) {
  28520. return this.items[index]
  28521. },
  28522. selectByIndex: function (index) {
  28523. if (
  28524. index < this.items.length &&
  28525. this.fireEvent('select', index) !== false
  28526. ) {
  28527. this.selectedIndex = index
  28528. this.value = this.items[index].value
  28529. this.setLabel(this.items[index].label)
  28530. }
  28531. }
  28532. }
  28533. utils.inherits(Combox, SplitButton)
  28534. })()
  28535. // ui/dialog.js
  28536. ///import core
  28537. ///import uicore
  28538. ///import ui/mask.js
  28539. ///import ui/button.js
  28540. ;(function () {
  28541. var utils = baidu.editor.utils,
  28542. domUtils = baidu.editor.dom.domUtils,
  28543. uiUtils = baidu.editor.ui.uiUtils,
  28544. Mask = baidu.editor.ui.Mask,
  28545. UIBase = baidu.editor.ui.UIBase,
  28546. Button = baidu.editor.ui.Button,
  28547. Dialog = (baidu.editor.ui.Dialog = function (options) {
  28548. if (options.name) {
  28549. var name = options.name
  28550. var cssRules = options.cssRules
  28551. if (!options.className) {
  28552. options.className = 'edui-for-' + name
  28553. }
  28554. if (cssRules) {
  28555. options.cssRules =
  28556. '.edui-default .edui-for-' +
  28557. name +
  28558. ' .edui-dialog-content {' +
  28559. cssRules +
  28560. '}'
  28561. }
  28562. }
  28563. this.initOptions(
  28564. utils.extend(
  28565. {
  28566. autoReset: true,
  28567. draggable: true,
  28568. onok: function () {},
  28569. oncancel: function () {},
  28570. onclose: function (t, ok) {
  28571. return ok ? this.onok() : this.oncancel()
  28572. },
  28573. //是否控制dialog中的scroll事件, 默认为不阻止
  28574. holdScroll: false
  28575. },
  28576. options
  28577. )
  28578. )
  28579. this.initDialog()
  28580. })
  28581. var modalMask
  28582. var dragMask
  28583. var activeDialog
  28584. Dialog.prototype = {
  28585. draggable: false,
  28586. uiName: 'dialog',
  28587. initDialog: function () {
  28588. var me = this,
  28589. theme = this.editor.options.theme
  28590. if (this.cssRules) {
  28591. utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules)
  28592. }
  28593. this.initUIBase()
  28594. this.modalMask =
  28595. modalMask ||
  28596. (modalMask = new Mask({
  28597. className: 'edui-dialog-modalmask',
  28598. theme: theme,
  28599. onclick: function () {
  28600. activeDialog && activeDialog.close(false)
  28601. }
  28602. }))
  28603. this.dragMask =
  28604. dragMask ||
  28605. (dragMask = new Mask({
  28606. className: 'edui-dialog-dragmask',
  28607. theme: theme
  28608. }))
  28609. this.closeButton = new Button({
  28610. className: 'edui-dialog-closebutton',
  28611. title: me.closeDialog,
  28612. theme: theme,
  28613. onclick: function () {
  28614. me.close(false)
  28615. }
  28616. })
  28617. this.fullscreen && this.initResizeEvent()
  28618. if (this.buttons) {
  28619. for (var i = 0; i < this.buttons.length; i++) {
  28620. if (!(this.buttons[i] instanceof Button)) {
  28621. this.buttons[i] = new Button(
  28622. utils.extend(
  28623. this.buttons[i],
  28624. {
  28625. editor: this.editor
  28626. },
  28627. true
  28628. )
  28629. )
  28630. }
  28631. }
  28632. }
  28633. },
  28634. initResizeEvent: function () {
  28635. var me = this
  28636. domUtils.on(window, 'resize', function () {
  28637. if (me._hidden || me._hidden === undefined) {
  28638. return
  28639. }
  28640. if (me.__resizeTimer) {
  28641. window.clearTimeout(me.__resizeTimer)
  28642. }
  28643. me.__resizeTimer = window.setTimeout(function () {
  28644. me.__resizeTimer = null
  28645. var dialogWrapNode = me.getDom(),
  28646. contentNode = me.getDom('content'),
  28647. wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
  28648. contentRect = UE.ui.uiUtils.getClientRect(contentNode),
  28649. vpRect = uiUtils.getViewportRect()
  28650. contentNode.style.width =
  28651. vpRect.width - wrapRect.width + contentRect.width + 'px'
  28652. contentNode.style.height =
  28653. vpRect.height - wrapRect.height + contentRect.height + 'px'
  28654. dialogWrapNode.style.width = vpRect.width + 'px'
  28655. dialogWrapNode.style.height = vpRect.height + 'px'
  28656. me.fireEvent('resize')
  28657. }, 100)
  28658. })
  28659. },
  28660. fitSize: function () {
  28661. var popBodyEl = this.getDom('body')
  28662. // if (!(baidu.editor.browser.ie && baidu.editor.browser.version == 7)) {
  28663. // uiUtils.removeStyle(popBodyEl, 'width');
  28664. // uiUtils.removeStyle(popBodyEl, 'height');
  28665. // }
  28666. var size = this.mesureSize()
  28667. popBodyEl.style.width = size.width + 'px'
  28668. popBodyEl.style.height = size.height + 'px'
  28669. return size
  28670. },
  28671. safeSetOffset: function (offset) {
  28672. var me = this
  28673. var el = me.getDom()
  28674. var vpRect = uiUtils.getViewportRect()
  28675. var rect = uiUtils.getClientRect(el)
  28676. var left = offset.left
  28677. if (left + rect.width > vpRect.right) {
  28678. left = vpRect.right - rect.width
  28679. }
  28680. var top = offset.top
  28681. if (top + rect.height > vpRect.bottom) {
  28682. top = vpRect.bottom - rect.height
  28683. }
  28684. el.style.left = Math.max(left, 0) + 'px'
  28685. el.style.top = Math.max(top, 0) + 'px'
  28686. },
  28687. showAtCenter: function () {
  28688. var vpRect = uiUtils.getViewportRect()
  28689. if (!this.fullscreen) {
  28690. this.getDom().style.display = ''
  28691. var popSize = this.fitSize()
  28692. var titleHeight = this.getDom('titlebar').offsetHeight | 0
  28693. var left = vpRect.width / 2 - popSize.width / 2
  28694. var top =
  28695. vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight
  28696. var popEl = this.getDom()
  28697. this.safeSetOffset({
  28698. left: Math.max(left | 0, 0),
  28699. top: Math.max(top | 0, 0)
  28700. })
  28701. if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
  28702. popEl.className += ' edui-state-centered'
  28703. }
  28704. } else {
  28705. var dialogWrapNode = this.getDom(),
  28706. contentNode = this.getDom('content')
  28707. dialogWrapNode.style.display = 'block'
  28708. var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
  28709. contentRect = UE.ui.uiUtils.getClientRect(contentNode)
  28710. dialogWrapNode.style.left = '-100000px'
  28711. contentNode.style.width =
  28712. vpRect.width - wrapRect.width + contentRect.width + 'px'
  28713. contentNode.style.height =
  28714. vpRect.height - wrapRect.height + contentRect.height + 'px'
  28715. dialogWrapNode.style.width = vpRect.width + 'px'
  28716. dialogWrapNode.style.height = vpRect.height + 'px'
  28717. dialogWrapNode.style.left = 0
  28718. //保存环境的overflow值
  28719. this._originalContext = {
  28720. html: {
  28721. overflowX: document.documentElement.style.overflowX,
  28722. overflowY: document.documentElement.style.overflowY
  28723. },
  28724. body: {
  28725. overflowX: document.body.style.overflowX,
  28726. overflowY: document.body.style.overflowY
  28727. }
  28728. }
  28729. document.documentElement.style.overflowX = 'hidden'
  28730. document.documentElement.style.overflowY = 'hidden'
  28731. document.body.style.overflowX = 'hidden'
  28732. document.body.style.overflowY = 'hidden'
  28733. }
  28734. this._show()
  28735. },
  28736. getContentHtml: function () {
  28737. var contentHtml = ''
  28738. if (typeof this.content == 'string') {
  28739. contentHtml = this.content
  28740. } else if (this.iframeUrl) {
  28741. contentHtml =
  28742. '<span id="' +
  28743. this.id +
  28744. '_contmask" class="dialogcontmask"></span><iframe id="' +
  28745. this.id +
  28746. '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="' +
  28747. this.iframeUrl +
  28748. '"></iframe>'
  28749. }
  28750. return contentHtml
  28751. },
  28752. getHtmlTpl: function () {
  28753. var footHtml = ''
  28754. if (this.buttons) {
  28755. var buff = []
  28756. for (var i = 0; i < this.buttons.length; i++) {
  28757. buff[i] = this.buttons[i].renderHtml()
  28758. }
  28759. footHtml =
  28760. '<div class="%%-foot">' +
  28761. '<div id="##_buttons" class="%%-buttons">' +
  28762. buff.join('') +
  28763. '</div>' +
  28764. '</div>'
  28765. }
  28766. return (
  28767. '<div id="##" class="%%"><div ' +
  28768. (!this.fullscreen
  28769. ? 'class="%%"'
  28770. : 'class="%%-wrap edui-dialog-fullscreen-flag"') +
  28771. '><div id="##_body" class="%%-body">' +
  28772. '<div class="%%-shadow"></div>' +
  28773. '<div id="##_titlebar" class="%%-titlebar">' +
  28774. '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
  28775. '<span class="%%-caption">' +
  28776. (this.title || '') +
  28777. '</span>' +
  28778. '</div>' +
  28779. this.closeButton.renderHtml() +
  28780. '</div>' +
  28781. '<div id="##_content" class="%%-content">' +
  28782. (this.autoReset ? '' : this.getContentHtml()) +
  28783. '</div>' +
  28784. footHtml +
  28785. '</div></div></div>'
  28786. )
  28787. },
  28788. postRender: function () {
  28789. // todo: 保持居中/记住上次关闭位置选项
  28790. if (!this.modalMask.getDom()) {
  28791. this.modalMask.render()
  28792. this.modalMask.hide()
  28793. }
  28794. if (!this.dragMask.getDom()) {
  28795. this.dragMask.render()
  28796. this.dragMask.hide()
  28797. }
  28798. var me = this
  28799. this.addListener('show', function () {
  28800. me.modalMask.show(this.getDom().style.zIndex - 2)
  28801. })
  28802. this.addListener('hide', function () {
  28803. me.modalMask.hide()
  28804. })
  28805. if (this.buttons) {
  28806. for (var i = 0; i < this.buttons.length; i++) {
  28807. this.buttons[i].postRender()
  28808. }
  28809. }
  28810. domUtils.on(window, 'resize', function () {
  28811. setTimeout(function () {
  28812. if (!me.isHidden()) {
  28813. me.safeSetOffset(uiUtils.getClientRect(me.getDom()))
  28814. }
  28815. })
  28816. })
  28817. //hold住scroll事件,防止dialog的滚动影响页面
  28818. // if( this.holdScroll ) {
  28819. //
  28820. // if( !me.iframeUrl ) {
  28821. // domUtils.on( document.getElementById( me.id + "_iframe"), !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  28822. // domUtils.preventDefault(e);
  28823. // } );
  28824. // } else {
  28825. // me.addListener('dialogafterreset', function(){
  28826. // window.setTimeout(function(){
  28827. // var iframeWindow = document.getElementById( me.id + "_iframe").contentWindow;
  28828. //
  28829. // if( browser.ie ) {
  28830. //
  28831. // var timer = window.setInterval(function(){
  28832. //
  28833. // if( iframeWindow.document && iframeWindow.document.body ) {
  28834. // window.clearInterval( timer );
  28835. // timer = null;
  28836. // domUtils.on( iframeWindow.document.body, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  28837. // domUtils.preventDefault(e);
  28838. // } );
  28839. // }
  28840. //
  28841. // }, 100);
  28842. //
  28843. // } else {
  28844. // domUtils.on( iframeWindow, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
  28845. // domUtils.preventDefault(e);
  28846. // } );
  28847. // }
  28848. //
  28849. // }, 1);
  28850. // });
  28851. // }
  28852. //
  28853. // }
  28854. this._hide()
  28855. },
  28856. mesureSize: function () {
  28857. var body = this.getDom('body')
  28858. var width = uiUtils.getClientRect(this.getDom('content')).width
  28859. var dialogBodyStyle = body.style
  28860. dialogBodyStyle.width = width
  28861. return uiUtils.getClientRect(body)
  28862. },
  28863. _onTitlebarMouseDown: function (evt, el) {
  28864. if (this.draggable) {
  28865. var rect
  28866. var vpRect = uiUtils.getViewportRect()
  28867. var me = this
  28868. uiUtils.startDrag(evt, {
  28869. ondragstart: function () {
  28870. rect = uiUtils.getClientRect(me.getDom())
  28871. me.getDom('contmask').style.visibility = 'visible'
  28872. me.dragMask.show(me.getDom().style.zIndex - 1)
  28873. },
  28874. ondragmove: function (x, y) {
  28875. var left = rect.left + x
  28876. var top = rect.top + y
  28877. me.safeSetOffset({
  28878. left: left,
  28879. top: top
  28880. })
  28881. },
  28882. ondragstop: function () {
  28883. me.getDom('contmask').style.visibility = 'hidden'
  28884. domUtils.removeClasses(me.getDom(), ['edui-state-centered'])
  28885. me.dragMask.hide()
  28886. }
  28887. })
  28888. }
  28889. },
  28890. reset: function () {
  28891. this.getDom('content').innerHTML = this.getContentHtml()
  28892. this.fireEvent('dialogafterreset')
  28893. },
  28894. _show: function () {
  28895. if (this._hidden) {
  28896. this.getDom().style.display = ''
  28897. //要高过编辑器的zindxe
  28898. this.editor.container.style.zIndex &&
  28899. (this.getDom().style.zIndex =
  28900. this.editor.container.style.zIndex * 1 + 10)
  28901. this._hidden = false
  28902. this.fireEvent('show')
  28903. baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex =
  28904. this.getDom().style.zIndex - 4
  28905. }
  28906. },
  28907. isHidden: function () {
  28908. return this._hidden
  28909. },
  28910. _hide: function () {
  28911. if (!this._hidden) {
  28912. var wrapNode = this.getDom()
  28913. wrapNode.style.display = 'none'
  28914. wrapNode.style.zIndex = ''
  28915. wrapNode.style.width = ''
  28916. wrapNode.style.height = ''
  28917. this._hidden = true
  28918. this.fireEvent('hide')
  28919. }
  28920. },
  28921. open: function () {
  28922. if (this.autoReset) {
  28923. //有可能还没有渲染
  28924. try {
  28925. this.reset()
  28926. } catch (e) {
  28927. this.render()
  28928. this.open()
  28929. }
  28930. }
  28931. this.showAtCenter()
  28932. if (this.iframeUrl) {
  28933. try {
  28934. this.getDom('iframe').focus()
  28935. } catch (ex) {}
  28936. }
  28937. activeDialog = this
  28938. },
  28939. _onCloseButtonClick: function (evt, el) {
  28940. this.close(false)
  28941. },
  28942. close: function (ok) {
  28943. if (this.fireEvent('close', ok) !== false) {
  28944. //还原环境
  28945. if (this.fullscreen) {
  28946. document.documentElement.style.overflowX =
  28947. this._originalContext.html.overflowX
  28948. document.documentElement.style.overflowY =
  28949. this._originalContext.html.overflowY
  28950. document.body.style.overflowX = this._originalContext.body.overflowX
  28951. document.body.style.overflowY = this._originalContext.body.overflowY
  28952. delete this._originalContext
  28953. }
  28954. this._hide()
  28955. //销毁content
  28956. var content = this.getDom('content')
  28957. var iframe = this.getDom('iframe')
  28958. if (content && iframe) {
  28959. var doc = iframe.contentDocument || iframe.contentWindow.document
  28960. doc && (doc.body.innerHTML = '')
  28961. domUtils.remove(content)
  28962. }
  28963. }
  28964. }
  28965. }
  28966. utils.inherits(Dialog, UIBase)
  28967. })()
  28968. // ui/menubutton.js
  28969. ///import core
  28970. ///import uicore
  28971. ///import ui/menu.js
  28972. ///import ui/splitbutton.js
  28973. ;(function () {
  28974. var utils = baidu.editor.utils,
  28975. Menu = baidu.editor.ui.Menu,
  28976. SplitButton = baidu.editor.ui.SplitButton,
  28977. MenuButton = (baidu.editor.ui.MenuButton = function (options) {
  28978. this.initOptions(options)
  28979. this.initMenuButton()
  28980. })
  28981. MenuButton.prototype = {
  28982. initMenuButton: function () {
  28983. var me = this
  28984. this.uiName = 'menubutton'
  28985. this.popup = new Menu({
  28986. items: me.items,
  28987. className: me.className,
  28988. editor: me.editor
  28989. })
  28990. this.popup.addListener('show', function () {
  28991. var list = this
  28992. for (var i = 0; i < list.items.length; i++) {
  28993. list.items[i].removeState('checked')
  28994. if (list.items[i].value == me._value) {
  28995. list.items[i].addState('checked')
  28996. this.value = me._value
  28997. }
  28998. }
  28999. })
  29000. this.initSplitButton()
  29001. },
  29002. setValue: function (value) {
  29003. this._value = value
  29004. }
  29005. }
  29006. utils.inherits(MenuButton, SplitButton)
  29007. })()
  29008. // ui/multiMenu.js
  29009. ///import core
  29010. ///import uicore
  29011. ///commands 表情
  29012. ;(function () {
  29013. var utils = baidu.editor.utils,
  29014. Popup = baidu.editor.ui.Popup,
  29015. SplitButton = baidu.editor.ui.SplitButton,
  29016. MultiMenuPop = (baidu.editor.ui.MultiMenuPop = function (options) {
  29017. this.initOptions(options)
  29018. this.initMultiMenu()
  29019. })
  29020. MultiMenuPop.prototype = {
  29021. initMultiMenu: function () {
  29022. var me = this
  29023. this.popup = new Popup({
  29024. content: '',
  29025. editor: me.editor,
  29026. iframe_rendered: false,
  29027. onshow: function () {
  29028. if (!this.iframe_rendered) {
  29029. this.iframe_rendered = true
  29030. this.getDom('content').innerHTML =
  29031. '<iframe id="' +
  29032. me.id +
  29033. '_iframe" src="' +
  29034. me.iframeUrl +
  29035. '" frameborder="0"></iframe>'
  29036. me.editor.container.style.zIndex &&
  29037. (this.getDom().style.zIndex =
  29038. me.editor.container.style.zIndex * 1 + 1)
  29039. }
  29040. }
  29041. // canSideUp:false,
  29042. // canSideLeft:false
  29043. })
  29044. this.onbuttonclick = function () {
  29045. this.showPopup()
  29046. }
  29047. this.initSplitButton()
  29048. }
  29049. }
  29050. utils.inherits(MultiMenuPop, SplitButton)
  29051. })()
  29052. // ui/shortcutmenu.js
  29053. ;(function () {
  29054. var UI = baidu.editor.ui,
  29055. UIBase = UI.UIBase,
  29056. uiUtils = UI.uiUtils,
  29057. utils = baidu.editor.utils,
  29058. domUtils = baidu.editor.dom.domUtils
  29059. var allMenus = [], //存储所有快捷菜单
  29060. timeID,
  29061. isSubMenuShow = false //是否有子pop显示
  29062. var ShortCutMenu = (UI.ShortCutMenu = function (options) {
  29063. this.initOptions(options)
  29064. this.initShortCutMenu()
  29065. })
  29066. ShortCutMenu.postHide = hideAllMenu
  29067. ShortCutMenu.prototype = {
  29068. isHidden: true,
  29069. SPACE: 5,
  29070. initShortCutMenu: function () {
  29071. this.items = this.items || []
  29072. this.initUIBase()
  29073. this.initItems()
  29074. this.initEvent()
  29075. allMenus.push(this)
  29076. },
  29077. initEvent: function () {
  29078. var me = this,
  29079. doc = me.editor.document
  29080. domUtils.on(doc, 'mousemove', function (e) {
  29081. if (me.isHidden === false) {
  29082. //有pop显示就不隐藏快捷菜单
  29083. if (me.getSubMenuMark() || me.eventType == 'contextmenu') return
  29084. var flag = true,
  29085. el = me.getDom(),
  29086. wt = el.offsetWidth,
  29087. ht = el.offsetHeight,
  29088. distanceX = wt / 2 + me.SPACE, //距离中心X标准
  29089. distanceY = ht / 2, //距离中心Y标准
  29090. x = Math.abs(e.screenX - me.left), //离中心距离横坐标
  29091. y = Math.abs(e.screenY - me.top) //离中心距离纵坐标
  29092. clearTimeout(timeID)
  29093. timeID = setTimeout(function () {
  29094. if (y > 0 && y < distanceY) {
  29095. me.setOpacity(el, '1')
  29096. } else if (y > distanceY && y < distanceY + 70) {
  29097. me.setOpacity(el, '0.5')
  29098. flag = false
  29099. } else if (y > distanceY + 70 && y < distanceY + 140) {
  29100. me.hide()
  29101. }
  29102. if (flag && x > 0 && x < distanceX) {
  29103. me.setOpacity(el, '1')
  29104. } else if (x > distanceX && x < distanceX + 70) {
  29105. me.setOpacity(el, '0.5')
  29106. } else if (x > distanceX + 70 && x < distanceX + 140) {
  29107. me.hide()
  29108. }
  29109. })
  29110. }
  29111. })
  29112. //ie\ff下 mouseout不准
  29113. if (browser.chrome) {
  29114. domUtils.on(doc, 'mouseout', function (e) {
  29115. var relatedTgt = e.relatedTarget || e.toElement
  29116. if (relatedTgt == null || relatedTgt.tagName == 'HTML') {
  29117. me.hide()
  29118. }
  29119. })
  29120. }
  29121. me.editor.addListener('afterhidepop', function () {
  29122. if (!me.isHidden) {
  29123. isSubMenuShow = true
  29124. }
  29125. })
  29126. },
  29127. initItems: function () {
  29128. if (utils.isArray(this.items)) {
  29129. for (var i = 0, len = this.items.length; i < len; i++) {
  29130. var item = this.items[i].toLowerCase()
  29131. if (UI[item]) {
  29132. this.items[i] = new UI[item](this.editor)
  29133. this.items[i].className += ' edui-shortcutsubmenu '
  29134. }
  29135. }
  29136. }
  29137. },
  29138. setOpacity: function (el, value) {
  29139. if (browser.ie && browser.version < 9) {
  29140. el.style.filter = 'alpha(opacity = ' + parseFloat(value) * 100 + ');'
  29141. } else {
  29142. el.style.opacity = value
  29143. }
  29144. },
  29145. getSubMenuMark: function () {
  29146. isSubMenuShow = false
  29147. var layerEle = uiUtils.getFixedLayer()
  29148. var list = domUtils.getElementsByTagName(
  29149. layerEle,
  29150. 'div',
  29151. function (node) {
  29152. return domUtils.hasClass(node, 'edui-shortcutsubmenu edui-popup')
  29153. }
  29154. )
  29155. for (var i = 0, node; (node = list[i++]); ) {
  29156. if (node.style.display != 'none') {
  29157. isSubMenuShow = true
  29158. }
  29159. }
  29160. return isSubMenuShow
  29161. },
  29162. show: function (e, hasContextmenu) {
  29163. var me = this,
  29164. offset = {},
  29165. el = this.getDom(),
  29166. fixedlayer = uiUtils.getFixedLayer()
  29167. function setPos(offset) {
  29168. if (offset.left < 0) {
  29169. offset.left = 0
  29170. }
  29171. if (offset.top < 0) {
  29172. offset.top = 0
  29173. }
  29174. el.style.cssText =
  29175. 'position:absolute;left:' +
  29176. offset.left +
  29177. 'px;top:' +
  29178. offset.top +
  29179. 'px;'
  29180. }
  29181. function setPosByCxtMenu(menu) {
  29182. if (!menu.tagName) {
  29183. menu = menu.getDom()
  29184. }
  29185. offset.left = parseInt(menu.style.left)
  29186. offset.top = parseInt(menu.style.top)
  29187. offset.top -= el.offsetHeight + 15
  29188. setPos(offset)
  29189. }
  29190. me.eventType = e.type
  29191. el.style.cssText = 'display:block;left:-9999px'
  29192. if (e.type == 'contextmenu' && hasContextmenu) {
  29193. var menu = domUtils.getElementsByTagName(
  29194. fixedlayer,
  29195. 'div',
  29196. 'edui-contextmenu'
  29197. )[0]
  29198. if (menu) {
  29199. setPosByCxtMenu(menu)
  29200. } else {
  29201. me.editor.addListener(
  29202. 'aftershowcontextmenu',
  29203. function (type, menu) {
  29204. setPosByCxtMenu(menu)
  29205. }
  29206. )
  29207. }
  29208. } else {
  29209. offset = uiUtils.getViewportOffsetByEvent(e)
  29210. offset.top -= el.offsetHeight + me.SPACE
  29211. offset.left += me.SPACE + 20
  29212. setPos(offset)
  29213. me.setOpacity(el, 0.2)
  29214. }
  29215. me.isHidden = false
  29216. me.left = e.screenX + el.offsetWidth / 2 - me.SPACE
  29217. me.top = e.screenY - el.offsetHeight / 2 - me.SPACE
  29218. if (me.editor) {
  29219. el.style.zIndex = me.editor.container.style.zIndex * 1 + 10
  29220. fixedlayer.style.zIndex = el.style.zIndex - 1
  29221. }
  29222. },
  29223. hide: function () {
  29224. if (this.getDom()) {
  29225. this.getDom().style.display = 'none'
  29226. }
  29227. this.isHidden = true
  29228. },
  29229. postRender: function () {
  29230. if (utils.isArray(this.items)) {
  29231. for (var i = 0, item; (item = this.items[i++]); ) {
  29232. item.postRender()
  29233. }
  29234. }
  29235. },
  29236. getHtmlTpl: function () {
  29237. var buff
  29238. if (utils.isArray(this.items)) {
  29239. buff = []
  29240. for (var i = 0; i < this.items.length; i++) {
  29241. buff[i] = this.items[i].renderHtml()
  29242. }
  29243. buff = buff.join('')
  29244. } else {
  29245. buff = this.items
  29246. }
  29247. return (
  29248. '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
  29249. buff +
  29250. '</div>'
  29251. )
  29252. }
  29253. }
  29254. utils.inherits(ShortCutMenu, UIBase)
  29255. function hideAllMenu(e) {
  29256. var tgt = e.target || e.srcElement,
  29257. cur = domUtils.findParent(
  29258. tgt,
  29259. function (node) {
  29260. return (
  29261. domUtils.hasClass(node, 'edui-shortcutmenu') ||
  29262. domUtils.hasClass(node, 'edui-popup')
  29263. )
  29264. },
  29265. true
  29266. )
  29267. if (!cur) {
  29268. for (var i = 0, menu; (menu = allMenus[i++]); ) {
  29269. menu.hide()
  29270. }
  29271. }
  29272. }
  29273. domUtils.on(document, 'mousedown', function (e) {
  29274. hideAllMenu(e)
  29275. })
  29276. domUtils.on(window, 'scroll', function (e) {
  29277. hideAllMenu(e)
  29278. })
  29279. })()
  29280. // ui/breakline.js
  29281. ;(function () {
  29282. var utils = baidu.editor.utils,
  29283. UIBase = baidu.editor.ui.UIBase,
  29284. Breakline = (baidu.editor.ui.Breakline = function (options) {
  29285. this.initOptions(options)
  29286. this.initSeparator()
  29287. })
  29288. Breakline.prototype = {
  29289. uiName: 'Breakline',
  29290. initSeparator: function () {
  29291. this.initUIBase()
  29292. },
  29293. getHtmlTpl: function () {
  29294. return '<br/>'
  29295. }
  29296. }
  29297. utils.inherits(Breakline, UIBase)
  29298. })()
  29299. // ui/message.js
  29300. ///import core
  29301. ///import uicore
  29302. ;(function () {
  29303. var utils = baidu.editor.utils,
  29304. domUtils = baidu.editor.dom.domUtils,
  29305. UIBase = baidu.editor.ui.UIBase,
  29306. Message = (baidu.editor.ui.Message = function (options) {
  29307. this.initOptions(options)
  29308. this.initMessage()
  29309. })
  29310. Message.prototype = {
  29311. initMessage: function () {
  29312. this.initUIBase()
  29313. },
  29314. getHtmlTpl: function () {
  29315. return (
  29316. '<div id="##" class="edui-message %%">' +
  29317. ' <div id="##_closer" class="edui-message-closer">×</div>' +
  29318. ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
  29319. ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
  29320. ' <div class="edui-shadow"></div>' +
  29321. ' <div id="##_content" class="edui-message-content">' +
  29322. ' </div>' +
  29323. ' </div>' +
  29324. '</div>'
  29325. )
  29326. },
  29327. reset: function (opt) {
  29328. var me = this
  29329. if (!opt.keepshow) {
  29330. clearTimeout(this.timer)
  29331. me.timer = setTimeout(function () {
  29332. me.hide()
  29333. }, opt.timeout || 4000)
  29334. }
  29335. opt.content !== undefined && me.setContent(opt.content)
  29336. opt.type !== undefined && me.setType(opt.type)
  29337. me.show()
  29338. },
  29339. postRender: function () {
  29340. var me = this,
  29341. closer = this.getDom('closer')
  29342. closer &&
  29343. domUtils.on(closer, 'click', function () {
  29344. me.hide()
  29345. })
  29346. },
  29347. setContent: function (content) {
  29348. this.getDom('content').innerHTML = content
  29349. },
  29350. setType: function (type) {
  29351. type = type || 'info'
  29352. var body = this.getDom('body')
  29353. body.className = body.className.replace(
  29354. /edui-message-type-[\w-]+/,
  29355. 'edui-message-type-' + type
  29356. )
  29357. },
  29358. getContent: function () {
  29359. return this.getDom('content').innerHTML
  29360. },
  29361. getType: function () {
  29362. var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/)
  29363. return arr ? arr[1] : ''
  29364. },
  29365. show: function () {
  29366. this.getDom().style.display = 'block'
  29367. },
  29368. hide: function () {
  29369. var dom = this.getDom()
  29370. if (dom) {
  29371. dom.style.display = 'none'
  29372. dom.parentNode && dom.parentNode.removeChild(dom)
  29373. }
  29374. }
  29375. }
  29376. utils.inherits(Message, UIBase)
  29377. })()
  29378. // adapter/editorui.js
  29379. //ui跟编辑器的适配層
  29380. //那个按钮弹出是dialog,是下拉筐等都是在这个js中配置
  29381. //自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化
  29382. ;(function () {
  29383. var utils = baidu.editor.utils
  29384. var editorui = baidu.editor.ui
  29385. var _Dialog = editorui.Dialog
  29386. editorui.buttons = {}
  29387. editorui.Dialog = function (options) {
  29388. var dialog = new _Dialog(options)
  29389. dialog.addListener('hide', function () {
  29390. if (dialog.editor) {
  29391. var editor = dialog.editor
  29392. try {
  29393. if (browser.gecko) {
  29394. var y = editor.window.scrollY,
  29395. x = editor.window.scrollX
  29396. editor.body.focus()
  29397. editor.window.scrollTo(x, y)
  29398. } else {
  29399. editor.focus()
  29400. }
  29401. } catch (ex) {}
  29402. }
  29403. })
  29404. return dialog
  29405. }
  29406. var iframeUrlMap = {
  29407. anchor: '~/dialogs/anchor/anchor.html',
  29408. insertimage: '~/dialogs/image/image.html',
  29409. link: '~/dialogs/link/link.html',
  29410. spechars: '~/dialogs/spechars/spechars.html',
  29411. searchreplace: '~/dialogs/searchreplace/searchreplace.html',
  29412. map: '~/dialogs/map/map.html',
  29413. gmap: '~/dialogs/gmap/gmap.html',
  29414. insertvideo: '~/dialogs/video/video.html',
  29415. help: '~/dialogs/help/help.html',
  29416. preview: '~/dialogs/preview/preview.html',
  29417. emotion: '~/dialogs/emotion/emotion.html',
  29418. wordimage: '~/dialogs/wordimage/wordimage.html',
  29419. attachment: '~/dialogs/attachment/attachment.html',
  29420. insertframe: '~/dialogs/insertframe/insertframe.html',
  29421. edittip: '~/dialogs/table/edittip.html',
  29422. edittable: '~/dialogs/table/edittable.html',
  29423. edittd: '~/dialogs/table/edittd.html',
  29424. webapp: '~/dialogs/webapp/webapp.html',
  29425. snapscreen: '~/dialogs/snapscreen/snapscreen.html',
  29426. scrawl: '~/dialogs/scrawl/scrawl.html',
  29427. music: '~/dialogs/music/music.html',
  29428. template: '~/dialogs/template/template.html',
  29429. background: '~/dialogs/background/background.html',
  29430. charts: '~/dialogs/charts/charts.html'
  29431. }
  29432. //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起
  29433. var btnCmds = [
  29434. 'undo',
  29435. 'redo',
  29436. 'formatmatch',
  29437. 'bold',
  29438. 'italic',
  29439. 'underline',
  29440. 'fontborder',
  29441. 'touppercase',
  29442. 'tolowercase',
  29443. 'strikethrough',
  29444. 'subscript',
  29445. 'superscript',
  29446. 'source',
  29447. 'indent',
  29448. 'outdent',
  29449. 'blockquote',
  29450. 'pasteplain',
  29451. 'pagebreak',
  29452. 'selectall',
  29453. 'print',
  29454. 'horizontal',
  29455. 'removeformat',
  29456. 'time',
  29457. 'date',
  29458. 'unlink',
  29459. 'insertparagraphbeforetable',
  29460. 'insertrow',
  29461. 'insertcol',
  29462. 'mergeright',
  29463. 'mergedown',
  29464. 'deleterow',
  29465. 'deletecol',
  29466. 'splittorows',
  29467. 'splittocols',
  29468. 'splittocells',
  29469. 'mergecells',
  29470. 'deletetable',
  29471. 'drafts'
  29472. ]
  29473. for (var i = 0, ci; (ci = btnCmds[i++]); ) {
  29474. ci = ci.toLowerCase()
  29475. editorui[ci] = (function (cmd) {
  29476. return function (editor) {
  29477. var ui = new editorui.Button({
  29478. className: 'edui-for-' + cmd,
  29479. title:
  29480. editor.options.labelMap[cmd] ||
  29481. editor.getLang('labelMap.' + cmd) ||
  29482. '',
  29483. onclick: function () {
  29484. editor.execCommand(cmd)
  29485. },
  29486. theme: editor.options.theme,
  29487. showText: false
  29488. })
  29489. editorui.buttons[cmd] = ui
  29490. editor.addListener(
  29491. 'selectionchange',
  29492. function (type, causeByUi, uiReady) {
  29493. var state = editor.queryCommandState(cmd)
  29494. if (state == -1) {
  29495. ui.setDisabled(true)
  29496. ui.setChecked(false)
  29497. } else {
  29498. if (!uiReady) {
  29499. ui.setDisabled(false)
  29500. ui.setChecked(state)
  29501. }
  29502. }
  29503. }
  29504. )
  29505. return ui
  29506. }
  29507. })(ci)
  29508. }
  29509. //清除文档
  29510. editorui.cleardoc = function (editor) {
  29511. var ui = new editorui.Button({
  29512. className: 'edui-for-cleardoc',
  29513. title:
  29514. editor.options.labelMap.cleardoc ||
  29515. editor.getLang('labelMap.cleardoc') ||
  29516. '',
  29517. theme: editor.options.theme,
  29518. onclick: function () {
  29519. if (confirm(editor.getLang('confirmClear'))) {
  29520. editor.execCommand('cleardoc')
  29521. }
  29522. }
  29523. })
  29524. editorui.buttons['cleardoc'] = ui
  29525. editor.addListener('selectionchange', function () {
  29526. ui.setDisabled(editor.queryCommandState('cleardoc') == -1)
  29527. })
  29528. return ui
  29529. }
  29530. //排版,图片排版,文字方向
  29531. var typeset = {
  29532. justify: ['left', 'right', 'center', 'justify'],
  29533. imagefloat: ['none', 'left', 'center', 'right'],
  29534. directionality: ['ltr', 'rtl']
  29535. }
  29536. for (var p in typeset) {
  29537. ;(function (cmd, val) {
  29538. for (var i = 0, ci; (ci = val[i++]); ) {
  29539. ;(function (cmd2) {
  29540. editorui[cmd.replace('float', '') + cmd2] = function (editor) {
  29541. var ui = new editorui.Button({
  29542. className: 'edui-for-' + cmd.replace('float', '') + cmd2,
  29543. title:
  29544. editor.options.labelMap[cmd.replace('float', '') + cmd2] ||
  29545. editor.getLang(
  29546. 'labelMap.' + cmd.replace('float', '') + cmd2
  29547. ) ||
  29548. '',
  29549. theme: editor.options.theme,
  29550. onclick: function () {
  29551. editor.execCommand(cmd, cmd2)
  29552. }
  29553. })
  29554. editorui.buttons[cmd] = ui
  29555. editor.addListener(
  29556. 'selectionchange',
  29557. function (type, causeByUi, uiReady) {
  29558. ui.setDisabled(editor.queryCommandState(cmd) == -1)
  29559. ui.setChecked(
  29560. editor.queryCommandValue(cmd) == cmd2 && !uiReady
  29561. )
  29562. }
  29563. )
  29564. return ui
  29565. }
  29566. })(ci)
  29567. }
  29568. })(p, typeset[p])
  29569. }
  29570. //字体颜色和背景颜色
  29571. for (var i = 0, ci; (ci = ['backcolor', 'forecolor'][i++]); ) {
  29572. editorui[ci] = (function (cmd) {
  29573. return function (editor) {
  29574. var ui = new editorui.ColorButton({
  29575. className: 'edui-for-' + cmd,
  29576. color: 'default',
  29577. title:
  29578. editor.options.labelMap[cmd] ||
  29579. editor.getLang('labelMap.' + cmd) ||
  29580. '',
  29581. editor: editor,
  29582. onpickcolor: function (t, color) {
  29583. editor.execCommand(cmd, color)
  29584. },
  29585. onpicknocolor: function () {
  29586. editor.execCommand(cmd, 'default')
  29587. this.setColor('transparent')
  29588. this.color = 'default'
  29589. },
  29590. onbuttonclick: function () {
  29591. editor.execCommand(cmd, this.color)
  29592. }
  29593. })
  29594. editorui.buttons[cmd] = ui
  29595. editor.addListener('selectionchange', function () {
  29596. ui.setDisabled(editor.queryCommandState(cmd) == -1)
  29597. })
  29598. return ui
  29599. }
  29600. })(ci)
  29601. }
  29602. var dialogBtns = {
  29603. noOk: ['searchreplace', 'help', 'spechars', 'webapp', 'preview'],
  29604. ok: [
  29605. 'attachment',
  29606. 'anchor',
  29607. 'link',
  29608. 'insertimage',
  29609. 'map',
  29610. 'gmap',
  29611. 'insertframe',
  29612. 'wordimage',
  29613. 'insertvideo',
  29614. 'insertframe',
  29615. 'edittip',
  29616. 'edittable',
  29617. 'edittd',
  29618. 'scrawl',
  29619. 'template',
  29620. 'music',
  29621. 'background',
  29622. 'charts'
  29623. ]
  29624. }
  29625. for (var p in dialogBtns) {
  29626. ;(function (type, vals) {
  29627. for (var i = 0, ci; (ci = vals[i++]); ) {
  29628. //todo opera下存在问题
  29629. if (browser.opera && ci === 'searchreplace') {
  29630. continue
  29631. }
  29632. ;(function (cmd) {
  29633. editorui[cmd] = function (editor, iframeUrl, title) {
  29634. iframeUrl =
  29635. iframeUrl ||
  29636. (editor.options.iframeUrlMap || {})[cmd] ||
  29637. iframeUrlMap[cmd]
  29638. title =
  29639. editor.options.labelMap[cmd] ||
  29640. editor.getLang('labelMap.' + cmd) ||
  29641. ''
  29642. var dialog
  29643. //没有iframeUrl不创建dialog
  29644. if (iframeUrl) {
  29645. dialog = new editorui.Dialog(
  29646. utils.extend(
  29647. {
  29648. iframeUrl: editor.ui.mapUrl(iframeUrl),
  29649. editor: editor,
  29650. className: 'edui-for-' + cmd,
  29651. title: title,
  29652. holdScroll: cmd === 'insertimage',
  29653. fullscreen: /charts|preview/.test(cmd),
  29654. closeDialog: editor.getLang('closeDialog')
  29655. },
  29656. type == 'ok'
  29657. ? {
  29658. buttons: [
  29659. {
  29660. className: 'edui-okbutton',
  29661. label: editor.getLang('ok'),
  29662. editor: editor,
  29663. onclick: function () {
  29664. dialog.close(true)
  29665. }
  29666. },
  29667. {
  29668. className: 'edui-cancelbutton',
  29669. label: editor.getLang('cancel'),
  29670. editor: editor,
  29671. onclick: function () {
  29672. dialog.close(false)
  29673. }
  29674. }
  29675. ]
  29676. }
  29677. : {}
  29678. )
  29679. )
  29680. editor.ui._dialogs[cmd + 'Dialog'] = dialog
  29681. }
  29682. var ui = new editorui.Button({
  29683. className: 'edui-for-' + cmd,
  29684. title: title,
  29685. onclick: function () {
  29686. if (dialog) {
  29687. switch (cmd) {
  29688. case 'wordimage':
  29689. var images = editor.execCommand('wordimage')
  29690. if (images && images.length) {
  29691. dialog.render()
  29692. dialog.open()
  29693. }
  29694. break
  29695. case 'scrawl':
  29696. if (editor.queryCommandState('scrawl') != -1) {
  29697. dialog.render()
  29698. dialog.open()
  29699. }
  29700. break
  29701. default:
  29702. dialog.render()
  29703. dialog.open()
  29704. }
  29705. }
  29706. },
  29707. theme: editor.options.theme,
  29708. disabled:
  29709. (cmd == 'scrawl' &&
  29710. editor.queryCommandState('scrawl') == -1) ||
  29711. cmd == 'charts'
  29712. })
  29713. editorui.buttons[cmd] = ui
  29714. editor.addListener('selectionchange', function () {
  29715. //只存在于右键菜单而无工具栏按钮的ui不需要检测状态
  29716. var unNeedCheckState = { edittable: 1 }
  29717. if (cmd in unNeedCheckState) return
  29718. var state = editor.queryCommandState(cmd)
  29719. if (ui.getDom()) {
  29720. ui.setDisabled(state == -1)
  29721. ui.setChecked(state)
  29722. }
  29723. })
  29724. return ui
  29725. }
  29726. })(ci.toLowerCase())
  29727. }
  29728. })(p, dialogBtns[p])
  29729. }
  29730. editorui.snapscreen = function (editor, iframeUrl, title) {
  29731. title =
  29732. editor.options.labelMap['snapscreen'] ||
  29733. editor.getLang('labelMap.snapscreen') ||
  29734. ''
  29735. var ui = new editorui.Button({
  29736. className: 'edui-for-snapscreen',
  29737. title: title,
  29738. onclick: function () {
  29739. editor.execCommand('snapscreen')
  29740. },
  29741. theme: editor.options.theme
  29742. })
  29743. editorui.buttons['snapscreen'] = ui
  29744. iframeUrl =
  29745. iframeUrl ||
  29746. (editor.options.iframeUrlMap || {})['snapscreen'] ||
  29747. iframeUrlMap['snapscreen']
  29748. if (iframeUrl) {
  29749. var dialog = new editorui.Dialog({
  29750. iframeUrl: editor.ui.mapUrl(iframeUrl),
  29751. editor: editor,
  29752. className: 'edui-for-snapscreen',
  29753. title: title,
  29754. buttons: [
  29755. {
  29756. className: 'edui-okbutton',
  29757. label: editor.getLang('ok'),
  29758. editor: editor,
  29759. onclick: function () {
  29760. dialog.close(true)
  29761. }
  29762. },
  29763. {
  29764. className: 'edui-cancelbutton',
  29765. label: editor.getLang('cancel'),
  29766. editor: editor,
  29767. onclick: function () {
  29768. dialog.close(false)
  29769. }
  29770. }
  29771. ]
  29772. })
  29773. dialog.render()
  29774. editor.ui._dialogs['snapscreenDialog'] = dialog
  29775. }
  29776. editor.addListener('selectionchange', function () {
  29777. ui.setDisabled(editor.queryCommandState('snapscreen') == -1)
  29778. })
  29779. return ui
  29780. }
  29781. editorui.insertcode = function (editor, list, title) {
  29782. list = editor.options['insertcode'] || []
  29783. title =
  29784. editor.options.labelMap['insertcode'] ||
  29785. editor.getLang('labelMap.insertcode') ||
  29786. ''
  29787. // if (!list.length) return;
  29788. var items = []
  29789. utils.each(list, function (key, val) {
  29790. items.push({
  29791. label: key,
  29792. value: val,
  29793. theme: editor.options.theme,
  29794. renderLabelHtml: function () {
  29795. return (
  29796. '<div class="edui-label %%-label" >' +
  29797. (this.label || '') +
  29798. '</div>'
  29799. )
  29800. }
  29801. })
  29802. })
  29803. var ui = new editorui.Combox({
  29804. editor: editor,
  29805. items: items,
  29806. onselect: function (t, index) {
  29807. editor.execCommand('insertcode', this.items[index].value)
  29808. },
  29809. onbuttonclick: function () {
  29810. this.showPopup()
  29811. },
  29812. title: title,
  29813. initValue: title,
  29814. className: 'edui-for-insertcode',
  29815. indexByValue: function (value) {
  29816. if (value) {
  29817. for (var i = 0, ci; (ci = this.items[i]); i++) {
  29818. if (ci.value.indexOf(value) != -1) return i
  29819. }
  29820. }
  29821. return -1
  29822. }
  29823. })
  29824. editorui.buttons['insertcode'] = ui
  29825. editor.addListener(
  29826. 'selectionchange',
  29827. function (type, causeByUi, uiReady) {
  29828. if (!uiReady) {
  29829. var state = editor.queryCommandState('insertcode')
  29830. if (state == -1) {
  29831. ui.setDisabled(true)
  29832. } else {
  29833. ui.setDisabled(false)
  29834. var value = editor.queryCommandValue('insertcode')
  29835. if (!value) {
  29836. ui.setValue(title)
  29837. return
  29838. }
  29839. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  29840. value && (value = value.replace(/['"]/g, '').split(',')[0])
  29841. ui.setValue(value)
  29842. }
  29843. }
  29844. }
  29845. )
  29846. return ui
  29847. }
  29848. editorui.fontfamily = function (editor, list, title) {
  29849. list = editor.options['fontfamily'] || []
  29850. title =
  29851. editor.options.labelMap['fontfamily'] ||
  29852. editor.getLang('labelMap.fontfamily') ||
  29853. ''
  29854. if (!list.length) return
  29855. for (var i = 0, ci, items = []; (ci = list[i]); i++) {
  29856. var langLabel = editor.getLang('fontfamily')[ci.name] || ''
  29857. ;(function (key, val) {
  29858. items.push({
  29859. label: key,
  29860. value: val,
  29861. theme: editor.options.theme,
  29862. renderLabelHtml: function () {
  29863. return (
  29864. '<div class="edui-label %%-label" style="font-family:' +
  29865. utils.unhtml(this.value) +
  29866. '">' +
  29867. (this.label || '') +
  29868. '</div>'
  29869. )
  29870. }
  29871. })
  29872. })(ci.label || langLabel, ci.val)
  29873. }
  29874. var ui = new editorui.Combox({
  29875. editor: editor,
  29876. items: items,
  29877. onselect: function (t, index) {
  29878. editor.execCommand('FontFamily', this.items[index].value)
  29879. },
  29880. onbuttonclick: function () {
  29881. this.showPopup()
  29882. },
  29883. title: title,
  29884. initValue: title,
  29885. className: 'edui-for-fontfamily',
  29886. indexByValue: function (value) {
  29887. if (value) {
  29888. for (var i = 0, ci; (ci = this.items[i]); i++) {
  29889. if (ci.value.indexOf(value) != -1) return i
  29890. }
  29891. }
  29892. return -1
  29893. }
  29894. })
  29895. editorui.buttons['fontfamily'] = ui
  29896. editor.addListener(
  29897. 'selectionchange',
  29898. function (type, causeByUi, uiReady) {
  29899. if (!uiReady) {
  29900. var state = editor.queryCommandState('FontFamily')
  29901. if (state == -1) {
  29902. ui.setDisabled(true)
  29903. } else {
  29904. ui.setDisabled(false)
  29905. var value = editor.queryCommandValue('FontFamily')
  29906. //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
  29907. value && (value = value.replace(/['"]/g, '').split(',')[0])
  29908. ui.setValue(value)
  29909. }
  29910. }
  29911. }
  29912. )
  29913. return ui
  29914. }
  29915. editorui.fontsize = function (editor, list, title) {
  29916. title =
  29917. editor.options.labelMap['fontsize'] ||
  29918. editor.getLang('labelMap.fontsize') ||
  29919. ''
  29920. list = list || editor.options['fontsize'] || []
  29921. if (!list.length) return
  29922. var items = []
  29923. for (var i = 0; i < list.length; i++) {
  29924. var size = list[i] + 'px'
  29925. items.push({
  29926. label: size,
  29927. value: size,
  29928. theme: editor.options.theme,
  29929. renderLabelHtml: function () {
  29930. return (
  29931. '<div class="edui-label %%-label" style="line-height:1;font-size:' +
  29932. this.value +
  29933. '">' +
  29934. (this.label || '') +
  29935. '</div>'
  29936. )
  29937. }
  29938. })
  29939. }
  29940. var ui = new editorui.Combox({
  29941. editor: editor,
  29942. items: items,
  29943. title: title,
  29944. initValue: title,
  29945. onselect: function (t, index) {
  29946. editor.execCommand('FontSize', this.items[index].value)
  29947. },
  29948. onbuttonclick: function () {
  29949. this.showPopup()
  29950. },
  29951. className: 'edui-for-fontsize'
  29952. })
  29953. editorui.buttons['fontsize'] = ui
  29954. editor.addListener(
  29955. 'selectionchange',
  29956. function (type, causeByUi, uiReady) {
  29957. if (!uiReady) {
  29958. var state = editor.queryCommandState('FontSize')
  29959. if (state == -1) {
  29960. ui.setDisabled(true)
  29961. } else {
  29962. ui.setDisabled(false)
  29963. ui.setValue(editor.queryCommandValue('FontSize'))
  29964. }
  29965. }
  29966. }
  29967. )
  29968. return ui
  29969. }
  29970. editorui.paragraph = function (editor, list, title) {
  29971. title =
  29972. editor.options.labelMap['paragraph'] ||
  29973. editor.getLang('labelMap.paragraph') ||
  29974. ''
  29975. list = editor.options['paragraph'] || []
  29976. if (utils.isEmptyObject(list)) return
  29977. var items = []
  29978. for (var i in list) {
  29979. items.push({
  29980. value: i,
  29981. label: list[i] || editor.getLang('paragraph')[i],
  29982. theme: editor.options.theme,
  29983. renderLabelHtml: function () {
  29984. return (
  29985. '<div class="edui-label %%-label"><span class="edui-for-' +
  29986. this.value +
  29987. '">' +
  29988. (this.label || '') +
  29989. '</span></div>'
  29990. )
  29991. }
  29992. })
  29993. }
  29994. var ui = new editorui.Combox({
  29995. editor: editor,
  29996. items: items,
  29997. title: title,
  29998. initValue: title,
  29999. className: 'edui-for-paragraph',
  30000. onselect: function (t, index) {
  30001. editor.execCommand('Paragraph', this.items[index].value)
  30002. },
  30003. onbuttonclick: function () {
  30004. this.showPopup()
  30005. }
  30006. })
  30007. editorui.buttons['paragraph'] = ui
  30008. editor.addListener(
  30009. 'selectionchange',
  30010. function (type, causeByUi, uiReady) {
  30011. if (!uiReady) {
  30012. var state = editor.queryCommandState('Paragraph')
  30013. if (state == -1) {
  30014. ui.setDisabled(true)
  30015. } else {
  30016. ui.setDisabled(false)
  30017. var value = editor.queryCommandValue('Paragraph')
  30018. var index = ui.indexByValue(value)
  30019. if (index != -1) {
  30020. ui.setValue(value)
  30021. } else {
  30022. ui.setValue(ui.initValue)
  30023. }
  30024. }
  30025. }
  30026. }
  30027. )
  30028. return ui
  30029. }
  30030. //自定义标题
  30031. editorui.customstyle = function (editor) {
  30032. var list = editor.options['customstyle'] || [],
  30033. title =
  30034. editor.options.labelMap['customstyle'] ||
  30035. editor.getLang('labelMap.customstyle') ||
  30036. ''
  30037. if (!list.length) return
  30038. var langCs = editor.getLang('customstyle')
  30039. for (var i = 0, items = [], t; (t = list[i++]); ) {
  30040. ;(function (t) {
  30041. var ck = {}
  30042. ck.label = t.label ? t.label : langCs[t.name]
  30043. ck.style = t.style
  30044. ck.className = t.className
  30045. ck.tag = t.tag
  30046. items.push({
  30047. label: ck.label,
  30048. value: ck,
  30049. theme: editor.options.theme,
  30050. renderLabelHtml: function () {
  30051. return (
  30052. '<div class="edui-label %%-label">' +
  30053. '<' +
  30054. ck.tag +
  30055. ' ' +
  30056. (ck.className ? ' class="' + ck.className + '"' : '') +
  30057. (ck.style ? ' style="' + ck.style + '"' : '') +
  30058. '>' +
  30059. ck.label +
  30060. '</' +
  30061. ck.tag +
  30062. '>' +
  30063. '</div>'
  30064. )
  30065. }
  30066. })
  30067. })(t)
  30068. }
  30069. var ui = new editorui.Combox({
  30070. editor: editor,
  30071. items: items,
  30072. title: title,
  30073. initValue: title,
  30074. className: 'edui-for-customstyle',
  30075. onselect: function (t, index) {
  30076. editor.execCommand('customstyle', this.items[index].value)
  30077. },
  30078. onbuttonclick: function () {
  30079. this.showPopup()
  30080. },
  30081. indexByValue: function (value) {
  30082. for (var i = 0, ti; (ti = this.items[i++]); ) {
  30083. if (ti.label == value) {
  30084. return i - 1
  30085. }
  30086. }
  30087. return -1
  30088. }
  30089. })
  30090. editorui.buttons['customstyle'] = ui
  30091. editor.addListener(
  30092. 'selectionchange',
  30093. function (type, causeByUi, uiReady) {
  30094. if (!uiReady) {
  30095. var state = editor.queryCommandState('customstyle')
  30096. if (state == -1) {
  30097. ui.setDisabled(true)
  30098. } else {
  30099. ui.setDisabled(false)
  30100. var value = editor.queryCommandValue('customstyle')
  30101. var index = ui.indexByValue(value)
  30102. if (index != -1) {
  30103. ui.setValue(value)
  30104. } else {
  30105. ui.setValue(ui.initValue)
  30106. }
  30107. }
  30108. }
  30109. }
  30110. )
  30111. return ui
  30112. }
  30113. editorui.inserttable = function (editor, iframeUrl, title) {
  30114. title =
  30115. editor.options.labelMap['inserttable'] ||
  30116. editor.getLang('labelMap.inserttable') ||
  30117. ''
  30118. var ui = new editorui.TableButton({
  30119. editor: editor,
  30120. title: title,
  30121. className: 'edui-for-inserttable',
  30122. onpicktable: function (t, numCols, numRows) {
  30123. editor.execCommand('InsertTable', {
  30124. numRows: numRows,
  30125. numCols: numCols,
  30126. border: 1
  30127. })
  30128. },
  30129. onbuttonclick: function () {
  30130. this.showPopup()
  30131. }
  30132. })
  30133. editorui.buttons['inserttable'] = ui
  30134. editor.addListener('selectionchange', function () {
  30135. ui.setDisabled(editor.queryCommandState('inserttable') == -1)
  30136. })
  30137. return ui
  30138. }
  30139. editorui.lineheight = function (editor) {
  30140. var val = editor.options.lineheight || []
  30141. if (!val.length) return
  30142. for (var i = 0, ci, items = []; (ci = val[i++]); ) {
  30143. items.push({
  30144. //todo:写死了
  30145. label: ci,
  30146. value: ci,
  30147. theme: editor.options.theme,
  30148. onclick: function () {
  30149. editor.execCommand('lineheight', this.value)
  30150. }
  30151. })
  30152. }
  30153. var ui = new editorui.MenuButton({
  30154. editor: editor,
  30155. className: 'edui-for-lineheight',
  30156. title:
  30157. editor.options.labelMap['lineheight'] ||
  30158. editor.getLang('labelMap.lineheight') ||
  30159. '',
  30160. items: items,
  30161. onbuttonclick: function () {
  30162. var value = editor.queryCommandValue('LineHeight') || this.value
  30163. editor.execCommand('LineHeight', value)
  30164. }
  30165. })
  30166. editorui.buttons['lineheight'] = ui
  30167. editor.addListener('selectionchange', function () {
  30168. var state = editor.queryCommandState('LineHeight')
  30169. if (state == -1) {
  30170. ui.setDisabled(true)
  30171. } else {
  30172. ui.setDisabled(false)
  30173. var value = editor.queryCommandValue('LineHeight')
  30174. value && ui.setValue((value + '').replace(/cm/, ''))
  30175. ui.setChecked(state)
  30176. }
  30177. })
  30178. return ui
  30179. }
  30180. var rowspacings = ['top', 'bottom']
  30181. for (var r = 0, ri; (ri = rowspacings[r++]); ) {
  30182. ;(function (cmd) {
  30183. editorui['rowspacing' + cmd] = function (editor) {
  30184. var val = editor.options['rowspacing' + cmd] || []
  30185. if (!val.length) return null
  30186. for (var i = 0, ci, items = []; (ci = val[i++]); ) {
  30187. items.push({
  30188. label: ci,
  30189. value: ci,
  30190. theme: editor.options.theme,
  30191. onclick: function () {
  30192. editor.execCommand('rowspacing', this.value, cmd)
  30193. }
  30194. })
  30195. }
  30196. var ui = new editorui.MenuButton({
  30197. editor: editor,
  30198. className: 'edui-for-rowspacing' + cmd,
  30199. title:
  30200. editor.options.labelMap['rowspacing' + cmd] ||
  30201. editor.getLang('labelMap.rowspacing' + cmd) ||
  30202. '',
  30203. items: items,
  30204. onbuttonclick: function () {
  30205. var value =
  30206. editor.queryCommandValue('rowspacing', cmd) || this.value
  30207. editor.execCommand('rowspacing', value, cmd)
  30208. }
  30209. })
  30210. editorui.buttons[cmd] = ui
  30211. editor.addListener('selectionchange', function () {
  30212. var state = editor.queryCommandState('rowspacing', cmd)
  30213. if (state == -1) {
  30214. ui.setDisabled(true)
  30215. } else {
  30216. ui.setDisabled(false)
  30217. var value = editor.queryCommandValue('rowspacing', cmd)
  30218. value && ui.setValue((value + '').replace(/%/, ''))
  30219. ui.setChecked(state)
  30220. }
  30221. })
  30222. return ui
  30223. }
  30224. })(ri)
  30225. }
  30226. //有序,无序列表
  30227. var lists = ['insertorderedlist', 'insertunorderedlist']
  30228. for (var l = 0, cl; (cl = lists[l++]); ) {
  30229. ;(function (cmd) {
  30230. editorui[cmd] = function (editor) {
  30231. var vals = editor.options[cmd],
  30232. _onMenuClick = function () {
  30233. editor.execCommand(cmd, this.value)
  30234. },
  30235. items = []
  30236. for (var i in vals) {
  30237. items.push({
  30238. label: vals[i] || editor.getLang()[cmd][i] || '',
  30239. value: i,
  30240. theme: editor.options.theme,
  30241. onclick: _onMenuClick
  30242. })
  30243. }
  30244. var ui = new editorui.MenuButton({
  30245. editor: editor,
  30246. className: 'edui-for-' + cmd,
  30247. title: editor.getLang('labelMap.' + cmd) || '',
  30248. items: items,
  30249. onbuttonclick: function () {
  30250. var value = editor.queryCommandValue(cmd) || this.value
  30251. editor.execCommand(cmd, value)
  30252. }
  30253. })
  30254. editorui.buttons[cmd] = ui
  30255. editor.addListener('selectionchange', function () {
  30256. var state = editor.queryCommandState(cmd)
  30257. if (state == -1) {
  30258. ui.setDisabled(true)
  30259. } else {
  30260. ui.setDisabled(false)
  30261. var value = editor.queryCommandValue(cmd)
  30262. ui.setValue(value)
  30263. ui.setChecked(state)
  30264. }
  30265. })
  30266. return ui
  30267. }
  30268. })(cl)
  30269. }
  30270. editorui.fullscreen = function (editor, title) {
  30271. title =
  30272. editor.options.labelMap['fullscreen'] ||
  30273. editor.getLang('labelMap.fullscreen') ||
  30274. ''
  30275. var ui = new editorui.Button({
  30276. className: 'edui-for-fullscreen',
  30277. title: title,
  30278. theme: editor.options.theme,
  30279. onclick: function () {
  30280. if (editor.ui) {
  30281. editor.ui.setFullScreen(!editor.ui.isFullScreen())
  30282. }
  30283. this.setChecked(editor.ui.isFullScreen())
  30284. }
  30285. })
  30286. editorui.buttons['fullscreen'] = ui
  30287. editor.addListener('selectionchange', function () {
  30288. var state = editor.queryCommandState('fullscreen')
  30289. ui.setDisabled(state == -1)
  30290. ui.setChecked(editor.ui.isFullScreen())
  30291. })
  30292. return ui
  30293. }
  30294. // 表情
  30295. editorui['emotion'] = function (editor, iframeUrl) {
  30296. var cmd = 'emotion'
  30297. var ui = new editorui.MultiMenuPop({
  30298. title:
  30299. editor.options.labelMap[cmd] ||
  30300. editor.getLang('labelMap.' + cmd + '') ||
  30301. '',
  30302. editor: editor,
  30303. className: 'edui-for-' + cmd,
  30304. iframeUrl: editor.ui.mapUrl(
  30305. iframeUrl ||
  30306. (editor.options.iframeUrlMap || {})[cmd] ||
  30307. iframeUrlMap[cmd]
  30308. )
  30309. })
  30310. editorui.buttons[cmd] = ui
  30311. editor.addListener('selectionchange', function () {
  30312. ui.setDisabled(editor.queryCommandState(cmd) == -1)
  30313. })
  30314. return ui
  30315. }
  30316. editorui.autotypeset = function (editor) {
  30317. var ui = new editorui.AutoTypeSetButton({
  30318. editor: editor,
  30319. title:
  30320. editor.options.labelMap['autotypeset'] ||
  30321. editor.getLang('labelMap.autotypeset') ||
  30322. '',
  30323. className: 'edui-for-autotypeset',
  30324. onbuttonclick: function () {
  30325. editor.execCommand('autotypeset')
  30326. }
  30327. })
  30328. editorui.buttons['autotypeset'] = ui
  30329. editor.addListener('selectionchange', function () {
  30330. ui.setDisabled(editor.queryCommandState('autotypeset') == -1)
  30331. })
  30332. return ui
  30333. }
  30334. /* 简单上传插件 */
  30335. editorui['simpleupload'] = function (editor) {
  30336. var name = 'simpleupload',
  30337. ui = new editorui.Button({
  30338. className: 'edui-for-' + name,
  30339. title:
  30340. editor.options.labelMap[name] ||
  30341. editor.getLang('labelMap.' + name) ||
  30342. '',
  30343. onclick: function () {},
  30344. theme: editor.options.theme,
  30345. showText: false
  30346. })
  30347. editorui.buttons[name] = ui
  30348. editor.addListener('ready', function () {
  30349. var b = ui.getDom('body'),
  30350. iconSpan = b.children[0]
  30351. editor.fireEvent('simpleuploadbtnready', iconSpan)
  30352. })
  30353. editor.addListener(
  30354. 'selectionchange',
  30355. function (type, causeByUi, uiReady) {
  30356. var state = editor.queryCommandState(name)
  30357. if (state == -1) {
  30358. ui.setDisabled(true)
  30359. ui.setChecked(false)
  30360. } else {
  30361. if (!uiReady) {
  30362. ui.setDisabled(false)
  30363. ui.setChecked(state)
  30364. }
  30365. }
  30366. }
  30367. )
  30368. return ui
  30369. }
  30370. })()
  30371. // adapter/editor.js
  30372. ///import core
  30373. ///commands 全屏
  30374. ///commandsName FullScreen
  30375. ///commandsTitle 全屏
  30376. ;(function () {
  30377. var utils = baidu.editor.utils,
  30378. uiUtils = baidu.editor.ui.uiUtils,
  30379. UIBase = baidu.editor.ui.UIBase,
  30380. domUtils = baidu.editor.dom.domUtils
  30381. var nodeStack = []
  30382. function EditorUI(options) {
  30383. this.initOptions(options)
  30384. this.initEditorUI()
  30385. }
  30386. EditorUI.prototype = {
  30387. uiName: 'editor',
  30388. initEditorUI: function () {
  30389. this.editor.ui = this
  30390. this._dialogs = {}
  30391. this.initUIBase()
  30392. this._initToolbars()
  30393. var editor = this.editor,
  30394. me = this
  30395. editor.addListener('ready', function () {
  30396. //提供getDialog方法
  30397. editor.getDialog = function (name) {
  30398. return editor.ui._dialogs[name + 'Dialog']
  30399. }
  30400. domUtils.on(editor.window, 'scroll', function (evt) {
  30401. baidu.editor.ui.Popup.postHide(evt)
  30402. })
  30403. //提供编辑器实时宽高(全屏时宽高不变化)
  30404. editor.ui._actualFrameWidth = editor.options.initialFrameWidth
  30405. UE.browser.ie &&
  30406. UE.browser.version === 6 &&
  30407. editor.container.ownerDocument.execCommand(
  30408. 'BackgroundImageCache',
  30409. false,
  30410. true
  30411. )
  30412. //display bottom-bar label based on config
  30413. if (editor.options.elementPathEnabled) {
  30414. editor.ui.getDom('elementpath').innerHTML =
  30415. '<div class="edui-editor-breadcrumb">' +
  30416. editor.getLang('elementPathTip') +
  30417. ':</div>'
  30418. }
  30419. if (editor.options.wordCount) {
  30420. function countFn() {
  30421. setCount(editor, me)
  30422. domUtils.un(editor.document, 'click', arguments.callee)
  30423. }
  30424. domUtils.on(editor.document, 'click', countFn)
  30425. editor.ui.getDom('wordcount').innerHTML =
  30426. editor.getLang('wordCountTip')
  30427. }
  30428. editor.ui._scale()
  30429. if (editor.options.scaleEnabled) {
  30430. if (editor.autoHeightEnabled) {
  30431. editor.disableAutoHeight()
  30432. }
  30433. me.enableScale()
  30434. } else {
  30435. me.disableScale()
  30436. }
  30437. if (
  30438. !editor.options.elementPathEnabled &&
  30439. !editor.options.wordCount &&
  30440. !editor.options.scaleEnabled
  30441. ) {
  30442. editor.ui.getDom('elementpath').style.display = 'none'
  30443. editor.ui.getDom('wordcount').style.display = 'none'
  30444. editor.ui.getDom('scale').style.display = 'none'
  30445. }
  30446. if (!editor.selection.isFocus()) return
  30447. editor.fireEvent('selectionchange', false, true)
  30448. })
  30449. editor.addListener('mousedown', function (t, evt) {
  30450. var el = evt.target || evt.srcElement
  30451. baidu.editor.ui.Popup.postHide(evt, el)
  30452. baidu.editor.ui.ShortCutMenu.postHide(evt)
  30453. })
  30454. editor.addListener('delcells', function () {
  30455. if (UE.ui['edittip']) {
  30456. new UE.ui['edittip'](editor)
  30457. }
  30458. editor.getDialog('edittip').open()
  30459. })
  30460. var pastePop,
  30461. isPaste = false,
  30462. timer
  30463. editor.addListener('afterpaste', function () {
  30464. if (editor.queryCommandState('pasteplain')) return
  30465. if (baidu.editor.ui.PastePicker) {
  30466. pastePop = new baidu.editor.ui.Popup({
  30467. content: new baidu.editor.ui.PastePicker({ editor: editor }),
  30468. editor: editor,
  30469. className: 'edui-wordpastepop'
  30470. })
  30471. pastePop.render()
  30472. }
  30473. isPaste = true
  30474. })
  30475. editor.addListener('afterinserthtml', function () {
  30476. clearTimeout(timer)
  30477. timer = setTimeout(function () {
  30478. if (pastePop && (isPaste || editor.ui._isTransfer)) {
  30479. if (pastePop.isHidden()) {
  30480. var span = domUtils.createElement(editor.document, 'span', {
  30481. style: 'line-height:0px;',
  30482. innerHTML: '\ufeff'
  30483. }),
  30484. range = editor.selection.getRange()
  30485. range.insertNode(span)
  30486. var tmp = getDomNode(span, 'firstChild', 'previousSibling')
  30487. tmp &&
  30488. pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp)
  30489. domUtils.remove(span)
  30490. } else {
  30491. pastePop.show()
  30492. }
  30493. delete editor.ui._isTransfer
  30494. isPaste = false
  30495. }
  30496. }, 200)
  30497. })
  30498. editor.addListener('contextmenu', function (t, evt) {
  30499. baidu.editor.ui.Popup.postHide(evt)
  30500. })
  30501. editor.addListener('keydown', function (t, evt) {
  30502. if (pastePop) pastePop.dispose(evt)
  30503. var keyCode = evt.keyCode || evt.which
  30504. if (evt.altKey && keyCode == 90) {
  30505. UE.ui.buttons['fullscreen'].onclick()
  30506. }
  30507. })
  30508. editor.addListener('wordcount', function (type) {
  30509. setCount(this, me)
  30510. })
  30511. function setCount(editor, ui) {
  30512. editor.setOpt({
  30513. wordCount: true,
  30514. maximumWords: 10000,
  30515. wordCountMsg:
  30516. editor.options.wordCountMsg || editor.getLang('wordCountMsg'),
  30517. wordOverFlowMsg:
  30518. editor.options.wordOverFlowMsg ||
  30519. editor.getLang('wordOverFlowMsg')
  30520. })
  30521. var opt = editor.options,
  30522. max = opt.maximumWords,
  30523. msg = opt.wordCountMsg,
  30524. errMsg = opt.wordOverFlowMsg,
  30525. countDom = ui.getDom('wordcount')
  30526. if (!opt.wordCount) {
  30527. return
  30528. }
  30529. var count = editor.getContentLength(true)
  30530. if (count > max) {
  30531. countDom.innerHTML = errMsg
  30532. editor.fireEvent('wordcountoverflow')
  30533. } else {
  30534. countDom.innerHTML = msg
  30535. .replace('{#leave}', max - count)
  30536. .replace('{#count}', count)
  30537. }
  30538. }
  30539. editor.addListener('selectionchange', function () {
  30540. if (editor.options.elementPathEnabled) {
  30541. me[
  30542. (editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') +
  30543. 'ableElementPath'
  30544. ]()
  30545. }
  30546. if (editor.options.scaleEnabled) {
  30547. me[
  30548. (editor.queryCommandState('scale') == -1 ? 'dis' : 'en') +
  30549. 'ableScale'
  30550. ]()
  30551. }
  30552. })
  30553. var popup = new baidu.editor.ui.Popup({
  30554. editor: editor,
  30555. content: '',
  30556. className: 'edui-bubble',
  30557. _onEditButtonClick: function () {
  30558. this.hide()
  30559. editor.ui._dialogs.linkDialog.open()
  30560. },
  30561. _onImgEditButtonClick: function (name) {
  30562. this.hide()
  30563. editor.ui._dialogs[name] && editor.ui._dialogs[name].open()
  30564. },
  30565. _onImgSetFloat: function (value) {
  30566. this.hide()
  30567. editor.execCommand('imagefloat', value)
  30568. },
  30569. _setIframeAlign: function (value) {
  30570. var frame = popup.anchorEl
  30571. var newFrame = frame.cloneNode(true)
  30572. switch (value) {
  30573. case -2:
  30574. newFrame.setAttribute('align', '')
  30575. break
  30576. case -1:
  30577. newFrame.setAttribute('align', 'left')
  30578. break
  30579. case 1:
  30580. newFrame.setAttribute('align', 'right')
  30581. break
  30582. }
  30583. frame.parentNode.insertBefore(newFrame, frame)
  30584. domUtils.remove(frame)
  30585. popup.anchorEl = newFrame
  30586. popup.showAnchor(popup.anchorEl)
  30587. },
  30588. _updateIframe: function () {
  30589. var frame = (editor._iframe = popup.anchorEl)
  30590. if (domUtils.hasClass(frame, 'ueditor_baidumap')) {
  30591. editor.selection.getRange().selectNode(frame).select()
  30592. editor.ui._dialogs.mapDialog.open()
  30593. popup.hide()
  30594. } else {
  30595. editor.ui._dialogs.insertframeDialog.open()
  30596. popup.hide()
  30597. }
  30598. },
  30599. _onRemoveButtonClick: function (cmdName) {
  30600. editor.execCommand(cmdName)
  30601. this.hide()
  30602. },
  30603. queryAutoHide: function (el) {
  30604. if (el && el.ownerDocument == editor.document) {
  30605. if (
  30606. el.tagName.toLowerCase() == 'img' ||
  30607. domUtils.findParentByTagName(el, 'a', true)
  30608. ) {
  30609. return el !== popup.anchorEl
  30610. }
  30611. }
  30612. return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el)
  30613. }
  30614. })
  30615. popup.render()
  30616. if (editor.options.imagePopup) {
  30617. editor.addListener('mouseover', function (t, evt) {
  30618. evt = evt || window.event
  30619. var el = evt.target || evt.srcElement
  30620. if (
  30621. editor.ui._dialogs.insertframeDialog &&
  30622. /iframe/gi.test(el.tagName)
  30623. ) {
  30624. var html = popup.formatHtml(
  30625. '<nobr>' +
  30626. editor.getLang('property') +
  30627. ': <span onclick=$$._setIframeAlign(-2) class="edui-clickable">' +
  30628. editor.getLang('default') +
  30629. '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(-1) class="edui-clickable">' +
  30630. editor.getLang('justifyleft') +
  30631. '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(1) class="edui-clickable">' +
  30632. editor.getLang('justifyright') +
  30633. '</span>&nbsp;&nbsp;' +
  30634. ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' +
  30635. editor.getLang('modify') +
  30636. '</span></nobr>'
  30637. )
  30638. if (html) {
  30639. popup.getDom('content').innerHTML = html
  30640. popup.anchorEl = el
  30641. popup.showAnchor(popup.anchorEl)
  30642. } else {
  30643. popup.hide()
  30644. }
  30645. }
  30646. })
  30647. editor.addListener('selectionchange', function (t, causeByUi) {
  30648. if (!causeByUi) return
  30649. var html = '',
  30650. str = '',
  30651. img = editor.selection.getRange().getClosedNode(),
  30652. dialogs = editor.ui._dialogs
  30653. if (img && img.tagName == 'IMG') {
  30654. var dialogName = 'insertimageDialog'
  30655. if (
  30656. img.className.indexOf('edui-faked-video') != -1 ||
  30657. img.className.indexOf('edui-upload-video') != -1
  30658. ) {
  30659. dialogName = 'insertvideoDialog'
  30660. }
  30661. if (img.className.indexOf('edui-faked-webapp') != -1) {
  30662. dialogName = 'webappDialog'
  30663. }
  30664. if (img.src.indexOf('http://api.map.baidu.com') != -1) {
  30665. dialogName = 'mapDialog'
  30666. }
  30667. if (img.className.indexOf('edui-faked-music') != -1) {
  30668. dialogName = 'musicDialog'
  30669. }
  30670. if (
  30671. img.src.indexOf('http://maps.google.com/maps/api/staticmap') !=
  30672. -1
  30673. ) {
  30674. dialogName = 'gmapDialog'
  30675. }
  30676. if (img.getAttribute('anchorname')) {
  30677. dialogName = 'anchorDialog'
  30678. html = popup.formatHtml(
  30679. '<nobr>' +
  30680. editor.getLang('property') +
  30681. ': <span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' +
  30682. editor.getLang('modify') +
  30683. '</span>&nbsp;&nbsp;' +
  30684. '<span onclick=$$._onRemoveButtonClick(\'anchor\') class="edui-clickable">' +
  30685. editor.getLang('delete') +
  30686. '</span></nobr>'
  30687. )
  30688. }
  30689. if (img.getAttribute('word_img')) {
  30690. //todo 放到dialog去做查询
  30691. editor.word_img = [img.getAttribute('word_img')]
  30692. dialogName = 'wordimageDialog'
  30693. }
  30694. if (
  30695. domUtils.hasClass(img, 'loadingclass') ||
  30696. domUtils.hasClass(img, 'loaderrorclass')
  30697. ) {
  30698. dialogName = ''
  30699. }
  30700. if (!dialogs[dialogName]) {
  30701. return
  30702. }
  30703. str =
  30704. '<nobr>' +
  30705. editor.getLang('property') +
  30706. ': ' +
  30707. '<span onclick=$$._onImgSetFloat("none") class="edui-clickable">' +
  30708. editor.getLang('default') +
  30709. '</span>&nbsp;&nbsp;' +
  30710. '<span onclick=$$._onImgSetFloat("left") class="edui-clickable">' +
  30711. editor.getLang('justifyleft') +
  30712. '</span>&nbsp;&nbsp;' +
  30713. '<span onclick=$$._onImgSetFloat("right") class="edui-clickable">' +
  30714. editor.getLang('justifyright') +
  30715. '</span>&nbsp;&nbsp;' +
  30716. '<span onclick=$$._onImgSetFloat("center") class="edui-clickable">' +
  30717. editor.getLang('justifycenter') +
  30718. '</span>&nbsp;&nbsp;' +
  30719. '<span onclick="$$._onImgEditButtonClick(\'' +
  30720. dialogName +
  30721. '\');" class="edui-clickable">' +
  30722. editor.getLang('modify') +
  30723. '</span></nobr>'
  30724. !html && (html = popup.formatHtml(str))
  30725. }
  30726. if (editor.ui._dialogs.linkDialog) {
  30727. var link = editor.queryCommandValue('link')
  30728. var url
  30729. if (
  30730. link &&
  30731. (url =
  30732. link.getAttribute('_href') || link.getAttribute('href', 2))
  30733. ) {
  30734. var txt = url
  30735. if (url.length > 30) {
  30736. txt = url.substring(0, 20) + '...'
  30737. }
  30738. if (html) {
  30739. html += '<div style="height:5px;"></div>'
  30740. }
  30741. html += popup.formatHtml(
  30742. '<nobr>' +
  30743. editor.getLang('anthorMsg') +
  30744. ': <a target="_blank" href="' +
  30745. url +
  30746. '" title="' +
  30747. url +
  30748. '" >' +
  30749. txt +
  30750. '</a>' +
  30751. ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' +
  30752. editor.getLang('modify') +
  30753. '</span>' +
  30754. ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' +
  30755. editor.getLang('clear') +
  30756. '</span></nobr>'
  30757. )
  30758. popup.showAnchor(link)
  30759. }
  30760. }
  30761. if (html) {
  30762. popup.getDom('content').innerHTML = html
  30763. popup.anchorEl = img || link
  30764. popup.showAnchor(popup.anchorEl)
  30765. } else {
  30766. popup.hide()
  30767. }
  30768. })
  30769. }
  30770. },
  30771. _initToolbars: function () {
  30772. var editor = this.editor
  30773. var toolbars = this.toolbars || []
  30774. var toolbarUis = []
  30775. for (var i = 0; i < toolbars.length; i++) {
  30776. var toolbar = toolbars[i]
  30777. var toolbarUi = new baidu.editor.ui.Toolbar({
  30778. theme: editor.options.theme
  30779. })
  30780. for (var j = 0; j < toolbar.length; j++) {
  30781. var toolbarItem = toolbar[j]
  30782. var toolbarItemUi = null
  30783. if (typeof toolbarItem == 'string') {
  30784. toolbarItem = toolbarItem.toLowerCase()
  30785. if (toolbarItem == '|') {
  30786. toolbarItem = 'Separator'
  30787. }
  30788. if (toolbarItem == '||') {
  30789. toolbarItem = 'Breakline'
  30790. }
  30791. if (baidu.editor.ui[toolbarItem]) {
  30792. toolbarItemUi = new baidu.editor.ui[toolbarItem](editor)
  30793. }
  30794. //fullscreen这里单独处理一下,放到首行去
  30795. if (toolbarItem == 'fullscreen') {
  30796. if (toolbarUis && toolbarUis[0]) {
  30797. toolbarUis[0].items.splice(0, 0, toolbarItemUi)
  30798. } else {
  30799. toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi)
  30800. }
  30801. continue
  30802. }
  30803. } else {
  30804. toolbarItemUi = toolbarItem
  30805. }
  30806. if (toolbarItemUi && toolbarItemUi.id) {
  30807. toolbarUi.add(toolbarItemUi)
  30808. }
  30809. }
  30810. toolbarUis[i] = toolbarUi
  30811. }
  30812. //接受外部定制的UI(修复因 utils.each 无法准确的循环出对象的全部元素而导致的自定义 UI 不符合预期的 BUG by HaoChuan9421)
  30813. // utils.each(UE._customizeUI,function(obj,key){
  30814. // var itemUI,index;
  30815. // if(obj.id && obj.id != editor.key){
  30816. // return false;
  30817. // }
  30818. // itemUI = obj.execFn.call(editor,editor,key);
  30819. // if(itemUI){
  30820. // index = obj.index;
  30821. // if(index === undefined){
  30822. // index = toolbarUi.items.length;
  30823. // }
  30824. // toolbarUi.add(itemUI,index)
  30825. // }
  30826. // });
  30827. for (var key in UE._customizeUI) {
  30828. var obj = UE._customizeUI[key]
  30829. var itemUI, index
  30830. if (!obj.id || obj.id == editor.key) {
  30831. itemUI = obj.execFn.call(editor, editor, key)
  30832. if (itemUI) {
  30833. index = obj.index
  30834. if (index === undefined) {
  30835. index = toolbarUi.items.length
  30836. }
  30837. toolbarUi.add(itemUI, index)
  30838. }
  30839. }
  30840. }
  30841. this.toolbars = toolbarUis
  30842. },
  30843. getHtmlTpl: function () {
  30844. return (
  30845. '<div id="##" class="%%">' +
  30846. '<div id="##_toolbarbox" class="%%-toolbarbox">' +
  30847. (this.toolbars.length
  30848. ? '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
  30849. this.renderToolbarBoxHtml() +
  30850. '</div></div>'
  30851. : '') +
  30852. '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
  30853. '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' +
  30854. this.editor.getLang('clickToUpload') +
  30855. '</div>' +
  30856. '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
  30857. '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
  30858. '<div style="height:0;overflow:hidden;clear:both;"></div>' +
  30859. '</div>' +
  30860. '<div id="##_message_holder" class="%%-messageholder"></div>' +
  30861. '</div>' +
  30862. '<div id="##_iframeholder" class="%%-iframeholder">' +
  30863. '</div>' +
  30864. //modify wdcount by matao
  30865. '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
  30866. '<td id="##_elementpath" class="%%-bottombar"></td>' +
  30867. '<td id="##_wordcount" class="%%-wordcount"></td>' +
  30868. '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
  30869. '</tr></table></div>' +
  30870. '<div id="##_scalelayer"></div>' +
  30871. '</div>'
  30872. )
  30873. },
  30874. showWordImageDialog: function () {
  30875. this._dialogs['wordimageDialog'].open()
  30876. },
  30877. renderToolbarBoxHtml: function () {
  30878. var buff = []
  30879. for (var i = 0; i < this.toolbars.length; i++) {
  30880. buff.push(this.toolbars[i].renderHtml())
  30881. }
  30882. return buff.join('')
  30883. },
  30884. setFullScreen: function (fullscreen) {
  30885. var editor = this.editor,
  30886. container = editor.container.parentNode.parentNode
  30887. if (this._fullscreen != fullscreen) {
  30888. this._fullscreen = fullscreen
  30889. this.editor.fireEvent('beforefullscreenchange', fullscreen)
  30890. if (baidu.editor.browser.gecko) {
  30891. var bk = editor.selection.getRange().createBookmark()
  30892. }
  30893. if (fullscreen) {
  30894. while (container.tagName != 'BODY') {
  30895. var position = baidu.editor.dom.domUtils.getComputedStyle(
  30896. container,
  30897. 'position'
  30898. )
  30899. nodeStack.push(position)
  30900. container.style.position = 'static'
  30901. container = container.parentNode
  30902. }
  30903. this._bakHtmlOverflow = document.documentElement.style.overflow
  30904. this._bakBodyOverflow = document.body.style.overflow
  30905. this._bakAutoHeight = this.editor.autoHeightEnabled
  30906. this._bakScrollTop = Math.max(
  30907. document.documentElement.scrollTop,
  30908. document.body.scrollTop
  30909. )
  30910. this._bakEditorContaninerWidth =
  30911. editor.iframe.parentNode.offsetWidth
  30912. if (this._bakAutoHeight) {
  30913. //当全屏时不能执行自动长高
  30914. editor.autoHeightEnabled = false
  30915. this.editor.disableAutoHeight()
  30916. }
  30917. document.documentElement.style.overflow = 'hidden'
  30918. //修复,滚动条不收起的问题
  30919. window.scrollTo(0, window.scrollY)
  30920. this._bakCssText = this.getDom().style.cssText
  30921. this._bakCssText1 = this.getDom('iframeholder').style.cssText
  30922. editor.iframe.parentNode.style.width = ''
  30923. this._updateFullScreen()
  30924. } else {
  30925. while (container.tagName != 'BODY') {
  30926. container.style.position = nodeStack.shift()
  30927. container = container.parentNode
  30928. }
  30929. this.getDom().style.cssText = this._bakCssText
  30930. this.getDom('iframeholder').style.cssText = this._bakCssText1
  30931. if (this._bakAutoHeight) {
  30932. editor.autoHeightEnabled = true
  30933. this.editor.enableAutoHeight()
  30934. }
  30935. document.documentElement.style.overflow = this._bakHtmlOverflow
  30936. document.body.style.overflow = this._bakBodyOverflow
  30937. editor.iframe.parentNode.style.width =
  30938. this._bakEditorContaninerWidth + 'px'
  30939. window.scrollTo(0, this._bakScrollTop)
  30940. }
  30941. if (browser.gecko && editor.body.contentEditable === 'true') {
  30942. var input = document.createElement('input')
  30943. document.body.appendChild(input)
  30944. editor.body.contentEditable = false
  30945. setTimeout(function () {
  30946. input.focus()
  30947. setTimeout(function () {
  30948. editor.body.contentEditable = true
  30949. editor.fireEvent('fullscreenchanged', fullscreen)
  30950. editor.selection.getRange().moveToBookmark(bk).select(true)
  30951. baidu.editor.dom.domUtils.remove(input)
  30952. fullscreen && window.scroll(0, 0)
  30953. }, 0)
  30954. }, 0)
  30955. }
  30956. if (editor.body.contentEditable === 'true') {
  30957. this.editor.fireEvent('fullscreenchanged', fullscreen)
  30958. this.triggerLayout()
  30959. }
  30960. }
  30961. },
  30962. _updateFullScreen: function () {
  30963. if (this._fullscreen) {
  30964. var vpRect = uiUtils.getViewportRect()
  30965. this.getDom().style.cssText =
  30966. 'border:0;position:absolute;left:0;top:' +
  30967. (this.editor.options.topOffset || 0) +
  30968. 'px;width:' +
  30969. vpRect.width +
  30970. 'px;height:' +
  30971. vpRect.height +
  30972. 'px;z-index:' +
  30973. (this.getDom().style.zIndex * 1 + 100)
  30974. uiUtils.setViewportOffset(this.getDom(), {
  30975. left: 0,
  30976. top: this.editor.options.topOffset || 0
  30977. })
  30978. this.editor.setHeight(
  30979. vpRect.height -
  30980. this.getDom('toolbarbox').offsetHeight -
  30981. this.getDom('bottombar').offsetHeight -
  30982. (this.editor.options.topOffset || 0),
  30983. true
  30984. )
  30985. //不手动调一下,会导致全屏失效
  30986. if (browser.gecko) {
  30987. try {
  30988. window.onresize()
  30989. } catch (e) {}
  30990. }
  30991. }
  30992. },
  30993. _updateElementPath: function () {
  30994. var bottom = this.getDom('elementpath'),
  30995. list
  30996. if (
  30997. this.elementPathEnabled &&
  30998. (list = this.editor.queryCommandValue('elementpath'))
  30999. ) {
  31000. var buff = []
  31001. for (var i = 0, ci; (ci = list[i]); i++) {
  31002. buff[i] = this.formatHtml(
  31003. '<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' +
  31004. i +
  31005. '&quot;);">' +
  31006. ci +
  31007. '</span>'
  31008. )
  31009. }
  31010. bottom.innerHTML =
  31011. '<div class="edui-editor-breadcrumb" onmousedown="return false;">' +
  31012. this.editor.getLang('elementPathTip') +
  31013. ': ' +
  31014. buff.join(' &gt; ') +
  31015. '</div>'
  31016. } else {
  31017. bottom.style.display = 'none'
  31018. }
  31019. },
  31020. disableElementPath: function () {
  31021. var bottom = this.getDom('elementpath')
  31022. bottom.innerHTML = ''
  31023. bottom.style.display = 'none'
  31024. this.elementPathEnabled = false
  31025. },
  31026. enableElementPath: function () {
  31027. var bottom = this.getDom('elementpath')
  31028. bottom.style.display = ''
  31029. this.elementPathEnabled = true
  31030. this._updateElementPath()
  31031. },
  31032. _scale: function () {
  31033. var doc = document,
  31034. editor = this.editor,
  31035. editorHolder = editor.container,
  31036. editorDocument = editor.document,
  31037. toolbarBox = this.getDom('toolbarbox'),
  31038. bottombar = this.getDom('bottombar'),
  31039. scale = this.getDom('scale'),
  31040. scalelayer = this.getDom('scalelayer')
  31041. var isMouseMove = false,
  31042. position = null,
  31043. minEditorHeight = 0,
  31044. minEditorWidth = editor.options.minFrameWidth,
  31045. pageX = 0,
  31046. pageY = 0,
  31047. scaleWidth = 0,
  31048. scaleHeight = 0
  31049. function down() {
  31050. position = domUtils.getXY(editorHolder)
  31051. if (!minEditorHeight) {
  31052. minEditorHeight =
  31053. editor.options.minFrameHeight +
  31054. toolbarBox.offsetHeight +
  31055. bottombar.offsetHeight
  31056. }
  31057. scalelayer.style.cssText =
  31058. 'position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:' +
  31059. editorHolder.offsetWidth +
  31060. 'px;height:' +
  31061. editorHolder.offsetHeight +
  31062. 'px;z-index:' +
  31063. (editor.options.zIndex + 1)
  31064. domUtils.on(doc, 'mousemove', move)
  31065. domUtils.on(editorDocument, 'mouseup', up)
  31066. domUtils.on(doc, 'mouseup', up)
  31067. }
  31068. var me = this
  31069. //by xuheng 全屏时关掉缩放
  31070. this.editor.addListener('fullscreenchanged', function (e, fullScreen) {
  31071. if (fullScreen) {
  31072. me.disableScale()
  31073. } else {
  31074. if (me.editor.options.scaleEnabled) {
  31075. me.enableScale()
  31076. var tmpNode = me.editor.document.createElement('span')
  31077. me.editor.body.appendChild(tmpNode)
  31078. me.editor.body.style.height =
  31079. Math.max(
  31080. domUtils.getXY(tmpNode).y,
  31081. me.editor.iframe.offsetHeight - 20
  31082. ) + 'px'
  31083. domUtils.remove(tmpNode)
  31084. }
  31085. }
  31086. })
  31087. function move(event) {
  31088. clearSelection()
  31089. var e = event || window.event
  31090. pageX = e.pageX || doc.documentElement.scrollLeft + e.clientX
  31091. pageY = e.pageY || doc.documentElement.scrollTop + e.clientY
  31092. scaleWidth = pageX - position.x
  31093. scaleHeight = pageY - position.y
  31094. if (scaleWidth >= minEditorWidth) {
  31095. isMouseMove = true
  31096. scalelayer.style.width = scaleWidth + 'px'
  31097. }
  31098. if (scaleHeight >= minEditorHeight) {
  31099. isMouseMove = true
  31100. scalelayer.style.height = scaleHeight + 'px'
  31101. }
  31102. }
  31103. function up() {
  31104. if (isMouseMove) {
  31105. isMouseMove = false
  31106. editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2
  31107. editorHolder.style.width = editor.ui._actualFrameWidth + 'px'
  31108. editor.setHeight(
  31109. scalelayer.offsetHeight -
  31110. bottombar.offsetHeight -
  31111. toolbarBox.offsetHeight -
  31112. 2,
  31113. true
  31114. )
  31115. }
  31116. if (scalelayer) {
  31117. scalelayer.style.display = 'none'
  31118. }
  31119. clearSelection()
  31120. domUtils.un(doc, 'mousemove', move)
  31121. domUtils.un(editorDocument, 'mouseup', up)
  31122. domUtils.un(doc, 'mouseup', up)
  31123. }
  31124. function clearSelection() {
  31125. if (browser.ie) doc.selection.clear()
  31126. else window.getSelection().removeAllRanges()
  31127. }
  31128. this.enableScale = function () {
  31129. //trace:2868
  31130. if (editor.queryCommandState('source') == 1) return
  31131. scale.style.display = ''
  31132. this.scaleEnabled = true
  31133. domUtils.on(scale, 'mousedown', down)
  31134. }
  31135. this.disableScale = function () {
  31136. scale.style.display = 'none'
  31137. this.scaleEnabled = false
  31138. domUtils.un(scale, 'mousedown', down)
  31139. }
  31140. },
  31141. isFullScreen: function () {
  31142. return this._fullscreen
  31143. },
  31144. postRender: function () {
  31145. UIBase.prototype.postRender.call(this)
  31146. for (var i = 0; i < this.toolbars.length; i++) {
  31147. this.toolbars[i].postRender()
  31148. }
  31149. var me = this
  31150. var timerId,
  31151. domUtils = baidu.editor.dom.domUtils,
  31152. updateFullScreenTime = function () {
  31153. clearTimeout(timerId)
  31154. timerId = setTimeout(function () {
  31155. me._updateFullScreen()
  31156. })
  31157. }
  31158. domUtils.on(window, 'resize', updateFullScreenTime)
  31159. me.addListener('destroy', function () {
  31160. domUtils.un(window, 'resize', updateFullScreenTime)
  31161. clearTimeout(timerId)
  31162. })
  31163. },
  31164. showToolbarMsg: function (msg, flag) {
  31165. this.getDom('toolbarmsg_label').innerHTML = msg
  31166. this.getDom('toolbarmsg').style.display = ''
  31167. //
  31168. if (!flag) {
  31169. var w = this.getDom('upload_dialog')
  31170. w.style.display = 'none'
  31171. }
  31172. },
  31173. hideToolbarMsg: function () {
  31174. this.getDom('toolbarmsg').style.display = 'none'
  31175. },
  31176. mapUrl: function (url) {
  31177. return url
  31178. ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '')
  31179. : ''
  31180. },
  31181. triggerLayout: function () {
  31182. var dom = this.getDom()
  31183. if (dom.style.zoom == '1') {
  31184. dom.style.zoom = '100%'
  31185. } else {
  31186. dom.style.zoom = '1'
  31187. }
  31188. }
  31189. }
  31190. utils.inherits(EditorUI, baidu.editor.ui.UIBase)
  31191. var instances = {}
  31192. UE.ui.Editor = function (options) {
  31193. var editor = new UE.Editor(options)
  31194. editor.options.editor = editor
  31195. utils.loadFile(document, {
  31196. href:
  31197. editor.options.themePath + editor.options.theme + '/css/ueditor.css',
  31198. tag: 'link',
  31199. type: 'text/css',
  31200. rel: 'stylesheet'
  31201. })
  31202. var oldRender = editor.render
  31203. editor.render = function (holder) {
  31204. if (holder.constructor === String) {
  31205. editor.key = holder
  31206. instances[holder] = editor
  31207. }
  31208. utils.domReady(function () {
  31209. editor.langIsReady
  31210. ? renderUI()
  31211. : editor.addListener('langReady', renderUI)
  31212. function renderUI() {
  31213. editor.setOpt({
  31214. labelMap: editor.options.labelMap || editor.getLang('labelMap')
  31215. })
  31216. new EditorUI(editor.options)
  31217. if (holder) {
  31218. if (holder.constructor === String) {
  31219. holder = document.getElementById(holder)
  31220. }
  31221. holder &&
  31222. holder.getAttribute('name') &&
  31223. (editor.options.textarea = holder.getAttribute('name'))
  31224. if (holder && /script|textarea/gi.test(holder.tagName)) {
  31225. var newDiv = document.createElement('div')
  31226. holder.parentNode.insertBefore(newDiv, holder)
  31227. var cont = holder.value || holder.innerHTML
  31228. editor.options.initialContent = /^[\t\r\n ]*$/.test(cont)
  31229. ? editor.options.initialContent
  31230. : cont
  31231. .replace(/>[\n\r\t]+([ ]{4})+/g, '>')
  31232. .replace(/[\n\r\t]+([ ]{4})+</g, '<')
  31233. .replace(/>[\n\r\t]+</g, '><')
  31234. holder.className && (newDiv.className = holder.className)
  31235. holder.style.cssText &&
  31236. (newDiv.style.cssText = holder.style.cssText)
  31237. if (/textarea/i.test(holder.tagName)) {
  31238. editor.textarea = holder
  31239. editor.textarea.style.display = 'none'
  31240. } else {
  31241. holder.parentNode.removeChild(holder)
  31242. }
  31243. if (holder.id) {
  31244. newDiv.id = holder.id
  31245. domUtils.removeAttributes(holder, 'id')
  31246. }
  31247. holder = newDiv
  31248. holder.innerHTML = ''
  31249. }
  31250. }
  31251. domUtils.addClass(holder, 'edui-' + editor.options.theme)
  31252. editor.ui.render(holder)
  31253. var opt = editor.options
  31254. //给实例添加一个编辑器的容器引用
  31255. editor.container = editor.ui.getDom()
  31256. var parents = domUtils.findParents(holder, true)
  31257. var displays = []
  31258. for (var i = 0, ci; (ci = parents[i]); i++) {
  31259. displays[i] = ci.style.display
  31260. ci.style.display = 'block'
  31261. }
  31262. if (opt.initialFrameWidth) {
  31263. opt.minFrameWidth = opt.initialFrameWidth
  31264. } else {
  31265. opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth
  31266. var styleWidth = holder.style.width
  31267. if (/%$/.test(styleWidth)) {
  31268. opt.initialFrameWidth = styleWidth
  31269. }
  31270. }
  31271. if (opt.initialFrameHeight) {
  31272. opt.minFrameHeight = opt.initialFrameHeight
  31273. } else {
  31274. opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight
  31275. }
  31276. for (var i = 0, ci; (ci = parents[i]); i++) {
  31277. ci.style.display = displays[i]
  31278. }
  31279. //编辑器最外容器设置了高度,会导致,编辑器不占位
  31280. //todo 先去掉,没有找到原因
  31281. if (holder.style.height) {
  31282. holder.style.height = ''
  31283. }
  31284. editor.container.style.width =
  31285. opt.initialFrameWidth +
  31286. (/%$/.test(opt.initialFrameWidth) ? '' : 'px')
  31287. editor.container.style.zIndex = opt.zIndex
  31288. oldRender.call(editor, editor.ui.getDom('iframeholder'))
  31289. editor.fireEvent('afteruiready')
  31290. }
  31291. })
  31292. }
  31293. return editor
  31294. }
  31295. /**
  31296. * @file
  31297. * @name UE
  31298. * @short UE
  31299. * @desc UEditor的顶部命名空间
  31300. */
  31301. /**
  31302. * @name getEditor
  31303. * @since 1.2.4+
  31304. * @grammar UE.getEditor(id,[opt]) => Editor实例
  31305. * @desc 提供一个全局的方法得到编辑器实例
  31306. *
  31307. * * ''id'' 放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
  31308. * * ''opt'' 编辑器的可选参数
  31309. * @example
  31310. * UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
  31311. * this.setContent('hello')
  31312. * }});
  31313. * UE.getEditor('containerId'); //返回刚创建的实例
  31314. *
  31315. */
  31316. UE.getEditor = function (id, opt) {
  31317. var editor = instances[id]
  31318. if (!editor) {
  31319. editor = instances[id] = new UE.ui.Editor(opt)
  31320. editor.render(id)
  31321. }
  31322. return editor
  31323. }
  31324. UE.delEditor = function (id) {
  31325. var editor
  31326. if ((editor = instances[id])) {
  31327. editor.key && editor.destroy()
  31328. delete instances[id]
  31329. }
  31330. }
  31331. UE.registerUI = function (uiName, fn, index, editorId) {
  31332. utils.each(uiName.split(/\s+/), function (name) {
  31333. UE._customizeUI[name] = {
  31334. id: editorId,
  31335. execFn: fn,
  31336. index: index
  31337. }
  31338. })
  31339. }
  31340. })()
  31341. // adapter/message.js
  31342. UE.registerUI('message', function (editor) {
  31343. var editorui = baidu.editor.ui
  31344. var Message = editorui.Message
  31345. var holder
  31346. var _messageItems = []
  31347. var me = editor
  31348. me.addListener('ready', function () {
  31349. holder = document.getElementById(me.ui.id + '_message_holder')
  31350. updateHolderPos()
  31351. // HaoChuan9421
  31352. // setTimeout(function(){
  31353. // updateHolderPos();
  31354. // }, 500);
  31355. })
  31356. me.addListener('showmessage', function (type, opt) {
  31357. opt = utils.isString(opt)
  31358. ? {
  31359. content: opt
  31360. }
  31361. : opt
  31362. var message = new Message({
  31363. timeout: opt.timeout,
  31364. type: opt.type,
  31365. content: opt.content,
  31366. keepshow: opt.keepshow,
  31367. editor: me
  31368. }),
  31369. mid = opt.id || 'msg_' + (+new Date()).toString(36)
  31370. message.render(holder)
  31371. _messageItems[mid] = message
  31372. message.reset(opt)
  31373. updateHolderPos()
  31374. return mid
  31375. })
  31376. me.addListener('updatemessage', function (type, id, opt) {
  31377. opt = utils.isString(opt)
  31378. ? {
  31379. content: opt
  31380. }
  31381. : opt
  31382. var message = _messageItems[id]
  31383. message.render(holder)
  31384. message && message.reset(opt)
  31385. })
  31386. me.addListener('hidemessage', function (type, id) {
  31387. var message = _messageItems[id]
  31388. message && message.hide()
  31389. })
  31390. function updateHolderPos() {
  31391. var toolbarbox = me.ui.getDom('toolbarbox')
  31392. if (toolbarbox) {
  31393. holder.style.top = toolbarbox.offsetHeight + 3 + 'px'
  31394. }
  31395. holder.style.zIndex =
  31396. Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1
  31397. }
  31398. })
  31399. // adapter/autosave.js
  31400. UE.registerUI('autosave', function (editor) {
  31401. var timer = null,
  31402. uid = null
  31403. editor.on('afterautosave', function () {
  31404. clearTimeout(timer)
  31405. timer = setTimeout(function () {
  31406. if (uid) {
  31407. editor.trigger('hidemessage', uid)
  31408. }
  31409. uid = editor.trigger('showmessage', {
  31410. content: editor.getLang('autosave.success'),
  31411. timeout: 2000
  31412. })
  31413. }, 2000)
  31414. })
  31415. })
  31416. })()