umeditor.js 381 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015
  1. /*!
  2. * UEditor Mini版本
  3. * version: 1.2.2
  4. * build: Fri Feb 10 2017 15:00:06 GMT+0800 (CST)
  5. */
  6. (function($){
  7. UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};
  8. window.UM = {
  9. plugins : {},
  10. commands : {},
  11. I18N : {},
  12. version : "1.2.2"
  13. };
  14. var dom = UM.dom = {};
  15. /**
  16. * 浏览器判断模块
  17. * @file
  18. * @module UE.browser
  19. * @since 1.2.6.1
  20. */
  21. /**
  22. * 提供浏览器检测的模块
  23. * @unfile
  24. * @module UE.browser
  25. */
  26. var browser = UM.browser = function(){
  27. var agent = navigator.userAgent.toLowerCase(),
  28. opera = window.opera,
  29. browser = {
  30. /**
  31. * @property {boolean} ie 检测当前浏览器是否为IE
  32. * @example
  33. * ```javascript
  34. * if ( UE.browser.ie ) {
  35. * console.log( '当前浏览器是IE' );
  36. * }
  37. * ```
  38. */
  39. ie : /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  40. /**
  41. * @property {boolean} opera 检测当前浏览器是否为Opera
  42. * @example
  43. * ```javascript
  44. * if ( UE.browser.opera ) {
  45. * console.log( '当前浏览器是Opera' );
  46. * }
  47. * ```
  48. */
  49. opera : ( !!opera && opera.version ),
  50. /**
  51. * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
  52. * @example
  53. * ```javascript
  54. * if ( UE.browser.webkit ) {
  55. * console.log( '当前浏览器是webkit内核浏览器' );
  56. * }
  57. * ```
  58. */
  59. webkit : ( agent.indexOf( ' applewebkit/' ) > -1 ),
  60. /**
  61. * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
  62. * @example
  63. * ```javascript
  64. * if ( UE.browser.mac ) {
  65. * console.log( '当前浏览器运行在mac平台下' );
  66. * }
  67. * ```
  68. */
  69. mac : ( agent.indexOf( 'macintosh' ) > -1 ),
  70. /**
  71. * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
  72. * @example
  73. * ```javascript
  74. * if ( UE.browser.quirks ) {
  75. * console.log( '当前浏览器运行处于“怪异模式”' );
  76. * }
  77. * ```
  78. */
  79. quirks : ( document.compatMode == 'BackCompat' )
  80. };
  81. /**
  82. * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
  83. * @example
  84. * ```javascript
  85. * if ( UE.browser.gecko ) {
  86. * console.log( '当前浏览器内核是gecko内核' );
  87. * }
  88. * ```
  89. */
  90. browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
  91. var version = 0;
  92. // Internet Explorer 6.0+
  93. if ( browser.ie ){
  94. var v1 = agent.match(/(?:msie\s([\w.]+))/);
  95. var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
  96. if(v1 && v2 && v1[1] && v2[1]){
  97. version = Math.max(v1[1]*1,v2[1]*1);
  98. }else if(v1 && v1[1]){
  99. version = v1[1]*1;
  100. }else if(v2 && v2[1]){
  101. version = v2[1]*1;
  102. }else{
  103. version = 0;
  104. }
  105. browser.ie11Compat = document.documentMode == 11;
  106. /**
  107. * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
  108. * @warning 如果浏览器不是IE, 则该值为undefined
  109. * @example
  110. * ```javascript
  111. * if ( UE.browser.ie9Compat ) {
  112. * console.log( '当前浏览器运行在IE9兼容模式下' );
  113. * }
  114. * ```
  115. */
  116. browser.ie9Compat = document.documentMode == 9;
  117. /**
  118. * @property { boolean } ie8 检测浏览器是否是IE8浏览器
  119. * @warning 如果浏览器不是IE, 则该值为undefined
  120. * @example
  121. * ```javascript
  122. * if ( UE.browser.ie8 ) {
  123. * console.log( '当前浏览器是IE8浏览器' );
  124. * }
  125. * ```
  126. */
  127. browser.ie8 = !!document.documentMode;
  128. /**
  129. * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
  130. * @warning 如果浏览器不是IE, 则该值为undefined
  131. * @example
  132. * ```javascript
  133. * if ( UE.browser.ie8Compat ) {
  134. * console.log( '当前浏览器运行在IE8兼容模式下' );
  135. * }
  136. * ```
  137. */
  138. browser.ie8Compat = document.documentMode == 8;
  139. /**
  140. * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
  141. * @warning 如果浏览器不是IE, 则该值为undefined
  142. * @example
  143. * ```javascript
  144. * if ( UE.browser.ie7Compat ) {
  145. * console.log( '当前浏览器运行在IE7兼容模式下' );
  146. * }
  147. * ```
  148. */
  149. browser.ie7Compat = ( ( version == 7 && !document.documentMode )
  150. || document.documentMode == 7 );
  151. /**
  152. * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
  153. * @warning 如果浏览器不是IE, 则该值为undefined
  154. * @example
  155. * ```javascript
  156. * if ( UE.browser.ie6Compat ) {
  157. * console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
  158. * }
  159. * ```
  160. */
  161. browser.ie6Compat = ( version < 7 || browser.quirks );
  162. browser.ie9above = version > 8;
  163. browser.ie9below = version < 9;
  164. }
  165. // Gecko.
  166. if ( browser.gecko ){
  167. var geckoRelease = agent.match( /rv:([\d\.]+)/ );
  168. if ( geckoRelease )
  169. {
  170. geckoRelease = geckoRelease[1].split( '.' );
  171. version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
  172. }
  173. }
  174. /**
  175. * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
  176. * @warning 如果浏览器不是chrome, 则该值为undefined
  177. * @example
  178. * ```javascript
  179. * if ( UE.browser.chrome ) {
  180. * console.log( '当前浏览器是Chrome' );
  181. * }
  182. * ```
  183. */
  184. if (/chrome\/(\d+\.\d)/i.test(agent)) {
  185. browser.chrome = + RegExp['\x241'];
  186. }
  187. /**
  188. * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
  189. * @warning 如果浏览器不是safari, 则该值为undefined
  190. * @example
  191. * ```javascript
  192. * if ( UE.browser.safari ) {
  193. * console.log( '当前浏览器是Safari' );
  194. * }
  195. * ```
  196. */
  197. if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
  198. browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
  199. }
  200. // Opera 9.50+
  201. if ( browser.opera )
  202. version = parseFloat( opera.version() );
  203. // WebKit 522+ (Safari 3+)
  204. if ( browser.webkit )
  205. version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
  206. /**
  207. * @property { Number } version 检测当前浏览器版本号
  208. * @remind
  209. * <ul>
  210. * <li>IE系列返回值为5,6,7,8,9,10等</li>
  211. * <li>gecko系列会返回10900,158900等</li>
  212. * <li>webkit系列会返回其build号 (如 522等)</li>
  213. * </ul>
  214. * @example
  215. * ```javascript
  216. * console.log( '当前浏览器版本号是: ' + UE.browser.version );
  217. * ```
  218. */
  219. browser.version = version;
  220. /**
  221. * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
  222. * @example
  223. * ```javascript
  224. * if ( UE.browser.isCompatible ) {
  225. * console.log( '浏览器与UEditor能够良好兼容' );
  226. * }
  227. * ```
  228. */
  229. browser.isCompatible =
  230. !browser.mobile && (
  231. ( browser.ie && version >= 6 ) ||
  232. ( browser.gecko && version >= 10801 ) ||
  233. ( browser.opera && version >= 9.5 ) ||
  234. ( browser.air && version >= 1 ) ||
  235. ( browser.webkit && version >= 522 ) ||
  236. false );
  237. return browser;
  238. }();
  239. //快捷方式
  240. var ie = browser.ie,
  241. webkit = browser.webkit,
  242. gecko = browser.gecko,
  243. opera = browser.opera;
  244. /**
  245. * @file
  246. * @name UM.Utils
  247. * @short Utils
  248. * @desc UEditor封装使用的静态工具函数
  249. * @import editor.js
  250. */
  251. var utils = UM.utils = {
  252. /**
  253. * 遍历数组,对象,nodeList
  254. * @name each
  255. * @grammar UM.utils.each(obj,iterator,[context])
  256. * @since 1.2.4+
  257. * @desc
  258. * * obj 要遍历的对象
  259. * * iterator 遍历的方法,方法的第一个是遍历的值,第二个是索引,第三个是obj
  260. * * context iterator的上下文
  261. * @example
  262. * UM.utils.each([1,2],function(v,i){
  263. * console.log(v)//值
  264. * console.log(i)//索引
  265. * })
  266. * UM.utils.each(document.getElementsByTagName('*'),function(n){
  267. * console.log(n.tagName)
  268. * })
  269. */
  270. each : function(obj, iterator, context) {
  271. if (obj == null) return;
  272. if (obj.length === +obj.length) {
  273. for (var i = 0, l = obj.length; i < l; i++) {
  274. if(iterator.call(context, obj[i], i, obj) === false)
  275. return false;
  276. }
  277. } else {
  278. for (var key in obj) {
  279. if (obj.hasOwnProperty(key)) {
  280. if(iterator.call(context, obj[key], key, obj) === false)
  281. return false;
  282. }
  283. }
  284. }
  285. },
  286. makeInstance:function (obj) {
  287. var noop = new Function();
  288. noop.prototype = obj;
  289. obj = new noop;
  290. noop.prototype = null;
  291. return obj;
  292. },
  293. /**
  294. * 将source对象中的属性扩展到target对象上
  295. * @name extend
  296. * @grammar UM.utils.extend(target,source) => Object //覆盖扩展
  297. * @grammar UM.utils.extend(target,source,true) ==> Object //保留扩展
  298. */
  299. extend:function (t, s, b) {
  300. if (s) {
  301. for (var k in s) {
  302. if (!b || !t.hasOwnProperty(k)) {
  303. t[k] = s[k];
  304. }
  305. }
  306. }
  307. return t;
  308. },
  309. extend2:function (t) {
  310. var a = arguments;
  311. for (var i = 1; i < a.length; i++) {
  312. var x = a[i];
  313. for (var k in x) {
  314. if (!t.hasOwnProperty(k)) {
  315. t[k] = x[k];
  316. }
  317. }
  318. }
  319. return t;
  320. },
  321. /**
  322. * 模拟继承机制,subClass继承superClass
  323. * @name inherits
  324. * @grammar UM.utils.inherits(subClass,superClass) => subClass
  325. * @example
  326. * function SuperClass(){
  327. * this.name = "小李";
  328. * }
  329. * SuperClass.prototype = {
  330. * hello:function(str){
  331. * console.log(this.name + str);
  332. * }
  333. * }
  334. * function SubClass(){
  335. * this.name = "小张";
  336. * }
  337. * UM.utils.inherits(SubClass,SuperClass);
  338. * var sub = new SubClass();
  339. * sub.hello("早上好!"); ==> "小张早上好!"
  340. */
  341. inherits:function (subClass, superClass) {
  342. var oldP = subClass.prototype,
  343. newP = utils.makeInstance(superClass.prototype);
  344. utils.extend(newP, oldP, true);
  345. subClass.prototype = newP;
  346. return (newP.constructor = subClass);
  347. },
  348. /**
  349. * 用指定的context作为fn上下文,也就是this
  350. * @name bind
  351. * @grammar UM.utils.bind(fn,context) => fn
  352. */
  353. bind:function (fn, context) {
  354. return function () {
  355. return fn.apply(context, arguments);
  356. };
  357. },
  358. /**
  359. * 创建延迟delay执行的函数fn
  360. * @name defer
  361. * @grammar UM.utils.defer(fn,delay) =>fn //延迟delay毫秒执行fn,返回fn
  362. * @grammar UM.utils.defer(fn,delay,exclusion) =>fn //延迟delay毫秒执行fn,若exclusion为真,则互斥执行fn
  363. * @example
  364. * function test(){
  365. * console.log("延迟输出!");
  366. * }
  367. * //非互斥延迟执行
  368. * var testDefer = UM.utils.defer(test,1000);
  369. * testDefer(); => "延迟输出!";
  370. * testDefer(); => "延迟输出!";
  371. * //互斥延迟执行
  372. * var testDefer1 = UM.utils.defer(test,1000,true);
  373. * testDefer1(); => //本次不执行
  374. * testDefer1(); => "延迟输出!";
  375. */
  376. defer:function (fn, delay, exclusion) {
  377. var timerID;
  378. return function () {
  379. if (exclusion) {
  380. clearTimeout(timerID);
  381. }
  382. timerID = setTimeout(fn, delay);
  383. };
  384. },
  385. /**
  386. * 查找元素item在数组array中的索引, 若找不到返回-1
  387. * @name indexOf
  388. * @grammar UM.utils.indexOf(array,item) => index|-1 //默认从数组开头部开始搜索
  389. * @grammar UM.utils.indexOf(array,item,start) => index|-1 //start指定开始查找的位置
  390. */
  391. indexOf:function (array, item, start) {
  392. var index = -1;
  393. start = this.isNumber(start) ? start : 0;
  394. this.each(array, function (v, i) {
  395. if (i >= start && v === item) {
  396. index = i;
  397. return false;
  398. }
  399. });
  400. return index;
  401. },
  402. /**
  403. * 移除数组array中的元素item
  404. * @name removeItem
  405. * @grammar UM.utils.removeItem(array,item)
  406. */
  407. removeItem:function (array, item) {
  408. for (var i = 0, l = array.length; i < l; i++) {
  409. if (array[i] === item) {
  410. array.splice(i, 1);
  411. i--;
  412. }
  413. }
  414. },
  415. /**
  416. * 删除字符串str的首尾空格
  417. * @name trim
  418. * @grammar UM.utils.trim(str) => String
  419. */
  420. trim:function (str) {
  421. return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
  422. },
  423. /**
  424. * 将字符串list(以','分隔)或者数组list转成哈希对象
  425. * @name listToMap
  426. * @grammar UM.utils.listToMap(list) => Object //Object形如{test:1,br:1,textarea:1}
  427. */
  428. listToMap:function (list) {
  429. if (!list)return {};
  430. list = utils.isArray(list) ? list : list.split(',');
  431. for (var i = 0, ci, obj = {}; ci = list[i++];) {
  432. obj[ci.toUpperCase()] = obj[ci] = 1;
  433. }
  434. return obj;
  435. },
  436. /**
  437. * 将str中的html符号转义,默认将转义''&<">''四个字符,可自定义reg来确定需要转义的字符
  438. * @name unhtml
  439. * @grammar UM.utils.unhtml(str); => String
  440. * @grammar UM.utils.unhtml(str,reg) => String
  441. * @example
  442. * var html = '<body>You say:"你好!Baidu & UEditor!"</body>';
  443. * UM.utils.unhtml(html); ==> &lt;body&gt;You say:&quot;你好!Baidu &amp; UEditor!&quot;&lt;/body&gt;
  444. * UM.utils.unhtml(html,/[<>]/g) ==> &lt;body&gt;You say:"你好!Baidu & UEditor!"&lt;/body&gt;
  445. */
  446. unhtml:function (str, reg) {
  447. return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
  448. if (b) {
  449. return a;
  450. } else {
  451. return {
  452. '<':'&lt;',
  453. '&':'&amp;',
  454. '"':'&quot;',
  455. '>':'&gt;',
  456. "'":'&#39;'
  457. }[a]
  458. }
  459. }) : '';
  460. },
  461. /**
  462. * 将str中的转义字符还原成html字符
  463. * @name html
  464. * @grammar UM.utils.html(str) => String //详细参见<code><a href = '#unhtml'>unhtml</a></code>
  465. */
  466. html:function (str) {
  467. return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
  468. return {
  469. '&lt;':'<',
  470. '&amp;':'&',
  471. '&quot;':'"',
  472. '&gt;':'>',
  473. '&#39;':"'"
  474. }[m]
  475. }) : '';
  476. },
  477. /**
  478. * 将css样式转换为驼峰的形式。如font-size => fontSize
  479. * @name cssStyleToDomStyle
  480. * @grammar UM.utils.cssStyleToDomStyle(cssName) => String
  481. */
  482. cssStyleToDomStyle:function () {
  483. var test = document.createElement('div').style,
  484. cache = {
  485. 'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
  486. };
  487. return function (cssName) {
  488. return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
  489. return match.charAt(1).toUpperCase();
  490. }));
  491. };
  492. }(),
  493. /**
  494. * 动态加载文件到doc中,并依据obj来设置属性,加载成功后执行回调函数fn
  495. * @name loadFile
  496. * @grammar UM.utils.loadFile(doc,obj)
  497. * @grammar UM.utils.loadFile(doc,obj,fn)
  498. * @example
  499. * //指定加载到当前document中一个script文件,加载成功后执行function
  500. * utils.loadFile( document, {
  501. * src:"test.js",
  502. * tag:"script",
  503. * type:"text/javascript",
  504. * defer:"defer"
  505. * }, function () {
  506. * console.log('加载成功!')
  507. * });
  508. */
  509. loadFile:function () {
  510. var tmpList = [];
  511. function getItem(doc, obj) {
  512. try {
  513. for (var i = 0, ci; ci = tmpList[i++];) {
  514. if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
  515. return ci;
  516. }
  517. }
  518. } catch (e) {
  519. return null;
  520. }
  521. }
  522. return function (doc, obj, fn) {
  523. var item = getItem(doc, obj);
  524. if (item) {
  525. if (item.ready) {
  526. fn && fn();
  527. } else {
  528. item.funs.push(fn)
  529. }
  530. return;
  531. }
  532. tmpList.push({
  533. doc:doc,
  534. url:obj.src || obj.href,
  535. funs:[fn]
  536. });
  537. if (!doc.body) {
  538. var html = [];
  539. for (var p in obj) {
  540. if (p == 'tag')continue;
  541. html.push(p + '="' + obj[p] + '"')
  542. }
  543. doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
  544. return;
  545. }
  546. if (obj.id && doc.getElementById(obj.id)) {
  547. return;
  548. }
  549. var element = doc.createElement(obj.tag);
  550. delete obj.tag;
  551. for (var p in obj) {
  552. element.setAttribute(p, obj[p]);
  553. }
  554. element.onload = element.onreadystatechange = function () {
  555. if (!this.readyState || /loaded|complete/.test(this.readyState)) {
  556. item = getItem(doc, obj);
  557. if (item.funs.length > 0) {
  558. item.ready = 1;
  559. for (var fi; fi = item.funs.pop();) {
  560. fi();
  561. }
  562. }
  563. element.onload = element.onreadystatechange = null;
  564. }
  565. };
  566. element.onerror = function () {
  567. throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file umeditor.config.js ')
  568. };
  569. doc.getElementsByTagName("head")[0].appendChild(element);
  570. }
  571. }(),
  572. /**
  573. * 判断obj对象是否为空
  574. * @name isEmptyObject
  575. * @grammar UM.utils.isEmptyObject(obj) => true|false
  576. * @example
  577. * UM.utils.isEmptyObject({}) ==>true
  578. * UM.utils.isEmptyObject([]) ==>true
  579. * UM.utils.isEmptyObject("") ==>true
  580. */
  581. isEmptyObject:function (obj) {
  582. if (obj == null) return true;
  583. if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
  584. for (var key in obj) if (obj.hasOwnProperty(key)) return false;
  585. return true;
  586. },
  587. /**
  588. * 统一将颜色值使用16进制形式表示
  589. * @name fixColor
  590. * @grammar UM.utils.fixColor(name,value) => value
  591. * @example
  592. * rgb(255,255,255) => "#ffffff"
  593. */
  594. fixColor:function (name, value) {
  595. if (/color/i.test(name) && /rgba?/.test(value)) {
  596. var array = value.split(",");
  597. if (array.length > 3)
  598. return "";
  599. value = "#";
  600. for (var i = 0, color; color = array[i++];) {
  601. color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
  602. value += color.length == 1 ? "0" + color : color;
  603. }
  604. value = value.toUpperCase();
  605. }
  606. return value;
  607. },
  608. /**
  609. * 深度克隆对象,从source到target
  610. * @name clone
  611. * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
  612. * @grammar UM.utils.clone(source,target) => target包含了source的所有内容,重名会覆盖
  613. */
  614. clone:function (source, target) {
  615. var tmp;
  616. target = target || {};
  617. for (var i in source) {
  618. if (source.hasOwnProperty(i)) {
  619. tmp = source[i];
  620. if (typeof tmp == 'object') {
  621. target[i] = utils.isArray(tmp) ? [] : {};
  622. utils.clone(source[i], target[i])
  623. } else {
  624. target[i] = tmp;
  625. }
  626. }
  627. }
  628. return target;
  629. },
  630. /**
  631. * 转换cm/pt到px
  632. * @name transUnitToPx
  633. * @grammar UM.utils.transUnitToPx('20pt') => '27px'
  634. * @grammar UM.utils.transUnitToPx('0pt') => '0'
  635. */
  636. transUnitToPx:function (val) {
  637. if (!/(pt|cm)/.test(val)) {
  638. return val
  639. }
  640. var unit;
  641. val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
  642. val = v;
  643. unit = u;
  644. });
  645. switch (unit) {
  646. case 'cm':
  647. val = parseFloat(val) * 25;
  648. break;
  649. case 'pt':
  650. val = Math.round(parseFloat(val) * 96 / 72);
  651. }
  652. return val + (val ? 'px' : '');
  653. },
  654. /**
  655. * 动态添加css样式
  656. * @name cssRule
  657. * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
  658. * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
  659. * @grammar UM.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
  660. * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
  661. */
  662. cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
  663. var indexList, index;
  664. doc = doc || document;
  665. if (doc.indexList) {
  666. indexList = doc.indexList;
  667. } else {
  668. indexList = doc.indexList = {};
  669. }
  670. var sheetStyle;
  671. if (!indexList[key]) {
  672. if (style === undefined) {
  673. return ''
  674. }
  675. sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
  676. indexList[key] = index;
  677. } else {
  678. sheetStyle = doc.styleSheets[indexList[key]];
  679. }
  680. if (style === undefined) {
  681. return sheetStyle.cssText
  682. }
  683. sheetStyle.cssText = style || ''
  684. } : function (key, style, doc) {
  685. doc = doc || document;
  686. var head = doc.getElementsByTagName('head')[0], node;
  687. if (!(node = doc.getElementById(key))) {
  688. if (style === undefined) {
  689. return ''
  690. }
  691. node = doc.createElement('style');
  692. node.id = key;
  693. head.appendChild(node)
  694. }
  695. if (style === undefined) {
  696. return node.innerHTML
  697. }
  698. if (style !== '') {
  699. node.innerHTML = style;
  700. } else {
  701. head.removeChild(node)
  702. }
  703. },
  704. /**
  705. * etpl 渲染函数
  706. * @name render
  707. * @grammar UM.utils.render(tpl, data) => string
  708. */
  709. render: function (tpl, data) {
  710. var _render = etpl.compile(tpl);
  711. return _render(data);
  712. }
  713. };
  714. /**
  715. * 判断str是否为字符串
  716. * @name isString
  717. * @grammar UM.utils.isString(str) => true|false
  718. */
  719. /**
  720. * 判断array是否为数组
  721. * @name isArray
  722. * @grammar UM.utils.isArray(obj) => true|false
  723. */
  724. /**
  725. * 判断obj对象是否为方法
  726. * @name isFunction
  727. * @grammar UM.utils.isFunction(obj) => true|false
  728. */
  729. /**
  730. * 判断obj对象是否为数字
  731. * @name isNumber
  732. * @grammar UM.utils.isNumber(obj) => true|false
  733. */
  734. utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
  735. UM.utils['is' + v] = function (obj) {
  736. return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
  737. }
  738. });
  739. /**
  740. * @file
  741. * @name UM.EventBase
  742. * @short EventBase
  743. * @import editor.js,core/utils.js
  744. * @desc UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
  745. * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
  746. */
  747. var EventBase = UM.EventBase = function () {};
  748. EventBase.prototype = {
  749. /**
  750. * 注册事件监听器
  751. * @name addListener
  752. * @grammar editor.addListener(types,fn) //types为事件名称,多个可用空格分隔
  753. * @example
  754. * editor.addListener('selectionchange',function(){
  755. * console.log("选区已经变化!");
  756. * })
  757. * editor.addListener('beforegetcontent aftergetcontent',function(type){
  758. * if(type == 'beforegetcontent'){
  759. * //do something
  760. * }else{
  761. * //do something
  762. * }
  763. * console.log(this.getContent) // this是注册的事件的编辑器实例
  764. * })
  765. */
  766. addListener:function (types, listener) {
  767. types = utils.trim(types).split(' ');
  768. for (var i = 0, ti; ti = types[i++];) {
  769. getListener(this, ti, true).push(listener);
  770. }
  771. },
  772. /**
  773. * 移除事件监听器
  774. * @name removeListener
  775. * @grammar editor.removeListener(types,fn) //types为事件名称,多个可用空格分隔
  776. * @example
  777. * //changeCallback为方法体
  778. * editor.removeListener("selectionchange",changeCallback);
  779. */
  780. removeListener:function (types, listener) {
  781. types = utils.trim(types).split(' ');
  782. for (var i = 0, ti; ti = types[i++];) {
  783. utils.removeItem(getListener(this, ti) || [], listener);
  784. }
  785. },
  786. /**
  787. * 触发事件
  788. * @name fireEvent
  789. * @grammar editor.fireEvent(types) //types为事件名称,多个可用空格分隔
  790. * @example
  791. * editor.fireEvent("selectionchange");
  792. */
  793. fireEvent:function () {
  794. var types = arguments[0];
  795. types = utils.trim(types).split(' ');
  796. for (var i = 0, ti; ti = types[i++];) {
  797. var listeners = getListener(this, ti),
  798. r, t, k;
  799. if (listeners) {
  800. k = listeners.length;
  801. while (k--) {
  802. if(!listeners[k])continue;
  803. t = listeners[k].apply(this, arguments);
  804. if(t === true){
  805. return t;
  806. }
  807. if (t !== undefined) {
  808. r = t;
  809. }
  810. }
  811. }
  812. if (t = this['on' + ti.toLowerCase()]) {
  813. r = t.apply(this, arguments);
  814. }
  815. }
  816. return r;
  817. }
  818. };
  819. /**
  820. * 获得对象所拥有监听类型的所有监听器
  821. * @public
  822. * @function
  823. * @param {Object} obj 查询监听器的对象
  824. * @param {String} type 事件类型
  825. * @param {Boolean} force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
  826. * @returns {Array} 监听器数组
  827. */
  828. function getListener(obj, type, force) {
  829. var allListeners;
  830. type = type.toLowerCase();
  831. return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
  832. && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
  833. }
  834. ///import editor.js
  835. ///import core/dom/dom.js
  836. ///import core/utils.js
  837. /**
  838. * dtd html语义化的体现类
  839. * @constructor
  840. * @namespace dtd
  841. */
  842. var dtd = dom.dtd = (function() {
  843. function _( s ) {
  844. for (var k in s) {
  845. s[k.toUpperCase()] = s[k];
  846. }
  847. return s;
  848. }
  849. var X = utils.extend2;
  850. var A = _({isindex:1,fieldset:1}),
  851. B = _({input:1,button:1,select:1,textarea:1,label:1}),
  852. C = X( _({a:1}), B ),
  853. D = X( {iframe:1}, C ),
  854. E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
  855. F = _({ins:1,del:1,script:1,style:1}),
  856. G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
  857. H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
  858. I = X( _({p:1}), H ),
  859. J = X( _({iframe:1}), H, B ),
  860. K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
  861. L = X( _({a:0}), J ),//a不能被切开,所以把他
  862. M = _({tr:1}),
  863. N = _({'#':1}),
  864. O = X( _({param:1}), K ),
  865. P = X( _({form:1}), A, D, E, I ),
  866. Q = _({li:1,ol:1,ul:1}),
  867. R = _({style:1,script:1}),
  868. S = _({base:1,link:1,meta:1,title:1}),
  869. T = X( S, R ),
  870. U = _({head:1,body:1}),
  871. V = _({html:1});
  872. var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),
  873. empty = _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
  874. return _({
  875. // $ 表示自定的属性
  876. // body外的元素列表.
  877. $nonBodyContent: X( V, U, S ),
  878. //块结构元素列表
  879. $block : block,
  880. //内联元素列表
  881. $inline : L,
  882. $inlineWithA : X(_({a:1}),L),
  883. $body : X( _({script:1,style:1}), block ),
  884. $cdata : _({script:1,style:1}),
  885. //自闭和元素
  886. $empty : empty,
  887. //不是自闭合,但不能让range选中里边
  888. $nonChild : _({iframe:1,textarea:1}),
  889. //列表元素列表
  890. $listItem : _({dd:1,dt:1,li:1}),
  891. //列表根元素列表
  892. $list: _({ul:1,ol:1,dl:1}),
  893. //不能认为是空的元素
  894. $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
  895. //如果没有子节点就可以删除的元素列表,像span,a
  896. $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
  897. $removeEmptyBlock : _({'p':1,'div':1}),
  898. //在table元素里的元素列表
  899. $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
  900. //不转换的标签
  901. $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
  902. html: U,
  903. head: T,
  904. style: N,
  905. script: N,
  906. body: P,
  907. base: {},
  908. link: {},
  909. meta: {},
  910. title: N,
  911. col : {},
  912. tr : _({td:1,th:1}),
  913. img : {},
  914. embed: {},
  915. colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
  916. noscript : P,
  917. td : P,
  918. br : {},
  919. th : P,
  920. center : P,
  921. kbd : L,
  922. button : X( I, E ),
  923. basefont : {},
  924. h5 : L,
  925. h4 : L,
  926. samp : L,
  927. h6 : L,
  928. ol : Q,
  929. h1 : L,
  930. h3 : L,
  931. option : N,
  932. h2 : L,
  933. form : X( A, D, E, I ),
  934. select : _({optgroup:1,option:1}),
  935. font : L,
  936. ins : L,
  937. menu : Q,
  938. abbr : L,
  939. label : L,
  940. table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
  941. code : L,
  942. tfoot : M,
  943. cite : L,
  944. li : P,
  945. input : {},
  946. iframe : P,
  947. strong : L,
  948. textarea : N,
  949. noframes : P,
  950. big : L,
  951. small : L,
  952. //trace:
  953. span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
  954. hr : L,
  955. dt : L,
  956. sub : L,
  957. optgroup : _({option:1}),
  958. param : {},
  959. bdo : L,
  960. 'var' : L,
  961. div : P,
  962. object : O,
  963. sup : L,
  964. dd : P,
  965. strike : L,
  966. area : {},
  967. dir : Q,
  968. map : X( _({area:1,form:1,p:1}), A, F, E ),
  969. applet : O,
  970. dl : _({dt:1,dd:1}),
  971. del : L,
  972. isindex : {},
  973. fieldset : X( _({legend:1}), K ),
  974. thead : M,
  975. ul : Q,
  976. acronym : L,
  977. b : L,
  978. a : X( _({a:1}), J ),
  979. blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
  980. caption : L,
  981. i : L,
  982. u : L,
  983. tbody : M,
  984. s : L,
  985. address : X( D, I ),
  986. tt : L,
  987. legend : L,
  988. q : L,
  989. pre : X( G, C ),
  990. p : X(_({'a':1}),L),
  991. em :L,
  992. dfn : L
  993. });
  994. })();
  995. /**
  996. * @file
  997. * @name UM.dom.domUtils
  998. * @short DomUtils
  999. * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js
  1000. * @desc UEditor封装的底层dom操作库
  1001. */
  1002. function getDomNode(node, start, ltr, startFromChild, fn, guard) {
  1003. var tmpNode = startFromChild && node[start],
  1004. parent;
  1005. !tmpNode && (tmpNode = node[ltr]);
  1006. while (!tmpNode && (parent = (parent || node).parentNode)) {
  1007. if (parent.tagName == 'BODY' || guard && !guard(parent)) {
  1008. return null;
  1009. }
  1010. tmpNode = parent[ltr];
  1011. }
  1012. if (tmpNode && fn && !fn(tmpNode)) {
  1013. return getDomNode(tmpNode, start, ltr, false, fn);
  1014. }
  1015. return tmpNode;
  1016. }
  1017. var attrFix = ie && browser.version < 9 ? {
  1018. tabindex: "tabIndex",
  1019. readonly: "readOnly",
  1020. "for": "htmlFor",
  1021. "class": "className",
  1022. maxlength: "maxLength",
  1023. cellspacing: "cellSpacing",
  1024. cellpadding: "cellPadding",
  1025. rowspan: "rowSpan",
  1026. colspan: "colSpan",
  1027. usemap: "useMap",
  1028. frameborder: "frameBorder"
  1029. } : {
  1030. tabindex: "tabIndex",
  1031. readonly: "readOnly"
  1032. },
  1033. styleBlock = utils.listToMap([
  1034. '-webkit-box', '-moz-box', 'block' ,
  1035. 'list-item' , 'table' , 'table-row-group' ,
  1036. 'table-header-group', 'table-footer-group' ,
  1037. 'table-row' , 'table-column-group' , 'table-column' ,
  1038. 'table-cell' , 'table-caption'
  1039. ]);
  1040. var domUtils = dom.domUtils = {
  1041. //节点常量
  1042. NODE_ELEMENT: 1,
  1043. NODE_DOCUMENT: 9,
  1044. NODE_TEXT: 3,
  1045. NODE_COMMENT: 8,
  1046. NODE_DOCUMENT_FRAGMENT: 11,
  1047. //位置关系
  1048. POSITION_IDENTICAL: 0,
  1049. POSITION_DISCONNECTED: 1,
  1050. POSITION_FOLLOWING: 2,
  1051. POSITION_PRECEDING: 4,
  1052. POSITION_IS_CONTAINED: 8,
  1053. POSITION_CONTAINS: 16,
  1054. //ie6使用其他的会有一段空白出现
  1055. fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
  1056. //-------------------------Node部分--------------------------------
  1057. keys: {
  1058. /*Backspace*/ 8: 1, /*Delete*/ 46: 1,
  1059. /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
  1060. 37: 1, 38: 1, 39: 1, 40: 1,
  1061. 13: 1 /*enter*/
  1062. },
  1063. breakParent:function (node, parent) {
  1064. var tmpNode,
  1065. parentClone = node,
  1066. clone = node,
  1067. leftNodes,
  1068. rightNodes;
  1069. do {
  1070. parentClone = parentClone.parentNode;
  1071. if (leftNodes) {
  1072. tmpNode = parentClone.cloneNode(false);
  1073. tmpNode.appendChild(leftNodes);
  1074. leftNodes = tmpNode;
  1075. tmpNode = parentClone.cloneNode(false);
  1076. tmpNode.appendChild(rightNodes);
  1077. rightNodes = tmpNode;
  1078. } else {
  1079. leftNodes = parentClone.cloneNode(false);
  1080. rightNodes = leftNodes.cloneNode(false);
  1081. }
  1082. while (tmpNode = clone.previousSibling) {
  1083. leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
  1084. }
  1085. while (tmpNode = clone.nextSibling) {
  1086. rightNodes.appendChild(tmpNode);
  1087. }
  1088. clone = parentClone;
  1089. } while (parent !== parentClone);
  1090. tmpNode = parent.parentNode;
  1091. tmpNode.insertBefore(leftNodes, parent);
  1092. tmpNode.insertBefore(rightNodes, parent);
  1093. tmpNode.insertBefore(node, rightNodes);
  1094. domUtils.remove(parent);
  1095. return node;
  1096. },
  1097. trimWhiteTextNode:function (node) {
  1098. function remove(dir) {
  1099. var child;
  1100. while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
  1101. node.removeChild(child);
  1102. }
  1103. }
  1104. remove('firstChild');
  1105. remove('lastChild');
  1106. },
  1107. /**
  1108. * 获取节点A相对于节点B的位置关系
  1109. * @name getPosition
  1110. * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB) => Number
  1111. * @example
  1112. * switch (returnValue) {
  1113. * case 0: //相等,同一节点
  1114. * case 1: //无关,节点不相连
  1115. * case 2: //跟随,即节点A头部位于节点B头部的后面
  1116. * case 4: //前置,即节点A头部位于节点B头部的前面
  1117. * case 8: //被包含,即节点A被节点B包含
  1118. * case 10://组合类型,即节点A满足跟随节点B且被节点B包含。实际上,如果被包含,必定跟随,所以returnValue事实上不会存在8的情况。
  1119. * case 16://包含,即节点A包含节点B
  1120. * case 20://组合类型,即节点A满足前置节点A且包含节点B。同样,如果包含,必定前置,所以returnValue事实上也不会存在16的情况
  1121. * }
  1122. */
  1123. getPosition: function (nodeA, nodeB) {
  1124. // 如果两个节点是同一个节点
  1125. if (nodeA === nodeB) {
  1126. // domUtils.POSITION_IDENTICAL
  1127. return 0;
  1128. }
  1129. var node,
  1130. parentsA = [nodeA],
  1131. parentsB = [nodeB];
  1132. node = nodeA;
  1133. while (node = node.parentNode) {
  1134. // 如果nodeB是nodeA的祖先节点
  1135. if (node === nodeB) {
  1136. // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
  1137. return 10;
  1138. }
  1139. parentsA.push(node);
  1140. }
  1141. node = nodeB;
  1142. while (node = node.parentNode) {
  1143. // 如果nodeA是nodeB的祖先节点
  1144. if (node === nodeA) {
  1145. // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
  1146. return 20;
  1147. }
  1148. parentsB.push(node);
  1149. }
  1150. parentsA.reverse();
  1151. parentsB.reverse();
  1152. if (parentsA[0] !== parentsB[0]) {
  1153. // domUtils.POSITION_DISCONNECTED
  1154. return 1;
  1155. }
  1156. var i = -1;
  1157. while (i++, parentsA[i] === parentsB[i]) {
  1158. }
  1159. nodeA = parentsA[i];
  1160. nodeB = parentsB[i];
  1161. while (nodeA = nodeA.nextSibling) {
  1162. if (nodeA === nodeB) {
  1163. // domUtils.POSITION_PRECEDING
  1164. return 4
  1165. }
  1166. }
  1167. // domUtils.POSITION_FOLLOWING
  1168. return 2;
  1169. },
  1170. /**
  1171. * 返回节点node在父节点中的索引位置
  1172. * @name getNodeIndex
  1173. * @grammar UM.dom.domUtils.getNodeIndex(node) => Number //索引值从0开始
  1174. */
  1175. getNodeIndex: function (node, ignoreTextNode) {
  1176. var preNode = node,
  1177. i = 0;
  1178. while (preNode = preNode.previousSibling) {
  1179. if (ignoreTextNode && preNode.nodeType == 3) {
  1180. if (preNode.nodeType != preNode.nextSibling.nodeType) {
  1181. i++;
  1182. }
  1183. continue;
  1184. }
  1185. i++;
  1186. }
  1187. return i;
  1188. },
  1189. /**
  1190. * 检测节点node是否在节点doc的树上,实质上是检测是否被doc包含
  1191. * @name inDoc
  1192. * @grammar UM.dom.domUtils.inDoc(node,doc) => true|false
  1193. */
  1194. inDoc: function (node, doc) {
  1195. return domUtils.getPosition(node, doc) == 10;
  1196. },
  1197. /**
  1198. * 查找node节点的祖先节点
  1199. * @name findParent
  1200. * @grammar UM.dom.domUtils.findParent(node) => Element // 直接返回node节点的父节点
  1201. * @grammar UM.dom.domUtils.findParent(node,filterFn) => Element //filterFn为过滤函数,node作为参数,返回true时才会将node作为符合要求的节点返回
  1202. * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf) => Element //includeSelf指定是否包含自身
  1203. */
  1204. findParent: function (node, filterFn, includeSelf) {
  1205. if (node && !domUtils.isBody(node)) {
  1206. node = includeSelf ? node : node.parentNode;
  1207. while (node) {
  1208. if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
  1209. return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
  1210. }
  1211. node = node.parentNode;
  1212. }
  1213. }
  1214. return null;
  1215. },
  1216. /**
  1217. * 通过tagName查找node节点的祖先节点
  1218. * @name findParentByTagName
  1219. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames) => Element //tagNames支持数组,区分大小写
  1220. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf) => Element //includeSelf指定是否包含自身
  1221. * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn) => Element //excludeFn指定例外过滤条件,返回true时忽略该节点
  1222. */
  1223. findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
  1224. tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
  1225. return domUtils.findParent(node, function (node) {
  1226. return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
  1227. }, includeSelf);
  1228. },
  1229. /**
  1230. * 查找节点node的祖先节点集合
  1231. * @name findParents
  1232. * @grammar UM.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
  1233. * @grammar UM.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
  1234. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
  1235. * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
  1236. */
  1237. findParents: function (node, includeSelf, filterFn, closerFirst) {
  1238. var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
  1239. while (node = domUtils.findParent(node, filterFn)) {
  1240. parents.push(node);
  1241. }
  1242. return closerFirst ? parents : parents.reverse();
  1243. },
  1244. /**
  1245. * 在节点node后面插入新节点newNode
  1246. * @name insertAfter
  1247. * @grammar UM.dom.domUtils.insertAfter(node,newNode) => newNode
  1248. */
  1249. insertAfter: function (node, newNode) {
  1250. return node.parentNode.insertBefore(newNode, node.nextSibling);
  1251. },
  1252. /**
  1253. * 删除节点node,并根据keepChildren指定是否保留子节点
  1254. * @name remove
  1255. * @grammar UM.dom.domUtils.remove(node) => node
  1256. * @grammar UM.dom.domUtils.remove(node,keepChildren) => node
  1257. */
  1258. remove: function (node, keepChildren) {
  1259. var parent = node.parentNode,
  1260. child;
  1261. if (parent) {
  1262. if (keepChildren && node.hasChildNodes()) {
  1263. while (child = node.firstChild) {
  1264. parent.insertBefore(child, node);
  1265. }
  1266. }
  1267. parent.removeChild(node);
  1268. }
  1269. return node;
  1270. },
  1271. /**
  1272. * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
  1273. * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
  1274. * @method getNextDomNode
  1275. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1276. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1277. * @example
  1278. * ```html
  1279. * <body>
  1280. * <div id="test">
  1281. * <span></span>
  1282. * </div>
  1283. * <i>xxx</i>
  1284. * </body>
  1285. * <script>
  1286. *
  1287. * //output: i节点
  1288. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1289. *
  1290. * </script>
  1291. * ```
  1292. * @example
  1293. * ```html
  1294. * <body>
  1295. * <div>
  1296. * <span></span>
  1297. * <i id="test">xxx</i>
  1298. * </div>
  1299. * <b>xxx</b>
  1300. * </body>
  1301. * <script>
  1302. *
  1303. * //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
  1304. * //output: b节点
  1305. * console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
  1306. *
  1307. * </script>
  1308. * ```
  1309. */
  1310. /**
  1311. * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
  1312. * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
  1313. * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
  1314. * @method getNextDomNode
  1315. * @param { Node } node 需要获取其后的兄弟节点的节点对象
  1316. * @param { Boolean } startFromChild 查找过程是否从其子节点开始
  1317. * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
  1318. * @see UE.dom.domUtils.getNextDomNode(Node)
  1319. */
  1320. getNextDomNode:function (node, startFromChild, filterFn, guard) {
  1321. return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
  1322. },
  1323. getPreDomNode:function (node, startFromChild, filterFn, guard) {
  1324. return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
  1325. },
  1326. /**
  1327. * 检测节点node是否属于bookmark节点
  1328. * @name isBookmarkNode
  1329. * @grammar UM.dom.domUtils.isBookmarkNode(node) => true|false
  1330. */
  1331. isBookmarkNode: function (node) {
  1332. return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
  1333. },
  1334. /**
  1335. * 获取节点node所在的window对象
  1336. * @name getWindow
  1337. * @grammar UM.dom.domUtils.getWindow(node) => window对象
  1338. */
  1339. getWindow: function (node) {
  1340. var doc = node.ownerDocument || node;
  1341. return doc.defaultView || doc.parentWindow;
  1342. },
  1343. /**
  1344. * 获取离nodeA与nodeB最近的公共的祖先节点
  1345. * @method getCommonAncestor
  1346. * @param { Node } nodeA 第一个节点
  1347. * @param { Node } nodeB 第二个节点
  1348. * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
  1349. * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
  1350. * @example
  1351. * ```javascript
  1352. * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
  1353. * //output: true
  1354. * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
  1355. * ```
  1356. */
  1357. getCommonAncestor:function (nodeA, nodeB) {
  1358. if (nodeA === nodeB)
  1359. return nodeA;
  1360. var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
  1361. while (parent = parent.parentNode) {
  1362. if (parent === nodeB) {
  1363. return parent;
  1364. }
  1365. parentsA.push(parent);
  1366. }
  1367. parent = nodeB;
  1368. while (parent = parent.parentNode) {
  1369. if (parent === nodeA)
  1370. return parent;
  1371. parentsB.push(parent);
  1372. }
  1373. parentsA.reverse();
  1374. parentsB.reverse();
  1375. while (i++, parentsA[i] === parentsB[i]) {
  1376. }
  1377. return i == 0 ? null : parentsA[i - 1];
  1378. },
  1379. /**
  1380. * 清除node节点左右连续为空的兄弟inline节点
  1381. * @method clearEmptySibling
  1382. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1383. * 则这些兄弟节点将被删除
  1384. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
  1385. * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
  1386. * @example
  1387. * ```html
  1388. * <body>
  1389. * <div></div>
  1390. * <span id="test"></span>
  1391. * <i></i>
  1392. * <b></b>
  1393. * <em>xxx</em>
  1394. * <span></span>
  1395. * </body>
  1396. * <script>
  1397. *
  1398. * UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
  1399. *
  1400. * //output: <div></div><span id="test"></span><em>xxx</em><span></span>
  1401. * console.log( document.body.innerHTML );
  1402. *
  1403. * </script>
  1404. * ```
  1405. */
  1406. /**
  1407. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1408. * 则忽略对右边兄弟节点的操作。
  1409. * @method clearEmptySibling
  1410. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1411. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1412. * 则这些兄弟节点将被删除
  1413. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1414. */
  1415. /**
  1416. * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
  1417. * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
  1418. * @method clearEmptySibling
  1419. * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
  1420. * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
  1421. * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
  1422. * 则这些兄弟节点将被删除
  1423. * @see UE.dom.domUtils.clearEmptySibling(Node)
  1424. */
  1425. clearEmptySibling:function (node, ignoreNext, ignorePre) {
  1426. function clear(next, dir) {
  1427. var tmpNode;
  1428. while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
  1429. //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
  1430. || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
  1431. tmpNode = next[dir];
  1432. domUtils.remove(next);
  1433. next = tmpNode;
  1434. }
  1435. }
  1436. !ignoreNext && clear(node.nextSibling, 'nextSibling');
  1437. !ignorePre && clear(node.previousSibling, 'previousSibling');
  1438. },
  1439. /**
  1440. * 将一个文本节点node拆分成两个文本节点,offset指定拆分位置
  1441. * @name split
  1442. * @grammar UM.dom.domUtils.split(node,offset) => TextNode //返回从切分位置开始的后一个文本节点
  1443. */
  1444. split: function (node, offset) {
  1445. var doc = node.ownerDocument;
  1446. if (browser.ie && offset == node.nodeValue.length) {
  1447. var next = doc.createTextNode('');
  1448. return domUtils.insertAfter(node, next);
  1449. }
  1450. var retval = node.splitText(offset);
  1451. //ie8下splitText不会跟新childNodes,我们手动触发他的更新
  1452. if (browser.ie8) {
  1453. var tmpNode = doc.createTextNode('');
  1454. domUtils.insertAfter(retval, tmpNode);
  1455. domUtils.remove(tmpNode);
  1456. }
  1457. return retval;
  1458. },
  1459. /**
  1460. * 检测节点node是否为空节点(包括空格、换行、占位符等字符)
  1461. * @name isWhitespace
  1462. * @grammar UM.dom.domUtils.isWhitespace(node) => true|false
  1463. */
  1464. isWhitespace: function (node) {
  1465. return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
  1466. },
  1467. /**
  1468. * 获取元素element相对于viewport的位置坐标
  1469. * @name getXY
  1470. * @grammar UM.dom.domUtils.getXY(element) => Object //返回坐标对象{x:left,y:top}
  1471. */
  1472. getXY: function (element) {
  1473. var x = 0, y = 0;
  1474. while (element.offsetParent) {
  1475. y += element.offsetTop;
  1476. x += element.offsetLeft;
  1477. element = element.offsetParent;
  1478. }
  1479. return { 'x': x, 'y': y};
  1480. },
  1481. /**
  1482. * 检查节点node是否是空inline节点
  1483. * @name isEmptyInlineElement
  1484. * @grammar UM.dom.domUtils.isEmptyInlineElement(node) => 1|0
  1485. * @example
  1486. * <b><i></i></b> => 1
  1487. * <b><i></i><u></u></b> => 1
  1488. * <b></b> => 1
  1489. * <b>xx<i></i></b> => 0
  1490. */
  1491. isEmptyInlineElement: function (node) {
  1492. if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
  1493. return 0;
  1494. }
  1495. node = node.firstChild;
  1496. while (node) {
  1497. //如果是创建的bookmark就跳过
  1498. if (domUtils.isBookmarkNode(node)) {
  1499. return 0;
  1500. }
  1501. if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
  1502. node.nodeType == 3 && !domUtils.isWhitespace(node)
  1503. ) {
  1504. return 0;
  1505. }
  1506. node = node.nextSibling;
  1507. }
  1508. return 1;
  1509. },
  1510. /**
  1511. * 检查节点node是否为块元素
  1512. * @name isBlockElm
  1513. * @grammar UM.dom.domUtils.isBlockElm(node) => true|false
  1514. */
  1515. isBlockElm: function (node) {
  1516. return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
  1517. },
  1518. /**
  1519. * 原生方法getElementsByTagName的封装
  1520. * @name getElementsByTagName
  1521. * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName) => Array //节点集合数组
  1522. */
  1523. getElementsByTagName: function (node, name, filter) {
  1524. if (filter && utils.isString(filter)) {
  1525. var className = filter;
  1526. filter = function (node) {
  1527. var result = false;
  1528. $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
  1529. if ($(node).hasClass(v)) {
  1530. result = true;
  1531. return false;
  1532. }
  1533. })
  1534. return result;
  1535. }
  1536. }
  1537. name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
  1538. var arr = [];
  1539. for (var n = 0, ni; ni = name[n++];) {
  1540. var list = node.getElementsByTagName(ni);
  1541. for (var i = 0, ci; ci = list[i++];) {
  1542. if (!filter || filter(ci))
  1543. arr.push(ci);
  1544. }
  1545. }
  1546. return arr;
  1547. },
  1548. /**
  1549. * 设置节点node及其子节点不会被选中
  1550. * @name unSelectable
  1551. * @grammar UM.dom.domUtils.unSelectable(node)
  1552. */
  1553. unSelectable: ie && browser.ie9below || browser.opera ? function (node) {
  1554. //for ie9
  1555. node.onselectstart = function () {
  1556. return false;
  1557. };
  1558. node.onclick = node.onkeyup = node.onkeydown = function () {
  1559. return false;
  1560. };
  1561. node.unselectable = 'on';
  1562. node.setAttribute("unselectable", "on");
  1563. for (var i = 0, ci; ci = node.all[i++];) {
  1564. switch (ci.tagName.toLowerCase()) {
  1565. case 'iframe' :
  1566. case 'textarea' :
  1567. case 'input' :
  1568. case 'select' :
  1569. break;
  1570. default :
  1571. ci.unselectable = 'on';
  1572. node.setAttribute("unselectable", "on");
  1573. }
  1574. }
  1575. } : function (node) {
  1576. node.style.MozUserSelect =
  1577. node.style.webkitUserSelect =
  1578. node.style.msUserSelect =
  1579. node.style.KhtmlUserSelect = 'none';
  1580. },
  1581. /**
  1582. * 删除节点node上的属性attrNames,attrNames为属性名称数组
  1583. * @name removeAttributes
  1584. * @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
  1585. * @example
  1586. * //Before remove
  1587. * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
  1588. * //Remove
  1589. * UM.dom.domUtils.removeAttributes(node,["id","name"]);
  1590. * //After remove
  1591. * <span style="font-size:14px;">xxxxx</span>
  1592. */
  1593. removeAttributes: function (node, attrNames) {
  1594. attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
  1595. for (var i = 0, ci; ci = attrNames[i++];) {
  1596. ci = attrFix[ci] || ci;
  1597. switch (ci) {
  1598. case 'className':
  1599. node[ci] = '';
  1600. break;
  1601. case 'style':
  1602. node.style.cssText = '';
  1603. if (node.getAttributeNode('style')) {
  1604. !browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))
  1605. }
  1606. }
  1607. node.removeAttribute(ci);
  1608. }
  1609. },
  1610. /**
  1611. * 在doc下创建一个标签名为tag,属性为attrs的元素
  1612. * @name createElement
  1613. * @grammar UM.dom.domUtils.createElement(doc,tag,attrs) => Node //返回创建的节点
  1614. */
  1615. createElement: function (doc, tag, attrs) {
  1616. return domUtils.setAttributes(doc.createElement(tag), attrs)
  1617. },
  1618. /**
  1619. * 为节点node添加属性attrs,attrs为属性键值对
  1620. * @name setAttributes
  1621. * @grammar UM.dom.domUtils.setAttributes(node,attrs) => node
  1622. */
  1623. setAttributes: function (node, attrs) {
  1624. for (var attr in attrs) {
  1625. if (attrs.hasOwnProperty(attr)) {
  1626. var value = attrs[attr];
  1627. switch (attr) {
  1628. case 'class':
  1629. //ie下要这样赋值,setAttribute不起作用
  1630. node.className = value;
  1631. break;
  1632. case 'style' :
  1633. node.style.cssText = node.style.cssText + ";" + value;
  1634. break;
  1635. case 'innerHTML':
  1636. node[attr] = value;
  1637. break;
  1638. case 'value':
  1639. node.value = value;
  1640. break;
  1641. default:
  1642. node.setAttribute(attrFix[attr] || attr, value);
  1643. }
  1644. }
  1645. }
  1646. return node;
  1647. },
  1648. /**
  1649. * 获取元素element的计算样式
  1650. * @name getComputedStyle
  1651. * @grammar UM.dom.domUtils.getComputedStyle(element,styleName) => String //返回对应样式名称的样式值
  1652. * @example
  1653. * getComputedStyle(document.body,"font-size") => "15px"
  1654. * getComputedStyle(form,"color") => "#ffccdd"
  1655. */
  1656. getComputedStyle: function (element, styleName) {
  1657. return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));
  1658. },
  1659. /**
  1660. * 阻止事件默认行为
  1661. * @param {Event} evt 需要组织的事件对象
  1662. */
  1663. preventDefault: function (evt) {
  1664. evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
  1665. },
  1666. /**
  1667. * 删除元素element指定的样式
  1668. * @method removeStyle
  1669. * @param { Element } element 需要删除样式的元素
  1670. * @param { String } styleName 需要删除的样式名
  1671. * @example
  1672. * ```html
  1673. * <span id="test" style="color: red; background: blue;"></span>
  1674. *
  1675. * <script>
  1676. *
  1677. * var testNode = document.getElementById("test");
  1678. *
  1679. * UE.dom.domUtils.removeStyle( testNode, 'color' );
  1680. *
  1681. * //output: background: blue;
  1682. * console.log( testNode.style.cssText );
  1683. *
  1684. * </script>
  1685. * ```
  1686. */
  1687. removeStyle:function (element, name) {
  1688. if(browser.ie ){
  1689. //针对color先单独处理一下
  1690. if(name == 'color'){
  1691. name = '(^|;)' + name;
  1692. }
  1693. element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
  1694. }else{
  1695. if (element.style.removeProperty) {
  1696. element.style.removeProperty (name);
  1697. }else {
  1698. element.style.removeAttribute (utils.cssStyleToDomStyle(name));
  1699. }
  1700. }
  1701. if (!element.style.cssText) {
  1702. domUtils.removeAttributes(element, ['style']);
  1703. }
  1704. },
  1705. /**
  1706. * 获取元素element的某个样式值
  1707. * @name getStyle
  1708. * @grammar UM.dom.domUtils.getStyle(element,name) => String
  1709. */
  1710. getStyle: function (element, name) {
  1711. var value = element.style[ utils.cssStyleToDomStyle(name) ];
  1712. return utils.fixColor(name, value);
  1713. },
  1714. /**
  1715. * 为元素element设置样式属性值
  1716. * @name setStyle
  1717. * @grammar UM.dom.domUtils.setStyle(element,name,value)
  1718. */
  1719. setStyle: function (element, name, value) {
  1720. element.style[utils.cssStyleToDomStyle(name)] = value;
  1721. if (!utils.trim(element.style.cssText)) {
  1722. this.removeAttributes(element, 'style')
  1723. }
  1724. },
  1725. /**
  1726. * 删除_moz_dirty属性
  1727. * @function
  1728. */
  1729. removeDirtyAttr: function (node) {
  1730. for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
  1731. ci.removeAttribute('_moz_dirty');
  1732. }
  1733. node.removeAttribute('_moz_dirty');
  1734. },
  1735. /**
  1736. * 返回子节点的数量
  1737. * @function
  1738. * @param {Node} node 父节点
  1739. * @param {Function} fn 过滤子节点的规则,若为空,则得到所有子节点的数量
  1740. * @return {Number} 符合条件子节点的数量
  1741. */
  1742. getChildCount: function (node, fn) {
  1743. var count = 0, first = node.firstChild;
  1744. fn = fn || function () {
  1745. return 1;
  1746. };
  1747. while (first) {
  1748. if (fn(first)) {
  1749. count++;
  1750. }
  1751. first = first.nextSibling;
  1752. }
  1753. return count;
  1754. },
  1755. /**
  1756. * 判断是否为空节点
  1757. * @function
  1758. * @param {Node} node 节点
  1759. * @return {Boolean} 是否为空节点
  1760. */
  1761. isEmptyNode: function (node) {
  1762. return !node.firstChild || domUtils.getChildCount(node, function (node) {
  1763. return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
  1764. }) == 0
  1765. },
  1766. /**
  1767. * 判断节点是否为br
  1768. * @function
  1769. * @param {Node} node 节点
  1770. */
  1771. isBr: function (node) {
  1772. return node.nodeType == 1 && node.tagName == 'BR';
  1773. },
  1774. isEmptyBlock: function (node, reg) {
  1775. if (node.nodeType != 1)
  1776. return 0;
  1777. reg = reg || new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g');
  1778. if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
  1779. return 0;
  1780. }
  1781. for (var n in dtd.$isNotEmpty) {
  1782. if (node.getElementsByTagName(n).length) {
  1783. return 0;
  1784. }
  1785. }
  1786. return 1;
  1787. },
  1788. //判断是否是编辑器自定义的参数
  1789. isCustomeNode: function (node) {
  1790. return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
  1791. },
  1792. fillNode: function (doc, node) {
  1793. var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
  1794. node.innerHTML = '';
  1795. node.appendChild(tmpNode);
  1796. },
  1797. isBoundaryNode: function (node, dir) {
  1798. var tmp;
  1799. while (!domUtils.isBody(node)) {
  1800. tmp = node;
  1801. node = node.parentNode;
  1802. if (tmp !== node[dir]) {
  1803. return false;
  1804. }
  1805. }
  1806. return true;
  1807. },
  1808. isFillChar: function (node, isInStart) {
  1809. return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
  1810. },
  1811. isBody: function(node){
  1812. return $(node).hasClass('edui-body-container');
  1813. }
  1814. };
  1815. var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  1816. ///import editor.js
  1817. ///import core/utils.js
  1818. ///import core/browser.js
  1819. ///import core/dom/dom.js
  1820. ///import core/dom/dtd.js
  1821. ///import core/dom/domUtils.js
  1822. /**
  1823. * @file
  1824. * @name UM.dom.Range
  1825. * @anthor zhanyi
  1826. * @short Range
  1827. * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
  1828. * @desc Range范围实现类,本类是UEditor底层核心类,统一w3cRange和ieRange之间的差异,包括接口和属性
  1829. */
  1830. (function () {
  1831. var guid = 0,
  1832. fillChar = domUtils.fillChar,
  1833. fillData;
  1834. /**
  1835. * 更新range的collapse状态
  1836. * @param {Range} range range对象
  1837. */
  1838. function updateCollapse(range) {
  1839. range.collapsed =
  1840. range.startContainer && range.endContainer &&
  1841. range.startContainer === range.endContainer &&
  1842. range.startOffset == range.endOffset;
  1843. }
  1844. function selectOneNode(rng){
  1845. return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
  1846. }
  1847. function setEndPoint(toStart, node, offset, range) {
  1848. //如果node是自闭合标签要处理
  1849. if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
  1850. offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
  1851. node = node.parentNode;
  1852. }
  1853. if (toStart) {
  1854. range.startContainer = node;
  1855. range.startOffset = offset;
  1856. if (!range.endContainer) {
  1857. range.collapse(true);
  1858. }
  1859. } else {
  1860. range.endContainer = node;
  1861. range.endOffset = offset;
  1862. if (!range.startContainer) {
  1863. range.collapse(false);
  1864. }
  1865. }
  1866. updateCollapse(range);
  1867. return range;
  1868. }
  1869. /**
  1870. * @name Range
  1871. * @grammar new UM.dom.Range(document) => Range 实例
  1872. * @desc 创建一个跟document绑定的空的Range实例
  1873. * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
  1874. * - ***startOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1875. * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
  1876. * - ***endOffset*** 容器节点中的偏移量,如果是elementNode就是childNodes中的第几个,如果是textNode就是nodeValue的第几个字符
  1877. * - ***document*** 跟range关联的document对象
  1878. * - ***collapsed*** 是否是闭合状态
  1879. */
  1880. var Range = dom.Range = function (document,body) {
  1881. var me = this;
  1882. me.startContainer =
  1883. me.startOffset =
  1884. me.endContainer =
  1885. me.endOffset = null;
  1886. me.document = document;
  1887. me.collapsed = true;
  1888. me.body = body;
  1889. };
  1890. /**
  1891. * 删除fillData
  1892. * @param doc
  1893. * @param excludeNode
  1894. */
  1895. function removeFillData(doc, excludeNode) {
  1896. try {
  1897. if (fillData && domUtils.inDoc(fillData, doc)) {
  1898. if (!fillData.nodeValue.replace(fillCharReg, '').length) {
  1899. var tmpNode = fillData.parentNode;
  1900. domUtils.remove(fillData);
  1901. while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
  1902. //safari的contains有bug
  1903. (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
  1904. ) {
  1905. fillData = tmpNode.parentNode;
  1906. domUtils.remove(tmpNode);
  1907. tmpNode = fillData;
  1908. }
  1909. } else {
  1910. fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
  1911. }
  1912. }
  1913. } catch (e) {
  1914. }
  1915. }
  1916. /**
  1917. *
  1918. * @param node
  1919. * @param dir
  1920. */
  1921. function mergeSibling(node, dir) {
  1922. var tmpNode;
  1923. node = node[dir];
  1924. while (node && domUtils.isFillChar(node)) {
  1925. tmpNode = node[dir];
  1926. domUtils.remove(node);
  1927. node = tmpNode;
  1928. }
  1929. }
  1930. function execContentsAction(range, action) {
  1931. //调整边界
  1932. //range.includeBookmark();
  1933. var start = range.startContainer,
  1934. end = range.endContainer,
  1935. startOffset = range.startOffset,
  1936. endOffset = range.endOffset,
  1937. doc = range.document,
  1938. frag = doc.createDocumentFragment(),
  1939. tmpStart, tmpEnd;
  1940. if (start.nodeType == 1) {
  1941. start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
  1942. }
  1943. if (end.nodeType == 1) {
  1944. end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
  1945. }
  1946. if (start === end && start.nodeType == 3) {
  1947. frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
  1948. //is not clone
  1949. if (action) {
  1950. start.deleteData(startOffset, endOffset - startOffset);
  1951. range.collapse(true);
  1952. }
  1953. return frag;
  1954. }
  1955. var current, currentLevel, clone = frag,
  1956. startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
  1957. for (var i = 0; startParents[i] == endParents[i];) {
  1958. i++;
  1959. }
  1960. for (var j = i, si; si = startParents[j]; j++) {
  1961. current = si.nextSibling;
  1962. if (si == start) {
  1963. if (!tmpStart) {
  1964. if (range.startContainer.nodeType == 3) {
  1965. clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
  1966. //is not clone
  1967. if (action) {
  1968. start.deleteData(startOffset, start.nodeValue.length - startOffset);
  1969. }
  1970. } else {
  1971. clone.appendChild(!action ? start.cloneNode(true) : start);
  1972. }
  1973. }
  1974. } else {
  1975. currentLevel = si.cloneNode(false);
  1976. clone.appendChild(currentLevel);
  1977. }
  1978. while (current) {
  1979. if (current === end || current === endParents[j]) {
  1980. break;
  1981. }
  1982. si = current.nextSibling;
  1983. clone.appendChild(!action ? current.cloneNode(true) : current);
  1984. current = si;
  1985. }
  1986. clone = currentLevel;
  1987. }
  1988. clone = frag;
  1989. if (!startParents[i]) {
  1990. clone.appendChild(startParents[i - 1].cloneNode(false));
  1991. clone = clone.firstChild;
  1992. }
  1993. for (var j = i, ei; ei = endParents[j]; j++) {
  1994. current = ei.previousSibling;
  1995. if (ei == end) {
  1996. if (!tmpEnd && range.endContainer.nodeType == 3) {
  1997. clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
  1998. //is not clone
  1999. if (action) {
  2000. end.deleteData(0, endOffset);
  2001. }
  2002. }
  2003. } else {
  2004. currentLevel = ei.cloneNode(false);
  2005. clone.appendChild(currentLevel);
  2006. }
  2007. //如果两端同级,右边第一次已经被开始做了
  2008. if (j != i || !startParents[i]) {
  2009. while (current) {
  2010. if (current === start) {
  2011. break;
  2012. }
  2013. ei = current.previousSibling;
  2014. clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
  2015. current = ei;
  2016. }
  2017. }
  2018. clone = currentLevel;
  2019. }
  2020. if (action) {
  2021. range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
  2022. }
  2023. tmpStart && domUtils.remove(tmpStart);
  2024. tmpEnd && domUtils.remove(tmpEnd);
  2025. return frag;
  2026. }
  2027. Range.prototype = {
  2028. /**
  2029. * @name deleteContents
  2030. * @grammar range.deleteContents() => Range
  2031. * @desc 删除当前选区范围中的所有内容并返回range实例,这时的range已经变成了闭合状态
  2032. * @example
  2033. * DOM Element :
  2034. * <b>x<i>x[x<i>xx]x</b>
  2035. * //执行方法后
  2036. * <b>x<i>x<i>|x</b>
  2037. * 注意range改变了
  2038. * range.startContainer => b
  2039. * range.startOffset => 2
  2040. * range.endContainer => b
  2041. * range.endOffset => 2
  2042. * range.collapsed => true
  2043. */
  2044. deleteContents:function () {
  2045. var txt;
  2046. if (!this.collapsed) {
  2047. execContentsAction(this, 1);
  2048. }
  2049. if (browser.webkit) {
  2050. txt = this.startContainer;
  2051. if (txt.nodeType == 3 && !txt.nodeValue.length) {
  2052. this.setStartBefore(txt).collapse(true);
  2053. domUtils.remove(txt);
  2054. }
  2055. }
  2056. return this;
  2057. },
  2058. inFillChar : function(){
  2059. var start = this.startContainer;
  2060. if(this.collapsed && start.nodeType == 3
  2061. && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
  2062. ){
  2063. return true;
  2064. }
  2065. return false;
  2066. },
  2067. /**
  2068. * @name setStart
  2069. * @grammar range.setStart(node,offset) => Range
  2070. * @desc 设置range的开始位置位于node节点内,偏移量为offset
  2071. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2072. */
  2073. setStart:function (node, offset) {
  2074. return setEndPoint(true, node, offset, this);
  2075. },
  2076. /**
  2077. * 设置range的结束位置位于node节点,偏移量为offset
  2078. * 如果node是elementNode那offset指的是childNodes中的第几个,如果是textNode那offset指的是nodeValue的第几个字符
  2079. * @name setEnd
  2080. * @grammar range.setEnd(node,offset) => Range
  2081. */
  2082. setEnd:function (node, offset) {
  2083. return setEndPoint(false, node, offset, this);
  2084. },
  2085. /**
  2086. * 将Range开始位置设置到node节点之后
  2087. * @name setStartAfter
  2088. * @grammar range.setStartAfter(node) => Range
  2089. * @example
  2090. * <b>xx<i>x|x</i>x</b>
  2091. * 执行setStartAfter(i)后
  2092. * range.startContainer =>b
  2093. * range.startOffset =>2
  2094. */
  2095. setStartAfter:function (node) {
  2096. return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2097. },
  2098. /**
  2099. * 将Range开始位置设置到node节点之前
  2100. * @name setStartBefore
  2101. * @grammar range.setStartBefore(node) => Range
  2102. * @example
  2103. * <b>xx<i>x|x</i>x</b>
  2104. * 执行setStartBefore(i)后
  2105. * range.startContainer =>b
  2106. * range.startOffset =>1
  2107. */
  2108. setStartBefore:function (node) {
  2109. return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
  2110. },
  2111. /**
  2112. * 将Range结束位置设置到node节点之后
  2113. * @name setEndAfter
  2114. * @grammar range.setEndAfter(node) => Range
  2115. * @example
  2116. * <b>xx<i>x|x</i>x</b>
  2117. * setEndAfter(i)后
  2118. * range.endContainer =>b
  2119. * range.endtOffset =>2
  2120. */
  2121. setEndAfter:function (node) {
  2122. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
  2123. },
  2124. /**
  2125. * 将Range结束位置设置到node节点之前
  2126. * @name setEndBefore
  2127. * @grammar range.setEndBefore(node) => Range
  2128. * @example
  2129. * <b>xx<i>x|x</i>x</b>
  2130. * 执行setEndBefore(i)后
  2131. * range.endContainer =>b
  2132. * range.endtOffset =>1
  2133. */
  2134. setEndBefore:function (node) {
  2135. return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
  2136. },
  2137. /**
  2138. * 将Range开始位置设置到node节点内的开始位置
  2139. * @name setStartAtFirst
  2140. * @grammar range.setStartAtFirst(node) => Range
  2141. */
  2142. setStartAtFirst:function (node) {
  2143. return this.setStart(node, 0);
  2144. },
  2145. /**
  2146. * 将Range开始位置设置到node节点内的结束位置
  2147. * @name setStartAtLast
  2148. * @grammar range.setStartAtLast(node) => Range
  2149. */
  2150. setStartAtLast:function (node) {
  2151. return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2152. },
  2153. /**
  2154. * 将Range结束位置设置到node节点内的开始位置
  2155. * @name setEndAtFirst
  2156. * @grammar range.setEndAtFirst(node) => Range
  2157. */
  2158. setEndAtFirst:function (node) {
  2159. return this.setEnd(node, 0);
  2160. },
  2161. /**
  2162. * 将Range结束位置设置到node节点内的结束位置
  2163. * @name setEndAtLast
  2164. * @grammar range.setEndAtLast(node) => Range
  2165. */
  2166. setEndAtLast:function (node) {
  2167. return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
  2168. },
  2169. /**
  2170. * 选中完整的指定节点,并返回包含该节点的range
  2171. * @name selectNode
  2172. * @grammar range.selectNode(node) => Range
  2173. */
  2174. selectNode:function (node) {
  2175. return this.setStartBefore(node).setEndAfter(node);
  2176. },
  2177. /**
  2178. * 选中node内部的所有节点,并返回对应的range
  2179. * @name selectNodeContents
  2180. * @grammar range.selectNodeContents(node) => Range
  2181. * @example
  2182. * <b>xx[x<i>xxx</i>]xxx</b>
  2183. * 执行后
  2184. * <b>[xxx<i>xxx</i>xxx]</b>
  2185. * range.startContainer =>b
  2186. * range.startOffset =>0
  2187. * range.endContainer =>b
  2188. * range.endOffset =>3
  2189. */
  2190. selectNodeContents:function (node) {
  2191. return this.setStart(node, 0).setEndAtLast(node);
  2192. },
  2193. /**
  2194. * 克隆一个新的range对象
  2195. * @name cloneRange
  2196. * @grammar range.cloneRange() => Range
  2197. */
  2198. cloneRange:function () {
  2199. var me = this;
  2200. return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  2201. },
  2202. /**
  2203. * 让选区闭合到尾部,若toStart为真,则闭合到头部
  2204. * @name collapse
  2205. * @grammar range.collapse() => Range
  2206. * @grammar range.collapse(true) => Range //闭合选区到头部
  2207. */
  2208. collapse:function (toStart) {
  2209. var me = this;
  2210. if (toStart) {
  2211. me.endContainer = me.startContainer;
  2212. me.endOffset = me.startOffset;
  2213. } else {
  2214. me.startContainer = me.endContainer;
  2215. me.startOffset = me.endOffset;
  2216. }
  2217. me.collapsed = true;
  2218. return me;
  2219. },
  2220. /**
  2221. * 调整range的边界,使其"收缩"到最小的位置
  2222. * @name shrinkBoundary
  2223. * @grammar range.shrinkBoundary() => Range //range开始位置和结束位置都调整,参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>
  2224. * @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置
  2225. * @example
  2226. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2227. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>
  2228. * [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>
  2229. */
  2230. shrinkBoundary:function (ignoreEnd) {
  2231. var me = this, child,
  2232. collapsed = me.collapsed;
  2233. function check(node){
  2234. return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
  2235. }
  2236. while (me.startContainer.nodeType == 1 //是element
  2237. && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
  2238. && check(child)) {
  2239. me.setStart(child, 0);
  2240. }
  2241. if (collapsed) {
  2242. return me.collapse(true);
  2243. }
  2244. if (!ignoreEnd) {
  2245. while (me.endContainer.nodeType == 1//是element
  2246. && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
  2247. && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
  2248. && check(child)) {
  2249. me.setEnd(child, child.childNodes.length);
  2250. }
  2251. }
  2252. return me;
  2253. },
  2254. /**
  2255. * 调整边界容器,如果是textNode,就调整到elementNode上
  2256. * @name trimBoundary
  2257. * @grammar range.trimBoundary([ignoreEnd]) => Range //true忽略结束边界
  2258. * @example
  2259. * DOM Element :
  2260. * <b>|xxx</b>
  2261. * startContainer = xxx; startOffset = 0
  2262. * //执行后本方法后
  2263. * startContainer = <b>; startOffset = 0
  2264. * @example
  2265. * Dom Element :
  2266. * <b>xx|x</b>
  2267. * startContainer = xxx; startOffset = 2
  2268. * //执行本方法后,xxx被实实在在地切分成两个TextNode
  2269. * startContainer = <b>; startOffset = 1
  2270. */
  2271. trimBoundary:function (ignoreEnd) {
  2272. this.txtToElmBoundary();
  2273. var start = this.startContainer,
  2274. offset = this.startOffset,
  2275. collapsed = this.collapsed,
  2276. end = this.endContainer;
  2277. if (start.nodeType == 3) {
  2278. if (offset == 0) {
  2279. this.setStartBefore(start);
  2280. } else {
  2281. if (offset >= start.nodeValue.length) {
  2282. this.setStartAfter(start);
  2283. } else {
  2284. var textNode = domUtils.split(start, offset);
  2285. //跟新结束边界
  2286. if (start === end) {
  2287. this.setEnd(textNode, this.endOffset - offset);
  2288. } else if (start.parentNode === end) {
  2289. this.endOffset += 1;
  2290. }
  2291. this.setStartBefore(textNode);
  2292. }
  2293. }
  2294. if (collapsed) {
  2295. return this.collapse(true);
  2296. }
  2297. }
  2298. if (!ignoreEnd) {
  2299. offset = this.endOffset;
  2300. end = this.endContainer;
  2301. if (end.nodeType == 3) {
  2302. if (offset == 0) {
  2303. this.setEndBefore(end);
  2304. } else {
  2305. offset < end.nodeValue.length && domUtils.split(end, offset);
  2306. this.setEndAfter(end);
  2307. }
  2308. }
  2309. }
  2310. return this;
  2311. },
  2312. /**
  2313. * 如果选区在文本的边界上,就扩展选区到文本的父节点上
  2314. * @name txtToElmBoundary
  2315. * @example
  2316. * Dom Element :
  2317. * <b> |xxx</b>
  2318. * startContainer = xxx; startOffset = 0
  2319. * //本方法执行后
  2320. * startContainer = <b>; startOffset = 0
  2321. * @example
  2322. * Dom Element :
  2323. * <b> xxx| </b>
  2324. * startContainer = xxx; startOffset = 3
  2325. * //本方法执行后
  2326. * startContainer = <b>; startOffset = 1
  2327. */
  2328. txtToElmBoundary:function (ignoreCollapsed) {
  2329. function adjust(r, c) {
  2330. var container = r[c + 'Container'],
  2331. offset = r[c + 'Offset'];
  2332. if (container.nodeType == 3) {
  2333. if (!offset) {
  2334. r['set' + c.replace(/(\w)/, function (a) {
  2335. return a.toUpperCase();
  2336. }) + 'Before'](container);
  2337. } else if (offset >= container.nodeValue.length) {
  2338. r['set' + c.replace(/(\w)/, function (a) {
  2339. return a.toUpperCase();
  2340. }) + 'After' ](container);
  2341. }
  2342. }
  2343. }
  2344. if (ignoreCollapsed || !this.collapsed) {
  2345. adjust(this, 'start');
  2346. adjust(this, 'end');
  2347. }
  2348. return this;
  2349. },
  2350. /**
  2351. * 在当前选区的开始位置前插入一个节点或者fragment,range的开始位置会在插入节点的前边
  2352. * @name insertNode
  2353. * @grammar range.insertNode(node) => Range //node可以是textNode,elementNode,fragment
  2354. * @example
  2355. * Range :
  2356. * xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2357. * 待插入Node :
  2358. * <p>ssss</p>
  2359. * 执行本方法后的Range :
  2360. * xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
  2361. */
  2362. insertNode:function (node) {
  2363. var first = node, length = 1;
  2364. if (node.nodeType == 11) {
  2365. first = node.firstChild;
  2366. length = node.childNodes.length;
  2367. }
  2368. this.trimBoundary(true);
  2369. var start = this.startContainer,
  2370. offset = this.startOffset;
  2371. var nextNode = start.childNodes[ offset ];
  2372. if (nextNode) {
  2373. start.insertBefore(node, nextNode);
  2374. } else {
  2375. start.appendChild(node);
  2376. }
  2377. if (first.parentNode === this.endContainer) {
  2378. this.endOffset = this.endOffset + length;
  2379. }
  2380. return this.setStartBefore(first);
  2381. },
  2382. /**
  2383. * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾
  2384. * @name setCursor
  2385. * @grammar range.setCursor([toEnd]) => Range //toEnd为true时,光标闭合到选区的末尾
  2386. */
  2387. setCursor:function (toEnd, noFillData) {
  2388. return this.collapse(!toEnd).select(noFillData);
  2389. },
  2390. /**
  2391. * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
  2392. * @name createBookmark
  2393. * @grammar range.createBookmark([serialize]) => Object //{start:开始标记,end:结束标记,id:serialize} serialize为真时,开始结束标记是插入节点的id,否则是插入节点的引用
  2394. */
  2395. createBookmark:function (serialize, same) {
  2396. var endNode,
  2397. startNode = this.document.createElement('span');
  2398. startNode.style.cssText = 'display:none;line-height:0px;';
  2399. startNode.appendChild(this.document.createTextNode('\u200D'));
  2400. startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  2401. if (!this.collapsed) {
  2402. endNode = startNode.cloneNode(true);
  2403. endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
  2404. }
  2405. this.insertNode(startNode);
  2406. if (endNode) {
  2407. this.collapse().insertNode(endNode).setEndBefore(endNode);
  2408. }
  2409. this.setStartAfter(startNode);
  2410. return {
  2411. start:serialize ? startNode.id : startNode,
  2412. end:endNode ? serialize ? endNode.id : endNode : null,
  2413. id:serialize
  2414. }
  2415. },
  2416. /**
  2417. * 移动边界到书签位置,并删除插入的书签节点
  2418. * @name moveToBookmark
  2419. * @grammar range.moveToBookmark(bookmark) => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的
  2420. */
  2421. moveToBookmark:function (bookmark) {
  2422. var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
  2423. end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
  2424. this.setStartBefore(start);
  2425. domUtils.remove(start);
  2426. if (end) {
  2427. this.setEndBefore(end);
  2428. domUtils.remove(end);
  2429. } else {
  2430. this.collapse(true);
  2431. }
  2432. return this;
  2433. },
  2434. /**
  2435. * 调整Range的边界,使其"缩小"到最合适的位置
  2436. * @name adjustmentBoundary
  2437. * @grammar range.adjustmentBoundary() => Range //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>
  2438. * @example
  2439. * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
  2440. * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>
  2441. */
  2442. adjustmentBoundary:function () {
  2443. if (!this.collapsed) {
  2444. while (!domUtils.isBody(this.startContainer) &&
  2445. this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
  2446. this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2447. ) {
  2448. this.setStartAfter(this.startContainer);
  2449. }
  2450. while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
  2451. this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
  2452. ) {
  2453. this.setEndBefore(this.endContainer);
  2454. }
  2455. }
  2456. return this;
  2457. },
  2458. /**
  2459. * 得到一个自闭合的节点,常用于获取自闭和的节点,例如图片节点
  2460. * @name getClosedNode
  2461. * @grammar range.getClosedNode() => node|null
  2462. * @example
  2463. * <b>xxxx[<img />]xxx</b>
  2464. */
  2465. getClosedNode:function () {
  2466. var node;
  2467. if (!this.collapsed) {
  2468. var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
  2469. if (selectOneNode(range)) {
  2470. var child = range.startContainer.childNodes[range.startOffset];
  2471. if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
  2472. node = child;
  2473. }
  2474. }
  2475. }
  2476. return node;
  2477. },
  2478. /**
  2479. * 根据当前range选中内容节点(在页面上表现为反白显示)
  2480. * @name select
  2481. * @grammar range.select(); => Range
  2482. */
  2483. select:browser.ie ? function (noFillData, textRange) {
  2484. var nativeRange;
  2485. if (!this.collapsed)
  2486. this.shrinkBoundary();
  2487. var node = this.getClosedNode();
  2488. if (node && !textRange) {
  2489. try {
  2490. nativeRange = this.document.body.createControlRange();
  2491. nativeRange.addElement(node);
  2492. nativeRange.select();
  2493. } catch (e) {}
  2494. return this;
  2495. }
  2496. var bookmark = this.createBookmark(),
  2497. start = bookmark.start,
  2498. end;
  2499. nativeRange = this.document.body.createTextRange();
  2500. nativeRange.moveToElementText(start);
  2501. nativeRange.moveStart('character', 1);
  2502. if (!this.collapsed) {
  2503. var nativeRangeEnd = this.document.body.createTextRange();
  2504. end = bookmark.end;
  2505. nativeRangeEnd.moveToElementText(end);
  2506. nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
  2507. } else {
  2508. if (!noFillData && this.startContainer.nodeType != 3) {
  2509. //使用<span>|x<span>固定住光标
  2510. var tmpText = this.document.createTextNode(fillChar),
  2511. tmp = this.document.createElement('span');
  2512. tmp.appendChild(this.document.createTextNode(fillChar));
  2513. start.parentNode.insertBefore(tmp, start);
  2514. start.parentNode.insertBefore(tmpText, start);
  2515. //当点b,i,u时,不能清除i上边的b
  2516. removeFillData(this.document, tmpText);
  2517. fillData = tmpText;
  2518. mergeSibling(tmp, 'previousSibling');
  2519. mergeSibling(start, 'nextSibling');
  2520. nativeRange.moveStart('character', -1);
  2521. nativeRange.collapse(true);
  2522. }
  2523. }
  2524. this.moveToBookmark(bookmark);
  2525. tmp && domUtils.remove(tmp);
  2526. //IE在隐藏状态下不支持range操作,catch一下
  2527. try {
  2528. nativeRange.select();
  2529. } catch (e) {
  2530. }
  2531. return this;
  2532. } : function (notInsertFillData) {
  2533. function checkOffset(rng){
  2534. function check(node,offset,dir){
  2535. if(node.nodeType == 3 && node.nodeValue.length < offset){
  2536. rng[dir + 'Offset'] = node.nodeValue.length
  2537. }
  2538. }
  2539. check(rng.startContainer,rng.startOffset,'start');
  2540. check(rng.endContainer,rng.endOffset,'end');
  2541. }
  2542. var win = domUtils.getWindow(this.document),
  2543. sel = win.getSelection(),
  2544. txtNode;
  2545. //FF下关闭自动长高时滚动条在关闭dialog时会跳
  2546. //ff下如果不body.focus将不能定位闭合光标到编辑器内
  2547. browser.gecko ? this.body.focus() : win.focus();
  2548. if (sel) {
  2549. sel.removeAllRanges();
  2550. // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
  2551. // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
  2552. if (this.collapsed && !notInsertFillData) {
  2553. // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
  2554. // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  2555. // var tmp = this.document.createTextNode('');
  2556. // this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  2557. // }
  2558. //
  2559. //处理光标落在文本节点的情况
  2560. //处理以下的情况
  2561. //<b>|xxxx</b>
  2562. //<b>xxxx</b>|xxxx
  2563. //xxxx<b>|</b>
  2564. var start = this.startContainer,child = start;
  2565. if(start.nodeType == 1){
  2566. child = start.childNodes[this.startOffset];
  2567. }
  2568. if( !(start.nodeType == 3 && this.startOffset) &&
  2569. (child ?
  2570. (!child.previousSibling || child.previousSibling.nodeType != 3)
  2571. :
  2572. (!start.lastChild || start.lastChild.nodeType != 3)
  2573. )
  2574. ){
  2575. txtNode = this.document.createTextNode(fillChar);
  2576. //跟着前边走
  2577. this.insertNode(txtNode);
  2578. removeFillData(this.document, txtNode);
  2579. mergeSibling(txtNode, 'previousSibling');
  2580. mergeSibling(txtNode, 'nextSibling');
  2581. fillData = txtNode;
  2582. this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
  2583. }
  2584. }
  2585. var nativeRange = this.document.createRange();
  2586. if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
  2587. var child = this.startContainer.childNodes[this.startOffset];
  2588. if(!child){
  2589. //往前靠拢
  2590. child = this.startContainer.lastChild;
  2591. if( child && domUtils.isBr(child)){
  2592. this.setStartBefore(child).collapse(true);
  2593. }
  2594. }else{
  2595. //向后靠拢
  2596. while(child && domUtils.isBlockElm(child)){
  2597. if(child.nodeType == 1 && child.childNodes[0]){
  2598. child = child.childNodes[0]
  2599. }else{
  2600. break;
  2601. }
  2602. }
  2603. child && this.setStartBefore(child).collapse(true)
  2604. }
  2605. }
  2606. //是createAddress最后一位算的不准,现在这里进行微调
  2607. checkOffset(this);
  2608. nativeRange.setStart(this.startContainer, this.startOffset);
  2609. nativeRange.setEnd(this.endContainer, this.endOffset);
  2610. sel.addRange(nativeRange);
  2611. }
  2612. return this;
  2613. },
  2614. createAddress : function(ignoreEnd,ignoreTxt){
  2615. var addr = {},me = this;
  2616. function getAddress(isStart){
  2617. var node = isStart ? me.startContainer : me.endContainer;
  2618. var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
  2619. addrs = [];
  2620. for(var i = 0,ci;ci = parents[i++];){
  2621. addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
  2622. }
  2623. var firstIndex = 0;
  2624. if(ignoreTxt){
  2625. if(node.nodeType == 3){
  2626. var tmpNode = node.previousSibling;
  2627. while(tmpNode && tmpNode.nodeType == 3){
  2628. firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
  2629. tmpNode = tmpNode.previousSibling;
  2630. }
  2631. firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
  2632. }else{
  2633. node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
  2634. if(node){
  2635. firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
  2636. }else{
  2637. node = isStart ? me.startContainer : me.endContainer;
  2638. var first = node.firstChild;
  2639. while(first){
  2640. if(domUtils.isFillChar(first)){
  2641. first = first.nextSibling;
  2642. continue;
  2643. }
  2644. firstIndex++;
  2645. if(first.nodeType == 3){
  2646. while( first && first.nodeType == 3){
  2647. first = first.nextSibling;
  2648. }
  2649. }else{
  2650. first = first.nextSibling;
  2651. }
  2652. }
  2653. }
  2654. }
  2655. }else{
  2656. firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
  2657. }
  2658. if(firstIndex < 0){
  2659. firstIndex = 0;
  2660. }
  2661. addrs.push(firstIndex);
  2662. return addrs;
  2663. }
  2664. addr.startAddress = getAddress(true);
  2665. if(!ignoreEnd){
  2666. addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
  2667. }
  2668. return addr;
  2669. },
  2670. moveToAddress : function(addr,ignoreEnd){
  2671. var me = this;
  2672. function getNode(address,isStart){
  2673. var tmpNode = me.body,
  2674. parentNode,offset;
  2675. for(var i= 0,ci,l=address.length;i<l;i++){
  2676. ci = address[i];
  2677. parentNode = tmpNode;
  2678. tmpNode = tmpNode.childNodes[ci];
  2679. if(!tmpNode){
  2680. offset = ci;
  2681. break;
  2682. }
  2683. }
  2684. if(isStart){
  2685. if(tmpNode){
  2686. me.setStartBefore(tmpNode)
  2687. }else{
  2688. me.setStart(parentNode,offset)
  2689. }
  2690. }else{
  2691. if(tmpNode){
  2692. me.setEndBefore(tmpNode)
  2693. }else{
  2694. me.setEnd(parentNode,offset)
  2695. }
  2696. }
  2697. }
  2698. getNode(addr.startAddress,true);
  2699. !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
  2700. return me;
  2701. },
  2702. equals : function(rng){
  2703. for(var p in this){
  2704. if(this.hasOwnProperty(p)){
  2705. if(this[p] !== rng[p])
  2706. return false
  2707. }
  2708. }
  2709. return true;
  2710. },
  2711. scrollIntoView : function(){
  2712. var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');
  2713. this.cloneRange().insertNode($span.get(0));
  2714. var winScrollTop = $(window).scrollTop(),
  2715. winHeight = $(window).height(),
  2716. spanTop = $span.offset().top;
  2717. if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){
  2718. if(spanTop > winScrollTop + winHeight){
  2719. window.scrollTo(0,spanTop - winHeight + $span.height())
  2720. }else{
  2721. window.scrollTo(0,winScrollTop - spanTop)
  2722. }
  2723. }
  2724. $span.remove();
  2725. },
  2726. getOffset : function(){
  2727. var bk = this.createBookmark();
  2728. var offset = $(bk.start).css('display','inline-block').offset();
  2729. this.moveToBookmark(bk);
  2730. return offset
  2731. }
  2732. };
  2733. })();
  2734. ///import editor.js
  2735. ///import core/browser.js
  2736. ///import core/dom/dom.js
  2737. ///import core/dom/dtd.js
  2738. ///import core/dom/domUtils.js
  2739. ///import core/dom/Range.js
  2740. /**
  2741. * @class UM.dom.Selection Selection类
  2742. */
  2743. (function () {
  2744. function getBoundaryInformation( range, start ) {
  2745. var getIndex = domUtils.getNodeIndex;
  2746. range = range.duplicate();
  2747. range.collapse( start );
  2748. var parent = range.parentElement();
  2749. //如果节点里没有子节点,直接退出
  2750. if ( !parent.hasChildNodes() ) {
  2751. return {container:parent, offset:0};
  2752. }
  2753. var siblings = parent.children,
  2754. child,
  2755. testRange = range.duplicate(),
  2756. startIndex = 0, endIndex = siblings.length - 1, index = -1,
  2757. distance;
  2758. while ( startIndex <= endIndex ) {
  2759. index = Math.floor( (startIndex + endIndex) / 2 );
  2760. child = siblings[index];
  2761. testRange.moveToElementText( child );
  2762. var position = testRange.compareEndPoints( 'StartToStart', range );
  2763. if ( position > 0 ) {
  2764. endIndex = index - 1;
  2765. } else if ( position < 0 ) {
  2766. startIndex = index + 1;
  2767. } else {
  2768. //trace:1043
  2769. return {container:parent, offset:getIndex( child )};
  2770. }
  2771. }
  2772. if ( index == -1 ) {
  2773. testRange.moveToElementText( parent );
  2774. testRange.setEndPoint( 'StartToStart', range );
  2775. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  2776. siblings = parent.childNodes;
  2777. if ( !distance ) {
  2778. child = siblings[siblings.length - 1];
  2779. return {container:child, offset:child.nodeValue.length};
  2780. }
  2781. var i = siblings.length;
  2782. while ( distance > 0 ){
  2783. distance -= siblings[ --i ].nodeValue.length;
  2784. }
  2785. return {container:siblings[i], offset:-distance};
  2786. }
  2787. testRange.collapse( position > 0 );
  2788. testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
  2789. distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
  2790. if ( !distance ) {
  2791. return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
  2792. {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
  2793. {container:child, offset:position > 0 ? 0 : child.childNodes.length}
  2794. }
  2795. while ( distance > 0 ) {
  2796. try {
  2797. var pre = child;
  2798. child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
  2799. distance -= child.nodeValue.length;
  2800. } catch ( e ) {
  2801. return {container:parent, offset:getIndex( pre )};
  2802. }
  2803. }
  2804. return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
  2805. }
  2806. /**
  2807. * 将ieRange转换为Range对象
  2808. * @param {Range} ieRange ieRange对象
  2809. * @param {Range} range Range对象
  2810. * @return {Range} range 返回转换后的Range对象
  2811. */
  2812. function transformIERangeToRange( ieRange, range ) {
  2813. if ( ieRange.item ) {
  2814. range.selectNode( ieRange.item( 0 ) );
  2815. } else {
  2816. var bi = getBoundaryInformation( ieRange, true );
  2817. range.setStart( bi.container, bi.offset );
  2818. if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
  2819. bi = getBoundaryInformation( ieRange, false );
  2820. range.setEnd( bi.container, bi.offset );
  2821. }
  2822. }
  2823. return range;
  2824. }
  2825. /**
  2826. * 获得ieRange
  2827. * @param {Selection} sel Selection对象
  2828. * @return {ieRange} 得到ieRange
  2829. */
  2830. function _getIERange( sel,txtRange ) {
  2831. var ieRange;
  2832. //ie下有可能报错
  2833. try {
  2834. ieRange = sel.getNative(txtRange).createRange();
  2835. } catch ( e ) {
  2836. return null;
  2837. }
  2838. var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
  2839. if ( ( el.ownerDocument || el ) === sel.document ) {
  2840. return ieRange;
  2841. }
  2842. return null;
  2843. }
  2844. var Selection = dom.Selection = function ( doc,body ) {
  2845. var me = this;
  2846. me.document = doc;
  2847. me.body = body;
  2848. if ( browser.ie9below ) {
  2849. $( body).on('beforedeactivate', function () {
  2850. me._bakIERange = me.getIERange();
  2851. } ).on('activate', function () {
  2852. try {
  2853. var ieNativRng = _getIERange( me );
  2854. if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) {
  2855. me._bakIERange.select();
  2856. }
  2857. } catch ( ex ) {
  2858. }
  2859. me._bakIERange = null;
  2860. } );
  2861. }
  2862. };
  2863. Selection.prototype = {
  2864. hasNativeRange : function(){
  2865. var rng;
  2866. if(!browser.ie || browser.ie9above){
  2867. var nativeSel = this.getNative();
  2868. if(!nativeSel.rangeCount){
  2869. return false;
  2870. }
  2871. rng = nativeSel.getRangeAt(0);
  2872. }else{
  2873. rng = _getIERange(this);
  2874. }
  2875. return this.rangeInBody(rng);
  2876. },
  2877. /**
  2878. * 获取原生seleciton对象
  2879. * @public
  2880. * @function
  2881. * @name UM.dom.Selection.getNative
  2882. * @return {Selection} 获得selection对象
  2883. */
  2884. getNative:function (txtRange) {
  2885. var doc = this.document;
  2886. try {
  2887. return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection();
  2888. } catch ( e ) {
  2889. return null;
  2890. }
  2891. },
  2892. /**
  2893. * 获得ieRange
  2894. * @public
  2895. * @function
  2896. * @name UM.dom.Selection.getIERange
  2897. * @return {ieRange} 返回ie原生的Range
  2898. */
  2899. getIERange:function (txtRange) {
  2900. var ieRange = _getIERange( this,txtRange );
  2901. if ( !ieRange || !this.rangeInBody(ieRange,txtRange)) {
  2902. if ( this._bakIERange ) {
  2903. return this._bakIERange;
  2904. }
  2905. }
  2906. return ieRange;
  2907. },
  2908. rangeInBody : function(rng,txtRange){
  2909. var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  2910. return node === this.body || domUtils.inDoc(node,this.body);
  2911. },
  2912. /**
  2913. * 缓存当前选区的range和选区的开始节点
  2914. * @public
  2915. * @function
  2916. * @name UM.dom.Selection.cache
  2917. */
  2918. cache:function () {
  2919. this.clear();
  2920. this._cachedRange = this.getRange();
  2921. this._cachedStartElement = this.getStart();
  2922. this._cachedStartElementPath = this.getStartElementPath();
  2923. },
  2924. getStartElementPath:function () {
  2925. if ( this._cachedStartElementPath ) {
  2926. return this._cachedStartElementPath;
  2927. }
  2928. var start = this.getStart();
  2929. if ( start ) {
  2930. return domUtils.findParents( start, true, null, true )
  2931. }
  2932. return [];
  2933. },
  2934. /**
  2935. * 清空缓存
  2936. * @public
  2937. * @function
  2938. * @name UM.dom.Selection.clear
  2939. */
  2940. clear:function () {
  2941. this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
  2942. },
  2943. /**
  2944. * 编辑器是否得到了选区
  2945. */
  2946. isFocus:function () {
  2947. return this.hasNativeRange()
  2948. },
  2949. /**
  2950. * 获取选区对应的Range
  2951. * @public
  2952. * @function
  2953. * @name UM.dom.Selection.getRange
  2954. * @returns {UM.dom.Range} 得到Range对象
  2955. */
  2956. getRange:function () {
  2957. var me = this;
  2958. function optimze( range ) {
  2959. var child = me.body.firstChild,
  2960. collapsed = range.collapsed;
  2961. while ( child && child.firstChild ) {
  2962. range.setStart( child, 0 );
  2963. child = child.firstChild;
  2964. }
  2965. if ( !range.startContainer ) {
  2966. range.setStart( me.body, 0 )
  2967. }
  2968. if ( collapsed ) {
  2969. range.collapse( true );
  2970. }
  2971. }
  2972. if ( me._cachedRange != null ) {
  2973. return this._cachedRange;
  2974. }
  2975. var range = new dom.Range( me.document,me.body );
  2976. if ( browser.ie9below ) {
  2977. var nativeRange = me.getIERange();
  2978. if ( nativeRange && this.rangeInBody(nativeRange)) {
  2979. try{
  2980. transformIERangeToRange( nativeRange, range );
  2981. }catch(e){
  2982. optimze( range );
  2983. }
  2984. } else {
  2985. optimze( range );
  2986. }
  2987. } else {
  2988. var sel = me.getNative();
  2989. if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) {
  2990. var firstRange = sel.getRangeAt( 0 );
  2991. var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
  2992. range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
  2993. if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
  2994. optimze( range );
  2995. }
  2996. } else {
  2997. //trace:1734 有可能已经不在dom树上了,标识的节点
  2998. if ( this._bakRange && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){
  2999. return this._bakRange;
  3000. }
  3001. optimze( range );
  3002. }
  3003. }
  3004. return this._bakRange = range;
  3005. },
  3006. /**
  3007. * 获取开始元素,用于状态反射
  3008. * @public
  3009. * @function
  3010. * @name UM.dom.Selection.getStart
  3011. * @return {Element} 获得开始元素
  3012. */
  3013. getStart:function () {
  3014. if ( this._cachedStartElement ) {
  3015. return this._cachedStartElement;
  3016. }
  3017. var range = browser.ie9below ? this.getIERange() : this.getRange(),
  3018. tmpRange,
  3019. start, tmp, parent;
  3020. if ( browser.ie9below ) {
  3021. if ( !range ) {
  3022. //todo 给第一个值可能会有问题
  3023. return this.document.body.firstChild;
  3024. }
  3025. //control元素
  3026. if ( range.item ){
  3027. return range.item( 0 );
  3028. }
  3029. tmpRange = range.duplicate();
  3030. //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
  3031. tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
  3032. tmpRange.collapse( 1 );
  3033. start = tmpRange.parentElement();
  3034. parent = tmp = range.parentElement();
  3035. while ( tmp = tmp.parentNode ) {
  3036. if ( tmp == start ) {
  3037. start = parent;
  3038. break;
  3039. }
  3040. }
  3041. } else {
  3042. start = range.startContainer;
  3043. if ( start.nodeType == 1 && start.hasChildNodes() ){
  3044. start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
  3045. }
  3046. if ( start.nodeType == 3 ){
  3047. return start.parentNode;
  3048. }
  3049. }
  3050. return start;
  3051. },
  3052. /**
  3053. * 得到选区中的文本
  3054. * @public
  3055. * @function
  3056. * @name UM.dom.Selection.getText
  3057. * @return {String} 选区中包含的文本
  3058. */
  3059. getText:function () {
  3060. var nativeSel, nativeRange;
  3061. if ( this.isFocus() && (nativeSel = this.getNative()) ) {
  3062. nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
  3063. return browser.ie9below ? nativeRange.text : nativeRange.toString();
  3064. }
  3065. return '';
  3066. }
  3067. };
  3068. })();
  3069. /**
  3070. * @file
  3071. * @name UM.Editor
  3072. * @short Editor
  3073. * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js
  3074. * @desc 编辑器主类,包含编辑器提供的大部分公用接口
  3075. */
  3076. (function () {
  3077. var uid = 0, _selectionChangeTimer;
  3078. /**
  3079. * @private
  3080. * @ignore
  3081. * @param form 编辑器所在的form元素
  3082. * @param editor 编辑器实例对象
  3083. */
  3084. function setValue(form, editor) {
  3085. var textarea;
  3086. if (editor.textarea) {
  3087. if (utils.isString(editor.textarea)) {
  3088. for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
  3089. if (ti.id == 'umeditor_textarea_' + editor.options.textarea) {
  3090. textarea = ti;
  3091. break;
  3092. }
  3093. }
  3094. } else {
  3095. textarea = editor.textarea;
  3096. }
  3097. }
  3098. if (!textarea) {
  3099. form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
  3100. 'name': editor.options.textarea,
  3101. 'id': 'umeditor_textarea_' + editor.options.textarea,
  3102. 'style': "display:none"
  3103. }));
  3104. //不要产生多个textarea
  3105. editor.textarea = textarea;
  3106. }
  3107. textarea.value = editor.hasContents() ?
  3108. (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
  3109. ''
  3110. }
  3111. function loadPlugins(me){
  3112. //初始化插件
  3113. for (var pi in UM.plugins) {
  3114. if(me.options.excludePlugins.indexOf(pi) == -1){
  3115. UM.plugins[pi].call(me);
  3116. me.plugins[pi] = 1;
  3117. }
  3118. }
  3119. me.langIsReady = true;
  3120. me.fireEvent("langReady");
  3121. }
  3122. function checkCurLang(I18N){
  3123. for(var lang in I18N){
  3124. return lang
  3125. }
  3126. }
  3127. /**
  3128. * UEditor编辑器类
  3129. * @name Editor
  3130. * @desc 创建一个跟编辑器实例
  3131. * - ***container*** 编辑器容器对象
  3132. * - ***iframe*** 编辑区域所在的iframe对象
  3133. * - ***window*** 编辑区域所在的window
  3134. * - ***document*** 编辑区域所在的document对象
  3135. * - ***body*** 编辑区域所在的body对象
  3136. * - ***selection*** 编辑区域的选区对象
  3137. */
  3138. var Editor = UM.Editor = function (options) {
  3139. var me = this;
  3140. me.uid = uid++;
  3141. EventBase.call(me);
  3142. me.commands = {};
  3143. me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);
  3144. me.shortcutkeys = {};
  3145. me.inputRules = [];
  3146. me.outputRules = [];
  3147. //设置默认的常用属性
  3148. me.setOpt({
  3149. isShow: true,
  3150. initialContent: '',
  3151. initialStyle:'',
  3152. autoClearinitialContent: false,
  3153. textarea: 'editorValue',
  3154. focus: false,
  3155. focusInEnd: true,
  3156. autoClearEmptyNode: true,
  3157. fullscreen: false,
  3158. readonly: false,
  3159. zIndex: 999,
  3160. enterTag: 'p',
  3161. lang: 'zh-cn',
  3162. langPath: me.options.UMEDITOR_HOME_URL + 'lang/',
  3163. theme: 'default',
  3164. themePath: me.options.UMEDITOR_HOME_URL + 'themes/',
  3165. allHtmlEnabled: false,
  3166. autoSyncData : true,
  3167. autoHeightEnabled : true,
  3168. excludePlugins:''
  3169. });
  3170. me.plugins = {};
  3171. if(!utils.isEmptyObject(UM.I18N)){
  3172. //修改默认的语言类型
  3173. me.options.lang = checkCurLang(UM.I18N);
  3174. loadPlugins(me)
  3175. }else{
  3176. utils.loadFile(document, {
  3177. src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
  3178. tag: "script",
  3179. type: "text/javascript",
  3180. defer: "defer"
  3181. }, function () {
  3182. loadPlugins(me)
  3183. });
  3184. }
  3185. };
  3186. Editor.prototype = {
  3187. /**
  3188. * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready,就马上执行fn,fn的中的this是编辑器实例。
  3189. * 大部分的实例接口都需要放在该方法内部执行,否则在IE下可能会报错。
  3190. * @name ready
  3191. * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function
  3192. * @example
  3193. * var editor = new UM.ui.Editor();
  3194. * editor.render("myEditor");
  3195. * editor.ready(function(){
  3196. * editor.setContent("欢迎使用UEditor!");
  3197. * })
  3198. */
  3199. ready: function (fn) {
  3200. var me = this;
  3201. if (fn) {
  3202. me.isReady ? fn.apply(me) : me.addListener('ready', fn);
  3203. }
  3204. },
  3205. /**
  3206. * 为编辑器设置默认参数值。若用户配置为空,则以默认配置为准
  3207. * @grammar editor.setOpt(key,value); //传入一个键、值对
  3208. * @grammar editor.setOpt({ key:value}); //传入一个json对象
  3209. */
  3210. setOpt: function (key, val) {
  3211. var obj = {};
  3212. if (utils.isString(key)) {
  3213. obj[key] = val
  3214. } else {
  3215. obj = key;
  3216. }
  3217. utils.extend(this.options, obj, true);
  3218. },
  3219. getOpt:function(key){
  3220. return this.options[key] || ''
  3221. },
  3222. /**
  3223. * 销毁编辑器实例对象
  3224. * @name destroy
  3225. * @grammar editor.destroy();
  3226. */
  3227. destroy: function () {
  3228. var me = this;
  3229. me.fireEvent('destroy');
  3230. var container = me.container.parentNode;
  3231. if(container === document.body){
  3232. container = me.container;
  3233. }
  3234. var textarea = me.textarea;
  3235. if (!textarea) {
  3236. textarea = document.createElement('textarea');
  3237. container.parentNode.insertBefore(textarea, container);
  3238. } else {
  3239. textarea.style.display = ''
  3240. }
  3241. textarea.style.width = me.body.offsetWidth + 'px';
  3242. textarea.style.height = me.body.offsetHeight + 'px';
  3243. textarea.value = me.getContent();
  3244. textarea.id = me.key;
  3245. if(container.contains(textarea)){
  3246. $(textarea).insertBefore(container);
  3247. }
  3248. container.innerHTML = '';
  3249. domUtils.remove(container);
  3250. UM.clearCache(me.id);
  3251. //trace:2004
  3252. for (var p in me) {
  3253. if (me.hasOwnProperty(p)) {
  3254. delete this[p];
  3255. }
  3256. }
  3257. },
  3258. initialCont : function(holder){
  3259. if(holder){
  3260. holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name'));
  3261. if (holder && /script|textarea/ig.test(holder.tagName)) {
  3262. var newDiv = document.createElement('div');
  3263. holder.parentNode.insertBefore(newDiv, holder);
  3264. this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml();
  3265. holder.className && (newDiv.className = holder.className);
  3266. holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  3267. if (/textarea/i.test(holder.tagName)) {
  3268. this.textarea = holder;
  3269. this.textarea.style.display = 'none';
  3270. } else {
  3271. holder.parentNode.removeChild(holder);
  3272. holder.id && (newDiv.id = holder.id);
  3273. }
  3274. holder = newDiv;
  3275. holder.innerHTML = '';
  3276. }
  3277. return holder;
  3278. }else{
  3279. return null;
  3280. }
  3281. },
  3282. /**
  3283. * 渲染编辑器的DOM到指定容器,必须且只能调用一次
  3284. * @name render
  3285. * @grammar editor.render(containerId); //可以指定一个容器ID
  3286. * @grammar editor.render(containerDom); //也可以直接指定容器对象
  3287. */
  3288. render: function (container) {
  3289. var me = this,
  3290. options = me.options,
  3291. getStyleValue=function(attr){
  3292. return parseInt($(container).css(attr));
  3293. };
  3294. if (utils.isString(container)) {
  3295. container = document.getElementById(container);
  3296. }
  3297. if (container) {
  3298. this.id = container.getAttribute('id');
  3299. UM.setEditor(this);
  3300. utils.cssRule('edui-style-body',me.options.initialStyle,document);
  3301. container = this.initialCont(container);
  3302. container.className += ' edui-body-container';
  3303. if(options.initialFrameWidth){
  3304. options.minFrameWidth = options.initialFrameWidth
  3305. }else{
  3306. //都没给值,先写死了
  3307. options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;
  3308. }
  3309. if(options.initialFrameHeight){
  3310. options.minFrameHeight = options.initialFrameHeight
  3311. }else{
  3312. options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;
  3313. }
  3314. container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth -
  3315. getStyleValue("padding-left")-
  3316. getStyleValue("padding-right") +'px';
  3317. var height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") );
  3318. if(this.options.autoHeightEnabled){
  3319. container.style.minHeight = height +'px';
  3320. container.style.height = '';
  3321. if(browser.ie && browser.version <= 6){
  3322. container.style.height = height ;
  3323. container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3324. }
  3325. }else{
  3326. $(container).height(height)
  3327. }
  3328. container.style.zIndex = options.zIndex;
  3329. this._setup(container);
  3330. }
  3331. },
  3332. /**
  3333. * 编辑器初始化
  3334. * @private
  3335. * @ignore
  3336. * @param {Element} doc 编辑器Iframe中的文档对象
  3337. */
  3338. _setup: function (cont) {
  3339. var me = this,
  3340. options = me.options;
  3341. cont.contentEditable = true;
  3342. document.body.spellcheck = false;
  3343. me.document = document;
  3344. me.window = document.defaultView || document.parentWindow;
  3345. me.body = cont;
  3346. me.$body = $(cont);
  3347. me.selection = new dom.Selection(document,me.body);
  3348. me._isEnabled = false;
  3349. //gecko初始化就能得到range,无法判断isFocus了
  3350. var geckoSel;
  3351. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3352. geckoSel.removeAllRanges();
  3353. }
  3354. this._initEvents();
  3355. //为form提交提供一个隐藏的textarea
  3356. for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {
  3357. if (form.tagName == 'FORM') {
  3358. me.form = form;
  3359. if(me.options.autoSyncData){
  3360. $(cont).on('blur',function(){
  3361. setValue(form,me);
  3362. })
  3363. }else{
  3364. $(form).on('submit', function () {
  3365. setValue(this, me);
  3366. })
  3367. }
  3368. break;
  3369. }
  3370. }
  3371. if (options.initialContent) {
  3372. if (options.autoClearinitialContent) {
  3373. var oldExecCommand = me.execCommand;
  3374. me.execCommand = function () {
  3375. me.fireEvent('firstBeforeExecCommand');
  3376. return oldExecCommand.apply(me, arguments);
  3377. };
  3378. this._setDefaultContent(options.initialContent);
  3379. } else
  3380. this.setContent(options.initialContent, false, true);
  3381. }
  3382. //编辑器不能为空内容
  3383. if (domUtils.isEmptyNode(me.body)) {
  3384. me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
  3385. }
  3386. //如果要求focus, 就把光标定位到内容开始
  3387. if (options.focus) {
  3388. setTimeout(function () {
  3389. me.focus(me.options.focusInEnd);
  3390. //如果自动清除开着,就不需要做selectionchange;
  3391. !me.options.autoClearinitialContent && me._selectionChange();
  3392. }, 0);
  3393. }
  3394. if (!me.container) {
  3395. me.container = cont.parentNode;
  3396. }
  3397. me._bindshortcutKeys();
  3398. me.isReady = 1;
  3399. me.fireEvent('ready');
  3400. options.onready && options.onready.call(me);
  3401. if(!browser.ie || browser.ie9above){
  3402. $(me.body).on( 'blur focus', function (e) {
  3403. var nSel = me.selection.getNative();
  3404. //chrome下会出现alt+tab切换时,导致选区位置不对
  3405. if (e.type == 'blur') {
  3406. if(nSel.rangeCount > 0 ){
  3407. me._bakRange = nSel.getRangeAt(0);
  3408. }
  3409. } else {
  3410. try {
  3411. me._bakRange && nSel.addRange(me._bakRange)
  3412. } catch (e) {
  3413. }
  3414. me._bakRange = null;
  3415. }
  3416. });
  3417. }
  3418. !options.isShow && me.setHide();
  3419. options.readonly && me.setDisabled();
  3420. },
  3421. /**
  3422. * 同步编辑器的数据,为提交数据做准备,主要用于你是手动提交的情况
  3423. * @name sync
  3424. * @grammar editor.sync(); //从编辑器的容器向上查找,如果找到就同步数据
  3425. * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
  3426. * @desc
  3427. * 后台取得数据得键值使用你容器上得''name''属性,如果没有就使用参数传入的''textarea''
  3428. * @example
  3429. * editor.sync();
  3430. * form.sumbit(); //form变量已经指向了form元素
  3431. *
  3432. */
  3433. sync: function (formId) {
  3434. var me = this,
  3435. form = formId ? document.getElementById(formId) :
  3436. domUtils.findParent(me.body.parentNode, function (node) {
  3437. return node.tagName == 'FORM'
  3438. }, true);
  3439. form && setValue(form, me);
  3440. },
  3441. /**
  3442. * 设置编辑器高度
  3443. * @name setHeight
  3444. * @grammar editor.setHeight(number); //纯数值,不带单位
  3445. */
  3446. setHeight: function (height,notSetHeight) {
  3447. !notSetHeight && (this.options.initialFrameHeight = height);
  3448. if(this.options.autoHeightEnabled){
  3449. $(this.body).css({
  3450. 'min-height':height + 'px'
  3451. });
  3452. if(browser.ie && browser.version <= 6 && this.container){
  3453. this.container.style.height = height ;
  3454. this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  3455. }
  3456. }else{
  3457. $(this.body).height(height)
  3458. }
  3459. this.fireEvent('resize');
  3460. },
  3461. /**
  3462. * 设置编辑器宽度
  3463. * @name setWidth
  3464. * @grammar editor.setWidth(number); //纯数值,不带单位
  3465. */
  3466. setWidth:function(width){
  3467. this.$container && this.$container.width(width);
  3468. $(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1);
  3469. this.fireEvent('resize');
  3470. },
  3471. addshortcutkey: function (cmd, keys) {
  3472. var obj = {};
  3473. if (keys) {
  3474. obj[cmd] = keys
  3475. } else {
  3476. obj = cmd;
  3477. }
  3478. utils.extend(this.shortcutkeys, obj)
  3479. },
  3480. _bindshortcutKeys: function () {
  3481. var me = this, shortcutkeys = this.shortcutkeys;
  3482. me.addListener('keydown', function (type, e) {
  3483. var keyCode = e.keyCode || e.which;
  3484. for (var i in shortcutkeys) {
  3485. var tmp = shortcutkeys[i].split(',');
  3486. for (var t = 0, ti; ti = tmp[t++];) {
  3487. ti = ti.split(':');
  3488. var key = ti[0], param = ti[1];
  3489. if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
  3490. if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
  3491. && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
  3492. && keyCode == RegExp.$3
  3493. ) ||
  3494. keyCode == RegExp.$1
  3495. ) {
  3496. if (me.queryCommandState(i,param) != -1)
  3497. me.execCommand(i, param);
  3498. domUtils.preventDefault(e);
  3499. }
  3500. }
  3501. }
  3502. }
  3503. });
  3504. },
  3505. /**
  3506. * 获取编辑器内容
  3507. * @name getContent
  3508. * @grammar editor.getContent() => String //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。
  3509. * @grammar editor.getContent(fn) => String
  3510. * @example
  3511. * getContent默认是会现调用hasContents来判断编辑器是否为空,如果是,就直接返回空字符串
  3512. * 你也可以传入一个fn来接替hasContents的工作,定制判断的规则
  3513. * editor.getContent(function(){
  3514. * return false //编辑器没有内容 ,getContent直接返回空
  3515. * })
  3516. */
  3517. getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
  3518. var me = this;
  3519. if (cmd && utils.isFunction(cmd)) {
  3520. fn = cmd;
  3521. cmd = '';
  3522. }
  3523. if (fn ? !fn() : !this.hasContents()) {
  3524. return '';
  3525. }
  3526. me.fireEvent('beforegetcontent');
  3527. var root = UM.htmlparser(me.body.innerHTML,ignoreBlank);
  3528. me.filterOutputRule(root);
  3529. me.fireEvent('aftergetcontent',root);
  3530. return root.toHtml(formatter);
  3531. },
  3532. /**
  3533. * 取得完整的html代码,可以直接显示成完整的html文档
  3534. * @name getAllHtml
  3535. * @grammar editor.getAllHtml() => String
  3536. */
  3537. getAllHtml: function () {
  3538. var me = this,
  3539. headHtml = [],
  3540. html = '';
  3541. me.fireEvent('getAllHtml', headHtml);
  3542. if (browser.ie && browser.version > 8) {
  3543. var headHtmlForIE9 = '';
  3544. utils.each(me.document.styleSheets, function (si) {
  3545. headHtmlForIE9 += ( si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
  3546. });
  3547. utils.each(me.document.getElementsByTagName('script'), function (si) {
  3548. headHtmlForIE9 += si.outerHTML;
  3549. });
  3550. }
  3551. return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
  3552. + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
  3553. + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
  3554. },
  3555. /**
  3556. * 得到编辑器的纯文本内容,但会保留段落格式
  3557. * @name getPlainTxt
  3558. * @grammar editor.getPlainTxt() => String
  3559. */
  3560. getPlainTxt: function () {
  3561. var reg = new RegExp(domUtils.fillChar, 'g'),
  3562. html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
  3563. html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
  3564. .replace(/<br\/?>/gi, '\n')
  3565. .replace(/<[^>/]+>/g, '')
  3566. .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
  3567. return dtd.$block[c] ? '\n' : b ? b : '';
  3568. });
  3569. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3570. return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
  3571. },
  3572. /**
  3573. * 获取编辑器中的纯文本内容,没有段落格式
  3574. * @name getContentTxt
  3575. * @grammar editor.getContentTxt() => String
  3576. */
  3577. getContentTxt: function () {
  3578. var reg = new RegExp(domUtils.fillChar, 'g');
  3579. //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
  3580. return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
  3581. },
  3582. /**
  3583. * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值,则必须放在ready方法内部执行
  3584. * @name setContent
  3585. * @grammar editor.setContent(html)
  3586. * @example
  3587. * var editor = new UM.ui.Editor()
  3588. * editor.ready(function(){
  3589. * //需要ready后执行,否则可能报错
  3590. * editor.setContent("欢迎使用UEditor!");
  3591. * })
  3592. */
  3593. setContent: function (html, isAppendTo, notFireSelectionchange) {
  3594. var me = this;
  3595. me.fireEvent('beforesetcontent', html);
  3596. var root = UM.htmlparser(html);
  3597. me.filterInputRule(root);
  3598. html = root.toHtml();
  3599. me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  3600. function isCdataDiv(node){
  3601. return node.tagName == 'DIV' && node.getAttribute('cdata_tag');
  3602. }
  3603. //给文本或者inline节点套p标签
  3604. if (me.options.enterTag == 'p') {
  3605. var child = this.body.firstChild, tmpNode;
  3606. if (!child || child.nodeType == 1 &&
  3607. (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
  3608. domUtils.isCustomeNode(child)
  3609. )
  3610. && child === this.body.lastChild) {
  3611. this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  3612. } else {
  3613. var p = me.document.createElement('p');
  3614. while (child) {
  3615. while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
  3616. tmpNode = child.nextSibling;
  3617. p.appendChild(child);
  3618. child = tmpNode;
  3619. }
  3620. if (p.firstChild) {
  3621. if (!child) {
  3622. me.body.appendChild(p);
  3623. break;
  3624. } else {
  3625. child.parentNode.insertBefore(p, child);
  3626. p = me.document.createElement('p');
  3627. }
  3628. }
  3629. child = child.nextSibling;
  3630. }
  3631. }
  3632. }
  3633. me.fireEvent('aftersetcontent');
  3634. me.fireEvent('contentchange');
  3635. !notFireSelectionchange && me._selectionChange();
  3636. //清除保存的选区
  3637. me._bakRange = me._bakIERange = me._bakNativeRange = null;
  3638. //trace:1742 setContent后gecko能得到焦点问题
  3639. var geckoSel;
  3640. if (browser.gecko && (geckoSel = this.selection.getNative())) {
  3641. geckoSel.removeAllRanges();
  3642. }
  3643. if(me.options.autoSyncData){
  3644. me.form && setValue(me.form,me);
  3645. }
  3646. },
  3647. /**
  3648. * 让编辑器获得焦点,toEnd确定focus位置
  3649. * @name focus
  3650. * @grammar editor.focus([toEnd]) //默认focus到编辑器头部,toEnd为true时focus到内容尾部
  3651. */
  3652. focus: function (toEnd) {
  3653. try {
  3654. var me = this,
  3655. rng = me.selection.getRange();
  3656. if (toEnd) {
  3657. rng.setStartAtLast(me.body.lastChild).setCursor(false, true);
  3658. } else {
  3659. rng.select(true);
  3660. }
  3661. this.fireEvent('focus');
  3662. } catch (e) {
  3663. }
  3664. },
  3665. /**
  3666. * 使编辑区域失去焦点
  3667. */
  3668. blur:function(){
  3669. var sel = this.selection.getNative();
  3670. sel.empty ? sel.empty() : sel.removeAllRanges();
  3671. this.fireEvent('blur')
  3672. },
  3673. /**
  3674. * 判断编辑器当前是否获得了焦点
  3675. */
  3676. isFocus : function(){
  3677. if(this.fireEvent('isfocus')===true){
  3678. return true;
  3679. }
  3680. return this.selection.isFocus();
  3681. },
  3682. /**
  3683. * 初始化UE事件及部分事件代理
  3684. * @private
  3685. * @ignore
  3686. */
  3687. _initEvents: function () {
  3688. var me = this,
  3689. cont = me.body,
  3690. _proxyDomEvent = function(){
  3691. me._proxyDomEvent.apply(me, arguments);
  3692. };
  3693. $(cont)
  3694. .on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)
  3695. .on( 'focus blur', _proxyDomEvent)
  3696. .on('mouseup keydown', function (evt) {
  3697. //特殊键不触发selectionchange
  3698. if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
  3699. return;
  3700. }
  3701. if (evt.button == 2)return;
  3702. me._selectionChange(250, evt);
  3703. });
  3704. },
  3705. /**
  3706. * 触发事件代理
  3707. * @private
  3708. * @ignore
  3709. */
  3710. _proxyDomEvent: function (evt) {
  3711. return this.fireEvent(evt.type.replace(/^on/, ''), evt);
  3712. },
  3713. /**
  3714. * 变化选区
  3715. * @private
  3716. * @ignore
  3717. */
  3718. _selectionChange: function (delay, evt) {
  3719. var me = this;
  3720. //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
  3721. // if ( !me.selection.isFocus() ){
  3722. // return;
  3723. // }
  3724. var hackForMouseUp = false;
  3725. var mouseX, mouseY;
  3726. if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
  3727. var range = this.selection.getRange();
  3728. if (!range.collapsed) {
  3729. hackForMouseUp = true;
  3730. mouseX = evt.clientX;
  3731. mouseY = evt.clientY;
  3732. }
  3733. }
  3734. clearTimeout(_selectionChangeTimer);
  3735. _selectionChangeTimer = setTimeout(function () {
  3736. if (!me.selection.getNative()) {
  3737. return;
  3738. }
  3739. //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
  3740. //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
  3741. var ieRange;
  3742. if (hackForMouseUp && me.selection.getNative().type == 'None') {
  3743. ieRange = me.document.body.createTextRange();
  3744. try {
  3745. ieRange.moveToPoint(mouseX, mouseY);
  3746. } catch (ex) {
  3747. ieRange = null;
  3748. }
  3749. }
  3750. var bakGetIERange;
  3751. if (ieRange) {
  3752. bakGetIERange = me.selection.getIERange;
  3753. me.selection.getIERange = function () {
  3754. return ieRange;
  3755. };
  3756. }
  3757. me.selection.cache();
  3758. if (bakGetIERange) {
  3759. me.selection.getIERange = bakGetIERange;
  3760. }
  3761. if (me.selection._cachedRange && me.selection._cachedStartElement) {
  3762. me.fireEvent('beforeselectionchange');
  3763. // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
  3764. me.fireEvent('selectionchange', !!evt);
  3765. me.fireEvent('afterselectionchange');
  3766. me.selection.clear();
  3767. }
  3768. }, delay || 50);
  3769. },
  3770. _callCmdFn: function (fnName, args) {
  3771. args = Array.prototype.slice.call(args,0);
  3772. var cmdName = args.shift().toLowerCase(),
  3773. cmd, cmdFn;
  3774. cmd = this.commands[cmdName] || UM.commands[cmdName];
  3775. cmdFn = cmd && cmd[fnName];
  3776. //没有querycommandstate或者没有command的都默认返回0
  3777. if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
  3778. return 0;
  3779. } else if (cmdFn) {
  3780. return cmdFn.apply(this, [cmdName].concat(args));
  3781. }
  3782. },
  3783. /**
  3784. * 执行编辑命令cmdName,完成富文本编辑效果
  3785. * @name execCommand
  3786. * @grammar editor.execCommand(cmdName) => {*}
  3787. */
  3788. execCommand: function (cmdName) {
  3789. if(!this.isFocus()){
  3790. var bakRange = this.selection._bakRange;
  3791. if(bakRange){
  3792. bakRange.select()
  3793. }else{
  3794. this.focus(true)
  3795. }
  3796. }
  3797. cmdName = cmdName.toLowerCase();
  3798. var me = this,
  3799. result,
  3800. cmd = me.commands[cmdName] || UM.commands[cmdName];
  3801. if (!cmd || !cmd.execCommand) {
  3802. return null;
  3803. }
  3804. if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
  3805. me.__hasEnterExecCommand = true;
  3806. if (me.queryCommandState.apply(me,arguments) != -1) {
  3807. me.fireEvent('saveScene');
  3808. me.fireEvent('beforeexeccommand', cmdName);
  3809. result = this._callCmdFn('execCommand', arguments);
  3810. (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
  3811. me.fireEvent('afterexeccommand', cmdName);
  3812. me.fireEvent('saveScene');
  3813. }
  3814. me.__hasEnterExecCommand = false;
  3815. } else {
  3816. result = this._callCmdFn('execCommand', arguments);
  3817. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
  3818. }
  3819. (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
  3820. return result;
  3821. },
  3822. /**
  3823. * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
  3824. * @name queryCommandState
  3825. * @grammar editor.queryCommandState(cmdName) => (-1|0|1)
  3826. * @desc
  3827. * * ''-1'' 当前命令不可用
  3828. * * ''0'' 当前命令可用
  3829. * * ''1'' 当前命令已经执行过了
  3830. */
  3831. queryCommandState: function (cmdName) {
  3832. try{
  3833. return this._callCmdFn('queryCommandState', arguments);
  3834. }catch(e){
  3835. return 0
  3836. }
  3837. },
  3838. /**
  3839. * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
  3840. * @name queryCommandValue
  3841. * @grammar editor.queryCommandValue(cmdName) => {*}
  3842. */
  3843. queryCommandValue: function (cmdName) {
  3844. try{
  3845. return this._callCmdFn('queryCommandValue', arguments);
  3846. }catch(e){
  3847. return null
  3848. }
  3849. },
  3850. /**
  3851. * 检查编辑区域中是否有内容,若包含tags中的节点类型,直接返回true
  3852. * @name hasContents
  3853. * @desc
  3854. * 默认有文本内容,或者有以下节点都不认为是空
  3855. * <code>{table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1}</code>
  3856. * @grammar editor.hasContents() => (true|false)
  3857. * @grammar editor.hasContents(tags) => (true|false) //若文档中包含tags数组里对应的tag,直接返回true
  3858. * @example
  3859. * editor.hasContents(['span']) //如果编辑器里有这些,不认为是空
  3860. */
  3861. hasContents: function (tags) {
  3862. if (tags) {
  3863. for (var i = 0, ci; ci = tags[i++];) {
  3864. if (this.body.getElementsByTagName(ci).length > 0) {
  3865. return true;
  3866. }
  3867. }
  3868. }
  3869. if (!domUtils.isEmptyBlock(this.body)) {
  3870. return true
  3871. }
  3872. //随时添加,定义的特殊标签如果存在,不能认为是空
  3873. tags = ['div'];
  3874. for (i = 0; ci = tags[i++];) {
  3875. var nodes = domUtils.getElementsByTagName(this.body, ci);
  3876. for (var n = 0, cn; cn = nodes[n++];) {
  3877. if (domUtils.isCustomeNode(cn)) {
  3878. return true;
  3879. }
  3880. }
  3881. }
  3882. return false;
  3883. },
  3884. /**
  3885. * 重置编辑器,可用来做多个tab使用同一个编辑器实例
  3886. * @name reset
  3887. * @desc
  3888. * * 清空编辑器内容
  3889. * * 清空回退列表
  3890. * @grammar editor.reset()
  3891. */
  3892. reset: function () {
  3893. this.fireEvent('reset');
  3894. },
  3895. isEnabled: function(){
  3896. return this._isEnabled != true;
  3897. },
  3898. setEnabled: function () {
  3899. var me = this, range;
  3900. me.body.contentEditable = true;
  3901. /* 恢复选区 */
  3902. if (me.lastBk) {
  3903. range = me.selection.getRange();
  3904. try {
  3905. range.moveToBookmark(me.lastBk);
  3906. delete me.lastBk
  3907. } catch (e) {
  3908. range.setStartAtFirst(me.body).collapse(true)
  3909. }
  3910. range.select(true);
  3911. }
  3912. /* 恢复query函数 */
  3913. if (me.bkqueryCommandState) {
  3914. me.queryCommandState = me.bkqueryCommandState;
  3915. delete me.bkqueryCommandState;
  3916. }
  3917. /* 恢复原生事件 */
  3918. if (me._bkproxyDomEvent) {
  3919. me._proxyDomEvent = me._bkproxyDomEvent;
  3920. delete me._bkproxyDomEvent;
  3921. }
  3922. /* 触发事件 */
  3923. me.fireEvent('setEnabled');
  3924. },
  3925. /**
  3926. * 设置当前编辑区域可以编辑
  3927. * @name enable
  3928. * @grammar editor.enable()
  3929. */
  3930. enable: function () {
  3931. return this.setEnabled();
  3932. },
  3933. setDisabled: function (except, keepDomEvent) {
  3934. var me = this;
  3935. me.body.contentEditable = false;
  3936. me._except = except ? utils.isArray(except) ? except : [except] : [];
  3937. /* 备份最后的选区 */
  3938. if (!me.lastBk) {
  3939. me.lastBk = me.selection.getRange().createBookmark(true);
  3940. }
  3941. /* 备份并重置query函数 */
  3942. if(!me.bkqueryCommandState) {
  3943. me.bkqueryCommandState = me.queryCommandState;
  3944. me.queryCommandState = function (type) {
  3945. if (utils.indexOf(me._except, type) != -1) {
  3946. return me.bkqueryCommandState.apply(me, arguments);
  3947. }
  3948. return -1;
  3949. };
  3950. }
  3951. /* 备份并墙原生事件 */
  3952. if(!keepDomEvent && !me._bkproxyDomEvent) {
  3953. me._bkproxyDomEvent = me._proxyDomEvent;
  3954. me._proxyDomEvent = function () {
  3955. return false;
  3956. };
  3957. }
  3958. /* 触发事件 */
  3959. me.fireEvent('selectionchange');
  3960. me.fireEvent('setDisabled', me._except);
  3961. },
  3962. /** 设置当前编辑区域不可编辑,except中的命令除外
  3963. * @name disable
  3964. * @grammar editor.disable()
  3965. * @grammar editor.disable(except) //例外的命令,也即即使设置了disable,此处配置的命令仍然可以执行
  3966. * @example
  3967. * //禁用工具栏中除加粗和插入图片之外的所有功能
  3968. * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array
  3969. */
  3970. disable: function (except) {
  3971. return this.setDisabled(except);
  3972. },
  3973. /**
  3974. * 设置默认内容
  3975. * @ignore
  3976. * @private
  3977. * @param {String} cont 要存入的内容
  3978. */
  3979. _setDefaultContent: function () {
  3980. function clear() {
  3981. var me = this;
  3982. if (me.document.getElementById('initContent')) {
  3983. me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
  3984. me.removeListener('firstBeforeExecCommand focus', clear);
  3985. setTimeout(function () {
  3986. me.focus();
  3987. me._selectionChange();
  3988. }, 0)
  3989. }
  3990. }
  3991. return function (cont) {
  3992. var me = this;
  3993. me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  3994. me.addListener('firstBeforeExecCommand focus', clear);
  3995. }
  3996. }(),
  3997. /**
  3998. * show方法的兼容版本
  3999. * @private
  4000. * @ignore
  4001. */
  4002. setShow: function () {
  4003. var me = this, range = me.selection.getRange();
  4004. if (me.container.style.display == 'none') {
  4005. //有可能内容丢失了
  4006. try {
  4007. range.moveToBookmark(me.lastBk);
  4008. delete me.lastBk
  4009. } catch (e) {
  4010. range.setStartAtFirst(me.body).collapse(true)
  4011. }
  4012. //ie下focus实效,所以做了个延迟
  4013. setTimeout(function () {
  4014. range.select(true);
  4015. }, 100);
  4016. me.container.style.display = '';
  4017. }
  4018. },
  4019. /**
  4020. * 显示编辑器
  4021. * @name show
  4022. * @grammar editor.show()
  4023. */
  4024. show: function () {
  4025. return this.setShow();
  4026. },
  4027. /**
  4028. * hide方法的兼容版本
  4029. * @private
  4030. * @ignore
  4031. */
  4032. setHide: function () {
  4033. var me = this;
  4034. if (!me.lastBk) {
  4035. me.lastBk = me.selection.getRange().createBookmark(true);
  4036. }
  4037. me.container.style.display = 'none'
  4038. },
  4039. /**
  4040. * 隐藏编辑器
  4041. * @name hide
  4042. * @grammar editor.hide()
  4043. */
  4044. hide: function () {
  4045. return this.setHide();
  4046. },
  4047. /**
  4048. * 根据制定的路径,获取对应的语言资源
  4049. * @name getLang
  4050. * @grammar editor.getLang(path) => (JSON|String) 路径根据的是lang目录下的语言文件的路径结构
  4051. * @example
  4052. * editor.getLang('contextMenu.delete') //如果当前是中文,那返回是的是删除
  4053. */
  4054. getLang: function (path) {
  4055. var lang = UM.I18N[this.options.lang];
  4056. if (!lang) {
  4057. throw Error("not import language file");
  4058. }
  4059. path = (path || "").split(".");
  4060. for (var i = 0, ci; ci = path[i++];) {
  4061. lang = lang[ci];
  4062. if (!lang)break;
  4063. }
  4064. return lang;
  4065. },
  4066. /**
  4067. * 计算编辑器当前内容的长度
  4068. * @name getContentLength
  4069. * @grammar editor.getContentLength(ingoneHtml,tagNames) =>
  4070. * @example
  4071. * editor.getLang(true)
  4072. */
  4073. getContentLength: function (ingoneHtml, tagNames) {
  4074. var count = this.getContent(false,false,true).length;
  4075. if (ingoneHtml) {
  4076. tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
  4077. count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
  4078. for (var i = 0, ci; ci = tagNames[i++];) {
  4079. count += this.body.getElementsByTagName(ci).length;
  4080. }
  4081. }
  4082. return count;
  4083. },
  4084. addInputRule: function (rule,ignoreUndo) {
  4085. rule.ignoreUndo = ignoreUndo;
  4086. this.inputRules.push(rule);
  4087. },
  4088. filterInputRule: function (root,isUndoLoad) {
  4089. for (var i = 0, ci; ci = this.inputRules[i++];) {
  4090. if(isUndoLoad && ci.ignoreUndo){
  4091. continue;
  4092. }
  4093. ci.call(this, root)
  4094. }
  4095. },
  4096. addOutputRule: function (rule,ignoreUndo) {
  4097. rule.ignoreUndo = ignoreUndo;
  4098. this.outputRules.push(rule)
  4099. },
  4100. filterOutputRule: function (root,isUndoLoad) {
  4101. for (var i = 0, ci; ci = this.outputRules[i++];) {
  4102. if(isUndoLoad && ci.ignoreUndo){
  4103. continue;
  4104. }
  4105. ci.call(this, root)
  4106. }
  4107. }
  4108. };
  4109. utils.inherits(Editor, EventBase);
  4110. })();
  4111. /**
  4112. * @file
  4113. * @name UM.filterWord
  4114. * @short filterWord
  4115. * @desc 用来过滤word粘贴过来的字符串
  4116. * @import editor.js,core/utils.js
  4117. * @anthor zhanyi
  4118. */
  4119. var filterWord = UM.filterWord = function () {
  4120. //是否是word过来的内容
  4121. function isWordDocument( str ) {
  4122. return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
  4123. }
  4124. //去掉小数
  4125. function transUnit( v ) {
  4126. v = v.replace( /[\d.]+\w+/g, function ( m ) {
  4127. return utils.transUnitToPx(m);
  4128. } );
  4129. return v;
  4130. }
  4131. function filterPasteWord( str ) {
  4132. return str.replace(/[\t\r\n]+/g,' ')
  4133. .replace( /<!--[\s\S]*?-->/ig, "" )
  4134. //转换图片
  4135. .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
  4136. //opera能自己解析出image所这里直接返回空
  4137. if(browser.opera){
  4138. return '';
  4139. }
  4140. try{
  4141. //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
  4142. if(/Bitmap/i.test(str)){
  4143. return '';
  4144. }
  4145. var width = str.match(/width:([ \d.]*p[tx])/i)[1],
  4146. height = str.match(/height:([ \d.]*p[tx])/i)[1],
  4147. src = str.match(/src=\s*"([^"]*)"/i)[1];
  4148. return '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
  4149. } catch(e){
  4150. return '';
  4151. }
  4152. })
  4153. //针对wps添加的多余标签处理
  4154. .replace(/<\/?div[^>]*>/g,'')
  4155. //去掉多余的属性
  4156. .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
  4157. .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
  4158. .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
  4159. //去掉多余的属性
  4160. .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
  4161. //保留list的标示
  4162. return name == 'class' && val == 'MsoListParagraph' ? str : ''
  4163. })
  4164. //清除多余的font/span不能匹配&nbsp;有可能是空格
  4165. .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
  4166. return c.replace(/[\t\r\n ]+/g,' ')
  4167. })
  4168. //处理style的问题
  4169. .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
  4170. var n = [],
  4171. s = style.replace( /^\s+|\s+$/, '' )
  4172. .replace(/&#39;/g,'\'')
  4173. .replace( /&quot;/gi, "'" )
  4174. .split( /;\s*/g );
  4175. for ( var i = 0,v; v = s[i];i++ ) {
  4176. var name, value,
  4177. parts = v.split( ":" );
  4178. if ( parts.length == 2 ) {
  4179. name = parts[0].toLowerCase();
  4180. value = parts[1].toLowerCase();
  4181. if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
  4182. ||
  4183. /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
  4184. ){
  4185. continue;
  4186. }
  4187. switch ( name ) {
  4188. case "mso-padding-alt":
  4189. case "mso-padding-top-alt":
  4190. case "mso-padding-right-alt":
  4191. case "mso-padding-bottom-alt":
  4192. case "mso-padding-left-alt":
  4193. case "mso-margin-alt":
  4194. case "mso-margin-top-alt":
  4195. case "mso-margin-right-alt":
  4196. case "mso-margin-bottom-alt":
  4197. case "mso-margin-left-alt":
  4198. //ie下会出现挤到一起的情况
  4199. //case "mso-table-layout-alt":
  4200. case "mso-height":
  4201. case "mso-width":
  4202. case "mso-vertical-align-alt":
  4203. //trace:1819 ff下会解析出padding在table上
  4204. if(!/<table/.test(tag))
  4205. n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
  4206. continue;
  4207. case "horiz-align":
  4208. n[i] = "text-align:" + value;
  4209. continue;
  4210. case "vert-align":
  4211. n[i] = "vertical-align:" + value;
  4212. continue;
  4213. case "font-color":
  4214. case "mso-foreground":
  4215. n[i] = "color:" + value;
  4216. continue;
  4217. case "mso-background":
  4218. case "mso-highlight":
  4219. n[i] = "background:" + value;
  4220. continue;
  4221. case "mso-default-height":
  4222. n[i] = "min-height:" + transUnit( value );
  4223. continue;
  4224. case "mso-default-width":
  4225. n[i] = "min-width:" + transUnit( value );
  4226. continue;
  4227. case "mso-padding-between-alt":
  4228. n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
  4229. continue;
  4230. case "text-line-through":
  4231. if ( (value == "single") || (value == "double") ) {
  4232. n[i] = "text-decoration:line-through";
  4233. }
  4234. continue;
  4235. case "mso-zero-height":
  4236. if ( value == "yes" ) {
  4237. n[i] = "display:none";
  4238. }
  4239. continue;
  4240. // case 'background':
  4241. // break;
  4242. case 'margin':
  4243. if ( !/[1-9]/.test( value ) ) {
  4244. continue;
  4245. }
  4246. }
  4247. if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name )
  4248. ||
  4249. /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
  4250. ) {
  4251. continue;
  4252. }
  4253. n[i] = name + ":" + parts[1];
  4254. }
  4255. }
  4256. return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
  4257. })
  4258. .replace(/[\d.]+(cm|pt)/g,function(str){
  4259. return utils.transUnitToPx(str)
  4260. })
  4261. }
  4262. return function ( html ) {
  4263. return (isWordDocument( html ) ? filterPasteWord( html ) : html);
  4264. };
  4265. }();
  4266. ///import editor.js
  4267. ///import core/utils.js
  4268. ///import core/dom/dom.js
  4269. ///import core/dom/dtd.js
  4270. ///import core/htmlparser.js
  4271. //模拟的节点类
  4272. //by zhanyi
  4273. (function () {
  4274. var uNode = UM.uNode = function (obj) {
  4275. this.type = obj.type;
  4276. this.data = obj.data;
  4277. this.tagName = obj.tagName;
  4278. this.parentNode = obj.parentNode;
  4279. this.attrs = obj.attrs || {};
  4280. this.children = obj.children;
  4281. };
  4282. var notTransAttrs = {
  4283. 'href':1,
  4284. 'src':1,
  4285. '_src':1,
  4286. '_href':1,
  4287. 'cdata_data':1
  4288. };
  4289. var notTransTagName = {
  4290. style:1,
  4291. script:1
  4292. };
  4293. var indentChar = ' ',
  4294. breakChar = '\n';
  4295. function insertLine(arr, current, begin) {
  4296. arr.push(breakChar);
  4297. return current + (begin ? 1 : -1);
  4298. }
  4299. function insertIndent(arr, current) {
  4300. //插入缩进
  4301. for (var i = 0; i < current; i++) {
  4302. arr.push(indentChar);
  4303. }
  4304. }
  4305. //创建uNode的静态方法
  4306. //支持标签和html
  4307. uNode.createElement = function (html) {
  4308. if (/[<>]/.test(html)) {
  4309. return UM.htmlparser(html).children[0]
  4310. } else {
  4311. return new uNode({
  4312. type:'element',
  4313. children:[],
  4314. tagName:html
  4315. })
  4316. }
  4317. };
  4318. uNode.createText = function (data,noTrans) {
  4319. return new UM.uNode({
  4320. type:'text',
  4321. 'data':noTrans ? data : utils.unhtml(data || '')
  4322. })
  4323. };
  4324. function nodeToHtml(node, arr, formatter, current) {
  4325. switch (node.type) {
  4326. case 'root':
  4327. for (var i = 0, ci; ci = node.children[i++];) {
  4328. //插入新行
  4329. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4330. insertLine(arr, current, true);
  4331. insertIndent(arr, current)
  4332. }
  4333. nodeToHtml(ci, arr, formatter, current)
  4334. }
  4335. break;
  4336. case 'text':
  4337. isText(node, arr);
  4338. break;
  4339. case 'element':
  4340. isElement(node, arr, formatter, current);
  4341. break;
  4342. case 'comment':
  4343. isComment(node, arr, formatter);
  4344. }
  4345. return arr;
  4346. }
  4347. function isText(node, arr) {
  4348. if(node.parentNode.tagName == 'pre'){
  4349. //源码模式下输入html标签,不能做转换处理,直接输出
  4350. arr.push(node.data)
  4351. }else{
  4352. arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' &nbsp;'))
  4353. }
  4354. }
  4355. function isElement(node, arr, formatter, current) {
  4356. var attrhtml = '';
  4357. if (node.attrs) {
  4358. attrhtml = [];
  4359. var attrs = node.attrs;
  4360. for (var a in attrs) {
  4361. //这里就针对
  4362. //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
  4363. //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
  4364. //有可能做的不够
  4365. attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
  4366. return '&quot;'
  4367. }) : utils.unhtml(attrs[a])) + '"' : ''))
  4368. }
  4369. attrhtml = attrhtml.join(' ');
  4370. }
  4371. arr.push('<' + node.tagName +
  4372. (attrhtml ? ' ' + attrhtml : '') +
  4373. (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
  4374. );
  4375. //插入新行
  4376. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4377. if(node.children && node.children.length){
  4378. current = insertLine(arr, current, true);
  4379. insertIndent(arr, current)
  4380. }
  4381. }
  4382. if (node.children && node.children.length) {
  4383. for (var i = 0, ci; ci = node.children[i++];) {
  4384. if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
  4385. insertLine(arr, current);
  4386. insertIndent(arr, current)
  4387. }
  4388. nodeToHtml(ci, arr, formatter, current)
  4389. }
  4390. }
  4391. if (!dtd.$empty[node.tagName]) {
  4392. if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
  4393. if(node.children && node.children.length){
  4394. current = insertLine(arr, current);
  4395. insertIndent(arr, current)
  4396. }
  4397. }
  4398. arr.push('<\/' + node.tagName + '>');
  4399. }
  4400. }
  4401. function isComment(node, arr) {
  4402. arr.push('<!--' + node.data + '-->');
  4403. }
  4404. function getNodeById(root, id) {
  4405. var node;
  4406. if (root.type == 'element' && root.getAttr('id') == id) {
  4407. return root;
  4408. }
  4409. if (root.children && root.children.length) {
  4410. for (var i = 0, ci; ci = root.children[i++];) {
  4411. if (node = getNodeById(ci, id)) {
  4412. return node;
  4413. }
  4414. }
  4415. }
  4416. }
  4417. function getNodesByTagName(node, tagName, arr) {
  4418. if (node.type == 'element' && node.tagName == tagName) {
  4419. arr.push(node);
  4420. }
  4421. if (node.children && node.children.length) {
  4422. for (var i = 0, ci; ci = node.children[i++];) {
  4423. getNodesByTagName(ci, tagName, arr)
  4424. }
  4425. }
  4426. }
  4427. function nodeTraversal(root,fn){
  4428. if(root.children && root.children.length){
  4429. for(var i= 0,ci;ci=root.children[i];){
  4430. nodeTraversal(ci,fn);
  4431. //ci被替换的情况,这里就不再走 fn了
  4432. if(ci.parentNode ){
  4433. if(ci.children && ci.children.length){
  4434. fn(ci)
  4435. }
  4436. if(ci.parentNode) i++
  4437. }
  4438. }
  4439. }else{
  4440. fn(root)
  4441. }
  4442. }
  4443. uNode.prototype = {
  4444. /**
  4445. * 当前节点对象,转换成html文本
  4446. * @method toHtml
  4447. * @return { String } 返回转换后的html字符串
  4448. * @example
  4449. * ```javascript
  4450. * node.toHtml();
  4451. * ```
  4452. */
  4453. /**
  4454. * 当前节点对象,转换成html文本
  4455. * @method toHtml
  4456. * @param { Boolean } formatter 是否格式化返回值
  4457. * @return { String } 返回转换后的html字符串
  4458. * @example
  4459. * ```javascript
  4460. * node.toHtml( true );
  4461. * ```
  4462. */
  4463. toHtml:function (formatter) {
  4464. var arr = [];
  4465. nodeToHtml(this, arr, formatter, 0);
  4466. return arr.join('')
  4467. },
  4468. /**
  4469. * 获取节点的html内容
  4470. * @method innerHTML
  4471. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4472. * @return { String } 返回节点的html内容
  4473. * @example
  4474. * ```javascript
  4475. * var htmlstr = node.innerHTML();
  4476. * ```
  4477. */
  4478. /**
  4479. * 设置节点的html内容
  4480. * @method innerHTML
  4481. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4482. * @param { String } htmlstr 传入要设置的html内容
  4483. * @return { UM.uNode } 返回节点本身
  4484. * @example
  4485. * ```javascript
  4486. * node.innerHTML('<span>text</span>');
  4487. * ```
  4488. */
  4489. innerHTML:function (htmlstr) {
  4490. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4491. return this;
  4492. }
  4493. if (utils.isString(htmlstr)) {
  4494. if(this.children){
  4495. for (var i = 0, ci; ci = this.children[i++];) {
  4496. ci.parentNode = null;
  4497. }
  4498. }
  4499. this.children = [];
  4500. var tmpRoot = UM.htmlparser(htmlstr);
  4501. for (var i = 0, ci; ci = tmpRoot.children[i++];) {
  4502. this.children.push(ci);
  4503. ci.parentNode = this;
  4504. }
  4505. return this;
  4506. } else {
  4507. var tmpRoot = new UM.uNode({
  4508. type:'root',
  4509. children:this.children
  4510. });
  4511. return tmpRoot.toHtml();
  4512. }
  4513. },
  4514. /**
  4515. * 获取节点的纯文本内容
  4516. * @method innerText
  4517. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4518. * @return { String } 返回节点的存文本内容
  4519. * @example
  4520. * ```javascript
  4521. * var textStr = node.innerText();
  4522. * ```
  4523. */
  4524. /**
  4525. * 设置节点的纯文本内容
  4526. * @method innerText
  4527. * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
  4528. * @param { String } textStr 传入要设置的文本内容
  4529. * @return { UM.uNode } 返回节点本身
  4530. * @example
  4531. * ```javascript
  4532. * node.innerText('<span>text</span>');
  4533. * ```
  4534. */
  4535. innerText:function (textStr,noTrans) {
  4536. if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4537. return this;
  4538. }
  4539. if (textStr) {
  4540. if(this.children){
  4541. for (var i = 0, ci; ci = this.children[i++];) {
  4542. ci.parentNode = null;
  4543. }
  4544. }
  4545. this.children = [];
  4546. this.appendChild(uNode.createText(textStr,noTrans));
  4547. return this;
  4548. } else {
  4549. return this.toHtml().replace(/<[^>]+>/g, '');
  4550. }
  4551. },
  4552. /**
  4553. * 获取当前对象的data属性
  4554. * @method getData
  4555. * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
  4556. * @example
  4557. * ```javascript
  4558. * node.getData();
  4559. * ```
  4560. */
  4561. getData:function () {
  4562. if (this.type == 'element')
  4563. return '';
  4564. return this.data
  4565. },
  4566. /**
  4567. * 获取当前节点下的第一个子节点
  4568. * @method firstChild
  4569. * @return { UM.uNode } 返回第一个子节点
  4570. * @example
  4571. * ```javascript
  4572. * node.firstChild(); //返回第一个子节点
  4573. * ```
  4574. */
  4575. firstChild:function () {
  4576. // if (this.type != 'element' || dtd.$empty[this.tagName]) {
  4577. // return this;
  4578. // }
  4579. return this.children ? this.children[0] : null;
  4580. },
  4581. /**
  4582. * 获取当前节点下的最后一个子节点
  4583. * @method lastChild
  4584. * @return { UM.uNode } 返回最后一个子节点
  4585. * @example
  4586. * ```javascript
  4587. * node.lastChild(); //返回最后一个子节点
  4588. * ```
  4589. */
  4590. lastChild:function () {
  4591. // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  4592. // return this;
  4593. // }
  4594. return this.children ? this.children[this.children.length - 1] : null;
  4595. },
  4596. /**
  4597. * 获取和当前节点有相同父亲节点的前一个节点
  4598. * @method previousSibling
  4599. * @return { UM.uNode } 返回前一个节点
  4600. * @example
  4601. * ```javascript
  4602. * node.children[2].previousSibling(); //返回子节点node.children[1]
  4603. * ```
  4604. */
  4605. previousSibling : function(){
  4606. var parent = this.parentNode;
  4607. for (var i = 0, ci; ci = parent.children[i]; i++) {
  4608. if (ci === this) {
  4609. return i == 0 ? null : parent.children[i-1];
  4610. }
  4611. }
  4612. },
  4613. /**
  4614. * 获取和当前节点有相同父亲节点的后一个节点
  4615. * @method nextSibling
  4616. * @return { UM.uNode } 返回后一个节点,找不到返回null
  4617. * @example
  4618. * ```javascript
  4619. * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
  4620. * ```
  4621. */
  4622. nextSibling : function(){
  4623. var parent = this.parentNode;
  4624. for (var i = 0, ci; ci = parent.children[i++];) {
  4625. if (ci === this) {
  4626. return parent.children[i];
  4627. }
  4628. }
  4629. },
  4630. /**
  4631. * 用新的节点替换当前节点
  4632. * @method replaceChild
  4633. * @param { UM.uNode } target 要替换成该节点参数
  4634. * @param { UM.uNode } source 要被替换掉的节点
  4635. * @return { UM.uNode } 返回替换之后的节点对象
  4636. * @example
  4637. * ```javascript
  4638. * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
  4639. * ```
  4640. */
  4641. replaceChild:function (target, source) {
  4642. if (this.children) {
  4643. if(target.parentNode){
  4644. target.parentNode.removeChild(target);
  4645. }
  4646. for (var i = 0, ci; ci = this.children[i]; i++) {
  4647. if (ci === source) {
  4648. this.children.splice(i, 1, target);
  4649. source.parentNode = null;
  4650. target.parentNode = this;
  4651. return target;
  4652. }
  4653. }
  4654. }
  4655. },
  4656. /**
  4657. * 在节点的子节点列表最后位置插入一个节点
  4658. * @method appendChild
  4659. * @param { UM.uNode } node 要插入的节点
  4660. * @return { UM.uNode } 返回刚插入的子节点
  4661. * @example
  4662. * ```javascript
  4663. * node.appendChild( newNode ); //在node内插入子节点newNode
  4664. * ```
  4665. */
  4666. appendChild:function (node) {
  4667. if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
  4668. if (!this.children) {
  4669. this.children = []
  4670. }
  4671. if(node.parentNode){
  4672. node.parentNode.removeChild(node);
  4673. }
  4674. for (var i = 0, ci; ci = this.children[i]; i++) {
  4675. if (ci === node) {
  4676. this.children.splice(i, 1);
  4677. break;
  4678. }
  4679. }
  4680. this.children.push(node);
  4681. node.parentNode = this;
  4682. return node;
  4683. }
  4684. },
  4685. /**
  4686. * 在传入节点的前面插入一个节点
  4687. * @method insertBefore
  4688. * @param { UM.uNode } target 要插入的节点
  4689. * @param { UM.uNode } source 在该参数节点前面插入
  4690. * @return { UM.uNode } 返回刚插入的子节点
  4691. * @example
  4692. * ```javascript
  4693. * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
  4694. * ```
  4695. */
  4696. insertBefore:function (target, source) {
  4697. if (this.children) {
  4698. if(target.parentNode){
  4699. target.parentNode.removeChild(target);
  4700. }
  4701. for (var i = 0, ci; ci = this.children[i]; i++) {
  4702. if (ci === source) {
  4703. this.children.splice(i, 0, target);
  4704. target.parentNode = this;
  4705. return target;
  4706. }
  4707. }
  4708. }
  4709. },
  4710. /**
  4711. * 在传入节点的后面插入一个节点
  4712. * @method insertAfter
  4713. * @param { UM.uNode } target 要插入的节点
  4714. * @param { UM.uNode } source 在该参数节点后面插入
  4715. * @return { UM.uNode } 返回刚插入的子节点
  4716. * @example
  4717. * ```javascript
  4718. * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
  4719. * ```
  4720. */
  4721. insertAfter:function (target, source) {
  4722. if (this.children) {
  4723. if(target.parentNode){
  4724. target.parentNode.removeChild(target);
  4725. }
  4726. for (var i = 0, ci; ci = this.children[i]; i++) {
  4727. if (ci === source) {
  4728. this.children.splice(i + 1, 0, target);
  4729. target.parentNode = this;
  4730. return target;
  4731. }
  4732. }
  4733. }
  4734. },
  4735. /**
  4736. * 从当前节点的子节点列表中,移除节点
  4737. * @method removeChild
  4738. * @param { UM.uNode } node 要移除的节点引用
  4739. * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
  4740. * @return { * } 返回刚移除的子节点
  4741. * @example
  4742. * ```javascript
  4743. * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
  4744. * ```
  4745. */
  4746. removeChild:function (node,keepChildren) {
  4747. if (this.children) {
  4748. for (var i = 0, ci; ci = this.children[i]; i++) {
  4749. if (ci === node) {
  4750. this.children.splice(i, 1);
  4751. ci.parentNode = null;
  4752. if(keepChildren && ci.children && ci.children.length){
  4753. for(var j= 0,cj;cj=ci.children[j];j++){
  4754. this.children.splice(i+j,0,cj);
  4755. cj.parentNode = this;
  4756. }
  4757. }
  4758. return ci;
  4759. }
  4760. }
  4761. }
  4762. },
  4763. /**
  4764. * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
  4765. * @method getAttr
  4766. * @param { String } attrName 要获取的属性名称
  4767. * @return { * } 返回attrs对象下的属性值
  4768. * @example
  4769. * ```javascript
  4770. * node.getAttr('title');
  4771. * ```
  4772. */
  4773. getAttr:function (attrName) {
  4774. return this.attrs && this.attrs[attrName.toLowerCase()]
  4775. },
  4776. /**
  4777. * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
  4778. * @method setAttr
  4779. * @param { String } attrName 要设置的属性名称
  4780. * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
  4781. * @return { * } 返回attrs对象下的属性值
  4782. * @example
  4783. * ```javascript
  4784. * node.setAttr('title','标题');
  4785. * ```
  4786. */
  4787. setAttr:function (attrName, attrVal) {
  4788. if (!attrName) {
  4789. delete this.attrs;
  4790. return;
  4791. }
  4792. if(!this.attrs){
  4793. this.attrs = {};
  4794. }
  4795. if (utils.isObject(attrName)) {
  4796. for (var a in attrName) {
  4797. if (!attrName[a]) {
  4798. delete this.attrs[a]
  4799. } else {
  4800. this.attrs[a.toLowerCase()] = attrName[a];
  4801. }
  4802. }
  4803. } else {
  4804. if (!attrVal) {
  4805. delete this.attrs[attrName]
  4806. } else {
  4807. this.attrs[attrName.toLowerCase()] = attrVal;
  4808. }
  4809. }
  4810. },
  4811. hasAttr: function( attrName ){
  4812. var attrVal = this.getAttr( attrName );
  4813. return ( attrVal !== null ) && ( attrVal !== undefined );
  4814. },
  4815. /**
  4816. * 获取当前节点在父节点下的位置索引
  4817. * @method getIndex
  4818. * @return { Number } 返回索引数值,如果没有父节点,返回-1
  4819. * @example
  4820. * ```javascript
  4821. * node.getIndex();
  4822. * ```
  4823. */
  4824. getIndex:function(){
  4825. var parent = this.parentNode;
  4826. for(var i= 0,ci;ci=parent.children[i];i++){
  4827. if(ci === this){
  4828. return i;
  4829. }
  4830. }
  4831. return -1;
  4832. },
  4833. /**
  4834. * 在当前节点下,根据id查找节点
  4835. * @method getNodeById
  4836. * @param { String } id 要查找的id
  4837. * @return { UM.uNode } 返回找到的节点
  4838. * @example
  4839. * ```javascript
  4840. * node.getNodeById('textId');
  4841. * ```
  4842. */
  4843. getNodeById:function (id) {
  4844. var node;
  4845. if (this.children && this.children.length) {
  4846. for (var i = 0, ci; ci = this.children[i++];) {
  4847. if (node = getNodeById(ci, id)) {
  4848. return node;
  4849. }
  4850. }
  4851. }
  4852. },
  4853. /**
  4854. * 在当前节点下,根据元素名称查找节点列表
  4855. * @method getNodesByTagName
  4856. * @param { String } tagNames 要查找的元素名称
  4857. * @return { Array } 返回找到的节点列表
  4858. * @example
  4859. * ```javascript
  4860. * node.getNodesByTagName('span');
  4861. * ```
  4862. */
  4863. getNodesByTagName:function (tagNames) {
  4864. tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
  4865. var arr = [], me = this;
  4866. utils.each(tagNames, function (tagName) {
  4867. if (me.children && me.children.length) {
  4868. for (var i = 0, ci; ci = me.children[i++];) {
  4869. getNodesByTagName(ci, tagName, arr)
  4870. }
  4871. }
  4872. });
  4873. return arr;
  4874. },
  4875. /**
  4876. * 根据样式名称,获取节点的样式值
  4877. * @method getStyle
  4878. * @param { String } name 要获取的样式名称
  4879. * @return { String } 返回样式值
  4880. * @example
  4881. * ```javascript
  4882. * node.getStyle('font-size');
  4883. * ```
  4884. */
  4885. getStyle:function (name) {
  4886. var cssStyle = this.getAttr('style');
  4887. if (!cssStyle) {
  4888. return ''
  4889. }
  4890. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
  4891. var match = cssStyle.match(reg);
  4892. if (match && match[0]) {
  4893. return match[2]
  4894. }
  4895. return '';
  4896. },
  4897. /**
  4898. * 给节点设置样式
  4899. * @method setStyle
  4900. * @param { String } name 要设置的的样式名称
  4901. * @param { String } val 要设置的的样值
  4902. * @example
  4903. * ```javascript
  4904. * node.setStyle('font-size', '12px');
  4905. * ```
  4906. */
  4907. setStyle:function (name, val) {
  4908. function exec(name, val) {
  4909. var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
  4910. cssStyle = cssStyle.replace(reg, '$1');
  4911. if (val) {
  4912. cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
  4913. }
  4914. }
  4915. var cssStyle = this.getAttr('style');
  4916. if (!cssStyle) {
  4917. cssStyle = '';
  4918. }
  4919. if (utils.isObject(name)) {
  4920. for (var a in name) {
  4921. exec(a, name[a])
  4922. }
  4923. } else {
  4924. exec(name, val)
  4925. }
  4926. this.setAttr('style', utils.trim(cssStyle))
  4927. },
  4928. hasClass: function( className ){
  4929. if( this.hasAttr('class') ) {
  4930. var classNames = this.getAttr('class').split(/\s+/),
  4931. hasClass = false;
  4932. $.each(classNames, function(key, item){
  4933. if( item === className ) {
  4934. hasClass = true;
  4935. }
  4936. });
  4937. return hasClass;
  4938. } else {
  4939. return false;
  4940. }
  4941. },
  4942. addClass: function( className ){
  4943. var classes = null,
  4944. hasClass = false;
  4945. if( this.hasAttr('class') ) {
  4946. classes = this.getAttr('class');
  4947. classes = classes.split(/\s+/);
  4948. classes.forEach( function( item ){
  4949. if( item===className ) {
  4950. hasClass = true;
  4951. return;
  4952. }
  4953. } );
  4954. !hasClass && classes.push( className );
  4955. this.setAttr('class', classes.join(" "));
  4956. } else {
  4957. this.setAttr('class', className);
  4958. }
  4959. },
  4960. removeClass: function( className ){
  4961. if( this.hasAttr('class') ) {
  4962. var cl = this.getAttr('class');
  4963. cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),'');
  4964. this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' '));
  4965. }
  4966. },
  4967. /**
  4968. * 传入一个函数,递归遍历当前节点下的所有节点
  4969. * @method traversal
  4970. * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
  4971. * @example
  4972. * ```javascript
  4973. * traversal(node, function(){
  4974. * console.log(node.type);
  4975. * });
  4976. * ```
  4977. */
  4978. traversal:function(fn){
  4979. if(this.children && this.children.length){
  4980. nodeTraversal(this,fn);
  4981. }
  4982. return this;
  4983. }
  4984. }
  4985. })();
  4986. //html字符串转换成uNode节点
  4987. //by zhanyi
  4988. var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) {
  4989. //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
  4990. //先去掉了,加上的原因忘了,这里先记录
  4991. var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
  4992. re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  4993. //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
  4994. var allowEmptyTags = {
  4995. b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
  4996. sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
  4997. };
  4998. htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
  4999. if(!ignoreBlank){
  5000. htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
  5001. //br暂时单独处理
  5002. if(b && allowEmptyTags[b.toLowerCase()]){
  5003. return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
  5004. }
  5005. return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
  5006. });
  5007. }
  5008. var notTransAttrs = {
  5009. 'href':1,
  5010. 'src':1
  5011. };
  5012. var uNode = UM.uNode,
  5013. needParentNode = {
  5014. 'td':'tr',
  5015. 'tr':['tbody','thead','tfoot'],
  5016. 'tbody':'table',
  5017. 'th':'tr',
  5018. 'thead':'table',
  5019. 'tfoot':'table',
  5020. 'caption':'table',
  5021. 'li':['ul', 'ol'],
  5022. 'dt':'dl',
  5023. 'dd':'dl',
  5024. 'option':'select'
  5025. },
  5026. needChild = {
  5027. 'ol':'li',
  5028. 'ul':'li'
  5029. };
  5030. function text(parent, data) {
  5031. if(needChild[parent.tagName]){
  5032. var tmpNode = uNode.createElement(needChild[parent.tagName]);
  5033. parent.appendChild(tmpNode);
  5034. tmpNode.appendChild(uNode.createText(data));
  5035. parent = tmpNode;
  5036. }else{
  5037. parent.appendChild(uNode.createText(data));
  5038. }
  5039. }
  5040. function element(parent, tagName, htmlattr) {
  5041. var needParentTag;
  5042. if (needParentTag = needParentNode[tagName]) {
  5043. var tmpParent = parent,hasParent;
  5044. while(tmpParent.type != 'root'){
  5045. if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
  5046. parent = tmpParent;
  5047. hasParent = true;
  5048. break;
  5049. }
  5050. tmpParent = tmpParent.parentNode;
  5051. }
  5052. if(!hasParent){
  5053. parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
  5054. }
  5055. }
  5056. //按dtd处理嵌套
  5057. // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  5058. // parent = parent.parentNode;
  5059. var elm = new uNode({
  5060. parentNode:parent,
  5061. type:'element',
  5062. tagName:tagName.toLowerCase(),
  5063. //是自闭合的处理一下
  5064. children:dtd.$empty[tagName] ? null : []
  5065. });
  5066. //如果属性存在,处理属性
  5067. if (htmlattr) {
  5068. var attrs = {}, match;
  5069. while (match = re_attr.exec(htmlattr)) {
  5070. attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
  5071. }
  5072. elm.attrs = attrs;
  5073. }
  5074. parent.children.push(elm);
  5075. //如果是自闭合节点返回父亲节点
  5076. return dtd.$empty[tagName] ? parent : elm
  5077. }
  5078. function comment(parent, data) {
  5079. parent.children.push(new uNode({
  5080. type:'comment',
  5081. data:data,
  5082. parentNode:parent
  5083. }));
  5084. }
  5085. var match, currentIndex = 0, nextIndex = 0;
  5086. //设置根节点
  5087. var root = new uNode({
  5088. type:'root',
  5089. children:[]
  5090. });
  5091. var currentParent = root;
  5092. while (match = re_tag.exec(htmlstr)) {
  5093. currentIndex = match.index;
  5094. try{
  5095. if (currentIndex > nextIndex) {
  5096. //text node
  5097. text(currentParent, htmlstr.slice(nextIndex, currentIndex));
  5098. }
  5099. if (match[3]) {
  5100. if(dtd.$cdata[currentParent.tagName]){
  5101. text(currentParent, match[0]);
  5102. }else{
  5103. //start tag
  5104. currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
  5105. }
  5106. } else if (match[1]) {
  5107. if(currentParent.type != 'root'){
  5108. if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
  5109. text(currentParent, match[0]);
  5110. }else{
  5111. var tmpParent = currentParent;
  5112. while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
  5113. currentParent = currentParent.parentNode;
  5114. if(currentParent.type == 'root'){
  5115. currentParent = tmpParent;
  5116. throw 'break'
  5117. }
  5118. }
  5119. //end tag
  5120. currentParent = currentParent.parentNode;
  5121. }
  5122. }
  5123. } else if (match[2]) {
  5124. //comment
  5125. comment(currentParent, match[2])
  5126. }
  5127. }catch(e){}
  5128. nextIndex = re_tag.lastIndex;
  5129. }
  5130. //如果结束是文本,就有可能丢掉,所以这里手动判断一下
  5131. //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
  5132. if (nextIndex < htmlstr.length) {
  5133. text(currentParent, htmlstr.slice(nextIndex));
  5134. }
  5135. return root;
  5136. };
  5137. /**
  5138. * @file
  5139. * @name UM.filterNode
  5140. * @short filterNode
  5141. * @desc 根据给定的规则过滤节点
  5142. * @import editor.js,core/utils.js
  5143. * @anthor zhanyi
  5144. */
  5145. var filterNode = UM.filterNode = function () {
  5146. function filterNode(node,rules){
  5147. switch (node.type) {
  5148. case 'text':
  5149. break;
  5150. case 'element':
  5151. var val;
  5152. if(val = rules[node.tagName]){
  5153. if(val === '-'){
  5154. node.parentNode.removeChild(node)
  5155. }else if(utils.isFunction(val)){
  5156. var parentNode = node.parentNode,
  5157. index = node.getIndex();
  5158. val(node);
  5159. if(node.parentNode){
  5160. if(node.children){
  5161. for(var i = 0,ci;ci=node.children[i];){
  5162. filterNode(ci,rules);
  5163. if(ci.parentNode){
  5164. i++;
  5165. }
  5166. }
  5167. }
  5168. }else{
  5169. for(var i = index,ci;ci=parentNode.children[i];){
  5170. filterNode(ci,rules);
  5171. if(ci.parentNode){
  5172. i++;
  5173. }
  5174. }
  5175. }
  5176. }else{
  5177. var attrs = val['$'];
  5178. if(attrs && node.attrs){
  5179. var tmpAttrs = {},tmpVal;
  5180. for(var a in attrs){
  5181. tmpVal = node.getAttr(a);
  5182. //todo 只先对style单独处理
  5183. if(a == 'style' && utils.isArray(attrs[a])){
  5184. var tmpCssStyle = [];
  5185. utils.each(attrs[a],function(v){
  5186. var tmp;
  5187. if(tmp = node.getStyle(v)){
  5188. tmpCssStyle.push(v + ':' + tmp);
  5189. }
  5190. });
  5191. tmpVal = tmpCssStyle.join(';')
  5192. }
  5193. if(tmpVal){
  5194. tmpAttrs[a] = tmpVal;
  5195. }
  5196. }
  5197. node.attrs = tmpAttrs;
  5198. }
  5199. if(node.children){
  5200. for(var i = 0,ci;ci=node.children[i];){
  5201. filterNode(ci,rules);
  5202. if(ci.parentNode){
  5203. i++;
  5204. }
  5205. }
  5206. }
  5207. }
  5208. }else{
  5209. //如果不在名单里扣出子节点并删除该节点,cdata除外
  5210. if(dtd.$cdata[node.tagName]){
  5211. node.parentNode.removeChild(node)
  5212. }else{
  5213. var parentNode = node.parentNode,
  5214. index = node.getIndex();
  5215. node.parentNode.removeChild(node,true);
  5216. for(var i = index,ci;ci=parentNode.children[i];){
  5217. filterNode(ci,rules);
  5218. if(ci.parentNode){
  5219. i++;
  5220. }
  5221. }
  5222. }
  5223. }
  5224. break;
  5225. case 'comment':
  5226. node.parentNode.removeChild(node)
  5227. }
  5228. }
  5229. return function(root,rules){
  5230. if(utils.isEmptyObject(rules)){
  5231. return root;
  5232. }
  5233. var val;
  5234. if(val = rules['-']){
  5235. utils.each(val.split(' '),function(k){
  5236. rules[k] = '-'
  5237. })
  5238. }
  5239. for(var i= 0,ci;ci=root.children[i];){
  5240. filterNode(ci,rules);
  5241. if(ci.parentNode){
  5242. i++;
  5243. }
  5244. }
  5245. return root;
  5246. }
  5247. }();
  5248. ///import core
  5249. /**
  5250. * @description 插入内容
  5251. * @name baidu.editor.execCommand
  5252. * @param {String} cmdName inserthtml插入内容的命令
  5253. * @param {String} html 要插入的内容
  5254. * @author zhanyi
  5255. */
  5256. UM.commands['inserthtml'] = {
  5257. execCommand: function (command,html,notNeedFilter){
  5258. var me = this,
  5259. range,
  5260. div;
  5261. if(!html){
  5262. return;
  5263. }
  5264. if(me.fireEvent('beforeinserthtml',html) === true){
  5265. return;
  5266. }
  5267. range = me.selection.getRange();
  5268. div = range.document.createElement( 'div' );
  5269. div.style.display = 'inline';
  5270. if (!notNeedFilter) {
  5271. var root = UM.htmlparser(html);
  5272. //如果给了过滤规则就先进行过滤
  5273. if(me.options.filterRules){
  5274. UM.filterNode(root,me.options.filterRules);
  5275. }
  5276. //执行默认的处理
  5277. me.filterInputRule(root);
  5278. html = root.toHtml()
  5279. }
  5280. div.innerHTML = utils.trim( html );
  5281. if ( !range.collapsed ) {
  5282. var tmpNode = range.startContainer;
  5283. if(domUtils.isFillChar(tmpNode)){
  5284. range.setStartBefore(tmpNode)
  5285. }
  5286. tmpNode = range.endContainer;
  5287. if(domUtils.isFillChar(tmpNode)){
  5288. range.setEndAfter(tmpNode)
  5289. }
  5290. range.txtToElmBoundary();
  5291. //结束边界可能放到了br的前边,要把br包含进来
  5292. // x[xxx]<br/>
  5293. if(range.endContainer && range.endContainer.nodeType == 1){
  5294. tmpNode = range.endContainer.childNodes[range.endOffset];
  5295. if(tmpNode && domUtils.isBr(tmpNode)){
  5296. range.setEndAfter(tmpNode);
  5297. }
  5298. }
  5299. if(range.startOffset == 0){
  5300. tmpNode = range.startContainer;
  5301. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  5302. tmpNode = range.endContainer;
  5303. if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  5304. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  5305. range.setStart(me.body.firstChild,0).collapse(true)
  5306. }
  5307. }
  5308. }
  5309. !range.collapsed && range.deleteContents();
  5310. if(range.startContainer.nodeType == 1){
  5311. var child = range.startContainer.childNodes[range.startOffset],pre;
  5312. if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
  5313. range.setEnd(pre,pre.childNodes.length).collapse();
  5314. while(child.firstChild){
  5315. pre.appendChild(child.firstChild);
  5316. }
  5317. domUtils.remove(child);
  5318. }
  5319. }
  5320. }
  5321. var child,parent,pre,tmp,hadBreak = 0, nextNode;
  5322. //如果当前位置选中了fillchar要干掉,要不会产生空行
  5323. if(range.inFillChar()){
  5324. child = range.startContainer;
  5325. if(domUtils.isFillChar(child)){
  5326. range.setStartBefore(child).collapse(true);
  5327. domUtils.remove(child);
  5328. }else if(domUtils.isFillChar(child,true)){
  5329. child.nodeValue = child.nodeValue.replace(fillCharReg,'');
  5330. range.startOffset--;
  5331. range.collapsed && range.collapse(true)
  5332. }
  5333. }
  5334. while ( child = div.firstChild ) {
  5335. if(hadBreak){
  5336. var p = me.document.createElement('p');
  5337. while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
  5338. nextNode = child.nextSibling;
  5339. p.appendChild(child);
  5340. child = nextNode;
  5341. }
  5342. if(p.firstChild){
  5343. child = p
  5344. }
  5345. }
  5346. range.insertNode( child );
  5347. nextNode = child.nextSibling;
  5348. if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
  5349. parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
  5350. if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
  5351. if(!dtd[parent.tagName][child.nodeName]){
  5352. pre = parent;
  5353. }else{
  5354. tmp = child.parentNode;
  5355. while (tmp !== parent){
  5356. pre = tmp;
  5357. tmp = tmp.parentNode;
  5358. }
  5359. }
  5360. domUtils.breakParent( child, pre || tmp );
  5361. //去掉break后前一个多余的节点 <p>|<[p> ==> <p></p><div></div><p>|</p>
  5362. var pre = child.previousSibling;
  5363. domUtils.trimWhiteTextNode(pre);
  5364. if(!pre.childNodes.length){
  5365. domUtils.remove(pre);
  5366. }
  5367. //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位
  5368. if(!browser.ie &&
  5369. (next = child.nextSibling) &&
  5370. domUtils.isBlockElm(next) &&
  5371. next.lastChild &&
  5372. !domUtils.isBr(next.lastChild)){
  5373. next.appendChild(me.document.createElement('br'));
  5374. }
  5375. hadBreak = 1;
  5376. }
  5377. }
  5378. var next = child.nextSibling;
  5379. if(!div.firstChild && next && domUtils.isBlockElm(next)){
  5380. range.setStart(next,0).collapse(true);
  5381. break;
  5382. }
  5383. range.setEndAfter( child ).collapse();
  5384. }
  5385. child = range.startContainer;
  5386. if(nextNode && domUtils.isBr(nextNode)){
  5387. domUtils.remove(nextNode)
  5388. }
  5389. //用chrome可能有空白展位符
  5390. if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
  5391. if(nextNode = child.nextSibling){
  5392. domUtils.remove(child);
  5393. if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
  5394. range.setStart(nextNode,0).collapse(true).shrinkBoundary()
  5395. }
  5396. }else{
  5397. try{
  5398. child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
  5399. }catch(e){
  5400. range.setStartBefore(child);
  5401. domUtils.remove(child)
  5402. }
  5403. }
  5404. }
  5405. //加上true因为在删除表情等时会删两次,第一次是删的fillData
  5406. try{
  5407. if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){
  5408. var start = range.startContainer,pre = start.childNodes[range.startOffset-1];
  5409. if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){
  5410. var txt = this.document.createTextNode(domUtils.fillChar);
  5411. range.insertNode(txt).setStart(txt,0).collapse(true);
  5412. }
  5413. }
  5414. setTimeout(function(){
  5415. range.select(true);
  5416. })
  5417. }catch(e){}
  5418. setTimeout(function(){
  5419. range = me.selection.getRange();
  5420. range.scrollIntoView();
  5421. me.fireEvent('afterinserthtml');
  5422. },200);
  5423. }
  5424. };
  5425. ///import core
  5426. ///import plugins\inserthtml.js
  5427. ///commands 插入图片,操作图片的对齐方式
  5428. ///commandsName InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter
  5429. ///commandsTitle 图片,默认,居左,居右,居中
  5430. ///commandsDialog dialogs\image
  5431. /**
  5432. * Created by .
  5433. * User: zhanyi
  5434. * for image
  5435. */
  5436. UM.commands['insertimage'] = {
  5437. execCommand:function (cmd, opt) {
  5438. opt = utils.isArray(opt) ? opt : [opt];
  5439. if (!opt.length) {
  5440. return;
  5441. }
  5442. var me = this;
  5443. var html = [], str = '', ci;
  5444. ci = opt[0];
  5445. if (opt.length == 1) {
  5446. str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5447. (ci.width ? 'width="' + ci.width + '" ' : '') +
  5448. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5449. (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
  5450. (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
  5451. (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
  5452. (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
  5453. (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
  5454. (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
  5455. if (ci['floatStyle'] == 'center') {
  5456. str = '<p style="text-align: center">' + str + '</p>';
  5457. }
  5458. html.push(str);
  5459. } else {
  5460. for (var i = 0; ci = opt[i++];) {
  5461. str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
  5462. (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
  5463. (ci.height ? ' height="' + ci.height + '" ' : '') +
  5464. ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
  5465. (ci.border || '') + '" ' +
  5466. (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
  5467. html.push(str);
  5468. }
  5469. }
  5470. me.execCommand('insertHtml', html.join(''), true);
  5471. }
  5472. };
  5473. ///import core
  5474. ///commands 段落格式,居左,居右,居中,两端对齐
  5475. ///commandsName JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify
  5476. ///commandsTitle 居左对齐,居中对齐,居右对齐,两端对齐
  5477. /**
  5478. * @description 居左右中
  5479. * @name UM.execCommand
  5480. * @param {String} cmdName justify执行对齐方式的命令
  5481. * @param {String} align 对齐方式:left居左,right居右,center居中,justify两端对齐
  5482. * @author zhanyi
  5483. */
  5484. UM.plugins['justify']=function(){
  5485. var me = this;
  5486. $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){
  5487. me.commands[cmdName] = {
  5488. execCommand:function (cmdName) {
  5489. return this.document.execCommand(cmdName);
  5490. },
  5491. queryCommandValue: function (cmdName) {
  5492. var val = this.document.queryCommandValue(cmdName);
  5493. return val === true || val === 'true' ? cmdName.replace(/justify/,'') : '';
  5494. },
  5495. queryCommandState: function (cmdName) {
  5496. return this.document.queryCommandState(cmdName) ? 1 : 0
  5497. }
  5498. };
  5499. })
  5500. };
  5501. ///import core
  5502. ///import plugins\removeformat.js
  5503. ///commands 字体颜色,背景色,字号,字体,下划线,删除线
  5504. ///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough
  5505. ///commandsTitle 字体颜色,背景色,字号,字体,下划线,删除线
  5506. /**
  5507. * @description 字体
  5508. * @name UM.execCommand
  5509. * @param {String} cmdName 执行的功能名称
  5510. * @param {String} value 传入的值
  5511. */
  5512. UM.plugins['font'] = function () {
  5513. var me = this,
  5514. fonts = {
  5515. 'forecolor': 'forecolor',
  5516. 'backcolor': 'backcolor',
  5517. 'fontsize': 'fontsize',
  5518. 'fontfamily': 'fontname'
  5519. },
  5520. cmdNameToStyle = {
  5521. 'forecolor': 'color',
  5522. 'backcolor': 'background-color',
  5523. 'fontsize': 'font-size',
  5524. 'fontfamily': 'font-family'
  5525. },
  5526. cmdNameToAttr = {
  5527. 'forecolor': 'color',
  5528. 'fontsize': 'size',
  5529. 'fontfamily': 'face'
  5530. };
  5531. me.setOpt({
  5532. 'fontfamily': [
  5533. { name: 'songti', val: '宋体,SimSun'},
  5534. { name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
  5535. { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
  5536. { name: 'heiti', val: '黑体, SimHei'},
  5537. { name: 'lishu', val: '隶书, SimLi'},
  5538. { name: 'andaleMono', val: 'andale mono'},
  5539. { name: 'arial', val: 'arial, helvetica,sans-serif'},
  5540. { name: 'arialBlack', val: 'arial black,avant garde'},
  5541. { name: 'comicSansMs', val: 'comic sans ms'},
  5542. { name: 'impact', val: 'impact,chicago'},
  5543. { name: 'timesNewRoman', val: 'times new roman'},
  5544. { name: 'sans-serif',val:'sans-serif'}
  5545. ],
  5546. 'fontsize': [10, 12, 16, 18,24, 32,48]
  5547. });
  5548. me.addOutputRule(function (root) {
  5549. utils.each(root.getNodesByTagName('font'), function (node) {
  5550. if (node.tagName == 'font') {
  5551. var cssStyle = [];
  5552. for (var p in node.attrs) {
  5553. switch (p) {
  5554. case 'size':
  5555. var val = node.attrs[p];
  5556. $.each({
  5557. '10':'1',
  5558. '12':'2',
  5559. '16':'3',
  5560. '18':'4',
  5561. '24':'5',
  5562. '32':'6',
  5563. '48':'7'
  5564. },function(k,v){
  5565. if(v == val){
  5566. val = k;
  5567. return false;
  5568. }
  5569. });
  5570. cssStyle.push('font-size:' + val + 'px');
  5571. break;
  5572. case 'color':
  5573. cssStyle.push('color:' + node.attrs[p]);
  5574. break;
  5575. case 'face':
  5576. cssStyle.push('font-family:' + node.attrs[p]);
  5577. break;
  5578. case 'style':
  5579. cssStyle.push(node.attrs[p]);
  5580. }
  5581. }
  5582. node.attrs = {
  5583. 'style': cssStyle.join(';')
  5584. };
  5585. }
  5586. node.tagName = 'span';
  5587. if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){
  5588. $.each(node.attrs,function(k,v){
  5589. node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;
  5590. })
  5591. node.parentNode.removeChild(node,true);
  5592. }
  5593. });
  5594. });
  5595. for(var p in fonts){
  5596. (function (cmd) {
  5597. me.commands[cmd] = {
  5598. execCommand: function (cmdName,value) {
  5599. if(value == 'transparent'){
  5600. return;
  5601. }
  5602. var rng = this.selection.getRange();
  5603. if(rng.collapsed){
  5604. var span = $('<span></span>').css(cmdNameToStyle[cmdName],value)[0];
  5605. rng.insertNode(span).setStart(span,0).setCursor();
  5606. }else{
  5607. if(cmdName == 'fontsize'){
  5608. value = {
  5609. '10':'1',
  5610. '12':'2',
  5611. '16':'3',
  5612. '18':'4',
  5613. '24':'5',
  5614. '32':'6',
  5615. '48':'7'
  5616. }[(value+"").replace(/px/,'')]
  5617. }
  5618. this.document.execCommand(fonts[cmdName],false, value);
  5619. if(browser.gecko){
  5620. $.each(this.$body.find('a'),function(i,a){
  5621. var parent = a.parentNode;
  5622. if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){
  5623. var cloneNode = parent.cloneNode(false);
  5624. cloneNode.innerHTML = a.innerHTML;
  5625. $(a).html('').append(cloneNode).insertBefore(parent);
  5626. $(parent).remove();
  5627. }
  5628. });
  5629. }
  5630. if(!browser.ie){
  5631. var nativeRange = this.selection.getNative().getRangeAt(0);
  5632. var common = nativeRange.commonAncestorContainer;
  5633. var rng = this.selection.getRange(),
  5634. bk = rng.createBookmark(true);
  5635. $(common).find('a').each(function(i,n){
  5636. var parent = n.parentNode;
  5637. if(parent.nodeName == 'FONT'){
  5638. var font = parent.cloneNode(false);
  5639. font.innerHTML = n.innerHTML;
  5640. $(n).html('').append(font);
  5641. }
  5642. });
  5643. rng.moveToBookmark(bk).select()
  5644. }
  5645. return true
  5646. }
  5647. },
  5648. queryCommandValue: function (cmdName) {
  5649. var start = me.selection.getStart();
  5650. var val = $(start).css(cmdNameToStyle[cmdName]);
  5651. if(val === undefined){
  5652. val = $(start).attr(cmdNameToAttr[cmdName])
  5653. }
  5654. return val ? utils.fixColor(cmdName,val).replace(/px/,'') : '';
  5655. },
  5656. queryCommandState: function (cmdName) {
  5657. return this.queryCommandValue(cmdName)
  5658. }
  5659. };
  5660. })(p);
  5661. }
  5662. };
  5663. ///import core
  5664. ///commands 超链接,取消链接
  5665. ///commandsName Link,Unlink
  5666. ///commandsTitle 超链接,取消链接
  5667. ///commandsDialog dialogs\link
  5668. /**
  5669. * 超链接
  5670. * @function
  5671. * @name UM.execCommand
  5672. * @param {String} cmdName link插入超链接
  5673. * @param {Object} options url地址,title标题,target是否打开新页
  5674. * @author zhanyi
  5675. */
  5676. /**
  5677. * 取消链接
  5678. * @function
  5679. * @name UM.execCommand
  5680. * @param {String} cmdName unlink取消链接
  5681. * @author zhanyi
  5682. */
  5683. UM.plugins['link'] = function(){
  5684. var me = this;
  5685. me.setOpt('autourldetectinie',false);
  5686. //在ie下禁用autolink
  5687. if(browser.ie && this.options.autourldetectinie === false){
  5688. this.addListener('keyup',function(cmd,evt){
  5689. var me = this,keyCode = evt.keyCode;
  5690. if(keyCode == 13 || keyCode == 32){
  5691. var rng = me.selection.getRange();
  5692. var start = rng.startContainer;
  5693. if(keyCode == 13){
  5694. if(start.nodeName == 'P'){
  5695. var pre = start.previousSibling;
  5696. if(pre && pre.nodeType == 1){
  5697. var pre = pre.lastChild;
  5698. if(pre && pre.nodeName == 'A' && !pre.getAttribute('_href')){
  5699. domUtils.remove(pre,true);
  5700. }
  5701. }
  5702. }
  5703. }else if(keyCode == 32){
  5704. if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){
  5705. start = start.previousSibling;
  5706. if(start && start.nodeName == 'A' && !start.getAttribute('_href')){
  5707. domUtils.remove(start,true);
  5708. }
  5709. }
  5710. }
  5711. }
  5712. });
  5713. }
  5714. this.addOutputRule(function(root){
  5715. $.each(root.getNodesByTagName('a'),function(i,a){
  5716. var _href = a.getAttr('_href');
  5717. if(!/^(ftp|https?|\/|file)/.test(_href)){
  5718. _href = 'http://' + _href;
  5719. }
  5720. a.setAttr('href', _href);
  5721. a.setAttr('_href')
  5722. if(a.getAttr('title')==''){
  5723. a.setAttr('title')
  5724. }
  5725. })
  5726. });
  5727. this.addInputRule(function(root){
  5728. $.each(root.getNodesByTagName('a'),function(i,a){
  5729. a.setAttr('_href', a.getAttr('href'));
  5730. })
  5731. });
  5732. me.commands['link'] = {
  5733. execCommand : function( cmdName, opt ) {
  5734. var me = this;
  5735. var rng = me.selection.getRange();
  5736. opt._href && (opt._href = utils.unhtml(opt._href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g));
  5737. opt.href && (opt.href = utils.unhtml(opt.href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g));
  5738. if(rng.collapsed){
  5739. var start = rng.startContainer;
  5740. if(start = domUtils.findParentByTagName(start,'a',true)){
  5741. $(start).attr(opt);
  5742. rng.selectNode(start).select()
  5743. }else{
  5744. rng.insertNode($('<a>' + opt.href +'</a>').attr(opt)[0]).select()
  5745. }
  5746. }else{
  5747. me.document.execCommand('createlink',false,'_umeditor_link');
  5748. utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){
  5749. return n.getAttribute('href') == '_umeditor_link'
  5750. }),function(l){
  5751. if($(l).text() == '_umeditor_link'){
  5752. $(l).text(opt.href);
  5753. }
  5754. domUtils.setAttributes(l,opt);
  5755. rng.selectNode(l).select()
  5756. })
  5757. }
  5758. },
  5759. queryCommandState:function(){
  5760. return this.queryCommandValue('link') ? 1 : 0;
  5761. },
  5762. queryCommandValue:function(){
  5763. var path = this.selection.getStartElementPath();
  5764. var result;
  5765. $.each(path,function(i,n){
  5766. if(n.nodeName == "A"){
  5767. result = n;
  5768. return false;
  5769. }
  5770. })
  5771. return result;
  5772. }
  5773. };
  5774. me.commands['unlink'] = {
  5775. execCommand : function() {
  5776. this.document.execCommand('unlink');
  5777. }
  5778. };
  5779. };
  5780. ///import core
  5781. ///commands 打印
  5782. ///commandsName Print
  5783. ///commandsTitle 打印
  5784. /**
  5785. * @description 打印
  5786. * @name baidu.editor.execCommand
  5787. * @param {String} cmdName print打印编辑器内容
  5788. * @author zhanyi
  5789. */
  5790. UM.commands['print'] = {
  5791. execCommand : function(){
  5792. var me = this,
  5793. id = 'editor_print_' + +new Date();
  5794. $('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').attr('id', id)
  5795. .css({
  5796. width:'0px',
  5797. height:'0px',
  5798. 'overflow':'hidden',
  5799. 'float':'left',
  5800. 'position':'absolute',
  5801. top:'-10000px',
  5802. left:'-10000px'
  5803. })
  5804. .appendTo(me.$container.find('.edui-dialog-container'));
  5805. var w = window.open('', id, ''),
  5806. d = w.document;
  5807. d.open();
  5808. d.write('<html><head></head><body><div>'+this.getContent(null,null,true)+'</div><script>' +
  5809. "setTimeout(function(){" +
  5810. "window.print();" +
  5811. "setTimeout(function(){" +
  5812. "window.parent.$('#" + id + "').remove();" +
  5813. "},100);" +
  5814. "},200);" +
  5815. '</script></body></html>');
  5816. d.close();
  5817. },
  5818. notNeedUndo : 1
  5819. };
  5820. ///import core
  5821. ///commands 格式
  5822. ///commandsName Paragraph
  5823. ///commandsTitle 段落格式
  5824. /**
  5825. * 段落样式
  5826. * @function
  5827. * @name UM.execCommand
  5828. * @param {String} cmdName paragraph插入段落执行命令
  5829. * @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
  5830. * @param {String} attrs 标签的属性
  5831. * @author zhanyi
  5832. */
  5833. UM.plugins['paragraph'] = function() {
  5834. var me = this;
  5835. me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});
  5836. me.commands['paragraph'] = {
  5837. execCommand : function( cmdName, style ) {
  5838. return this.document.execCommand('formatBlock',false,'<' + style + '>');
  5839. },
  5840. queryCommandValue : function() {
  5841. try{
  5842. var val = this.document.queryCommandValue('formatBlock')
  5843. }catch(e){
  5844. }
  5845. return val ;
  5846. }
  5847. };
  5848. };
  5849. ///import core
  5850. ///import plugins\inserthtml.js
  5851. ///commands 分割线
  5852. ///commandsName Horizontal
  5853. ///commandsTitle 分隔线
  5854. /**
  5855. * 分割线
  5856. * @function
  5857. * @name UM.execCommand
  5858. * @param {String} cmdName horizontal插入分割线
  5859. */
  5860. UM.plugins['horizontal'] = function(){
  5861. var me = this;
  5862. me.commands['horizontal'] = {
  5863. execCommand : function( ) {
  5864. this.document.execCommand('insertHorizontalRule');
  5865. var rng = me.selection.getRange().txtToElmBoundary(true),
  5866. start = rng.startContainer;
  5867. if(domUtils.isBody(rng.startContainer)){
  5868. var next = rng.startContainer.childNodes[rng.startOffset];
  5869. if(!next){
  5870. next = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[0]
  5871. }
  5872. rng.setStart(next,0).setCursor()
  5873. }else{
  5874. while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){
  5875. var parent = start.parentNode;
  5876. parent.appendChild(start.firstChild);
  5877. parent.removeChild(start);
  5878. start = parent;
  5879. }
  5880. while(dtd.$inline[start.tagName]){
  5881. start = start.parentNode;
  5882. }
  5883. if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){
  5884. var hr = start.lastChild;
  5885. $(hr).insertBefore(start);
  5886. rng.setStart(start,0).setCursor();
  5887. }else{
  5888. hr = $('hr',start)[0];
  5889. domUtils.breakParent(hr,start);
  5890. var pre = hr.previousSibling;
  5891. if(pre && domUtils.isEmptyBlock(pre)){
  5892. $(pre).remove()
  5893. }
  5894. rng.setStart(hr.nextSibling,0).setCursor();
  5895. }
  5896. }
  5897. }
  5898. };
  5899. };
  5900. ///import core
  5901. ///commands 清空文档
  5902. ///commandsName ClearDoc
  5903. ///commandsTitle 清空文档
  5904. /**
  5905. *
  5906. * 清空文档
  5907. * @function
  5908. * @name UM.execCommand
  5909. * @param {String} cmdName cleardoc清空文档
  5910. */
  5911. UM.commands['cleardoc'] = {
  5912. execCommand : function() {
  5913. var me = this,
  5914. range = me.selection.getRange();
  5915. me.body.innerHTML = "<p>"+(ie ? "" : "<br/>")+"</p>";
  5916. range.setStart(me.body.firstChild,0).setCursor(false,true);
  5917. setTimeout(function(){
  5918. me.fireEvent("clearDoc");
  5919. },0);
  5920. }
  5921. };
  5922. ///import core
  5923. ///commands 撤销和重做
  5924. ///commandsName Undo,Redo
  5925. ///commandsTitle 撤销,重做
  5926. /**
  5927. * @description 回退
  5928. * @author zhanyi
  5929. */
  5930. UM.plugins['undo'] = function () {
  5931. var saveSceneTimer;
  5932. var me = this,
  5933. maxUndoCount = me.options.maxUndoCount || 20,
  5934. maxInputCount = me.options.maxInputCount || 20,
  5935. fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
  5936. var noNeedFillCharTags = {
  5937. ol:1,ul:1,table:1,tbody:1,tr:1,body:1
  5938. };
  5939. var orgState = me.options.autoClearEmptyNode;
  5940. function compareAddr(indexA, indexB) {
  5941. if (indexA.length != indexB.length)
  5942. return 0;
  5943. for (var i = 0, l = indexA.length; i < l; i++) {
  5944. if (indexA[i] != indexB[i])
  5945. return 0
  5946. }
  5947. return 1;
  5948. }
  5949. function compareRangeAddress(rngAddrA, rngAddrB) {
  5950. if (rngAddrA.collapsed != rngAddrB.collapsed) {
  5951. return 0;
  5952. }
  5953. if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
  5954. return 0;
  5955. }
  5956. return 1;
  5957. }
  5958. function UndoManager() {
  5959. this.list = [];
  5960. this.index = 0;
  5961. this.hasUndo = false;
  5962. this.hasRedo = false;
  5963. this.undo = function () {
  5964. if (this.hasUndo) {
  5965. if (!this.list[this.index - 1] && this.list.length == 1) {
  5966. this.reset();
  5967. return;
  5968. }
  5969. while (this.list[this.index].content == this.list[this.index - 1].content) {
  5970. this.index--;
  5971. if (this.index == 0) {
  5972. return this.restore(0);
  5973. }
  5974. }
  5975. this.restore(--this.index);
  5976. }
  5977. };
  5978. this.redo = function () {
  5979. if (this.hasRedo) {
  5980. while (this.list[this.index].content == this.list[this.index + 1].content) {
  5981. this.index++;
  5982. if (this.index == this.list.length - 1) {
  5983. return this.restore(this.index);
  5984. }
  5985. }
  5986. this.restore(++this.index);
  5987. }
  5988. };
  5989. this.restore = function () {
  5990. var me = this.editor;
  5991. var scene = this.list[this.index];
  5992. var root = UM.htmlparser(scene.content.replace(fillchar, ''));
  5993. me.options.autoClearEmptyNode = false;
  5994. me.filterInputRule(root,true);
  5995. me.options.autoClearEmptyNode = orgState;
  5996. //trace:873
  5997. //去掉展位符
  5998. me.body.innerHTML = root.toHtml();
  5999. me.fireEvent('afterscencerestore');
  6000. //处理undo后空格不展位的问题
  6001. if (browser.ie) {
  6002. utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
  6003. if(domUtils.isEmptyNode(node)){
  6004. domUtils.fillNode(me.document, node);
  6005. }
  6006. })
  6007. }
  6008. try{
  6009. var rng = new dom.Range(me.document,me.body).moveToAddress(scene.address);
  6010. if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){
  6011. var tmpNode = rng.startContainer.childNodes[rng.startOffset];
  6012. if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){
  6013. rng.insertNode(me.document.createTextNode(' ')).collapse(true);
  6014. }
  6015. }
  6016. rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
  6017. }catch(e){}
  6018. this.update();
  6019. this.clearKey();
  6020. //不能把自己reset了
  6021. me.fireEvent('reset', true);
  6022. };
  6023. this.getScene = function () {
  6024. var me = this.editor;
  6025. var rng = me.selection.getRange(),
  6026. rngAddress = rng.createAddress(false,true);
  6027. me.fireEvent('beforegetscene');
  6028. var root = UM.htmlparser(me.body.innerHTML,true);
  6029. me.options.autoClearEmptyNode = false;
  6030. me.filterOutputRule(root,true);
  6031. me.options.autoClearEmptyNode = orgState;
  6032. var cont = root.toHtml();
  6033. browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
  6034. me.fireEvent('aftergetscene');
  6035. return {
  6036. address:rngAddress,
  6037. content:cont
  6038. }
  6039. };
  6040. this.save = function (notCompareRange,notSetCursor) {
  6041. clearTimeout(saveSceneTimer);
  6042. var currentScene = this.getScene(notSetCursor),
  6043. lastScene = this.list[this.index];
  6044. //内容相同位置相同不存
  6045. if (lastScene && lastScene.content == currentScene.content &&
  6046. ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
  6047. ) {
  6048. return;
  6049. }
  6050. this.list = this.list.slice(0, this.index + 1);
  6051. this.list.push(currentScene);
  6052. //如果大于最大数量了,就把最前的剔除
  6053. if (this.list.length > maxUndoCount) {
  6054. this.list.shift();
  6055. }
  6056. this.index = this.list.length - 1;
  6057. this.clearKey();
  6058. //跟新undo/redo状态
  6059. this.update();
  6060. };
  6061. this.update = function () {
  6062. this.hasRedo = !!this.list[this.index + 1];
  6063. this.hasUndo = !!this.list[this.index - 1];
  6064. };
  6065. this.reset = function () {
  6066. this.list = [];
  6067. this.index = 0;
  6068. this.hasUndo = false;
  6069. this.hasRedo = false;
  6070. this.clearKey();
  6071. };
  6072. this.clearKey = function () {
  6073. keycont = 0;
  6074. lastKeyCode = null;
  6075. };
  6076. }
  6077. me.undoManger = new UndoManager();
  6078. me.undoManger.editor = me;
  6079. function saveScene() {
  6080. this.undoManger.save();
  6081. }
  6082. me.addListener('saveScene', function () {
  6083. var args = Array.prototype.splice.call(arguments,1);
  6084. this.undoManger.save.apply(this.undoManger,args);
  6085. });
  6086. me.addListener('beforeexeccommand', saveScene);
  6087. me.addListener('afterexeccommand', saveScene);
  6088. me.addListener('reset', function (type, exclude) {
  6089. if (!exclude) {
  6090. this.undoManger.reset();
  6091. }
  6092. });
  6093. me.commands['redo'] = me.commands['undo'] = {
  6094. execCommand:function (cmdName) {
  6095. this.undoManger[cmdName]();
  6096. },
  6097. queryCommandState:function (cmdName) {
  6098. return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
  6099. },
  6100. notNeedUndo:1
  6101. };
  6102. var keys = {
  6103. // /*Backspace*/ 8:1, /*Delete*/ 46:1,
  6104. /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
  6105. 37:1, 38:1, 39:1, 40:1
  6106. },
  6107. keycont = 0,
  6108. lastKeyCode;
  6109. //输入法状态下不计算字符数
  6110. var inputType = false;
  6111. me.addListener('ready', function () {
  6112. $(this.body).on('compositionstart', function () {
  6113. inputType = true;
  6114. }).on('compositionend', function () {
  6115. inputType = false;
  6116. })
  6117. });
  6118. //快捷键
  6119. me.addshortcutkey({
  6120. "Undo":"ctrl+90", //undo
  6121. "Redo":"ctrl+89,shift+ctrl+z" //redo
  6122. });
  6123. var isCollapsed = true;
  6124. me.addListener('keydown', function (type, evt) {
  6125. var me = this;
  6126. var keyCode = evt.keyCode || evt.which;
  6127. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6128. if (inputType)
  6129. return;
  6130. if(!me.selection.getRange().collapsed){
  6131. me.undoManger.save(false,true);
  6132. isCollapsed = false;
  6133. return;
  6134. }
  6135. if (me.undoManger.list.length == 0) {
  6136. me.undoManger.save(true);
  6137. }
  6138. clearTimeout(saveSceneTimer);
  6139. function save(cont){
  6140. if (cont.selection.getRange().collapsed)
  6141. cont.fireEvent('contentchange');
  6142. cont.undoManger.save(false,true);
  6143. cont.fireEvent('selectionchange');
  6144. }
  6145. saveSceneTimer = setTimeout(function(){
  6146. if(inputType){
  6147. var interalTimer = setInterval(function(){
  6148. if(!inputType){
  6149. save(me);
  6150. clearInterval(interalTimer)
  6151. }
  6152. },300)
  6153. return;
  6154. }
  6155. save(me);
  6156. },200);
  6157. lastKeyCode = keyCode;
  6158. keycont++;
  6159. if (keycont >= maxInputCount ) {
  6160. save(me)
  6161. }
  6162. }
  6163. });
  6164. me.addListener('keyup', function (type, evt) {
  6165. var keyCode = evt.keyCode || evt.which;
  6166. if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
  6167. if (inputType)
  6168. return;
  6169. if(!isCollapsed){
  6170. this.undoManger.save(false,true);
  6171. isCollapsed = true;
  6172. }
  6173. }
  6174. });
  6175. };
  6176. ///import core
  6177. ///import plugins/inserthtml.js
  6178. ///import plugins/undo.js
  6179. ///import plugins/serialize.js
  6180. ///commands 粘贴
  6181. ///commandsName PastePlain
  6182. ///commandsTitle 纯文本粘贴模式
  6183. /**
  6184. * @description 粘贴
  6185. * @author zhanyi
  6186. */
  6187. UM.plugins['paste'] = function () {
  6188. function getClipboardData(callback) {
  6189. var doc = this.document;
  6190. if (doc.getElementById('baidu_pastebin')) {
  6191. return;
  6192. }
  6193. var range = this.selection.getRange(),
  6194. bk = range.createBookmark(),
  6195. //创建剪贴的容器div
  6196. pastebin = doc.createElement('div');
  6197. pastebin.id = 'baidu_pastebin';
  6198. // Safari 要求div必须有内容,才能粘贴内容进来
  6199. browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
  6200. this.body.appendChild(pastebin);
  6201. //trace:717 隐藏的span不能得到top
  6202. //bk.start.innerHTML = '&nbsp;';
  6203. bk.start.style.display = '';
  6204. pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
  6205. //要在现在光标平行的位置加入,否则会出现跳动的问题
  6206. $(bk.start).position().top + 'px';
  6207. range.selectNodeContents(pastebin).select(true);
  6208. setTimeout(function () {
  6209. if (browser.webkit) {
  6210. for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
  6211. if (domUtils.isEmptyNode(pi)) {
  6212. domUtils.remove(pi);
  6213. } else {
  6214. pastebin = pi;
  6215. break;
  6216. }
  6217. }
  6218. }
  6219. try {
  6220. pastebin.parentNode.removeChild(pastebin);
  6221. } catch (e) {
  6222. }
  6223. range.moveToBookmark(bk).select(true);
  6224. callback(pastebin);
  6225. }, 0);
  6226. }
  6227. var me = this;
  6228. function filter(div) {
  6229. var html;
  6230. if (div.firstChild) {
  6231. //去掉cut中添加的边界值
  6232. var nodes = domUtils.getElementsByTagName(div, 'span');
  6233. for (var i = 0, ni; ni = nodes[i++];) {
  6234. if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
  6235. domUtils.remove(ni);
  6236. }
  6237. }
  6238. if (browser.webkit) {
  6239. var brs = div.querySelectorAll('div br');
  6240. for (var i = 0, bi; bi = brs[i++];) {
  6241. var pN = bi.parentNode;
  6242. if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
  6243. pN.innerHTML = '<p><br/></p>';
  6244. domUtils.remove(pN);
  6245. }
  6246. }
  6247. var divs = div.querySelectorAll('#baidu_pastebin');
  6248. for (var i = 0, di; di = divs[i++];) {
  6249. var tmpP = me.document.createElement('p');
  6250. di.parentNode.insertBefore(tmpP, di);
  6251. while (di.firstChild) {
  6252. tmpP.appendChild(di.firstChild);
  6253. }
  6254. domUtils.remove(di);
  6255. }
  6256. var metas = div.querySelectorAll('meta');
  6257. for (var i = 0, ci; ci = metas[i++];) {
  6258. domUtils.remove(ci);
  6259. }
  6260. var brs = div.querySelectorAll('br');
  6261. for (i = 0; ci = brs[i++];) {
  6262. if (/^apple-/i.test(ci.className)) {
  6263. domUtils.remove(ci);
  6264. }
  6265. }
  6266. }
  6267. if (browser.gecko) {
  6268. var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
  6269. for (i = 0; ci = dirtyNodes[i++];) {
  6270. ci.removeAttribute('_moz_dirty');
  6271. }
  6272. }
  6273. if (!browser.ie) {
  6274. var spans = div.querySelectorAll('span.Apple-style-span');
  6275. for (var i = 0, ci; ci = spans[i++];) {
  6276. domUtils.remove(ci, true);
  6277. }
  6278. }
  6279. //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
  6280. html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  6281. //过滤word粘贴过来的冗余属性
  6282. html = UM.filterWord(html);
  6283. //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
  6284. var root = UM.htmlparser(html);
  6285. //如果给了过滤规则就先进行过滤
  6286. if (me.options.filterRules) {
  6287. UM.filterNode(root, me.options.filterRules);
  6288. }
  6289. //执行默认的处理
  6290. me.filterInputRule(root);
  6291. //针对chrome的处理
  6292. if (browser.webkit) {
  6293. var br = root.lastChild();
  6294. if (br && br.type == 'element' && br.tagName == 'br') {
  6295. root.removeChild(br)
  6296. }
  6297. utils.each(me.body.querySelectorAll('div'), function (node) {
  6298. if (domUtils.isEmptyBlock(node)) {
  6299. domUtils.remove(node)
  6300. }
  6301. })
  6302. }
  6303. html = {'html': root.toHtml()};
  6304. me.fireEvent('beforepaste', html, root);
  6305. //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
  6306. if(!html.html){
  6307. return;
  6308. }
  6309. me.execCommand('insertHtml', html.html, true);
  6310. me.fireEvent("afterpaste", html);
  6311. }
  6312. }
  6313. me.addListener('ready', function () {
  6314. $(me.body).on( 'cut', function () {
  6315. var range = me.selection.getRange();
  6316. if (!range.collapsed && me.undoManger) {
  6317. me.undoManger.save();
  6318. }
  6319. }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
  6320. //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
  6321. if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
  6322. return;
  6323. }
  6324. getClipboardData.call(me, function (div) {
  6325. filter(div);
  6326. });
  6327. });
  6328. });
  6329. };
  6330. ///import core
  6331. ///commands 有序列表,无序列表
  6332. ///commandsName InsertOrderedList,InsertUnorderedList
  6333. ///commandsTitle 有序列表,无序列表
  6334. /**
  6335. * 有序列表
  6336. * @function
  6337. * @name UM.execCommand
  6338. * @param {String} cmdName insertorderlist插入有序列表
  6339. * @param {String} style 值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman
  6340. * @author zhanyi
  6341. */
  6342. /**
  6343. * 无序链接
  6344. * @function
  6345. * @name UM.execCommand
  6346. * @param {String} cmdName insertunorderlist插入无序列表
  6347. * * @param {String} style 值为:circle,disc,square
  6348. * @author zhanyi
  6349. */
  6350. UM.plugins['list'] = function () {
  6351. var me = this;
  6352. me.setOpt( {
  6353. 'insertorderedlist':{
  6354. 'decimal':'',
  6355. 'lower-alpha':'',
  6356. 'lower-roman':'',
  6357. 'upper-alpha':'',
  6358. 'upper-roman':''
  6359. },
  6360. 'insertunorderedlist':{
  6361. 'circle':'',
  6362. 'disc':'',
  6363. 'square':''
  6364. }
  6365. } );
  6366. this.addInputRule(function(root){
  6367. utils.each(root.getNodesByTagName('li'), function (node) {
  6368. if(node.children.length == 0){
  6369. node.parentNode.removeChild(node);
  6370. }
  6371. })
  6372. });
  6373. me.commands['insertorderedlist'] =
  6374. me.commands['insertunorderedlist'] = {
  6375. execCommand:function (cmdName) {
  6376. this.document.execCommand(cmdName);
  6377. var rng = this.selection.getRange(),
  6378. bk = rng.createBookmark(true);
  6379. this.$body.find('ol,ul').each(function(i,n){
  6380. var parent = n.parentNode;
  6381. if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){
  6382. $(n).children().each(function(j,li){
  6383. var p = parent.cloneNode(false);
  6384. $(p).append(li.innerHTML);
  6385. $(li).html('').append(p);
  6386. });
  6387. $(n).insertBefore(parent);
  6388. $(parent).remove();
  6389. }
  6390. if(dtd.$inline[parent.tagName]){
  6391. if(parent.tagName == 'SPAN'){
  6392. $(n).children().each(function(k,li){
  6393. var span = parent.cloneNode(false);
  6394. if(li.firstChild.nodeName != 'P'){
  6395. while(li.firstChild){
  6396. span.appendChild(li.firstChild)
  6397. };
  6398. $('<p></p>').appendTo(li).append(span);
  6399. }else{
  6400. while(li.firstChild){
  6401. span.appendChild(li.firstChild)
  6402. };
  6403. $(li.firstChild).append(span);
  6404. }
  6405. })
  6406. }
  6407. domUtils.remove(parent,true)
  6408. }
  6409. });
  6410. rng.moveToBookmark(bk).select();
  6411. return true;
  6412. },
  6413. queryCommandState:function (cmdName) {
  6414. return this.document.queryCommandState(cmdName);
  6415. }
  6416. };
  6417. };
  6418. ///import core
  6419. ///import plugins/serialize.js
  6420. ///import plugins/undo.js
  6421. ///commands 查看源码
  6422. ///commandsName Source
  6423. ///commandsTitle 查看源码
  6424. (function (){
  6425. var sourceEditors = {
  6426. textarea: function (editor, holder){
  6427. var textarea = holder.ownerDocument.createElement('textarea');
  6428. textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';
  6429. // todo: IE下只有onresize属性可用... 很纠结
  6430. if (browser.ie && browser.version < 8) {
  6431. textarea.style.width = holder.offsetWidth + 'px';
  6432. textarea.style.height = holder.offsetHeight + 'px';
  6433. holder.onresize = function (){
  6434. textarea.style.width = holder.offsetWidth + 'px';
  6435. textarea.style.height = holder.offsetHeight + 'px';
  6436. };
  6437. }
  6438. holder.appendChild(textarea);
  6439. return {
  6440. container : textarea,
  6441. setContent: function (content){
  6442. textarea.value = content;
  6443. },
  6444. getContent: function (){
  6445. return textarea.value;
  6446. },
  6447. select: function (){
  6448. var range;
  6449. if (browser.ie) {
  6450. range = textarea.createTextRange();
  6451. range.collapse(true);
  6452. range.select();
  6453. } else {
  6454. //todo: chrome下无法设置焦点
  6455. textarea.setSelectionRange(0, 0);
  6456. textarea.focus();
  6457. }
  6458. },
  6459. dispose: function (){
  6460. holder.removeChild(textarea);
  6461. // todo
  6462. holder.onresize = null;
  6463. textarea = null;
  6464. holder = null;
  6465. }
  6466. };
  6467. }
  6468. };
  6469. UM.plugins['source'] = function (){
  6470. var me = this;
  6471. var opt = this.options;
  6472. var sourceMode = false;
  6473. var sourceEditor;
  6474. opt.sourceEditor = 'textarea';
  6475. me.setOpt({
  6476. sourceEditorFirst:false
  6477. });
  6478. function createSourceEditor(holder){
  6479. return sourceEditors.textarea(me, holder);
  6480. }
  6481. var bakCssText;
  6482. //解决在源码模式下getContent不能得到最新的内容问题
  6483. var oldGetContent = me.getContent,
  6484. bakAddress;
  6485. me.commands['source'] = {
  6486. execCommand: function (){
  6487. sourceMode = !sourceMode;
  6488. if (sourceMode) {
  6489. bakAddress = me.selection.getRange().createAddress(false,true);
  6490. me.undoManger && me.undoManger.save(true);
  6491. if(browser.gecko){
  6492. me.body.contentEditable = false;
  6493. }
  6494. // bakCssText = me.body.style.cssText;
  6495. me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';
  6496. me.fireEvent('beforegetcontent');
  6497. var root = UM.htmlparser(me.body.innerHTML);
  6498. me.filterOutputRule(root);
  6499. root.traversal(function (node) {
  6500. if (node.type == 'element') {
  6501. switch (node.tagName) {
  6502. case 'td':
  6503. case 'th':
  6504. case 'caption':
  6505. if(node.children && node.children.length == 1){
  6506. if(node.firstChild().tagName == 'br' ){
  6507. node.removeChild(node.firstChild())
  6508. }
  6509. };
  6510. break;
  6511. case 'pre':
  6512. node.innerText(node.innerText().replace(/&nbsp;/g,' '))
  6513. }
  6514. }
  6515. });
  6516. me.fireEvent('aftergetcontent');
  6517. var content = root.toHtml(true);
  6518. sourceEditor = createSourceEditor(me.body.parentNode);
  6519. sourceEditor.setContent(content);
  6520. var getStyleValue=function(attr){
  6521. return parseInt($(me.body).css(attr));
  6522. };
  6523. $(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right"))
  6524. .height($(me.body).height());
  6525. setTimeout(function (){
  6526. sourceEditor.select();
  6527. });
  6528. //重置getContent,源码模式下取值也能是最新的数据
  6529. me.getContent = function (){
  6530. return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  6531. };
  6532. } else {
  6533. me.$body.css({
  6534. 'position':'',
  6535. 'left':'',
  6536. 'top':''
  6537. });
  6538. // me.body.style.cssText = bakCssText;
  6539. var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
  6540. //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
  6541. cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
  6542. if(b && !dtd.$inlineWithA[b.toLowerCase()]){
  6543. return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
  6544. }
  6545. return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
  6546. });
  6547. me.setContent(cont);
  6548. sourceEditor.dispose();
  6549. sourceEditor = null;
  6550. //还原getContent方法
  6551. me.getContent = oldGetContent;
  6552. var first = me.body.firstChild;
  6553. //trace:1106 都删除空了,下边会报错,所以补充一个p占位
  6554. if(!first){
  6555. me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
  6556. }
  6557. //要在ifm为显示时ff才能取到selection,否则报错
  6558. //这里不能比较位置了
  6559. me.undoManger && me.undoManger.save(true);
  6560. if(browser.gecko){
  6561. me.body.contentEditable = true;
  6562. }
  6563. try{
  6564. me.selection.getRange().moveToAddress(bakAddress).select();
  6565. }catch(e){}
  6566. }
  6567. this.fireEvent('sourcemodechanged', sourceMode);
  6568. },
  6569. queryCommandState: function (){
  6570. return sourceMode|0;
  6571. },
  6572. notNeedUndo : 1
  6573. };
  6574. var oldQueryCommandState = me.queryCommandState;
  6575. me.queryCommandState = function (cmdName){
  6576. cmdName = cmdName.toLowerCase();
  6577. if (sourceMode) {
  6578. //源码模式下可以开启的命令
  6579. return cmdName in {
  6580. 'source' : 1,
  6581. 'fullscreen' : 1
  6582. } ? oldQueryCommandState.apply(this, arguments) : -1
  6583. }
  6584. return oldQueryCommandState.apply(this, arguments);
  6585. };
  6586. };
  6587. })();
  6588. ///import core
  6589. ///import plugins/undo.js
  6590. ///commands 设置回车标签p或br
  6591. ///commandsName EnterKey
  6592. ///commandsTitle 设置回车标签p或br
  6593. /**
  6594. * @description 处理回车
  6595. * @author zhanyi
  6596. */
  6597. UM.plugins['enterkey'] = function() {
  6598. var hTag,
  6599. me = this,
  6600. tag = me.options.enterTag;
  6601. me.addListener('keyup', function(type, evt) {
  6602. var keyCode = evt.keyCode || evt.which;
  6603. if (keyCode == 13) {
  6604. var range = me.selection.getRange(),
  6605. start = range.startContainer,
  6606. doSave;
  6607. //修正在h1-h6里边回车后不能嵌套p的问题
  6608. if (!browser.ie) {
  6609. if (/h\d/i.test(hTag)) {
  6610. if (browser.gecko) {
  6611. var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
  6612. if (!h) {
  6613. me.document.execCommand('formatBlock', false, '<p>');
  6614. doSave = 1;
  6615. }
  6616. } else {
  6617. //chrome remove div
  6618. if (start.nodeType == 1) {
  6619. var tmp = me.document.createTextNode(''),div;
  6620. range.insertNode(tmp);
  6621. div = domUtils.findParentByTagName(tmp, 'div', true);
  6622. if (div) {
  6623. var p = me.document.createElement('p');
  6624. while (div.firstChild) {
  6625. p.appendChild(div.firstChild);
  6626. }
  6627. div.parentNode.insertBefore(p, div);
  6628. domUtils.remove(div);
  6629. range.setStartBefore(tmp).setCursor();
  6630. doSave = 1;
  6631. }
  6632. domUtils.remove(tmp);
  6633. }
  6634. }
  6635. if (me.undoManger && doSave) {
  6636. me.undoManger.save();
  6637. }
  6638. }
  6639. //没有站位符,会出现多行的问题
  6640. browser.opera && range.select();
  6641. }else{
  6642. me.fireEvent('saveScene',true,true)
  6643. }
  6644. }
  6645. });
  6646. me.addListener('keydown', function(type, evt) {
  6647. var keyCode = evt.keyCode || evt.which;
  6648. if (keyCode == 13) {//回车
  6649. if(me.fireEvent('beforeenterkeydown')){
  6650. domUtils.preventDefault(evt);
  6651. return;
  6652. }
  6653. me.fireEvent('saveScene',true,true);
  6654. hTag = '';
  6655. var range = me.selection.getRange();
  6656. if (!range.collapsed) {
  6657. //跨td不能删
  6658. var start = range.startContainer,
  6659. end = range.endContainer,
  6660. startTd = domUtils.findParentByTagName(start, 'td', true),
  6661. endTd = domUtils.findParentByTagName(end, 'td', true);
  6662. if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
  6663. evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
  6664. return;
  6665. }
  6666. }
  6667. if (tag == 'p') {
  6668. if (!browser.ie) {
  6669. start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
  6670. //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
  6671. //trace:2431
  6672. if (!start && !browser.opera) {
  6673. me.document.execCommand('formatBlock', false, '<p>');
  6674. if (browser.gecko) {
  6675. range = me.selection.getRange();
  6676. start = domUtils.findParentByTagName(range.startContainer, 'p', true);
  6677. start && domUtils.removeDirtyAttr(start);
  6678. }
  6679. } else {
  6680. hTag = start.tagName;
  6681. start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
  6682. }
  6683. }
  6684. }
  6685. }
  6686. });
  6687. browser.ie && me.addListener('setDisabled',function(){
  6688. $(me.body).find('p').each(function(i,p){
  6689. if(domUtils.isEmptyBlock(p)){
  6690. p.innerHTML = '&nbsp;'
  6691. }
  6692. })
  6693. })
  6694. };
  6695. ///import core
  6696. ///commands 预览
  6697. ///commandsName Preview
  6698. ///commandsTitle 预览
  6699. /**
  6700. * 预览
  6701. * @function
  6702. * @name UM.execCommand
  6703. * @param {String} cmdName preview预览编辑器内容
  6704. */
  6705. UM.commands['preview'] = {
  6706. execCommand : function(){
  6707. var w = window.open('', '_blank', ''),
  6708. d = w.document,
  6709. c = this.getContent(null,null,true),
  6710. path = this.getOpt('UMEDITOR_HOME_URL'),
  6711. formula = c.indexOf('mathquill-embedded-latex')!=-1 ?
  6712. '<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +
  6713. '<script src="' + path + 'third-party/jquery.min.js"></script>' +
  6714. '<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>':'';
  6715. d.open();
  6716. d.write('<html><head>' + formula + '</head><body><div>'+c+'</div></body></html>');
  6717. d.close();
  6718. },
  6719. notNeedUndo : 1
  6720. };
  6721. ///import core
  6722. ///commands 加粗,斜体,上标,下标
  6723. ///commandsName Bold,Italic,Subscript,Superscript
  6724. ///commandsTitle 加粗,加斜,下标,上标
  6725. /**
  6726. * b u i等基础功能实现
  6727. * @function
  6728. * @name UM.execCommands
  6729. * @param {String} cmdName bold加粗。italic斜体。subscript上标。superscript下标。
  6730. */
  6731. UM.plugins['basestyle'] = function(){
  6732. var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'],
  6733. me = this;
  6734. //添加快捷键
  6735. me.addshortcutkey({
  6736. "Bold" : "ctrl+66",//^B
  6737. "Italic" : "ctrl+73", //^I
  6738. "Underline" : "ctrl+shift+85",//^U
  6739. "strikeThrough" : 'ctrl+shift+83' //^s
  6740. });
  6741. //过滤最后的产出数据
  6742. me.addOutputRule(function(root){
  6743. $.each(root.getNodesByTagName('b i u strike s'),function(i,node){
  6744. switch (node.tagName){
  6745. case 'b':
  6746. node.tagName = 'strong';
  6747. break;
  6748. case 'i':
  6749. node.tagName = 'em';
  6750. break;
  6751. case 'u':
  6752. node.tagName = 'span';
  6753. node.setStyle('text-decoration','underline');
  6754. break;
  6755. case 's':
  6756. case 'strike':
  6757. node.tagName = 'span';
  6758. node.setStyle('text-decoration','line-through')
  6759. }
  6760. });
  6761. });
  6762. $.each(basestyles,function(i,cmd){
  6763. me.commands[cmd] = {
  6764. execCommand : function( cmdName ) {
  6765. var rng = this.selection.getRange();
  6766. if(rng.collapsed && this.queryCommandState(cmdName) != 1){
  6767. var node = this.document.createElement({
  6768. 'bold':'strong',
  6769. 'underline':'u',
  6770. 'superscript':'sup',
  6771. 'subscript':'sub',
  6772. 'italic':'em',
  6773. 'strikethrough':'strike'
  6774. }[cmdName]);
  6775. rng.insertNode(node).setStart(node,0).setCursor(false);
  6776. return true;
  6777. }else{
  6778. return this.document.execCommand(cmdName)
  6779. }
  6780. },
  6781. queryCommandState : function(cmdName) {
  6782. if(browser.gecko){
  6783. return this.document.queryCommandState(cmdName)
  6784. }
  6785. var path = this.selection.getStartElementPath(),result = false;
  6786. $.each(path,function(i,n){
  6787. switch (cmdName){
  6788. case 'bold':
  6789. if(n.nodeName == 'STRONG' || n.nodeName == 'B'){
  6790. result = 1;
  6791. return false;
  6792. }
  6793. break;
  6794. case 'underline':
  6795. if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){
  6796. result = 1;
  6797. return false;
  6798. }
  6799. break;
  6800. case 'superscript':
  6801. if(n.nodeName == 'SUP'){
  6802. result = 1;
  6803. return false;
  6804. }
  6805. break;
  6806. case 'subscript':
  6807. if(n.nodeName == 'SUB'){
  6808. result = 1;
  6809. return false;
  6810. }
  6811. break;
  6812. case 'italic':
  6813. if(n.nodeName == 'EM' || n.nodeName == 'I'){
  6814. result = 1;
  6815. return false;
  6816. }
  6817. break;
  6818. case 'strikethrough':
  6819. if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){
  6820. result = 1;
  6821. return false;
  6822. }
  6823. break;
  6824. }
  6825. })
  6826. return result
  6827. }
  6828. };
  6829. })
  6830. };
  6831. ///import core
  6832. ///import plugins/inserthtml.js
  6833. ///commands 视频
  6834. ///commandsName InsertVideo
  6835. ///commandsTitle 插入视频
  6836. ///commandsDialog dialogs\video
  6837. UM.plugins['video'] = function (){
  6838. var me =this,
  6839. div;
  6840. /**
  6841. * 创建插入视频字符窜
  6842. * @param url 视频地址
  6843. * @param width 视频宽度
  6844. * @param height 视频高度
  6845. * @param align 视频对齐
  6846. * @param toEmbed 是否以flash代替显示
  6847. * @param addParagraph 是否需要添加P 标签
  6848. */
  6849. function creatInsertStr(url,width,height,id,align,toEmbed){
  6850. return !toEmbed ?
  6851. '<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-video"' +
  6852. ' src="' + me.options.UMEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UMEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'
  6853. :
  6854. '<video class="edui-faked-video" ' +
  6855. ' src="' + url + '" width="' + width + '" height="' + height + '"' + (align ? ' style="float:' + align + '"': '') +
  6856. ' controls autoplay preload="auto"></video>';
  6857. }
  6858. function switchImgAndEmbed(root,img2embed){
  6859. utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){
  6860. if(node.getAttr('class') == 'edui-faked-video'){
  6861. var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed);
  6862. node.parentNode.replaceChild(UM.uNode.createElement(html),node)
  6863. }
  6864. })
  6865. }
  6866. me.addOutputRule(function(root){
  6867. switchImgAndEmbed(root,true)
  6868. });
  6869. me.addInputRule(function(root){
  6870. switchImgAndEmbed(root)
  6871. });
  6872. me.commands["insertvideo"] = {
  6873. execCommand: function (cmd, videoObjs){
  6874. videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
  6875. var html = [],id = 'tmpVedio';
  6876. for(var i=0,vi,len = videoObjs.length;i<len;i++){
  6877. vi = videoObjs[i];
  6878. vi.url = utils.unhtml(vi.url, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g);
  6879. html.push(creatInsertStr( vi.url, vi.width || 420, vi.height || 280, id + i,vi.align,false));
  6880. }
  6881. me.execCommand("inserthtml",html.join(""),true);
  6882. },
  6883. queryCommandState : function(){
  6884. var img = me.selection.getRange().getClosedNode(),
  6885. flag = img && (img.className == "edui-faked-video");
  6886. return flag ? 1 : 0;
  6887. }
  6888. };
  6889. };
  6890. ///import core
  6891. ///commands 全选
  6892. ///commandsName SelectAll
  6893. ///commandsTitle 全选
  6894. /**
  6895. * 选中所有
  6896. * @function
  6897. * @name UM.execCommand
  6898. * @param {String} cmdName selectall选中编辑器里的所有内容
  6899. * @author zhanyi
  6900. */
  6901. UM.plugins['selectall'] = function(){
  6902. var me = this;
  6903. me.commands['selectall'] = {
  6904. execCommand : function(){
  6905. //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
  6906. var me = this,body = me.body,
  6907. range = me.selection.getRange();
  6908. range.selectNodeContents(body);
  6909. if(domUtils.isEmptyBlock(body)){
  6910. //opera不能自动合并到元素的里边,要手动处理一下
  6911. if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
  6912. range.setStartAtFirst(body.firstChild);
  6913. }
  6914. range.collapse(true);
  6915. }
  6916. range.select(true);
  6917. },
  6918. notNeedUndo : 1
  6919. };
  6920. //快捷键
  6921. me.addshortcutkey({
  6922. "selectAll" : "ctrl+65"
  6923. });
  6924. };
  6925. //UM.plugins['removeformat'] = function () {
  6926. // var me = this;
  6927. // me.commands['removeformat'] = {
  6928. // execCommand: function () {
  6929. // me.document.execCommand('removeformat');
  6930. //
  6931. // /* 处理ie8和firefox选区有链接时,清除格式的bug */
  6932. // if (browser.gecko || browser.ie8 || browser.webkit) {
  6933. // var nativeRange = this.selection.getNative().getRangeAt(0),
  6934. // common = nativeRange.commonAncestorContainer,
  6935. // rng = me.selection.getRange(),
  6936. // bk = rng.createBookmark();
  6937. //
  6938. // function isEleInBookmark(node, bk){
  6939. // if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&
  6940. // (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {
  6941. // return true;
  6942. // } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||
  6943. // (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {
  6944. // return true;
  6945. // }
  6946. // return false;
  6947. // }
  6948. //
  6949. // $(common).find('a').each(function (k, a) {
  6950. // if ( isEleInBookmark(a, bk) ) {
  6951. // a.removeAttribute('style');
  6952. // }
  6953. // });
  6954. //
  6955. // }
  6956. // }
  6957. // };
  6958. //
  6959. //};
  6960. //
  6961. UM.plugins['removeformat'] = function(){
  6962. var me = this;
  6963. me.setOpt({
  6964. 'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
  6965. 'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
  6966. });
  6967. me.commands['removeformat'] = {
  6968. execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
  6969. var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
  6970. removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
  6971. range = new dom.Range( this.document ),
  6972. bookmark,node,parent,
  6973. filter = function( node ) {
  6974. return node.nodeType == 1;
  6975. };
  6976. function isRedundantSpan (node) {
  6977. if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
  6978. return 0;
  6979. }
  6980. if (browser.ie) {
  6981. //ie 下判断实效,所以只能简单用style来判断
  6982. //return node.style.cssText == '' ? 1 : 0;
  6983. var attrs = node.attributes;
  6984. if ( attrs.length ) {
  6985. for ( var i = 0,l = attrs.length; i<l; i++ ) {
  6986. if ( attrs[i].specified ) {
  6987. return 0;
  6988. }
  6989. }
  6990. return 1;
  6991. }
  6992. }
  6993. return !node.attributes.length;
  6994. }
  6995. function doRemove( range ) {
  6996. var bookmark1 = range.createBookmark();
  6997. if ( range.collapsed ) {
  6998. range.enlarge( true );
  6999. }
  7000. //不能把a标签切了
  7001. if(!notIncludeA){
  7002. var aNode = domUtils.findParentByTagName(range.startContainer,'a',true);
  7003. if(aNode){
  7004. range.setStartBefore(aNode);
  7005. }
  7006. aNode = domUtils.findParentByTagName(range.endContainer,'a',true);
  7007. if(aNode){
  7008. range.setEndAfter(aNode);
  7009. }
  7010. }
  7011. bookmark = range.createBookmark();
  7012. node = bookmark.start;
  7013. //切开始
  7014. while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
  7015. domUtils.breakParent( node, parent );
  7016. domUtils.clearEmptySibling( node );
  7017. }
  7018. if ( bookmark.end ) {
  7019. //切结束
  7020. node = bookmark.end;
  7021. while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
  7022. domUtils.breakParent( node, parent );
  7023. domUtils.clearEmptySibling( node );
  7024. }
  7025. //开始去除样式
  7026. var current = domUtils.getNextDomNode( bookmark.start, false, filter ),
  7027. next;
  7028. while ( current ) {
  7029. if ( current == bookmark.end ) {
  7030. break;
  7031. }
  7032. next = domUtils.getNextDomNode( current, true, filter );
  7033. if ( !dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode( current ) ) {
  7034. if ( tagReg.test( current.tagName ) ) {
  7035. if ( style ) {
  7036. domUtils.removeStyle( current, style );
  7037. if ( isRedundantSpan( current ) && style != 'text-decoration'){
  7038. domUtils.remove( current, true );
  7039. }
  7040. } else {
  7041. domUtils.remove( current, true );
  7042. }
  7043. } else {
  7044. //trace:939 不能把list上的样式去掉
  7045. if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){
  7046. domUtils.removeAttributes( current, removeFormatAttributes );
  7047. if ( isRedundantSpan( current ) ){
  7048. domUtils.remove( current, true );
  7049. }
  7050. }
  7051. }
  7052. }
  7053. current = next;
  7054. }
  7055. }
  7056. //trace:1035
  7057. //trace:1096 不能把td上的样式去掉,比如边框
  7058. var pN = bookmark.start.parentNode;
  7059. if(domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]){
  7060. domUtils.removeAttributes( pN,removeFormatAttributes );
  7061. }
  7062. pN = bookmark.end.parentNode;
  7063. if(bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName]&& !dtd.$list[pN.tagName]){
  7064. domUtils.removeAttributes( pN,removeFormatAttributes );
  7065. }
  7066. range.moveToBookmark( bookmark ).moveToBookmark(bookmark1);
  7067. //清除冗余的代码 <b><bookmark></b>
  7068. var node = range.startContainer,
  7069. tmp,
  7070. collapsed = range.collapsed;
  7071. while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
  7072. tmp = node.parentNode;
  7073. range.setStartBefore(node);
  7074. //trace:937
  7075. //更新结束边界
  7076. if(range.startContainer === range.endContainer){
  7077. range.endOffset--;
  7078. }
  7079. domUtils.remove(node);
  7080. node = tmp;
  7081. }
  7082. if(!collapsed){
  7083. node = range.endContainer;
  7084. while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
  7085. tmp = node.parentNode;
  7086. range.setEndBefore(node);
  7087. domUtils.remove(node);
  7088. node = tmp;
  7089. }
  7090. }
  7091. }
  7092. range = this.selection.getRange();
  7093. if(!range.collapsed) {
  7094. doRemove( range );
  7095. range.select();
  7096. }
  7097. }
  7098. };
  7099. };
  7100. /*
  7101. * 处理特殊键的兼容性问题
  7102. */
  7103. UM.plugins['keystrokes'] = function() {
  7104. var me = this;
  7105. var collapsed = true;
  7106. me.addListener('keydown', function(type, evt) {
  7107. var keyCode = evt.keyCode || evt.which,
  7108. rng = me.selection.getRange();
  7109. //处理全选的情况
  7110. if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
  7111. || keyCode >= 48 && keyCode <= 57 ||
  7112. keyCode >= 96 && keyCode <= 111 || {
  7113. 13:1,
  7114. 8:1,
  7115. 46:1
  7116. }[keyCode])
  7117. ){
  7118. var tmpNode = rng.startContainer;
  7119. if(domUtils.isFillChar(tmpNode)){
  7120. rng.setStartBefore(tmpNode)
  7121. }
  7122. tmpNode = rng.endContainer;
  7123. if(domUtils.isFillChar(tmpNode)){
  7124. rng.setEndAfter(tmpNode)
  7125. }
  7126. rng.txtToElmBoundary();
  7127. //结束边界可能放到了br的前边,要把br包含进来
  7128. // x[xxx]<br/>
  7129. if(rng.endContainer && rng.endContainer.nodeType == 1){
  7130. tmpNode = rng.endContainer.childNodes[rng.endOffset];
  7131. if(tmpNode && domUtils.isBr(tmpNode)){
  7132. rng.setEndAfter(tmpNode);
  7133. }
  7134. }
  7135. if(rng.startOffset == 0){
  7136. tmpNode = rng.startContainer;
  7137. if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
  7138. tmpNode = rng.endContainer;
  7139. if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
  7140. me.fireEvent('saveScene');
  7141. me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
  7142. rng.setStart(me.body.firstChild,0).setCursor(false,true);
  7143. me._selectionChange();
  7144. return;
  7145. }
  7146. }
  7147. }
  7148. }
  7149. //处理backspace
  7150. if (keyCode == 8) {
  7151. rng = me.selection.getRange();
  7152. collapsed = rng.collapsed;
  7153. if(me.fireEvent('delkeydown',evt)){
  7154. return;
  7155. }
  7156. var start,end;
  7157. //避免按两次删除才能生效的问题
  7158. if(rng.collapsed && rng.inFillChar()){
  7159. start = rng.startContainer;
  7160. if(domUtils.isFillChar(start)){
  7161. rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
  7162. domUtils.remove(start)
  7163. }else{
  7164. start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
  7165. rng.startOffset--;
  7166. rng.collapse(true).select(true)
  7167. }
  7168. }
  7169. //解决选中control元素不能删除的问题
  7170. if (start = rng.getClosedNode()) {
  7171. me.fireEvent('saveScene');
  7172. rng.setStartBefore(start);
  7173. domUtils.remove(start);
  7174. rng.setCursor();
  7175. me.fireEvent('saveScene');
  7176. domUtils.preventDefault(evt);
  7177. return;
  7178. }
  7179. //阻止在table上的删除
  7180. if (!browser.ie) {
  7181. start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
  7182. end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
  7183. if (start && !end || !start && end || start !== end) {
  7184. evt.preventDefault();
  7185. return;
  7186. }
  7187. }
  7188. start = rng.startContainer;
  7189. if(rng.collapsed && start.nodeType == 1){
  7190. var currentNode = start.childNodes[rng.startOffset-1];
  7191. if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){
  7192. me.fireEvent('saveScene');
  7193. rng.setStartBefore(currentNode).collapse(true);
  7194. domUtils.remove(currentNode);
  7195. rng.select();
  7196. me.fireEvent('saveScene');
  7197. }
  7198. }
  7199. //trace:3613
  7200. if(browser.chrome){
  7201. if(rng.collapsed){
  7202. while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){
  7203. rng.setStartBefore(rng.startContainer)
  7204. }
  7205. var pre = rng.startContainer.childNodes[rng.startOffset-1];
  7206. if(pre && pre.nodeName == 'BR'){
  7207. rng.setStartBefore(pre);
  7208. me.fireEvent('saveScene');
  7209. $(pre).remove();
  7210. rng.setCursor();
  7211. me.fireEvent('saveScene');
  7212. }
  7213. }
  7214. }
  7215. }
  7216. //trace:1634
  7217. //ff的del键在容器空的时候,也会删除
  7218. if(browser.gecko && keyCode == 46){
  7219. var range = me.selection.getRange();
  7220. if(range.collapsed){
  7221. start = range.startContainer;
  7222. if(domUtils.isEmptyBlock(start)){
  7223. var parent = start.parentNode;
  7224. while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
  7225. start = parent;
  7226. parent = parent.parentNode;
  7227. }
  7228. if(start === parent.lastChild)
  7229. evt.preventDefault();
  7230. return;
  7231. }
  7232. }
  7233. }
  7234. });
  7235. me.addListener('keyup', function(type, evt) {
  7236. var keyCode = evt.keyCode || evt.which,
  7237. rng,me = this;
  7238. if(keyCode == 8){
  7239. if(me.fireEvent('delkeyup')){
  7240. return;
  7241. }
  7242. rng = me.selection.getRange();
  7243. if(rng.collapsed){
  7244. var tmpNode,
  7245. autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
  7246. if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
  7247. if(domUtils.isEmptyBlock(tmpNode)){
  7248. var pre = tmpNode.previousSibling;
  7249. if(pre && pre.nodeName != 'TABLE'){
  7250. domUtils.remove(tmpNode);
  7251. rng.setStartAtLast(pre).setCursor(false,true);
  7252. return;
  7253. }else{
  7254. var next = tmpNode.nextSibling;
  7255. if(next && next.nodeName != 'TABLE'){
  7256. domUtils.remove(tmpNode);
  7257. rng.setStartAtFirst(next).setCursor(false,true);
  7258. return;
  7259. }
  7260. }
  7261. }
  7262. }
  7263. //处理当删除到body时,要重新给p标签展位
  7264. if(domUtils.isBody(rng.startContainer)){
  7265. var tmpNode = domUtils.createElement(me.document,'p',{
  7266. 'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
  7267. });
  7268. rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
  7269. }
  7270. }
  7271. //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
  7272. if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
  7273. if(browser.ie){
  7274. var span = rng.document.createElement('span');
  7275. rng.insertNode(span).setStartBefore(span).collapse(true);
  7276. rng.select();
  7277. domUtils.remove(span)
  7278. }else{
  7279. rng.select()
  7280. }
  7281. }
  7282. }
  7283. })
  7284. };
  7285. /**
  7286. * 自动保存草稿
  7287. */
  7288. UM.plugins['autosave'] = function() {
  7289. var me = this,
  7290. //无限循环保护
  7291. lastSaveTime = new Date(),
  7292. //最小保存间隔时间
  7293. MIN_TIME = 20,
  7294. //auto save key
  7295. saveKey = null;
  7296. //默认间隔时间
  7297. me.setOpt('saveInterval', 500);
  7298. //存储媒介封装
  7299. var LocalStorage = UM.LocalStorage = ( function () {
  7300. var storage = window.localStorage || getUserData() || null,
  7301. LOCAL_FILE = "localStorage";
  7302. return {
  7303. saveLocalData: function ( key, data ) {
  7304. if ( storage && data) {
  7305. storage.setItem( key, data );
  7306. return true;
  7307. }
  7308. return false;
  7309. },
  7310. getLocalData: function ( key ) {
  7311. if ( storage ) {
  7312. return storage.getItem( key );
  7313. }
  7314. return null;
  7315. },
  7316. removeItem: function ( key ) {
  7317. storage && storage.removeItem( key );
  7318. }
  7319. };
  7320. function getUserData () {
  7321. var container = document.createElement( "div" );
  7322. container.style.display = "none";
  7323. if( !container.addBehavior ) {
  7324. return null;
  7325. }
  7326. container.addBehavior("#default#userdata");
  7327. return {
  7328. getItem: function ( key ) {
  7329. var result = null;
  7330. try {
  7331. document.body.appendChild( container );
  7332. container.load( LOCAL_FILE );
  7333. result = container.getAttribute( key );
  7334. document.body.removeChild( container );
  7335. } catch ( e ) {
  7336. }
  7337. return result;
  7338. },
  7339. setItem: function ( key, value ) {
  7340. document.body.appendChild( container );
  7341. container.setAttribute( key, value );
  7342. container.save( LOCAL_FILE );
  7343. document.body.removeChild( container );
  7344. },
  7345. // 暂时没有用到
  7346. // clear: function () {
  7347. //
  7348. // var expiresTime = new Date();
  7349. // expiresTime.setFullYear( expiresTime.getFullYear() - 1 );
  7350. // document.body.appendChild( container );
  7351. // container.expires = expiresTime.toUTCString();
  7352. // container.save( LOCAL_FILE );
  7353. // document.body.removeChild( container );
  7354. //
  7355. // },
  7356. removeItem: function ( key ) {
  7357. document.body.appendChild( container );
  7358. container.removeAttribute( key );
  7359. container.save( LOCAL_FILE );
  7360. document.body.removeChild( container );
  7361. }
  7362. };
  7363. }
  7364. } )();
  7365. function save ( editor ) {
  7366. var saveData = null;
  7367. if ( new Date() - lastSaveTime < MIN_TIME ) {
  7368. return;
  7369. }
  7370. if ( !editor.hasContents() ) {
  7371. //这里不能调用命令来删除, 会造成事件死循环
  7372. saveKey && LocalStorage.removeItem( saveKey );
  7373. return;
  7374. }
  7375. lastSaveTime = new Date();
  7376. editor._saveFlag = null;
  7377. saveData = me.body.innerHTML;
  7378. if ( editor.fireEvent( "beforeautosave", {
  7379. content: saveData
  7380. } ) === false ) {
  7381. return;
  7382. }
  7383. LocalStorage.saveLocalData( saveKey, saveData );
  7384. editor.fireEvent( "afterautosave", {
  7385. content: saveData
  7386. } );
  7387. }
  7388. me.addListener('ready', function(){
  7389. var _suffix = "-drafts-data",
  7390. key = null;
  7391. if ( me.key ) {
  7392. key = me.key + _suffix;
  7393. } else {
  7394. key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;
  7395. }
  7396. //页面地址+编辑器ID 保持唯一
  7397. saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;
  7398. });
  7399. me.addListener('contentchange', function(){
  7400. if ( !saveKey ) {
  7401. return;
  7402. }
  7403. if ( me._saveFlag ) {
  7404. window.clearTimeout( me._saveFlag );
  7405. }
  7406. if ( me.options.saveInterval > 0 ) {
  7407. me._saveFlag = window.setTimeout( function () {
  7408. save( me );
  7409. }, me.options.saveInterval );
  7410. } else {
  7411. save(me);
  7412. }
  7413. })
  7414. me.commands['clearlocaldata'] = {
  7415. execCommand:function (cmd, name) {
  7416. if ( saveKey && LocalStorage.getLocalData( saveKey ) ) {
  7417. LocalStorage.removeItem( saveKey )
  7418. }
  7419. },
  7420. notNeedUndo: true,
  7421. ignoreContentChange:true
  7422. };
  7423. me.commands['getlocaldata'] = {
  7424. execCommand:function (cmd, name) {
  7425. return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : '';
  7426. },
  7427. notNeedUndo: true,
  7428. ignoreContentChange:true
  7429. };
  7430. me.commands['drafts'] = {
  7431. execCommand:function (cmd, name) {
  7432. if ( saveKey ) {
  7433. me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '<p>'+(browser.ie ? '&nbsp;' : '<br/>')+'</p>';
  7434. me.focus(true);
  7435. }
  7436. },
  7437. queryCommandState: function () {
  7438. return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1;
  7439. },
  7440. notNeedUndo: true,
  7441. ignoreContentChange:true
  7442. }
  7443. };
  7444. /**
  7445. * @description
  7446. * 1.拖放文件到编辑区域,自动上传并插入到选区
  7447. * 2.插入粘贴板的图片,自动上传并插入到选区
  7448. * @author Jinqn
  7449. * @date 2013-10-14
  7450. */
  7451. UM.plugins['autoupload'] = function () {
  7452. var me = this;
  7453. me.setOpt('pasteImageEnabled', true);
  7454. me.setOpt('dropFileEnabled', true);
  7455. var sendAndInsertImage = function (file, editor) {
  7456. //模拟数据
  7457. var fd = new FormData();
  7458. fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));
  7459. fd.append('type', 'ajax');
  7460. var xhr = new XMLHttpRequest();
  7461. xhr.open("post", me.options.imageUrl, true);
  7462. xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  7463. xhr.addEventListener('load', function (e) {
  7464. try {
  7465. var json = eval('('+e.target.response+')'),
  7466. link = json.url,
  7467. picLink = me.options.imagePath + link;
  7468. editor.execCommand('insertimage', {
  7469. src: picLink,
  7470. _src: picLink
  7471. });
  7472. } catch (er) {
  7473. }
  7474. });
  7475. xhr.send(fd);
  7476. };
  7477. function getPasteImage(e) {
  7478. return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
  7479. }
  7480. function getDropImage(e) {
  7481. return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
  7482. }
  7483. me.addListener('ready', function () {
  7484. if (window.FormData && window.FileReader) {
  7485. var autoUploadHandler = function (e) {
  7486. var hasImg = false,
  7487. items;
  7488. //获取粘贴板文件列表或者拖放文件列表
  7489. items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);
  7490. if (items) {
  7491. var len = items.length,
  7492. file;
  7493. while (len--) {
  7494. file = items[len];
  7495. if (file.getAsFile) file = file.getAsFile();
  7496. if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {
  7497. sendAndInsertImage(file, me);
  7498. hasImg = true;
  7499. }
  7500. }
  7501. if (hasImg) return false;
  7502. }
  7503. };
  7504. me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);
  7505. me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);
  7506. //取消拖放图片时出现的文字光标位置提示
  7507. me.$body.on('dragover', function (e) {
  7508. if (e.originalEvent.dataTransfer.types[0] == 'Files') {
  7509. return false;
  7510. }
  7511. });
  7512. }
  7513. });
  7514. };
  7515. /**
  7516. * 公式插件
  7517. */
  7518. UM.plugins['formula'] = function () {
  7519. var me = this;
  7520. function getActiveIframe() {
  7521. return me.$body.find('iframe.edui-formula-active')[0] || null;
  7522. }
  7523. function blurActiveIframe(){
  7524. var iframe = getActiveIframe();
  7525. iframe && iframe.contentWindow.formula.blur();
  7526. }
  7527. me.addInputRule(function (root) {
  7528. $.each(root.getNodesByTagName('span'), function (i, node) {
  7529. if (node.hasClass('mathquill-embedded-latex')) {
  7530. var firstChild, latex = '';
  7531. while(firstChild = node.firstChild()){
  7532. latex += firstChild.data;
  7533. node.removeChild(firstChild);
  7534. }
  7535. node.tagName = 'iframe';
  7536. node.setAttr({
  7537. 'frameborder': '0',
  7538. 'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',
  7539. 'data-latex': utils.unhtml(latex)
  7540. });
  7541. }
  7542. });
  7543. });
  7544. me.addOutputRule(function (root) {
  7545. $.each(root.getNodesByTagName('iframe'), function (i, node) {
  7546. if (node.hasClass('mathquill-embedded-latex')) {
  7547. node.tagName = 'span';
  7548. node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));
  7549. node.setAttr({
  7550. 'frameborder': '',
  7551. 'src': '',
  7552. 'data-latex': ''
  7553. });
  7554. }
  7555. });
  7556. });
  7557. me.addListener('click', function(){
  7558. blurActiveIframe();
  7559. });
  7560. me.addListener('afterexeccommand', function(type, cmd){
  7561. if(cmd != 'formula') {
  7562. blurActiveIframe();
  7563. }
  7564. });
  7565. me.commands['formula'] = {
  7566. execCommand: function (cmd, latex) {
  7567. var iframe = getActiveIframe();
  7568. if (iframe) {
  7569. iframe.contentWindow.formula.insertLatex(latex);
  7570. } else {
  7571. me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');
  7572. browser.ie && browser.ie9below && setTimeout(function(){
  7573. var rng = me.selection.getRange(),
  7574. startContainer = rng.startContainer;
  7575. if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){
  7576. rng.insertNode(me.document.createTextNode(' '));
  7577. rng.setCursor()
  7578. }
  7579. },100)
  7580. }
  7581. },
  7582. queryCommandState: function (cmd) {
  7583. return 0;
  7584. },
  7585. queryCommandValue: function (cmd) {
  7586. var iframe = getActiveIframe();
  7587. return iframe && iframe.contentWindow.formula.getLatex();
  7588. }
  7589. }
  7590. };
  7591. /**
  7592. * @file xssFilter.js
  7593. * @desc xss过滤器
  7594. * @author robbenmu
  7595. */
  7596. UM.plugins.xssFilter = function() {
  7597. var config = UMEDITOR_CONFIG;
  7598. var whiteList = config.whiteList;
  7599. function filter(node) {
  7600. var tagName = node.tagName;
  7601. var attrs = node.attrs;
  7602. if (!whiteList.hasOwnProperty(tagName)) {
  7603. node.parentNode.removeChild(node);
  7604. return false;
  7605. }
  7606. UM.utils.each(attrs, function (val, key) {
  7607. if (UM.utils.indexOf(whiteList[tagName], key) === -1) {
  7608. node.setAttr(key);
  7609. }
  7610. });
  7611. }
  7612. // 添加inserthtml\paste等操作用的过滤规则
  7613. if (whiteList && config.xssFilterRules) {
  7614. this.options.filterRules = function () {
  7615. var result = {};
  7616. UM.utils.each(whiteList, function(val, key) {
  7617. result[key] = function (node) {
  7618. return filter(node);
  7619. };
  7620. });
  7621. return result;
  7622. }();
  7623. }
  7624. var tagList = [];
  7625. UM.utils.each(whiteList, function (val, key) {
  7626. tagList.push(key);
  7627. });
  7628. // 添加input过滤规则
  7629. //
  7630. if (whiteList && config.inputXssFilter) {
  7631. this.addInputRule(function (root) {
  7632. root.traversal(function(node) {
  7633. if (node.type !== 'element') {
  7634. return false;
  7635. }
  7636. filter(node);
  7637. });
  7638. });
  7639. }
  7640. // 添加output过滤规则
  7641. //
  7642. if (whiteList && config.outputXssFilter) {
  7643. this.addOutputRule(function (root) {
  7644. root.traversal(function(node) {
  7645. if (node.type !== 'element') {
  7646. return false;
  7647. }
  7648. filter(node);
  7649. });
  7650. });
  7651. }
  7652. };
  7653. (function ($) {
  7654. //对jquery的扩展
  7655. $.parseTmpl = function parse(str, data) {
  7656. var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) {
  7657. return "',obj." + code.replace(/\\'/g, "'") + ",'";
  7658. }).replace(/<%([\s\S]+?)%>/g,function (match, code) {
  7659. return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
  7660. }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
  7661. var func = new Function('obj', tmpl);
  7662. return data ? func(data) : func;
  7663. };
  7664. $.extend2 = function (t, s) {
  7665. var a = arguments,
  7666. notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,
  7667. len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;
  7668. for (var i = 1; i < len; i++) {
  7669. var x = a[i];
  7670. for (var k in x) {
  7671. if (!notCover || !t.hasOwnProperty(k)) {
  7672. t[k] = x[k];
  7673. }
  7674. }
  7675. }
  7676. return t;
  7677. };
  7678. $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;
  7679. //所有ui的基类
  7680. var _eventHandler = [];
  7681. var _widget = function () {
  7682. };
  7683. var _prefix = 'edui';
  7684. _widget.prototype = {
  7685. on: function (ev, cb) {
  7686. this.root().on(ev, $.proxy(cb, this));
  7687. return this;
  7688. },
  7689. off: function (ev, cb) {
  7690. this.root().off(ev, $.proxy(cb, this));
  7691. return this;
  7692. },
  7693. trigger: function (ev, data) {
  7694. return this.root().trigger(ev, data) === false ? false : this;
  7695. },
  7696. root: function ($el) {
  7697. return this._$el || (this._$el = $el);
  7698. },
  7699. destroy: function () {
  7700. },
  7701. data: function (key, val) {
  7702. if (val !== undefined) {
  7703. this.root().data(_prefix + key, val);
  7704. return this;
  7705. } else {
  7706. return this.root().data(_prefix + key)
  7707. }
  7708. },
  7709. register: function (eventName, $el, fn) {
  7710. _eventHandler.push({
  7711. 'evtname': eventName,
  7712. '$els': $.isArray($el) ? $el : [$el],
  7713. handler: $.proxy(fn, $el)
  7714. })
  7715. }
  7716. };
  7717. //从jq实例上拿到绑定的widget实例
  7718. $.fn.edui = function (obj) {
  7719. return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');
  7720. };
  7721. function _createClass(ClassObj, properties, supperClass) {
  7722. ClassObj.prototype = $.extend2(
  7723. $.extend({}, properties),
  7724. (UM.ui[supperClass] || _widget).prototype,
  7725. true
  7726. );
  7727. ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;
  7728. //父class的defaultOpt 合并
  7729. if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) {
  7730. var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,
  7731. subDefaultOptions = ClassObj.prototype.defaultOpt;
  7732. ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} );
  7733. }
  7734. return ClassObj
  7735. }
  7736. var _guid = 1;
  7737. function mergeToJQ(ClassObj, className) {
  7738. $[_prefix + className] = ClassObj;
  7739. $.fn[_prefix + className] = function (opt) {
  7740. var result, args = Array.prototype.slice.call(arguments, 1);
  7741. this.each(function (i, el) {
  7742. var $this = $(el);
  7743. var obj = $this.edui();
  7744. if (!obj) {
  7745. ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);
  7746. $this.edui(obj)
  7747. }
  7748. if ($.type(opt) == 'string') {
  7749. if (opt == 'this') {
  7750. result = obj;
  7751. } else {
  7752. result = obj[opt].apply(obj, args);
  7753. if (result !== obj && result !== undefined) {
  7754. return false;
  7755. }
  7756. result = null;
  7757. }
  7758. }
  7759. });
  7760. return result !== null ? result : this;
  7761. }
  7762. }
  7763. UM.ui = {
  7764. define: function (className, properties, supperClass) {
  7765. var ClassObj = UM.ui[className] = _createClass(function (options, $el) {
  7766. var _obj = function () {
  7767. };
  7768. $.extend(_obj.prototype, ClassObj.prototype, {
  7769. guid: className + _guid++,
  7770. widgetName: className
  7771. }
  7772. );
  7773. var obj = new _obj;
  7774. if ($.type(options) == 'string') {
  7775. obj.init && obj.init({});
  7776. obj.root().edui(obj);
  7777. obj.root().find('a').click(function (evt) {
  7778. evt.preventDefault()
  7779. });
  7780. return obj.root()[_prefix + className].apply(obj.root(), arguments)
  7781. } else {
  7782. $el && obj.root($el);
  7783. obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);
  7784. try{
  7785. obj.root().find('a').click(function (evt) {
  7786. evt.preventDefault()
  7787. });
  7788. }catch(e){
  7789. }
  7790. return obj.root().edui(obj);
  7791. }
  7792. },properties, supperClass);
  7793. mergeToJQ(ClassObj, className);
  7794. }
  7795. };
  7796. $(function () {
  7797. $(document).on('click mouseup mousedown dblclick mouseover', function (evt) {
  7798. $.each(_eventHandler, function (i, obj) {
  7799. if (obj.evtname == evt.type) {
  7800. $.each(obj.$els, function (i, $el) {
  7801. if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {
  7802. obj.handler(evt);
  7803. }
  7804. })
  7805. }
  7806. })
  7807. })
  7808. })
  7809. })(jQuery);
  7810. //button 类
  7811. UM.ui.define('button', {
  7812. tpl: '<<%if : !${texttype}%>div class="edui-btn edui-btn-${icon} <%if : ${name}%>edui-btn-name-${name}<%/if%>" unselectable="on" onmousedown="return false" <%else%>a class="edui-text-btn"<%/if%><% if: ${title} %> data-original-title="${title}" <%/if%>> ' +
  7813. '<% if: ${icon} %><div unselectable="on" class="edui-icon-${icon} edui-icon"></div><%/if%><%if: ${text} %><span unselectable="on" onmousedown="return false" class="edui-button-label">${text}</span><%/if%>' +
  7814. '<%if: ${caret} && ${text}%><span class="edui-button-spacing"></span><%/if%>' +
  7815. '<% if: ${caret} %><span unselectable="on" onmousedown="return false" class="edui-caret"></span><%/if%></<%if: !${texttype}%>div<%else%>a<%/if%>>',
  7816. defaultOpt: {
  7817. text: '',
  7818. title: '',
  7819. icon: '',
  7820. width: '',
  7821. caret: false,
  7822. texttype: false,
  7823. click: function () {
  7824. }
  7825. },
  7826. init: function (options) {
  7827. var me = this;
  7828. me.root($(UM.utils.render(me.tpl, options)))
  7829. .click(function (evt) {
  7830. me.wrapclick(options.click, evt)
  7831. });
  7832. me.root().hover(function () {
  7833. if(!me.root().hasClass("edui-disabled")){
  7834. me.root().toggleClass('edui-hover')
  7835. }
  7836. })
  7837. return me;
  7838. },
  7839. wrapclick: function (fn, evt) {
  7840. if (!this.disabled()) {
  7841. this.root().trigger('wrapclick');
  7842. $.proxy(fn, this, evt)()
  7843. }
  7844. return this;
  7845. },
  7846. label: function (text) {
  7847. if (text === undefined) {
  7848. return this.root().find('.edui-button-label').text();
  7849. } else {
  7850. this.root().find('.edui-button-label').text(text);
  7851. return this;
  7852. }
  7853. },
  7854. disabled: function (state) {
  7855. if (state === undefined) {
  7856. return this.root().hasClass('edui-disabled')
  7857. }
  7858. this.root().toggleClass('edui-disabled', state);
  7859. if(this.root().hasClass('edui-disabled')){
  7860. this.root().removeClass('edui-hover')
  7861. }
  7862. return this;
  7863. },
  7864. active: function (state) {
  7865. if (state === undefined) {
  7866. return this.root().hasClass('edui-active')
  7867. }
  7868. this.root().toggleClass('edui-active', state)
  7869. return this;
  7870. },
  7871. mergeWith: function ($obj) {
  7872. var me = this;
  7873. me.data('$mergeObj', $obj);
  7874. $obj.edui().data('$mergeObj', me.root());
  7875. if (!$.contains(document.body, $obj[0])) {
  7876. $obj.appendTo(me.root());
  7877. }
  7878. me.on('click',function () {
  7879. me.wrapclick(function () {
  7880. $obj.edui().show();
  7881. })
  7882. }).register('click', me.root(), function (evt) {
  7883. $obj.hide()
  7884. });
  7885. }
  7886. });
  7887. //toolbar 类
  7888. (function () {
  7889. UM.ui.define('toolbar', {
  7890. tpl: '<div class="edui-toolbar" ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false" ></div></div>'
  7891. ,
  7892. init: function () {
  7893. var $root = this.root($(this.tpl));
  7894. this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))
  7895. },
  7896. appendToBtnmenu : function(data){
  7897. var $cont = this.data('$btnToolbar');
  7898. data = $.isArray(data) ? data : [data];
  7899. $.each(data,function(i,$item){
  7900. $cont.append($item)
  7901. })
  7902. }
  7903. });
  7904. })();
  7905. //menu 类
  7906. UM.ui.define('menu',{
  7907. show : function($obj,dir,fnname,topOffset,leftOffset){
  7908. fnname = fnname || 'position';
  7909. if(this.trigger('beforeshow') === false){
  7910. return;
  7911. }else{
  7912. this.root().css($.extend({display:'block'},$obj ? {
  7913. top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),
  7914. left : $obj[fnname]().left + (dir == 'right' ? $obj.outerWidth() : 0) - (leftOffset || 0)
  7915. }:{}))
  7916. this.trigger('aftershow');
  7917. }
  7918. },
  7919. hide : function(all){
  7920. var $parentmenu;
  7921. if(this.trigger('beforehide') === false){
  7922. return;
  7923. } else {
  7924. if($parentmenu = this.root().data('parentmenu')){
  7925. if($parentmenu.data('parentmenu')|| all)
  7926. $parentmenu.edui().hide();
  7927. }
  7928. this.root().css('display','none');
  7929. this.trigger('afterhide');
  7930. }
  7931. },
  7932. attachTo : function($obj){
  7933. var me = this;
  7934. if(!$obj.data('$mergeObj')){
  7935. $obj.data('$mergeObj',me.root());
  7936. $obj.on('wrapclick',function(evt){
  7937. me.show()
  7938. });
  7939. me.register('click',$obj,function(evt){
  7940. me.hide()
  7941. });
  7942. me.data('$mergeObj',$obj)
  7943. }
  7944. }
  7945. });
  7946. //dropmenu 类
  7947. UM.ui.define('dropmenu', {
  7948. tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +
  7949. '<%for: ${data} as ${ci}%>' +
  7950. '<%if: ${ci.divider}%><li class="edui-divider"></li><%else%>' +
  7951. '<li class="${ci.active} || ${ci.disabled}" data-value="${ci.value}">' +
  7952. '<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em>${ci.label}</a>' +
  7953. '</li><%/if%>' +
  7954. '<%/for%>' +
  7955. '</ul>',
  7956. defaultOpt: {
  7957. data: [],
  7958. click: function () {
  7959. }
  7960. },
  7961. init: function (options) {
  7962. var me = this;
  7963. var eventName = {
  7964. click: 1,
  7965. mouseover: 1,
  7966. mouseout: 1
  7967. };
  7968. this.root($(UM.utils.render(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) {
  7969. $.proxy(options.click, me, evt, $(this).data('value'), $(this))()
  7970. }).find('li').each(function (i, el) {
  7971. var $this = $(this);
  7972. if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {
  7973. var data = options.data[i];
  7974. $.each(eventName, function (k) {
  7975. data[k] && $this[k](function (evt) {
  7976. $.proxy(data[k], el)(evt, data, me.root)
  7977. })
  7978. })
  7979. }
  7980. })
  7981. },
  7982. disabled: function (cb) {
  7983. $('li[class!=edui-divider]', this.root()).each(function () {
  7984. var $el = $(this);
  7985. if (cb === true) {
  7986. $el.addClass('edui-disabled')
  7987. } else if ($.isFunction(cb)) {
  7988. $el.toggleClass('edui-disabled', cb(li))
  7989. } else {
  7990. $el.removeClass('edui-disabled')
  7991. }
  7992. });
  7993. },
  7994. val: function (val) {
  7995. var currentVal;
  7996. $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {
  7997. var $el = $(this);
  7998. if (val === undefined) {
  7999. if ($el.find('em.edui-dropmenu-checked').length) {
  8000. currentVal = $el.data('value');
  8001. return false
  8002. }
  8003. } else {
  8004. $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)
  8005. }
  8006. });
  8007. if (val === undefined) {
  8008. return currentVal
  8009. }
  8010. },
  8011. addSubmenu: function (label, menu, index) {
  8012. index = index || 0;
  8013. var $list = $('li[class!=edui-divider]', this.root());
  8014. var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);
  8015. if (index >= 0 && index < $list.length) {
  8016. $node.insertBefore($list[index]);
  8017. } else if (index < 0) {
  8018. $node.insertBefore($list[0]);
  8019. } else if (index >= $list.length) {
  8020. $node.appendTo($list);
  8021. }
  8022. }
  8023. }, 'menu');
  8024. //splitbutton 类
  8025. ///import button
  8026. UM.ui.define('splitbutton',{
  8027. tpl :'<div class="edui-splitbutton <%if: ${name}%>edui-splitbutton-${name}<%/if%>" unselectable="on" <%if: ${title}%>data-original-title="${title}"<%/if%>><div class="edui-btn" unselectable="on" ><%if: ${icon}%><div unselectable="on" class="edui-icon-${icon} edui-icon"></div><%/if%><%if: ${text}%>${text}<%/if%></div>'+
  8028. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
  8029. '<div unselectable="on" class="edui-caret"><\/div>'+
  8030. '</div>'+
  8031. '</div>',
  8032. defaultOpt:{
  8033. text:'',
  8034. title:'',
  8035. click:function(){}
  8036. },
  8037. init : function(options){
  8038. var me = this;
  8039. me.root( $(UM.utils.render(me.tpl, options)));
  8040. me.root().find('.edui-btn:first').click(function(evt){
  8041. if(!me.disabled()){
  8042. $.proxy(options.click,me)();
  8043. }
  8044. });
  8045. me.root().find('.edui-dropdown-toggle').click(function(){
  8046. if(!me.disabled()){
  8047. me.trigger('arrowclick')
  8048. }
  8049. });
  8050. me.root().hover(function () {
  8051. if(!me.root().hasClass("edui-disabled")){
  8052. me.root().toggleClass('edui-hover')
  8053. }
  8054. });
  8055. return me;
  8056. },
  8057. wrapclick:function(fn,evt){
  8058. if(!this.disabled()){
  8059. $.proxy(fn,this,evt)()
  8060. }
  8061. return this;
  8062. },
  8063. disabled : function(state){
  8064. if(state === undefined){
  8065. return this.root().hasClass('edui-disabled')
  8066. }
  8067. this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state);
  8068. return this;
  8069. },
  8070. active:function(state){
  8071. if(state === undefined){
  8072. return this.root().hasClass('edui-active')
  8073. }
  8074. this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state);
  8075. return this;
  8076. },
  8077. mergeWith:function($obj){
  8078. var me = this;
  8079. me.data('$mergeObj',$obj);
  8080. $obj.edui().data('$mergeObj',me.root());
  8081. if(!$.contains(document.body,$obj[0])){
  8082. $obj.appendTo(me.root());
  8083. }
  8084. me.root().delegate('.edui-dropdown-toggle','click',function(){
  8085. me.wrapclick(function(){
  8086. $obj.edui().show();
  8087. })
  8088. });
  8089. me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){
  8090. $obj.hide()
  8091. });
  8092. }
  8093. });
  8094. /**
  8095. * Created with JetBrains PhpStorm.
  8096. * User: hn
  8097. * Date: 13-7-10
  8098. * Time: 下午3:07
  8099. * To change this template use File | Settings | File Templates.
  8100. */
  8101. UM.ui.define('colorsplitbutton',{
  8102. tpl : '<div class="edui-splitbutton <%if : ${name}%>edui-splitbutton-${name}<%/if%>" unselectable="on" <%if: ${title}%>data-original-title="${title}"<%/if%>><div class="edui-btn" unselectable="on" ><%if: ${icon}%><div unselectable="on" class="edui-icon-${icon} edui-icon"></div><%/if%><div class="edui-splitbutton-color-label" <%if: ${color}%>style="background: ${color}"<%/if%>></div><%if: ${text}%>${text}<%/if%></div>'+
  8103. '<div unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
  8104. '<div unselectable="on" class="edui-caret"><\/div>'+
  8105. '</div>'+
  8106. '</div>',
  8107. defaultOpt: {
  8108. color: ''
  8109. },
  8110. init: function( options ){
  8111. var me = this;
  8112. me.supper.init.call( me, options );
  8113. },
  8114. colorLabel: function(){
  8115. return this.root().find('.edui-splitbutton-color-label');
  8116. }
  8117. }, 'splitbutton');
  8118. //popup 类
  8119. UM.ui.define('popup', {
  8120. tpl: '<div class="edui-dropdown-menu edui-popup"'+
  8121. '<%if:!${stopprop}%>onmousedown="return false"<%/if%>'+
  8122. '><div class="edui-popup-body" unselectable="on" onmousedown="return false">${subtpl|raw}</div>' +
  8123. '<div class="edui-popup-caret"></div>' +
  8124. '</div>',
  8125. defaultOpt: {
  8126. stopprop:false,
  8127. subtpl: '',
  8128. width: '',
  8129. height: ''
  8130. },
  8131. init: function (options) {
  8132. this.root($(UM.utils.render(this.tpl, options)));
  8133. return this;
  8134. },
  8135. mergeTpl: function (data) {
  8136. return UM.utils.render(this.tpl, {subtpl: data});
  8137. },
  8138. show: function ($obj, posObj) {
  8139. if (!posObj) posObj = {};
  8140. var fnname = posObj.fnname || 'position';
  8141. if (this.trigger('beforeshow') === false) {
  8142. return;
  8143. } else {
  8144. this.root().css($.extend({display: 'block'}, $obj ? {
  8145. top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),
  8146. left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),
  8147. position: 'absolute'
  8148. } : {}));
  8149. this.root().find('.edui-popup-caret').css({
  8150. top: posObj.caretTop || 0,
  8151. left: posObj.caretLeft || 0,
  8152. position: 'absolute'
  8153. }).addClass(posObj.caretDir || "up")
  8154. }
  8155. this.trigger("aftershow");
  8156. },
  8157. hide: function () {
  8158. this.root().css('display', 'none');
  8159. this.trigger('afterhide')
  8160. },
  8161. attachTo: function ($obj, posObj) {
  8162. var me = this
  8163. if (!$obj.data('$mergeObj')) {
  8164. $obj.data('$mergeObj', me.root());
  8165. $obj.on('wrapclick', function (evt) {
  8166. me.show($obj, posObj)
  8167. });
  8168. me.register('click', $obj, function (evt) {
  8169. me.hide()
  8170. });
  8171. me.data('$mergeObj', $obj)
  8172. }
  8173. },
  8174. getBodyContainer: function () {
  8175. return this.root().find(".edui-popup-body");
  8176. }
  8177. });
  8178. //scale 类
  8179. UM.ui.define('scale', {
  8180. tpl: '<div class="edui-scale" unselectable="on">' +
  8181. '<span class="edui-scale-hand0"></span>' +
  8182. '<span class="edui-scale-hand1"></span>' +
  8183. '<span class="edui-scale-hand2"></span>' +
  8184. '<span class="edui-scale-hand3"></span>' +
  8185. '<span class="edui-scale-hand4"></span>' +
  8186. '<span class="edui-scale-hand5"></span>' +
  8187. '<span class="edui-scale-hand6"></span>' +
  8188. '<span class="edui-scale-hand7"></span>' +
  8189. '</div>',
  8190. defaultOpt: {
  8191. $doc: $(document),
  8192. $wrap: $(document)
  8193. },
  8194. init: function (options) {
  8195. if(options.$doc) this.defaultOpt.$doc = options.$doc;
  8196. if(options.$wrap) this.defaultOpt.$wrap = options.$wrap;
  8197. this.root($(UM.utils.render(this.tpl, options)));
  8198. this.initStyle();
  8199. this.startPos = this.prePos = {x: 0, y: 0};
  8200. this.dragId = -1;
  8201. return this;
  8202. },
  8203. initStyle: function () {
  8204. utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +
  8205. '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
  8206. + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
  8207. + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
  8208. + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
  8209. + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
  8210. + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
  8211. + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
  8212. + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
  8213. + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
  8214. },
  8215. _eventHandler: function (e) {
  8216. var me = this,
  8217. $doc = me.defaultOpt.$doc;
  8218. switch (e.type) {
  8219. case 'mousedown':
  8220. var hand = e.target || e.srcElement, hand;
  8221. if (hand.className.indexOf('edui-scale-hand') != -1) {
  8222. me.dragId = hand.className.slice(-1);
  8223. me.startPos.x = me.prePos.x = e.clientX;
  8224. me.startPos.y = me.prePos.y = e.clientY;
  8225. $doc.bind('mousemove', $.proxy(me._eventHandler, me));
  8226. }
  8227. break;
  8228. case 'mousemove':
  8229. if (me.dragId != -1) {
  8230. me.updateContainerStyle(me.dragId, {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y});
  8231. me.prePos.x = e.clientX;
  8232. me.prePos.y = e.clientY;
  8233. me.updateTargetElement();
  8234. }
  8235. break;
  8236. case 'mouseup':
  8237. if (me.dragId != -1) {
  8238. me.dragId = -1;
  8239. me.updateTargetElement();
  8240. var $target = me.data('$scaleTarget');
  8241. if ($target.parent()) me.attachTo(me.data('$scaleTarget'));
  8242. }
  8243. $doc.unbind('mousemove', $.proxy(me._eventHandler, me));
  8244. break;
  8245. default:
  8246. break;
  8247. }
  8248. },
  8249. updateTargetElement: function () {
  8250. var me = this,
  8251. $root = me.root(),
  8252. $target = me.data('$scaleTarget');
  8253. $target.css({width: $root.width(), height: $root.height()});
  8254. me.attachTo($target);
  8255. },
  8256. updateContainerStyle: function (dir, offset) {
  8257. var me = this,
  8258. $dom = me.root(),
  8259. tmp,
  8260. rect = [
  8261. //[left, top, width, height]
  8262. [0, 0, -1, -1],
  8263. [0, 0, 0, -1],
  8264. [0, 0, 1, -1],
  8265. [0, 0, -1, 0],
  8266. [0, 0, 1, 0],
  8267. [0, 0, -1, 1],
  8268. [0, 0, 0, 1],
  8269. [0, 0, 1, 1]
  8270. ];
  8271. if (rect[dir][0] != 0) {
  8272. tmp = parseInt($dom.offset().left) + offset.x;
  8273. $dom.css('left', me._validScaledProp('left', tmp));
  8274. }
  8275. if (rect[dir][1] != 0) {
  8276. tmp = parseInt($dom.offset().top) + offset.y;
  8277. $dom.css('top', me._validScaledProp('top', tmp));
  8278. }
  8279. if (rect[dir][2] != 0) {
  8280. tmp = $dom.width() + rect[dir][2] * offset.x;
  8281. $dom.css('width', me._validScaledProp('width', tmp));
  8282. }
  8283. if (rect[dir][3] != 0) {
  8284. tmp = $dom.height() + rect[dir][3] * offset.y;
  8285. $dom.css('height', me._validScaledProp('height', tmp));
  8286. }
  8287. },
  8288. _validScaledProp: function (prop, value) {
  8289. var $ele = this.root(),
  8290. $wrap = this.defaultOpt.$doc,
  8291. calc = function(val, a, b){
  8292. return (val + a) > b ? b - a : value;
  8293. };
  8294. value = isNaN(value) ? 0 : value;
  8295. switch (prop) {
  8296. case 'left':
  8297. return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());
  8298. case 'top':
  8299. return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height());
  8300. case 'width':
  8301. return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());
  8302. case 'height':
  8303. return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());
  8304. }
  8305. },
  8306. show: function ($obj) {
  8307. var me = this;
  8308. if ($obj) me.attachTo($obj);
  8309. me.root().bind('mousedown', $.proxy(me._eventHandler, me));
  8310. me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));
  8311. me.root().show();
  8312. me.trigger("aftershow");
  8313. },
  8314. hide: function () {
  8315. var me = this;
  8316. me.root().unbind('mousedown', $.proxy(me._eventHandler, me));
  8317. me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));
  8318. me.root().hide();
  8319. me.trigger('afterhide')
  8320. },
  8321. attachTo: function ($obj) {
  8322. var me = this,
  8323. imgPos = $obj.offset(),
  8324. $root = me.root(),
  8325. $wrap = me.defaultOpt.$wrap,
  8326. posObj = $wrap.offset();
  8327. me.data('$scaleTarget', $obj);
  8328. me.root().css({
  8329. position: 'absolute',
  8330. width: $obj.width(),
  8331. height: $obj.height(),
  8332. left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),
  8333. top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))
  8334. });
  8335. },
  8336. getScaleTarget: function () {
  8337. return this.data('$scaleTarget')[0];
  8338. }
  8339. });
  8340. //colorpicker 类
  8341. UM.ui.define('colorpicker', {
  8342. tpl: function (opt) {
  8343. var COLORS = (
  8344. 'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
  8345. 'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
  8346. 'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
  8347. 'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
  8348. 'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
  8349. '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
  8350. 'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  8351. var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if : ${name}%> edui-colorpicker-${name}<%/if%>" >' +
  8352. '<table unselectable="on" onmousedown="return false">' +
  8353. '<tr><td colspan="10">'+opt.lang_themeColor+'</td> </tr>' +
  8354. '<tr class="edui-colorpicker-firstrow" >';
  8355. for (var i = 0; i < COLORS.length; i++) {
  8356. if (i && i % 10 === 0) {
  8357. html += '</tr>' + (i == 60 ? '<tr><td colspan="10">'+opt.lang_standardColor+'</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';
  8358. }
  8359. html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +
  8360. ' data-color="#' + COLORS[i] + '"' +
  8361. ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
  8362. (i < 10 || i >= 60 ? 'border-width:1px;' :
  8363. i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
  8364. 'border-width:0 1px 0 1px;') +
  8365. '"' +
  8366. '></a></td>' : '';
  8367. }
  8368. html += '</tr></table></div>';
  8369. return html;
  8370. },
  8371. init: function (options) {
  8372. var me = this;
  8373. me.root($(UM.utils.render(me.supper.mergeTpl(me.tpl(options)),options)));
  8374. me.root().on("click",function (e) {
  8375. me.trigger('pickcolor', $(e.target).data('color'));
  8376. });
  8377. }
  8378. }, 'popup');
  8379. /**
  8380. * Created with JetBrains PhpStorm.
  8381. * User: hn
  8382. * Date: 13-5-29
  8383. * Time: 下午8:01
  8384. * To change this template use File | Settings | File Templates.
  8385. */
  8386. (function(){
  8387. var widgetName = 'combobox',
  8388. itemClassName = 'edui-combobox-item',
  8389. HOVER_CLASS = 'edui-combobox-item-hover',
  8390. ICON_CLASS = 'edui-combobox-checked-icon',
  8391. labelClassName = 'edui-combobox-item-label';
  8392. UM.ui.define( widgetName, ( function(){
  8393. return {
  8394. tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if: ${comboboxName} !==''%> edui-combobox-${comboboxName}<%/if%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +
  8395. "<%if: ${autoRecord} %>" +
  8396. "<%for : ${recordStack} as ${recordItem}, ${index}%>" +
  8397. "<%var : style = ${itemStyles}[${recordItem}]%><%var : record = ${items}[${recordItem}]%>" +
  8398. "<li class=\"${itemClassName}<%if: ${selected} == ${recordItem}%> edui-combobox-checked<%/if%>\" data-item-index=\"${recordItem}\" unselectable=\"on\" onmousedown=\"return false\">" +
  8399. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8400. "<label class=\"${labelClassName}\" style=\"${style}\" unselectable=\"on\" onmousedown=\"return false\">${record}</label>" +
  8401. "</li>" +
  8402. "<%/for%>" +
  8403. "<%if: ${index} %>" +
  8404. "<li class=\"edui-combobox-item-separator\"></li>" +
  8405. "<%/if%>" +
  8406. "<%/if%>" +
  8407. "<%for: ${items} as ${item}, ${itemIndex}%>" +
  8408. "<%var : labelStyle = ${itemStyles}[${itemIndex}]%>" +
  8409. "<li class=\"${itemClassName}<%if: ${selected} == ${item} %> edui-combobox-checked<%/if%> edui-combobox-item-${itemIndex}\" data-item-index=\"${itemIndex}\" unselectable=\"on\" onmousedown=\"return false\">" +
  8410. "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
  8411. "<label class=\"${labelClassName}\" style=\"${labelStyle}\" unselectable=\"on\" onmousedown=\"return false\">${item}</label>" +
  8412. "</li>" +
  8413. "<%/for%>" +
  8414. "</ul>",
  8415. defaultOpt: {
  8416. //记录栈初始列表
  8417. recordStack: [],
  8418. //可用项列表
  8419. items: [],
  8420. //item对应的值列表
  8421. value: [],
  8422. comboboxName: '',
  8423. selected: '',
  8424. //自动记录
  8425. autoRecord: true,
  8426. //最多记录条数
  8427. recordCount: 5
  8428. },
  8429. init: function( options ){
  8430. var me = this;
  8431. $.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), {
  8432. itemClassName: itemClassName,
  8433. iconClass: ICON_CLASS,
  8434. labelClassName: labelClassName
  8435. } );
  8436. this._transStack( options );
  8437. me.root( $( UM.utils.render(me.tpl, options) ) );
  8438. this.data( 'options', options ).initEvent();
  8439. },
  8440. initEvent: function(){
  8441. var me = this;
  8442. me.initSelectItem();
  8443. this.initItemActive();
  8444. },
  8445. /**
  8446. * 初始化选择项
  8447. */
  8448. initSelectItem: function(){
  8449. var me = this,
  8450. labelClass = "."+labelClassName;
  8451. me.root().delegate('.' + itemClassName, 'click', function(){
  8452. var $li = $(this),
  8453. index = $li.attr('data-item-index');
  8454. me.trigger('comboboxselect', {
  8455. index: index,
  8456. label: $li.find(labelClass).text(),
  8457. value: me.data('options').value[ index ]
  8458. }).select( index );
  8459. me.hide();
  8460. return false;
  8461. });
  8462. },
  8463. initItemActive: function(){
  8464. var fn = {
  8465. mouseenter: 'addClass',
  8466. mouseleave: 'removeClass'
  8467. };
  8468. if ($.IE6) {
  8469. this.root().delegate( '.'+itemClassName, 'mouseenter mouseleave', function( evt ){
  8470. $(this)[ fn[ evt.type ] ]( HOVER_CLASS );
  8471. }).one('afterhide', function(){
  8472. });
  8473. }
  8474. },
  8475. /**
  8476. * 选择给定索引的项
  8477. * @param index 项索引
  8478. * @returns {*} 如果存在对应索引的项,则返回该项;否则返回null
  8479. */
  8480. select: function( index ){
  8481. var itemCount = this.data('options').itemCount,
  8482. items = this.data('options').autowidthitem;
  8483. if ( items && !items.length ) {
  8484. items = this.data('options').items;
  8485. }
  8486. if( itemCount == 0 ) {
  8487. return null;
  8488. }
  8489. if( index < 0 ) {
  8490. index = itemCount + index % itemCount;
  8491. } else if ( index >= itemCount ) {
  8492. index = itemCount-1;
  8493. }
  8494. this.trigger( 'changebefore', items[ index ] );
  8495. this._update( index );
  8496. this.trigger( 'changeafter', items[ index ] );
  8497. return null;
  8498. },
  8499. selectItemByLabel: function( label ){
  8500. var itemMapping = this.data('options').itemMapping,
  8501. me = this,
  8502. index = null;
  8503. !$.isArray( label ) && ( label = [ label ] );
  8504. $.each( label, function( i, item ){
  8505. index = itemMapping[ item ];
  8506. if( index !== undefined ) {
  8507. me.select( index );
  8508. return false;
  8509. }
  8510. } );
  8511. },
  8512. /**
  8513. * 转换记录栈
  8514. */
  8515. _transStack: function( options ) {
  8516. var temp = [],
  8517. itemIndex = -1,
  8518. selected = -1;
  8519. $.each( options.recordStack, function( index, item ){
  8520. itemIndex = options.itemMapping[ item ];
  8521. if( $.isNumeric( itemIndex ) ) {
  8522. temp.push( itemIndex );
  8523. //selected的合法性检测
  8524. if( item == options.selected ) {
  8525. selected = itemIndex;
  8526. }
  8527. }
  8528. } );
  8529. options.recordStack = temp;
  8530. options.selected = selected;
  8531. temp = null;
  8532. },
  8533. _optionAdaptation: function( options ) {
  8534. if( !( 'itemStyles' in options ) ) {
  8535. options.itemStyles = [];
  8536. for( var i = 0, len = options.items.length; i < len; i++ ) {
  8537. options.itemStyles.push('');
  8538. }
  8539. }
  8540. options.autowidthitem = options.autowidthitem || options.items;
  8541. options.itemCount = options.items.length;
  8542. return options;
  8543. },
  8544. _createItemMapping: function( stackItem, items ){
  8545. var temp = {},
  8546. result = {
  8547. recordStack: [],
  8548. mapping: {}
  8549. };
  8550. $.each( items, function( index, item ){
  8551. temp[ item ] = index;
  8552. } );
  8553. result.itemMapping = temp;
  8554. $.each( stackItem, function( index, item ){
  8555. if( temp[ item ] !== undefined ) {
  8556. result.recordStack.push( temp[ item ] );
  8557. result.mapping[ item ] = temp[ item ];
  8558. }
  8559. } );
  8560. return result;
  8561. },
  8562. _update: function ( index ) {
  8563. var options = this.data("options"),
  8564. newStack = [],
  8565. newChilds = null;
  8566. $.each( options.recordStack, function( i, item ){
  8567. if( item != index ) {
  8568. newStack.push( +item );
  8569. }
  8570. } );
  8571. //压入最新的记录
  8572. newStack.unshift( +index );
  8573. if( newStack.length > options.recordCount ) {
  8574. newStack.length = options.recordCount;
  8575. }
  8576. options.recordStack = newStack;
  8577. options.selected = +index;
  8578. newChilds = $( UM.utils.render(this.tpl, options ) );
  8579. //重新渲染
  8580. this.root().html( newChilds.html() );
  8581. newChilds = null;
  8582. newStack = null;
  8583. }
  8584. };
  8585. } )(), 'menu' );
  8586. })();
  8587. /**
  8588. * Combox 抽象基类
  8589. * User: hn
  8590. * Date: 13-5-29
  8591. * Time: 下午8:01
  8592. * To change this template use File | Settings | File Templates.
  8593. */
  8594. (function(){
  8595. var widgetName = 'buttoncombobox';
  8596. UM.ui.define( widgetName, ( function(){
  8597. return {
  8598. defaultOpt: {
  8599. //按钮初始文字
  8600. label: '',
  8601. title: ''
  8602. },
  8603. init: function( options ) {
  8604. var me = this;
  8605. var btnWidget = $.eduibutton({
  8606. caret: true,
  8607. name: options.comboboxName,
  8608. title: options.title,
  8609. text: options.label,
  8610. click: function(){
  8611. me.show( this.root() );
  8612. }
  8613. });
  8614. me.supper.init.call( me, options );
  8615. //监听change, 改变button显示内容
  8616. me.on('changebefore', function( e, label ){
  8617. btnWidget.eduibutton('label', label );
  8618. });
  8619. me.data( 'button', btnWidget );
  8620. me.attachTo(btnWidget)
  8621. },
  8622. button: function(){
  8623. return this.data( 'button' );
  8624. }
  8625. }
  8626. } )(), 'combobox' );
  8627. })();
  8628. /*modal 类*/
  8629. UM.ui.define('modal', {
  8630. tpl: '<div class="edui-modal" tabindex="-1" >' +
  8631. '<div class="edui-modal-header">' +
  8632. '<div class="edui-close" data-hide="modal"></div>' +
  8633. '<h3 class="edui-title">${title}</h3>' +
  8634. '</div>' +
  8635. '<div class="edui-modal-body" style="<%if: ${width}%>width:${width}px;<%/if%>' +
  8636. '<%if: ${height}%>height:${height}px;<%/if%>">' +
  8637. ' </div>' +
  8638. '<% if: ${cancellabel} || ${oklabel} %>' +
  8639. '<div class="edui-modal-footer">' +
  8640. '<div class="edui-modal-tip"></div>' +
  8641. '<%if: ${oklabel}%><div class="edui-btn edui-btn-primary" data-ok="modal">${oklabel}</div><%/if%>' +
  8642. '<%if: ${cancellabel}%><div class="edui-btn" data-hide="modal">${cancellabel}</div><%/if%>' +
  8643. '</div>' +
  8644. '<%/if%></div>',
  8645. defaultOpt: {
  8646. title: "",
  8647. cancellabel: "",
  8648. oklabel: "",
  8649. width: '',
  8650. height: '',
  8651. backdrop: true,
  8652. keyboard: true
  8653. },
  8654. init: function (options) {
  8655. var me = this;
  8656. me.root($(UM.utils.render(me.tpl, options || {})));
  8657. me.data("options", options);
  8658. if (options.okFn) {
  8659. me.on('ok', $.proxy(options.okFn, me))
  8660. }
  8661. if (options.cancelFn) {
  8662. me.on('beforehide', $.proxy(options.cancelFn, me))
  8663. }
  8664. me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))
  8665. .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));
  8666. $('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){
  8667. $(this).toggleClass('edui-hover')
  8668. });
  8669. },
  8670. toggle: function () {
  8671. var me = this;
  8672. return me[!me.data("isShown") ? 'show' : 'hide']();
  8673. },
  8674. show: function () {
  8675. var me = this;
  8676. me.trigger("beforeshow");
  8677. if (me.data("isShown")) return;
  8678. me.data("isShown", true);
  8679. me.escape();
  8680. me.backdrop(function () {
  8681. me.autoCenter();
  8682. me.root()
  8683. .show()
  8684. .focus()
  8685. .trigger('aftershow');
  8686. })
  8687. },
  8688. showTip: function ( text ) {
  8689. $( '.edui-modal-tip', this.root() ).html( text ).fadeIn();
  8690. },
  8691. hideTip: function ( text ) {
  8692. $( '.edui-modal-tip', this.root() ).fadeOut( function (){
  8693. $(this).html('');
  8694. } );
  8695. },
  8696. autoCenter: function () {
  8697. //ie6下不用处理了
  8698. !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));
  8699. },
  8700. hide: function () {
  8701. var me = this;
  8702. me.trigger("beforehide");
  8703. if (!me.data("isShown")) return;
  8704. me.data("isShown", false);
  8705. me.escape();
  8706. me.hideModal();
  8707. },
  8708. escape: function () {
  8709. var me = this;
  8710. if (me.data("isShown") && me.data("options").keyboard) {
  8711. me.root().on('keyup', function (e) {
  8712. e.which == 27 && me.hide();
  8713. })
  8714. }
  8715. else if (!me.data("isShown")) {
  8716. me.root().off('keyup');
  8717. }
  8718. },
  8719. hideModal: function () {
  8720. var me = this;
  8721. me.root().hide();
  8722. me.backdrop(function () {
  8723. me.removeBackdrop();
  8724. me.trigger('afterhide');
  8725. })
  8726. },
  8727. removeBackdrop: function () {
  8728. this.$backdrop && this.$backdrop.remove();
  8729. this.$backdrop = null;
  8730. },
  8731. backdrop: function (callback) {
  8732. var me = this;
  8733. if (me.data("isShown") && me.data("options").backdrop) {
  8734. me.$backdrop = $('<div class="edui-modal-backdrop" />').click(
  8735. me.data("options").backdrop == 'static' ?
  8736. $.proxy(me.root()[0].focus, me.root()[0])
  8737. : $.proxy(me.hide, me)
  8738. )
  8739. }
  8740. me.trigger('afterbackdrop');
  8741. callback && callback();
  8742. },
  8743. attachTo: function ($obj) {
  8744. var me = this
  8745. if (!$obj.data('$mergeObj')) {
  8746. $obj.data('$mergeObj', me.root());
  8747. $obj.on('click', function () {
  8748. me.toggle($obj)
  8749. });
  8750. me.data('$mergeObj', $obj)
  8751. }
  8752. },
  8753. ok: function () {
  8754. var me = this;
  8755. me.trigger('beforeok');
  8756. if (me.trigger("ok", me) === false) {
  8757. return;
  8758. }
  8759. me.hide();
  8760. },
  8761. getBodyContainer: function () {
  8762. return this.root().find('.edui-modal-body')
  8763. }
  8764. });
  8765. /*tooltip 类*/
  8766. UM.ui.define('tooltip', {
  8767. tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +
  8768. '<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +
  8769. '<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +
  8770. '</div>',
  8771. init: function (options) {
  8772. var me = this;
  8773. me.root($(UM.utils.render(me.tpl, options || {})));
  8774. },
  8775. content: function (e) {
  8776. var me = this,
  8777. title = $(e.currentTarget).attr("data-original-title");
  8778. me.root().find('.edui-tooltip-inner')['text'](title);
  8779. },
  8780. position: function (e) {
  8781. var me = this,
  8782. $obj = $(e.currentTarget);
  8783. me.root().css($.extend({display: 'block'}, $obj ? {
  8784. top: $obj.outerHeight(),
  8785. left: (($obj.outerWidth() - me.root().outerWidth()) / 2)
  8786. } : {}))
  8787. },
  8788. show: function (e) {
  8789. if ($(e.currentTarget).hasClass('edui-disabled')) return;
  8790. var me = this;
  8791. me.content(e);
  8792. me.root().appendTo($(e.currentTarget));
  8793. me.position(e);
  8794. me.root().css('display', 'block');
  8795. },
  8796. hide: function () {
  8797. var me = this;
  8798. me.root().css('display', 'none')
  8799. },
  8800. attachTo: function ($obj) {
  8801. var me = this;
  8802. function tmp($obj) {
  8803. var me = this;
  8804. if (!$.contains(document.body, me.root()[0])) {
  8805. me.root().appendTo($obj);
  8806. }
  8807. me.data('tooltip', me.root());
  8808. $obj.each(function () {
  8809. if ($(this).attr("data-original-title")) {
  8810. $(this).on('mouseenter', $.proxy(me.show, me))
  8811. .on('mouseleave click', $.proxy(me.hide, me))
  8812. }
  8813. });
  8814. }
  8815. if ($.type($obj) === "undefined") {
  8816. $("[data-original-title]").each(function (i, el) {
  8817. tmp.call(me, $(el));
  8818. })
  8819. } else {
  8820. if (!$obj.data('tooltip')) {
  8821. tmp.call(me, $obj);
  8822. }
  8823. }
  8824. }
  8825. });
  8826. /*tab 类*/
  8827. UM.ui.define('tab', {
  8828. init: function (options) {
  8829. var me = this,
  8830. slr = options.selector;
  8831. if ($.type(slr)) {
  8832. me.root($(slr, options.context));
  8833. me.data("context", options.context);
  8834. $(slr, me.data("context")).on('click', function (e) {
  8835. me.show(e);
  8836. });
  8837. }
  8838. },
  8839. show: function (e) {
  8840. var me = this,
  8841. $cur = $(e.target),
  8842. $ul = $cur.closest('ul'),
  8843. selector,
  8844. previous,
  8845. $target,
  8846. e;
  8847. selector = $cur.attr('data-context');
  8848. selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
  8849. var $tmp = $cur.parent('li');
  8850. if (!$tmp.length || $tmp.hasClass('edui-active')) return;
  8851. previous = $ul.find('.edui-active:last a')[0];
  8852. e = $.Event('beforeshow', {
  8853. target: $cur[0],
  8854. relatedTarget: previous
  8855. });
  8856. me.trigger(e);
  8857. if (e.isDefaultPrevented()) return;
  8858. $target = $(selector, me.data("context"));
  8859. me.activate($cur.parent('li'), $ul);
  8860. me.activate($target, $target.parent(), function () {
  8861. me.trigger({
  8862. type: 'aftershow', relatedTarget: previous
  8863. })
  8864. });
  8865. },
  8866. activate: function (element, container, callback) {
  8867. if (element === undefined) {
  8868. return $(".edui-tab-item.edui-active",this.root()).index();
  8869. }
  8870. var $active = container.find('> .edui-active');
  8871. $active.removeClass('edui-active');
  8872. element.addClass('edui-active');
  8873. callback && callback();
  8874. }
  8875. });
  8876. //button 类
  8877. UM.ui.define('separator', {
  8878. tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',
  8879. init: function (options) {
  8880. var me = this;
  8881. me.root($($.parseTmpl(me.tpl, options)));
  8882. return me;
  8883. }
  8884. });
  8885. /**
  8886. * @file adapter.js
  8887. * @desc adapt ui to editor
  8888. * @import core/Editor.js, core/utils.js
  8889. */
  8890. (function () {
  8891. var _editorUI = {},
  8892. _editors = {},
  8893. _readyFn = [],
  8894. _activeWidget = null,
  8895. _widgetData = {},
  8896. _widgetCallBack = {},
  8897. _cacheUI = {},
  8898. _maxZIndex = null;
  8899. utils.extend(UM, {
  8900. defaultWidth : 500,
  8901. defaultHeight : 500,
  8902. registerUI: function (name, fn) {
  8903. utils.each(name.split(/\s+/), function (uiname) {
  8904. _editorUI[uiname] = fn;
  8905. })
  8906. },
  8907. setEditor : function(editor){
  8908. !_editors[editor.id] && (_editors[editor.id] = editor);
  8909. },
  8910. registerWidget : function(name,pro,cb){
  8911. _widgetData[name] = $.extend2(pro,{
  8912. $root : '',
  8913. _preventDefault:false,
  8914. root:function($el){
  8915. return this.$root || (this.$root = $el);
  8916. },
  8917. preventDefault:function(){
  8918. this._preventDefault = true;
  8919. },
  8920. clear:false
  8921. });
  8922. if(cb){
  8923. _widgetCallBack[name] = cb;
  8924. }
  8925. },
  8926. getWidgetData : function(name){
  8927. return _widgetData[name]
  8928. },
  8929. setWidgetBody : function(name,$widget,editor){
  8930. if(!editor._widgetData){
  8931. utils.extend(editor,{
  8932. _widgetData : {},
  8933. getWidgetData : function(name){
  8934. return this._widgetData[name];
  8935. },
  8936. getWidgetCallback : function(widgetName){
  8937. var me = this;
  8938. return function(){
  8939. return _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0)))
  8940. }
  8941. }
  8942. })
  8943. }
  8944. var pro = _widgetData[name];
  8945. if(!pro){
  8946. return null;
  8947. }
  8948. pro = editor._widgetData[name];
  8949. if(!pro){
  8950. pro = _widgetData[name];
  8951. pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);
  8952. }
  8953. pro.root($widget.edui().getBodyContainer());
  8954. pro.initContent(editor,$widget);
  8955. if(!pro._preventDefault){
  8956. pro.initEvent(editor,$widget);
  8957. }
  8958. pro.width && $widget.width(pro.width);
  8959. },
  8960. setActiveWidget : function($widget){
  8961. _activeWidget = $widget;
  8962. },
  8963. getEditor: function (id, options) {
  8964. var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));
  8965. _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex');
  8966. return editor;
  8967. },
  8968. setTopEditor: function(editor){
  8969. $.each(_editors, function(i, o){
  8970. if(editor == o) {
  8971. editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);
  8972. } else {
  8973. o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));
  8974. }
  8975. });
  8976. },
  8977. clearCache : function(id){
  8978. if ( _editors[id]) {
  8979. delete _editors[id]
  8980. }
  8981. },
  8982. delEditor: function (id) {
  8983. var editor;
  8984. if (editor = _editors[id]) {
  8985. editor.destroy();
  8986. }
  8987. },
  8988. ready: function( fn ){
  8989. _readyFn.push( fn );
  8990. },
  8991. createEditor: function (id, opt) {
  8992. var editor = new UM.Editor(opt);
  8993. var T = this;
  8994. editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T));
  8995. function renderUI(){
  8996. var $container = this.createUI('#' + id, editor);
  8997. editor.key=id;
  8998. editor.ready(function(){
  8999. $.each( _readyFn, function( index, fn ){
  9000. $.proxy( fn, editor )();
  9001. } );
  9002. });
  9003. var options = editor.options;
  9004. if(options.initialFrameWidth){
  9005. options.minFrameWidth = options.initialFrameWidth
  9006. }else{
  9007. options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth;
  9008. }
  9009. $container.css({
  9010. width: options.initialFrameWidth,
  9011. zIndex:editor.getOpt('zIndex')
  9012. });
  9013. //ie6下缓存图片
  9014. UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);
  9015. editor.render(id);
  9016. //添加tooltip;
  9017. $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1);
  9018. $container.find('a').click(function(evt){
  9019. evt.preventDefault()
  9020. });
  9021. editor.fireEvent("afteruiready");
  9022. }
  9023. return editor;
  9024. },
  9025. createUI: function (id, editor) {
  9026. var $editorCont = $(id),
  9027. $container = $('<div class="edui-container"><div class="edui-editor-body"></div></div>').insertBefore($editorCont);
  9028. editor.$container = $container;
  9029. editor.container = $container[0];
  9030. editor.$body = $editorCont;
  9031. //修正在ie9+以上的版本中,自动长高收起时的,残影问题
  9032. if(browser.ie && browser.ie9above){
  9033. var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');
  9034. $span.insertAfter($container);
  9035. }
  9036. //初始化注册的ui组件
  9037. $.each(_editorUI,function(n,v){
  9038. var widget = v.call(editor,n);
  9039. if(widget){
  9040. _cacheUI[n] = widget;
  9041. }
  9042. });
  9043. $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));
  9044. $container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));
  9045. return $container;
  9046. },
  9047. createToolbar: function (options, editor) {
  9048. var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();
  9049. //创建下来菜单列表
  9050. if (options.toolbar && options.toolbar.length) {
  9051. var btns = [];
  9052. $.each(options.toolbar,function(i,uiNames){
  9053. $.each(uiNames.split(/\s+/),function(index,name){
  9054. if(name == '|'){
  9055. $.eduiseparator && btns.push($.eduiseparator());
  9056. }else{
  9057. var ui = _cacheUI[name];
  9058. if(name=="fullscreen"){
  9059. ui&&btns.unshift(ui);
  9060. }else{
  9061. ui && btns.push(ui);
  9062. }
  9063. }
  9064. });
  9065. btns.length && toolbar.appendToBtnmenu(btns);
  9066. });
  9067. } else {
  9068. $toolbar.find('.edui-btn-toolbar').remove()
  9069. }
  9070. return $toolbar;
  9071. }
  9072. })
  9073. })();
  9074. UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +
  9075. 'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',
  9076. function(name) {
  9077. var me = this;
  9078. var $btn = $.eduibutton({
  9079. icon : name,
  9080. click : function(){
  9081. me.execCommand(name);
  9082. },
  9083. title: this.getLang('labelMap')[name] || ''
  9084. });
  9085. this.addListener('selectionchange',function(){
  9086. var state = this.queryCommandState(name);
  9087. $btn.edui().disabled(state == -1).active(state == 1)
  9088. });
  9089. return $btn;
  9090. }
  9091. );
  9092. /**
  9093. * 全屏组件
  9094. */
  9095. (function(){
  9096. //状态缓存
  9097. var STATUS_CACHE = {},
  9098. //状态值列表
  9099. STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ],
  9100. CONTENT_AREA_STATUS = {},
  9101. //页面状态
  9102. DOCUMENT_STATUS = {},
  9103. DOCUMENT_ELEMENT_STATUS = {},
  9104. FULLSCREENS = {};
  9105. UM.registerUI('fullscreen', function( name ){
  9106. var me = this,
  9107. $button = $.eduibutton({
  9108. 'icon': 'fullscreen',
  9109. 'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9110. 'click': function(){
  9111. //切换
  9112. me.execCommand( name );
  9113. UM.setTopEditor(me);
  9114. }
  9115. });
  9116. me.addListener( "selectionchange", function () {
  9117. var state = this.queryCommandState( name );
  9118. $button.edui().disabled( state == -1 ).active( state == 1 );
  9119. } );
  9120. //切换至全屏
  9121. me.addListener('ready', function(){
  9122. me.options.fullscreen && Fullscreen.getInstance( me ).toggle();
  9123. });
  9124. return $button;
  9125. });
  9126. UM.commands[ 'fullscreen' ] = {
  9127. execCommand: function (cmdName) {
  9128. Fullscreen.getInstance( this ).toggle();
  9129. },
  9130. queryCommandState: function (cmdName) {
  9131. return this._edui_fullscreen_status;
  9132. },
  9133. notNeedUndo: 1
  9134. };
  9135. function Fullscreen( editor ) {
  9136. var me = this;
  9137. if( !editor ) {
  9138. throw new Error('invalid params, notfound editor');
  9139. }
  9140. me.editor = editor;
  9141. //记录初始化的全屏组件
  9142. FULLSCREENS[ editor.uid ] = this;
  9143. editor.addListener('destroy', function(){
  9144. delete FULLSCREENS[ editor.uid ];
  9145. me.editor = null;
  9146. });
  9147. }
  9148. Fullscreen.prototype = {
  9149. /**
  9150. * 全屏状态切换
  9151. */
  9152. toggle: function(){
  9153. var editor = this.editor,
  9154. //当前编辑器的缩放状态
  9155. _edui_fullscreen_status = this.isFullState();
  9156. editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status );
  9157. //更新状态
  9158. this.update( !_edui_fullscreen_status );
  9159. !_edui_fullscreen_status ? this.enlarge() : this.revert();
  9160. editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status );
  9161. if(editor.body.contentEditable === 'true'){
  9162. editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status );
  9163. }
  9164. editor.fireEvent( 'selectionchange' );
  9165. },
  9166. /**
  9167. * 执行放大
  9168. */
  9169. enlarge: function(){
  9170. this.saveSataus();
  9171. this.setDocumentStatus();
  9172. this.resize();
  9173. },
  9174. /**
  9175. * 全屏还原
  9176. */
  9177. revert: function(){
  9178. //还原CSS表达式
  9179. var options = this.editor.options,
  9180. height = /%$/.test(options.initialFrameHeight) ? '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));
  9181. $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  9182. //还原容器状态
  9183. this.revertContainerStatus();
  9184. this.revertContentAreaStatus();
  9185. this.revertDocumentStatus();
  9186. },
  9187. /**
  9188. * 更新状态
  9189. * @param isFull 当前状态是否是全屏状态
  9190. */
  9191. update: function( isFull ) {
  9192. this.editor._edui_fullscreen_status = isFull;
  9193. },
  9194. /**
  9195. * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态, 则不做调整
  9196. */
  9197. resize: function(){
  9198. var $win = null,
  9199. height = 0,
  9200. width = 0,
  9201. borderWidth = 0,
  9202. paddingWidth = 0,
  9203. editor = this.editor,
  9204. me = this,
  9205. bound = null,
  9206. editorBody = null;
  9207. if( !this.isFullState() ) {
  9208. return;
  9209. }
  9210. $win = $( window );
  9211. width = $win.width();
  9212. height = $win.height();
  9213. editorBody = this.getEditorHolder();
  9214. //文本编辑区border宽度
  9215. borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0;
  9216. //容器border宽度
  9217. borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0;
  9218. //容器padding
  9219. paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0;
  9220. //干掉css表达式
  9221. $.IE6 && editorBody.style.setExpression( 'height', null );
  9222. bound = this.getBound();
  9223. $( editor.container ).css( {
  9224. width: width + 'px',
  9225. height: height + 'px',
  9226. position: !$.IE6 ? 'fixed' : 'absolute',
  9227. top: bound.top,
  9228. left: bound.left,
  9229. margin: 0,
  9230. padding: 0,
  9231. overflowX: 'hidden',
  9232. overflowY: 'hidden'
  9233. } );
  9234. $( editorBody ).css({
  9235. width: width - 2*borderWidth - paddingWidth + 'px',
  9236. height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px',
  9237. overflowX: 'hidden',
  9238. overflowY: 'auto'
  9239. });
  9240. },
  9241. /**
  9242. * 保存状态
  9243. */
  9244. saveSataus: function(){
  9245. var styles = this.editor.container.style,
  9246. tmp = null,
  9247. cache = {};
  9248. for( var i= 0, len = STATUS_LIST.length; i<len; i++ ) {
  9249. tmp = STATUS_LIST[ i ];
  9250. cache[ tmp ] = styles[ tmp ];
  9251. }
  9252. STATUS_CACHE[ this.editor.uid ] = cache;
  9253. this.saveContentAreaStatus();
  9254. this.saveDocumentStatus();
  9255. },
  9256. saveContentAreaStatus: function(){
  9257. var $holder = $(this.getEditorHolder());
  9258. CONTENT_AREA_STATUS[ this.editor.uid ] = {
  9259. width: $holder.css("width"),
  9260. overflowX: $holder.css("overflowX"),
  9261. overflowY: $holder.css("overflowY"),
  9262. height: $holder.css("height")
  9263. };
  9264. },
  9265. /**
  9266. * 保存与指定editor相关的页面的状态
  9267. */
  9268. saveDocumentStatus: function(){
  9269. var $doc = $( this.getEditorDocumentBody() );
  9270. DOCUMENT_STATUS[ this.editor.uid ] = {
  9271. overflowX: $doc.css( 'overflowX' ),
  9272. overflowY: $doc.css( 'overflowY' )
  9273. };
  9274. DOCUMENT_ELEMENT_STATUS[ this.editor.uid ] = {
  9275. overflowX: $( this.getEditorDocumentElement() ).css( 'overflowX'),
  9276. overflowY: $( this.getEditorDocumentElement() ).css( 'overflowY' )
  9277. };
  9278. },
  9279. /**
  9280. * 恢复容器状态
  9281. */
  9282. revertContainerStatus: function(){
  9283. $( this.editor.container ).css( this.getEditorStatus() );
  9284. },
  9285. /**
  9286. * 恢复编辑区状态
  9287. */
  9288. revertContentAreaStatus: function(){
  9289. var holder = this.getEditorHolder(),
  9290. state = this.getContentAreaStatus();
  9291. if ( this.supportMin() ) {
  9292. delete state.height;
  9293. holder.style.height = null;
  9294. }
  9295. $( holder ).css( state );
  9296. },
  9297. /**
  9298. * 恢复页面状态
  9299. */
  9300. revertDocumentStatus: function() {
  9301. var status = this.getDocumentStatus();
  9302. $( this.getEditorDocumentBody() ).css( 'overflowX', status.body.overflowX );
  9303. $( this.getEditorDocumentElement() ).css( 'overflowY', status.html.overflowY );
  9304. },
  9305. setDocumentStatus: function(){
  9306. $(this.getEditorDocumentBody()).css( {
  9307. overflowX: 'hidden',
  9308. overflowY: 'hidden'
  9309. } );
  9310. $(this.getEditorDocumentElement()).css( {
  9311. overflowX: 'hidden',
  9312. overflowY: 'hidden'
  9313. } );
  9314. },
  9315. /**
  9316. * 检测当前编辑器是否处于全屏状态全屏状态
  9317. * @returns {boolean} 是否处于全屏状态
  9318. */
  9319. isFullState: function(){
  9320. return !!this.editor._edui_fullscreen_status;
  9321. },
  9322. /**
  9323. * 获取编辑器状态
  9324. */
  9325. getEditorStatus: function(){
  9326. return STATUS_CACHE[ this.editor.uid ];
  9327. },
  9328. getContentAreaStatus: function(){
  9329. return CONTENT_AREA_STATUS[ this.editor.uid ];
  9330. },
  9331. getEditorDocumentElement: function(){
  9332. return this.editor.container.ownerDocument.documentElement;
  9333. },
  9334. getEditorDocumentBody: function(){
  9335. return this.editor.container.ownerDocument.body;
  9336. },
  9337. /**
  9338. * 获取编辑区包裹对象
  9339. */
  9340. getEditorHolder: function(){
  9341. return this.editor.body;
  9342. },
  9343. /**
  9344. * 获取编辑器状态
  9345. * @returns {*}
  9346. */
  9347. getDocumentStatus: function(){
  9348. return {
  9349. 'body': DOCUMENT_STATUS[ this.editor.uid ],
  9350. 'html': DOCUMENT_ELEMENT_STATUS[ this.editor.uid ]
  9351. };
  9352. },
  9353. supportMin: function () {
  9354. var node = null;
  9355. if ( !this._support ) {
  9356. node = document.createElement("div");
  9357. this._support = "minWidth" in node.style;
  9358. node = null;
  9359. }
  9360. return this._support;
  9361. },
  9362. getBound: function () {
  9363. var tags = {
  9364. html: true,
  9365. body: true
  9366. },
  9367. result = {
  9368. top: 0,
  9369. left: 0
  9370. },
  9371. offsetParent = null;
  9372. if ( !$.IE6 ) {
  9373. return result;
  9374. }
  9375. offsetParent = this.editor.container.offsetParent;
  9376. if( offsetParent && !tags[ offsetParent.nodeName.toLowerCase() ] ) {
  9377. tags = offsetParent.getBoundingClientRect();
  9378. result.top = -tags.top;
  9379. result.left = -tags.left;
  9380. }
  9381. return result;
  9382. },
  9383. getStyleValue: function (attr) {
  9384. return parseInt(domUtils.getComputedStyle( this.getEditorHolder() ,attr));
  9385. }
  9386. };
  9387. $.extend( Fullscreen, {
  9388. /**
  9389. * 监听resize
  9390. */
  9391. listen: function(){
  9392. var timer = null;
  9393. if( Fullscreen._hasFullscreenListener ) {
  9394. return;
  9395. }
  9396. Fullscreen._hasFullscreenListener = true;
  9397. $( window ).on( 'resize', function(){
  9398. if( timer !== null ) {
  9399. window.clearTimeout( timer );
  9400. timer = null;
  9401. }
  9402. timer = window.setTimeout(function(){
  9403. for( var key in FULLSCREENS ) {
  9404. FULLSCREENS[ key ].resize();
  9405. }
  9406. timer = null;
  9407. }, 50);
  9408. } );
  9409. },
  9410. getInstance: function ( editor ) {
  9411. if ( !FULLSCREENS[editor.uid ] ) {
  9412. new Fullscreen( editor );
  9413. }
  9414. return FULLSCREENS[editor.uid ];
  9415. }
  9416. });
  9417. //开始监听
  9418. Fullscreen.listen();
  9419. })();
  9420. UM.registerUI('link image video map formula',function(name){
  9421. var me = this, currentRange, $dialog,
  9422. opt = {
  9423. title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9424. url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js'
  9425. };
  9426. var $btn = $.eduibutton({
  9427. icon: name,
  9428. title: this.getLang('labelMap')[name] || ''
  9429. });
  9430. //加载模版数据
  9431. utils.loadFile(document,{
  9432. src: opt.url,
  9433. tag: "script",
  9434. type: "text/javascript",
  9435. defer: "defer"
  9436. },function(){
  9437. //调整数据
  9438. var data = UM.getWidgetData(name);
  9439. if(!data) return;
  9440. if(data.buttons){
  9441. var ok = data.buttons.ok;
  9442. if(ok){
  9443. opt.oklabel = ok.label || me.getLang('ok');
  9444. if(ok.exec){
  9445. opt.okFn = function(){
  9446. return $.proxy(ok.exec,null,me,$dialog)()
  9447. }
  9448. }
  9449. }
  9450. var cancel = data.buttons.cancel;
  9451. if(cancel){
  9452. opt.cancellabel = cancel.label || me.getLang('cancel');
  9453. if(cancel.exec){
  9454. opt.cancelFn = function(){
  9455. return $.proxy(cancel.exec,null,me,$dialog)()
  9456. }
  9457. }
  9458. }
  9459. }
  9460. data.width && (opt.width = data.width);
  9461. data.height && (opt.height = data.height);
  9462. $dialog = $.eduimodal(opt);
  9463. $dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)
  9464. .find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');
  9465. $dialog.edui().on('beforehide',function () {
  9466. var rng = me.selection.getRange();
  9467. if (rng.equals(currentRange)) {
  9468. rng.select()
  9469. }
  9470. }).on('beforeshow', function () {
  9471. var $root = this.root(),
  9472. win = null,
  9473. offset = null;
  9474. currentRange = me.selection.getRange();
  9475. if (!$root.parent()[0]) {
  9476. me.$container.find('.edui-dialog-container').append($root);
  9477. }
  9478. //IE6下 特殊处理, 通过计算进行定位
  9479. if( $.IE6 ) {
  9480. win = {
  9481. width: $( window ).width(),
  9482. height: $( window ).height()
  9483. };
  9484. offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();
  9485. $root.css({
  9486. position: 'absolute',
  9487. margin: 0,
  9488. left: ( win.width - $root.width() ) / 2 - offset.left,
  9489. top: 100 - offset.top
  9490. });
  9491. }
  9492. UM.setWidgetBody(name,$dialog,me);
  9493. UM.setTopEditor(me);
  9494. }).on('afterbackdrop',function(){
  9495. this.$backdrop.css('zIndex',me.getOpt('zIndex')+1).appendTo(me.$container.find('.edui-dialog-container'))
  9496. $dialog.css('zIndex',me.getOpt('zIndex')+2)
  9497. }).on('beforeok',function(){
  9498. try{
  9499. currentRange.select()
  9500. }catch(e){}
  9501. }).attachTo($btn)
  9502. });
  9503. me.addListener('selectionchange', function () {
  9504. var state = this.queryCommandState(name);
  9505. $btn.edui().disabled(state == -1).active(state == 1)
  9506. });
  9507. return $btn;
  9508. });
  9509. UM.registerUI( 'emotion formula', function( name ){
  9510. var me = this,
  9511. url = me.options.UMEDITOR_HOME_URL + 'dialogs/' +name+ '/'+name+'.js';
  9512. var $btn = $.eduibutton({
  9513. icon: name,
  9514. title: this.getLang('labelMap')[name] || ''
  9515. });
  9516. //加载模版数据
  9517. utils.loadFile(document,{
  9518. src: url,
  9519. tag: "script",
  9520. type: "text/javascript",
  9521. defer: "defer"
  9522. },function(){
  9523. var opt = {
  9524. url : url
  9525. };
  9526. //调整数据
  9527. var data = UM.getWidgetData(name);
  9528. data.width && (opt.width = data.width);
  9529. data.height && (opt.height = data.height);
  9530. $.eduipopup(opt).css('zIndex',me.options.zIndex + 1)
  9531. .addClass('edui-popup-' + name)
  9532. .edui()
  9533. .on('beforeshow',function(){
  9534. var $root = this.root();
  9535. if(!$root.parent().length){
  9536. me.$container.find('.edui-dialog-container').append($root);
  9537. }
  9538. UM.setWidgetBody(name,$root,me);
  9539. UM.setTopEditor(me);
  9540. }).attachTo($btn,{
  9541. offsetTop:-5,
  9542. offsetLeft:10,
  9543. caretLeft:11,
  9544. caretTop:-8
  9545. });
  9546. me.addListener('selectionchange', function () {
  9547. var state = this.queryCommandState(name);
  9548. $btn.edui().disabled(state == -1).active(state == 1);
  9549. });
  9550. });
  9551. return $btn;
  9552. } );
  9553. UM.registerUI('imagescale',function () {
  9554. var me = this,
  9555. $imagescale;
  9556. me.setOpt('imageScaleEnabled', true);
  9557. if (browser.webkit && me.getOpt('imageScaleEnabled')) {
  9558. me.addListener('click', function (type, e) {
  9559. var range = me.selection.getRange(),
  9560. img = range.getClosedNode(),
  9561. target = e.target;
  9562. /* 点击第一个图片的后面,八个角不消失 fix:3652 */
  9563. if (img && img.tagName == 'IMG' && target == img) {
  9564. if (!$imagescale) {
  9565. $imagescale = $.eduiscale({'$wrap':me.$container}).css('zIndex', me.options.zIndex);
  9566. me.$container.append($imagescale);
  9567. var _keyDownHandler = function () {
  9568. $imagescale.edui().hide();
  9569. }, _mouseDownHandler = function (e) {
  9570. var ele = e.target || e.srcElement;
  9571. if (ele && ele.className.indexOf('edui-scale') == -1) {
  9572. _keyDownHandler(e);
  9573. }
  9574. }, timer;
  9575. $imagescale.edui()
  9576. .on('aftershow', function () {
  9577. $(document).bind('keydown', _keyDownHandler);
  9578. $(document).bind('mousedown', _mouseDownHandler);
  9579. me.selection.getNative().removeAllRanges();
  9580. })
  9581. .on('afterhide', function () {
  9582. $(document).unbind('keydown', _keyDownHandler);
  9583. $(document).unbind('mousedown', _mouseDownHandler);
  9584. var target = $imagescale.edui().getScaleTarget();
  9585. if (target.parentNode) {
  9586. me.selection.getRange().selectNode(target).select();
  9587. }
  9588. })
  9589. .on('mousedown', function (e) {
  9590. me.selection.getNative().removeAllRanges();
  9591. var ele = e.target || e.srcElement;
  9592. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9593. timer = setTimeout(function() {
  9594. $imagescale.edui().hide();
  9595. }, 200);
  9596. }
  9597. })
  9598. .on('mouseup', function (e) {
  9599. var ele = e.target || e.srcElement;
  9600. if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
  9601. clearTimeout(timer);
  9602. }
  9603. });
  9604. }
  9605. $imagescale.edui().show($(img));
  9606. } else {
  9607. if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();
  9608. }
  9609. });
  9610. me.addListener('click', function (type, e) {
  9611. if (e.target.tagName == 'IMG') {
  9612. var range = new dom.Range(me.document, me.body);
  9613. range.selectNode(e.target).select();
  9614. }
  9615. });
  9616. }
  9617. });
  9618. UM.registerUI('autofloat',function(){
  9619. var me = this,
  9620. lang = me.getLang();
  9621. me.setOpt({
  9622. autoFloatEnabled: true,
  9623. topOffset: 0
  9624. });
  9625. var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
  9626. topOffset = me.options.topOffset;
  9627. //如果不固定toolbar的位置,则直接退出
  9628. if(!optsAutoFloatEnabled){
  9629. return;
  9630. }
  9631. me.ready(function(){
  9632. var LteIE6 = browser.ie && browser.version <= 6,
  9633. quirks = browser.quirks;
  9634. function checkHasUI(){
  9635. if(!UM.ui){
  9636. alert(lang.autofloatMsg);
  9637. return 0;
  9638. }
  9639. return 1;
  9640. }
  9641. function fixIE6FixedPos(){
  9642. var docStyle = document.body.style;
  9643. docStyle.backgroundImage = 'url("about:blank")';
  9644. docStyle.backgroundAttachment = 'fixed';
  9645. }
  9646. var bakCssText,
  9647. placeHolder = document.createElement('div'),
  9648. toolbarBox,orgTop,
  9649. getPosition=function(element){
  9650. var bcr;
  9651. //trace IE6下在控制编辑器显隐时可能会报错,catch一下
  9652. try{
  9653. bcr = element.getBoundingClientRect();
  9654. }catch(e){
  9655. bcr={left:0,top:0,height:0,width:0}
  9656. }
  9657. var rect = {
  9658. left: Math.round(bcr.left),
  9659. top: Math.round(bcr.top),
  9660. height: Math.round(bcr.bottom - bcr.top),
  9661. width: Math.round(bcr.right - bcr.left)
  9662. };
  9663. var doc;
  9664. while ((doc = element.ownerDocument) !== document &&
  9665. (element = domUtils.getWindow(doc).frameElement)) {
  9666. bcr = element.getBoundingClientRect();
  9667. rect.left += bcr.left;
  9668. rect.top += bcr.top;
  9669. }
  9670. rect.bottom = rect.top + rect.height;
  9671. rect.right = rect.left + rect.width;
  9672. return rect;
  9673. };
  9674. var isFullScreening = false;
  9675. function setFloating(){
  9676. if(isFullScreening){
  9677. return;
  9678. }
  9679. var toobarBoxPos = domUtils.getXY(toolbarBox),
  9680. origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
  9681. origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
  9682. toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
  9683. toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
  9684. toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
  9685. if (LteIE6 || (quirks && browser.ie)) {
  9686. if(toolbarBox.style.position != 'absolute'){
  9687. toolbarBox.style.position = 'absolute';
  9688. }
  9689. toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset + 'px';
  9690. } else {
  9691. if(toolbarBox.style.position != 'fixed'){
  9692. toolbarBox.style.position = 'fixed';
  9693. toolbarBox.style.top = topOffset +"px";
  9694. ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
  9695. }
  9696. }
  9697. }
  9698. function unsetFloating(){
  9699. if(placeHolder.parentNode){
  9700. placeHolder.parentNode.removeChild(placeHolder);
  9701. }
  9702. toolbarBox.style.cssText = bakCssText;
  9703. }
  9704. function updateFloating(){
  9705. var rect3 = getPosition(me.container);
  9706. var offset=me.options.toolbarTopOffset||0;
  9707. if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
  9708. setFloating();
  9709. }else{
  9710. unsetFloating();
  9711. }
  9712. }
  9713. var defer_updateFloating = utils.defer(function(){
  9714. updateFloating();
  9715. },browser.ie ? 200 : 100,true);
  9716. me.addListener('destroy',function(){
  9717. $(window).off('scroll resize',updateFloating);
  9718. me.removeListener('keydown', defer_updateFloating);
  9719. });
  9720. if(checkHasUI(me)){
  9721. toolbarBox = $('.edui-toolbar',me.container)[0];
  9722. me.addListener("afteruiready",function(){
  9723. setTimeout(function(){
  9724. orgTop = $(toolbarBox).offset().top;
  9725. },100);
  9726. });
  9727. bakCssText = toolbarBox.style.cssText;
  9728. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9729. if(LteIE6){
  9730. fixIE6FixedPos();
  9731. }
  9732. $(window).on('scroll resize',updateFloating);
  9733. me.addListener('keydown', defer_updateFloating);
  9734. me.addListener('resize', function(){
  9735. unsetFloating();
  9736. placeHolder.style.height = toolbarBox.offsetHeight + 'px';
  9737. updateFloating();
  9738. });
  9739. me.addListener('beforefullscreenchange', function (t, enabled){
  9740. if (enabled) {
  9741. unsetFloating();
  9742. isFullScreening = enabled;
  9743. }
  9744. });
  9745. me.addListener('fullscreenchanged', function (t, enabled){
  9746. if (!enabled) {
  9747. updateFloating();
  9748. }
  9749. isFullScreening = enabled;
  9750. });
  9751. me.addListener('sourcemodechanged', function (t, enabled){
  9752. setTimeout(function (){
  9753. updateFloating();
  9754. },0);
  9755. });
  9756. me.addListener("clearDoc",function(){
  9757. setTimeout(function(){
  9758. updateFloating();
  9759. },0);
  9760. })
  9761. }
  9762. })
  9763. });
  9764. UM.registerUI('source',function(name){
  9765. var me = this;
  9766. me.addListener('fullscreenchanged',function(){
  9767. me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())
  9768. });
  9769. var $btn = $.eduibutton({
  9770. icon : name,
  9771. click : function(){
  9772. me.execCommand(name);
  9773. UM.setTopEditor(me);
  9774. },
  9775. title: this.getLang('labelMap')[name] || ''
  9776. });
  9777. this.addListener('selectionchange',function(){
  9778. var state = this.queryCommandState(name);
  9779. $btn.edui().disabled(state == -1).active(state == 1)
  9780. });
  9781. return $btn;
  9782. });
  9783. UM.registerUI('paragraph fontfamily fontsize', function( name ) {
  9784. var me = this,
  9785. label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
  9786. options = {
  9787. label: label,
  9788. title: label,
  9789. comboboxName: name,
  9790. items: me.options[ name ] || [],
  9791. itemStyles: [],
  9792. value: [],
  9793. autowidthitem: []
  9794. },
  9795. $combox = null,
  9796. comboboxWidget = null;
  9797. if(options.items.length == 0){
  9798. return null;
  9799. }
  9800. switch ( name ) {
  9801. case 'paragraph':
  9802. options = transForParagraph( options );
  9803. break;
  9804. case 'fontfamily':
  9805. options = transForFontfamily( options );
  9806. break;
  9807. case 'fontsize':
  9808. options = transForFontsize( options );
  9809. break;
  9810. }
  9811. //实例化
  9812. $combox = $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1);
  9813. comboboxWidget = $combox.edui();
  9814. comboboxWidget.on('comboboxselect', function( evt, res ){
  9815. me.execCommand( name, res.value );
  9816. }).on("beforeshow", function(){
  9817. if( $combox.parent().length === 0 ) {
  9818. $combox.appendTo( me.$container.find('.edui-dialog-container') );
  9819. }
  9820. UM.setTopEditor(me);
  9821. });
  9822. //状态反射
  9823. this.addListener('selectionchange',function( evt ){
  9824. var state = this.queryCommandState( name ),
  9825. value = this.queryCommandValue( name );
  9826. //设置按钮状态
  9827. comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 );
  9828. if(value){
  9829. //设置label
  9830. value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);
  9831. comboboxWidget.selectItemByLabel( value );
  9832. }
  9833. });
  9834. return comboboxWidget.button().addClass('edui-combobox');
  9835. /**
  9836. * 宽度自适应工具函数
  9837. * @param word 单词内容
  9838. * @param hasSuffix 是否含有后缀
  9839. */
  9840. function wordCountAdaptive ( word, hasSuffix ) {
  9841. var $tmpNode = $('<span>' ).html( word ).css( {
  9842. display: 'inline',
  9843. position: 'absolute',
  9844. top: -10000000,
  9845. left: -100000
  9846. } ).appendTo( document.body),
  9847. width = $tmpNode.width();
  9848. $tmpNode.remove();
  9849. $tmpNode = null;
  9850. if( width < 50 ) {
  9851. return word;
  9852. } else {
  9853. word = word.slice( 0, hasSuffix ? -4 : -1 );
  9854. if( !word.length ) {
  9855. return '...';
  9856. }
  9857. return wordCountAdaptive( word + '...', true );
  9858. }
  9859. }
  9860. //段落参数转换
  9861. function transForParagraph ( options ) {
  9862. var tempItems = [];
  9863. for( var key in options.items ) {
  9864. options.value.push( key );
  9865. tempItems.push( key );
  9866. options.autowidthitem.push( wordCountAdaptive( key ) );
  9867. }
  9868. options.items = tempItems;
  9869. options.autoRecord = false;
  9870. return options;
  9871. }
  9872. //字体参数转换
  9873. function transForFontfamily ( options ) {
  9874. var temp = null,
  9875. tempItems = [];
  9876. for( var i = 0, len = options.items.length; i < len; i++ ) {
  9877. temp = options.items[ i ].val;
  9878. tempItems.push( temp.split(/\s*,\s*/)[0] );
  9879. options.itemStyles.push('font-family: ' + temp);
  9880. options.value.push( temp );
  9881. options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) );
  9882. }
  9883. options.items = tempItems;
  9884. return options;
  9885. }
  9886. //字体大小参数转换
  9887. function transForFontsize ( options ) {
  9888. var temp = null,
  9889. tempItems = [];
  9890. options.itemStyles = [];
  9891. options.value = [];
  9892. for( var i = 0, len = options.items.length; i < len; i++ ) {
  9893. temp = options.items[ i ];
  9894. tempItems.push( temp );
  9895. options.itemStyles.push('font-size: ' + temp +'px');
  9896. }
  9897. options.value = options.items;
  9898. options.items = tempItems;
  9899. options.autoRecord = false;
  9900. return options;
  9901. }
  9902. });
  9903. UM.registerUI('forecolor backcolor', function( name ) {
  9904. function getCurrentColor() {
  9905. return domUtils.getComputedStyle( $colorLabel[0], 'background-color' );
  9906. }
  9907. var me = this,
  9908. $colorPickerWidget = null,
  9909. $colorLabel = null,
  9910. $btn = null;
  9911. //querycommand
  9912. this.addListener('selectionchange', function(){
  9913. var state = this.queryCommandState( name );
  9914. $btn.edui().disabled( state == -1 ).active( state == 1 );
  9915. });
  9916. $btn = $.eduicolorsplitbutton({
  9917. icon: name,
  9918. caret: true,
  9919. name: name,
  9920. title: me.getLang("labelMap")[name],
  9921. click: function() {
  9922. me.execCommand( name, getCurrentColor() );
  9923. }
  9924. });
  9925. $colorLabel = $btn.edui().colorLabel();
  9926. $colorPickerWidget = $.eduicolorpicker({
  9927. name: name,
  9928. lang_clearColor: me.getLang('clearColor') || '',
  9929. lang_themeColor: me.getLang('themeColor') || '',
  9930. lang_standardColor: me.getLang('standardColor') || ''
  9931. })
  9932. .on('pickcolor', function( evt, color ){
  9933. window.setTimeout( function(){
  9934. $colorLabel.css("backgroundColor", color);
  9935. me.execCommand( name, color );
  9936. }, 0 );
  9937. })
  9938. .on('show',function(){
  9939. UM.setActiveWidget( colorPickerWidget.root() );
  9940. }).css('zIndex',me.getOpt('zIndex') + 1);
  9941. $btn.edui().on('arrowclick',function(){
  9942. if(!$colorPickerWidget.parent().length){
  9943. me.$container.find('.edui-dialog-container').append($colorPickerWidget);
  9944. }
  9945. $colorPickerWidget.edui().show($btn,{
  9946. caretDir:"down",
  9947. offsetTop:-5,
  9948. offsetLeft:8,
  9949. caretLeft:11,
  9950. caretTop:-8
  9951. });
  9952. UM.setTopEditor(me);
  9953. }).register('click', $btn, function () {
  9954. $colorPickerWidget.edui().hide()
  9955. });
  9956. return $btn;
  9957. });
  9958. })(jQuery)