wangEditor.js 259 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571
  1. (function (factory) {
  2. if (typeof window.define === 'function') {
  3. if (window.define.amd) {
  4. // AMD模式
  5. window.define('wangEditor', ["jquery"], factory);
  6. } else if (window.define.cmd) {
  7. // CMD模式
  8. window.define(function (require, exports, module) {
  9. return factory;
  10. });
  11. } else {
  12. // 全局模式
  13. factory(window.jQuery);
  14. }
  15. } else if (typeof module === "object" && typeof module.exports === "object") {
  16. // commonjs
  17. // 引用 css —— webapck
  18. window.wangEditorCssPath ? require(window.wangEditorCssPath) : require('../css/wangEditor.css');
  19. module.exports = factory(
  20. // 传入 jquery ,支持使用 npm 方式或者自己定义jquery的路径
  21. window.wangEditorJQueryPath ? require(window.wangEditorJQueryPath) : require('jquery')
  22. );
  23. } else {
  24. // 全局模式
  25. factory(window.jQuery);
  26. }
  27. })(function($){
  28. // 验证是否引用jquery
  29. // if (!$ || !$.fn || !$.fn.jquery) {
  30. // alert('在引用wangEditor.js之前,先引用jQuery,否则无法使用 wangEditor');
  31. // return;
  32. // }
  33. // 定义扩展函数
  34. var _e = function (fn) {
  35. var E = window.wangEditor;
  36. if (E) {
  37. // 执行传入的函数
  38. fn(E, $);
  39. }
  40. };
  41. // 定义构造函数
  42. (function (window, $) {
  43. // if (window.wangEditor) {
  44. // // 重复引用
  45. // alert('一个页面不能重复引用 wangEditor.js 或 wangEditor.min.js !!!');
  46. // return;
  47. // }
  48. // 编辑器(整体)构造函数
  49. var E = function (elem) {
  50. // 支持 id 和 element 两种形式
  51. if (typeof elem === 'string') {
  52. elem = '#' + elem;
  53. }
  54. // ---------------获取基本节点------------------
  55. var $elem = $(elem);
  56. if ($elem.length !== 1) {
  57. return;
  58. }
  59. var nodeName = $elem[0].nodeName;
  60. if (nodeName !== 'TEXTAREA' && nodeName !== 'DIV') {
  61. // 只能是 textarea 和 div ,其他类型的元素不行
  62. return;
  63. }
  64. this.valueNodeName = nodeName.toLowerCase();
  65. this.$valueContainer = $elem;
  66. // 记录 elem 的 prev 和 parent(最后渲染 editor 要用到)
  67. this.$prev = $elem.prev();
  68. this.$parent = $elem.parent();
  69. // ------------------初始化------------------
  70. this.init();
  71. };
  72. E.fn = E.prototype;
  73. E.$body = $('body');
  74. E.$document = $(document);
  75. E.$window = $(window);
  76. E.userAgent = navigator.userAgent;
  77. E.getComputedStyle = window.getComputedStyle;
  78. E.w3cRange = typeof document.createRange === 'function';
  79. E.hostname = location.hostname.toLowerCase();
  80. E.websiteHost = 'wangeditor.github.io|www.wangeditor.com|wangeditor.coding.me';
  81. E.isOnWebsite = E.websiteHost.indexOf(E.hostname) >= 0;
  82. E.docsite = 'http://www.kancloud.cn/wangfupeng/wangeditor2/113961';
  83. // 暴露给全局对象
  84. window.wangEditor = E;
  85. // 注册 plugin 事件,用于用户自定义插件
  86. // 用户在引用 wangEditor.js 之后,还可以通过 E.plugin() 注入自定义函数,
  87. // 该函数将会在 editor.create() 方法的最后一步执行
  88. E.plugin = function (fn) {
  89. if (!E._plugins) {
  90. E._plugins = [];
  91. }
  92. if (typeof fn === 'function') {
  93. E._plugins.push(fn);
  94. }
  95. };
  96. })(window, $);
  97. // editor 绑定事件
  98. _e(function (E, $) {
  99. E.fn.init = function () {
  100. // 初始化 editor 默认配置
  101. this.initDefaultConfig();
  102. // 增加container
  103. this.addEditorContainer();
  104. // 增加编辑区域
  105. this.addTxt();
  106. // 增加menuContainer
  107. this.addMenuContainer();
  108. // 初始化菜单集合
  109. this.menus = {};
  110. // 初始化commandHooks
  111. this.commandHooks();
  112. };
  113. });
  114. // editor api
  115. _e(function (E, $) {
  116. // 预定义 ready 事件
  117. E.fn.ready = function (fn) {
  118. if (!this.readyFns) {
  119. this.readyFns = [];
  120. }
  121. this.readyFns.push(fn);
  122. };
  123. // 处理ready事件
  124. E.fn.readyHeadler = function () {
  125. var fns = this.readyFns;
  126. while (fns.length) {
  127. fns.shift().call(this);
  128. }
  129. };
  130. // 更新内容到 $valueContainer
  131. E.fn.updateValue = function () {
  132. var editor = this;
  133. var $valueContainer = editor.$valueContainer;
  134. var $txt = editor.txt.$txt;
  135. if ($valueContainer === $txt) {
  136. // 传入生成编辑器的div,即是编辑区域
  137. return;
  138. }
  139. var value = $txt.html();
  140. $valueContainer.val(value);
  141. };
  142. // 获取初始化的内容
  143. E.fn.getInitValue = function () {
  144. var editor = this;
  145. var $valueContainer = editor.$valueContainer;
  146. var currentValue = '';
  147. var nodeName = editor.valueNodeName;
  148. if (nodeName === 'div') {
  149. currentValue = $valueContainer.html();
  150. } else if (nodeName === 'textarea') {
  151. currentValue = $valueContainer.val();
  152. }
  153. return currentValue;
  154. };
  155. // 触发菜单updatestyle
  156. E.fn.updateMenuStyle = function () {
  157. var menus = this.menus;
  158. $.each(menus, function (k, menu) {
  159. menu.updateSelected();
  160. });
  161. };
  162. // 除了传入的 menuIds,其他全部启用
  163. E.fn.enableMenusExcept = function (menuIds) {
  164. if (this._disabled) {
  165. // 编辑器处于禁用状态,则不执行改操作
  166. return;
  167. }
  168. // menuIds参数:支持数组和字符串
  169. menuIds = menuIds || [];
  170. if (typeof menuIds === 'string') {
  171. menuIds = [menuIds];
  172. }
  173. $.each(this.menus, function (k, menu) {
  174. if (menuIds.indexOf(k) >= 0) {
  175. return;
  176. }
  177. menu.disabled(false);
  178. });
  179. };
  180. // 除了传入的 menuIds,其他全部禁用
  181. E.fn.disableMenusExcept = function (menuIds) {
  182. if (this._disabled) {
  183. // 编辑器处于禁用状态,则不执行改操作
  184. return;
  185. }
  186. // menuIds参数:支持数组和字符串
  187. menuIds = menuIds || [];
  188. if (typeof menuIds === 'string') {
  189. menuIds = [menuIds];
  190. }
  191. $.each(this.menus, function (k, menu) {
  192. if (menuIds.indexOf(k) >= 0) {
  193. return;
  194. }
  195. menu.disabled(true);
  196. });
  197. };
  198. // 隐藏所有 dropPanel droplist modal
  199. E.fn.hideDropPanelAndModal = function () {
  200. var menus = this.menus;
  201. $.each(menus, function (k, menu) {
  202. var m = menu.dropPanel || menu.dropList || menu.modal;
  203. if (m && m.hide) {
  204. m.hide();
  205. }
  206. });
  207. };
  208. });
  209. // selection range API
  210. _e(function (E, $) {
  211. // 用到 w3c range 的函数,如果检测到浏览器不支持 w3c range,则赋值为空函数
  212. var ieRange = !E.w3cRange;
  213. function emptyFn() {}
  214. // 设置或读取当前的range
  215. E.fn.currentRange = function (cr){
  216. if (cr) {
  217. this._rangeData = cr;
  218. } else {
  219. return this._rangeData;
  220. }
  221. };
  222. // 将当前选区折叠
  223. E.fn.collapseRange = function (range, opt) {
  224. // opt 参数说明:'start'-折叠到开始; 'end'-折叠到结束
  225. opt = opt || 'end';
  226. opt = opt === 'start' ? true : false;
  227. range = range || this.currentRange();
  228. if (range) {
  229. // 合并,保存
  230. range.collapse(opt);
  231. this.currentRange(range);
  232. }
  233. };
  234. // 获取选区的文字
  235. E.fn.getRangeText = ieRange ? emptyFn : function (range) {
  236. range = range || this.currentRange();
  237. if (!range) {
  238. return;
  239. }
  240. return range.toString();
  241. };
  242. // 获取选区对应的DOM对象
  243. E.fn.getRangeElem = ieRange ? emptyFn : function (range) {
  244. range = range || this.currentRange();
  245. var dom = range.commonAncestorContainer;
  246. if (dom.nodeType === 1) {
  247. return dom;
  248. } else {
  249. return dom.parentNode;
  250. }
  251. };
  252. // 选区内容是否为空?
  253. E.fn.isRangeEmpty = ieRange ? emptyFn : function (range) {
  254. range = range || this.currentRange();
  255. if (range && range.startContainer) {
  256. if (range.startContainer === range.endContainer) {
  257. if (range.startOffset === range.endOffset) {
  258. return true;
  259. }
  260. }
  261. }
  262. return false;
  263. };
  264. // 保存选区数据
  265. E.fn.saveSelection = ieRange ? emptyFn : function (range) {
  266. var self = this,
  267. _parentElem,
  268. selection,
  269. txt = self.txt.$txt.get(0);
  270. if (range) {
  271. _parentElem = range.commonAncestorContainer;
  272. } else {
  273. selection = document.getSelection();
  274. if (selection.getRangeAt && selection.rangeCount) {
  275. range = document.getSelection().getRangeAt(0);
  276. _parentElem = range.commonAncestorContainer;
  277. }
  278. }
  279. // 确定父元素一定要包含在编辑器区域内
  280. if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
  281. // 保存选择区域
  282. self.currentRange(range);
  283. }
  284. };
  285. // 恢复选中区域
  286. E.fn.restoreSelection = ieRange ? emptyFn : function (range) {
  287. var selection;
  288. range = range || this.currentRange();
  289. if (!range) {
  290. return;
  291. }
  292. // 使用 try catch 来防止 IE 某些情况报错
  293. try {
  294. selection = document.getSelection();
  295. selection.removeAllRanges();
  296. selection.addRange(range);
  297. } catch (ex) {
  298. E.error('执行 editor.restoreSelection 时,IE可能会有异常,不影响使用');
  299. }
  300. };
  301. // 根据elem恢复选区
  302. E.fn.restoreSelectionByElem = ieRange ? emptyFn : function (elem, opt) {
  303. // opt参数说明:'start'-折叠到开始,'end'-折叠到结束,'all'-全部选中
  304. if (!elem) {
  305. return;
  306. }
  307. opt = opt || 'end'; // 默认为折叠到结束
  308. // 根据elem获取选区
  309. this.setRangeByElem(elem);
  310. // 根据 opt 折叠选区
  311. if (opt === 'start') {
  312. this.collapseRange(this.currentRange(), 'start');
  313. }
  314. if (opt === 'end') {
  315. this.collapseRange(this.currentRange(), 'end');
  316. }
  317. // 恢复选区
  318. this.restoreSelection();
  319. };
  320. // 初始化选区
  321. E.fn.initSelection = ieRange ? emptyFn : function () {
  322. var editor = this;
  323. if( editor.currentRange() ){
  324. //如果currentRange有值,则不用再初始化
  325. return;
  326. }
  327. var range;
  328. var $txt = editor.txt.$txt;
  329. var $firstChild = $txt.children().first();
  330. if ($firstChild.length) {
  331. editor.restoreSelectionByElem($firstChild.get(0));
  332. }
  333. };
  334. // 根据元素创建选区
  335. E.fn.setRangeByElem = ieRange ? emptyFn : function (elem) {
  336. var editor = this;
  337. var txtElem = editor.txt.$txt.get(0);
  338. if (!elem || !$.contains(txtElem, elem)) {
  339. return;
  340. }
  341. // 找到elem的第一个 textNode 和 最后一个 textNode
  342. var firstTextNode = elem.firstChild;
  343. while (firstTextNode) {
  344. if (firstTextNode.nodeType === 3) {
  345. break;
  346. }
  347. // 继续向下
  348. firstTextNode = firstTextNode.firstChild;
  349. }
  350. var lastTextNode = elem.lastChild;
  351. while (lastTextNode) {
  352. if (lastTextNode.nodeType === 3) {
  353. break;
  354. }
  355. // 继续向下
  356. lastTextNode = lastTextNode.lastChild;
  357. }
  358. var range = document.createRange();
  359. if (firstTextNode && lastTextNode) {
  360. // 说明 elem 有内容,能取到子元素
  361. range.setStart(firstTextNode, 0);
  362. range.setEnd(lastTextNode, lastTextNode.textContent.length);
  363. } else {
  364. // 说明 elem 无内容
  365. range.setStart(elem, 0);
  366. range.setEnd(elem, 0);
  367. }
  368. // 保存选区
  369. editor.saveSelection(range);
  370. };
  371. });
  372. // selection range API - IE8及以下
  373. _e(function (E, $) {
  374. if (E.w3cRange) {
  375. // 说明支持 W3C 的range方法
  376. return;
  377. }
  378. // -----------------IE8时,需要重写以下方法-------------------
  379. // 获取选区的文字
  380. E.fn.getRangeText = function (range) {
  381. range = range || this.currentRange();
  382. if (!range) {
  383. return;
  384. }
  385. return range.text;
  386. };
  387. // 获取选区对应的DOM对象
  388. E.fn.getRangeElem = function (range) {
  389. range = range || this.currentRange();
  390. if (!range) {
  391. return;
  392. }
  393. var dom = range.parentElement();
  394. if (dom.nodeType === 1) {
  395. return dom;
  396. } else {
  397. return dom.parentNode;
  398. }
  399. };
  400. // 选区内容是否为空?
  401. E.fn.isRangeEmpty = function (range) {
  402. range = range || this.currentRange();
  403. if (range && range.text) {
  404. return false;
  405. }
  406. return true;
  407. };
  408. // 保存选区数据
  409. E.fn.saveSelection = function (range) {
  410. var self = this,
  411. _parentElem,
  412. selection,
  413. txt = self.txt.$txt.get(0);
  414. if (range) {
  415. _parentElem = range.parentElement();
  416. } else {
  417. range = document.selection.createRange();
  418. if(typeof range.parentElement === 'undefined'){
  419. //IE6、7中,insertImage后会执行此处
  420. //由于找不到range.parentElement,所以干脆将_parentElem赋值为null
  421. _parentElem = null;
  422. }else{
  423. _parentElem = range.parentElement();
  424. }
  425. }
  426. // 确定父元素一定要包含在编辑器区域内
  427. if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
  428. // 保存选择区域
  429. self.currentRange(range);
  430. }
  431. };
  432. // 恢复选中区域
  433. E.fn.restoreSelection = function (currentRange){
  434. var editor = this,
  435. selection,
  436. range;
  437. currentRange = currentRange || editor.currentRange();
  438. if(!currentRange){
  439. return;
  440. }
  441. range = document.selection.createRange();
  442. try {
  443. // 此处,plupload上传上传图片时,IE8-会报一个『参数无效』的错误
  444. range.setEndPoint('EndToEnd', currentRange);
  445. } catch (ex) {
  446. }
  447. if(currentRange.text.length === 0){
  448. try {
  449. // IE8 插入表情会报错
  450. range.collapse(false);
  451. } catch (ex) {
  452. }
  453. }else{
  454. range.setEndPoint('StartToStart', currentRange);
  455. }
  456. range.select();
  457. };
  458. });
  459. // editor command hooks
  460. _e(function (E, $) {
  461. E.fn.commandHooks = function () {
  462. var editor = this;
  463. var commandHooks = {};
  464. // insertHtml
  465. commandHooks.insertHtml = function (html) {
  466. var $elem = $(html);
  467. var rangeElem = editor.getRangeElem();
  468. var targetElem;
  469. targetElem = editor.getLegalTags(rangeElem);
  470. if (!targetElem) {
  471. return;
  472. }
  473. $(targetElem).after($elem);
  474. };
  475. // 保存到对象
  476. editor.commandHooks = commandHooks;
  477. };
  478. });
  479. // editor command API
  480. _e(function (E, $) {
  481. // 基本命令
  482. E.fn.command = function (e, commandName, commandValue, callback) {
  483. var editor = this;
  484. var hooks;
  485. function commandFn() {
  486. if (!commandName) {
  487. return;
  488. }
  489. if (editor.queryCommandSupported(commandName)) {
  490. // 默认命令
  491. document.execCommand(commandName, false, commandValue);
  492. } else {
  493. // hooks 命令
  494. hooks = editor.commandHooks;
  495. if (commandName in hooks) {
  496. hooks[commandName](commandValue);
  497. }
  498. }
  499. }
  500. this.customCommand(e, commandFn, callback);
  501. };
  502. // 针对一个elem对象执行基础命令
  503. E.fn.commandForElem = function (elemOpt, e, commandName, commandValue, callback) {
  504. // 取得查询elem的查询条件和验证函数
  505. var selector;
  506. var check;
  507. if (typeof elemOpt === 'string') {
  508. selector = elemOpt;
  509. } else {
  510. selector = elemOpt.selector;
  511. check = elemOpt.check;
  512. }
  513. // 查询elem
  514. var rangeElem = this.getRangeElem();
  515. rangeElem = this.getSelfOrParentByName(rangeElem, selector, check);
  516. // 根据elem设置range
  517. if (rangeElem) {
  518. this.setRangeByElem(rangeElem);
  519. }
  520. // 然后执行基础命令
  521. this.command(e, commandName, commandValue, callback);
  522. };
  523. // 自定义命令
  524. E.fn.customCommand = function (e, commandFn, callback) {
  525. var editor = this;
  526. var range = editor.currentRange();
  527. if (!range) {
  528. // 目前没有选区,则无法执行命令
  529. e && e.preventDefault();
  530. return;
  531. }
  532. // 记录内容,以便撤销(执行命令之前就要记录)
  533. editor.undoRecord();
  534. // 恢复选区(有 range 参数)
  535. this.restoreSelection(range);
  536. // 执行命令事件
  537. commandFn.call(editor);
  538. // 保存选区(无参数,要从浏览器直接获取range信息)
  539. this.saveSelection();
  540. // 重新恢复选区(无参数,要取得刚刚从浏览器得到的range信息)
  541. this.restoreSelection();
  542. // 执行 callback
  543. if (callback && typeof callback === 'function') {
  544. callback.call(editor);
  545. }
  546. // 最后插入空行
  547. editor.txt.insertEmptyP();
  548. // 包裹暴露的img和text
  549. editor.txt.wrapImgAndText();
  550. // 更新内容
  551. editor.updateValue();
  552. // 更新菜单样式
  553. editor.updateMenuStyle();
  554. // 隐藏 dropPanel dropList modal 设置 200ms 间隔
  555. function hidePanelAndModal() {
  556. editor.hideDropPanelAndModal();
  557. }
  558. setTimeout(hidePanelAndModal, 200);
  559. if (e) {
  560. e.preventDefault();
  561. }
  562. };
  563. // 封装 document.queryCommandValue 函数
  564. // IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
  565. E.fn.queryCommandValue = function (commandName) {
  566. var result = '';
  567. try {
  568. result = document.queryCommandValue(commandName);
  569. } catch (ex) {
  570. }
  571. return result;
  572. };
  573. // 封装 document.queryCommandState 函数
  574. // IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
  575. E.fn.queryCommandState = function (commandName) {
  576. var result = false;
  577. try {
  578. result = document.queryCommandState(commandName);
  579. } catch (ex) {
  580. }
  581. return result;
  582. };
  583. // 封装 document.queryCommandSupported 函数
  584. E.fn.queryCommandSupported = function (commandName) {
  585. var result = false;
  586. try {
  587. result = document.queryCommandSupported(commandName);
  588. } catch (ex) {
  589. }
  590. return result;
  591. };
  592. });
  593. // dom selector
  594. _e(function (E, $) {
  595. var matchesSelector;
  596. // matchesSelector hook
  597. function _matchesSelectorForIE(selector) {
  598. var elem = this;
  599. var $elems = $(selector);
  600. var result = false;
  601. // 用jquery查找 selector 所有对象,如果其中有一个和传入 elem 相同,则证明 elem 符合 selector
  602. $elems.each(function () {
  603. if (this === elem) {
  604. result = true;
  605. return false;
  606. }
  607. });
  608. return result;
  609. }
  610. // 从当前的elem,往上去查找合法标签 如 p head table blockquote ul ol 等
  611. E.fn.getLegalTags = function (elem) {
  612. var legalTags = this.config.legalTags;
  613. if (!legalTags) {
  614. E.error('配置项中缺少 legalTags 的配置');
  615. return;
  616. }
  617. return this.getSelfOrParentByName(elem, legalTags);
  618. };
  619. // 根据条件,查询自身或者父元素,符合即返回
  620. E.fn.getSelfOrParentByName = function (elem, selector, check) {
  621. if (!elem || !selector) {
  622. return;
  623. }
  624. if (!matchesSelector) {
  625. // 定义 matchesSelector 函数
  626. matchesSelector = elem.webkitMatchesSelector ||
  627. elem.mozMatchesSelector ||
  628. elem.oMatchesSelector ||
  629. elem.matchesSelector;
  630. }
  631. if (!matchesSelector) {
  632. // 如果浏览器本身不支持 matchesSelector 则使用自定义的hook
  633. matchesSelector = _matchesSelectorForIE;
  634. }
  635. var txt = this.txt.$txt.get(0);
  636. while (elem && txt !== elem && $.contains(txt, elem)) {
  637. if (matchesSelector.call(elem, selector)) {
  638. // 符合 selector 查询条件
  639. if (!check) {
  640. // 没有 check 验证函数,直接返回即可
  641. return elem;
  642. }
  643. if (check(elem)) {
  644. // 如果有 check 验证函数,还需 check 函数的确认
  645. return elem;
  646. }
  647. }
  648. // 如果上一步没经过验证,则将跳转到父元素
  649. elem = elem.parentNode;
  650. }
  651. return;
  652. };
  653. });
  654. // undo redo
  655. _e(function (E, $) {
  656. var length = 20; // 缓存的最大长度
  657. function _getRedoList(editor) {
  658. if (editor._redoList == null) {
  659. editor._redoList = [];
  660. }
  661. return editor._redoList;
  662. }
  663. function _getUndoList(editor) {
  664. if (editor._undoList == null) {
  665. editor._undoList = [];
  666. }
  667. return editor._undoList;
  668. }
  669. // 数据处理
  670. function _handle(editor, data, type) {
  671. // var range = data.range;
  672. // var range2 = range.cloneRange && range.cloneRange();
  673. var val = data.val;
  674. var html = editor.txt.$txt.html();
  675. if(val == null) {
  676. return;
  677. }
  678. if (val === html) {
  679. if (type === 'redo') {
  680. editor.redo();
  681. return;
  682. } else if (type === 'undo') {
  683. editor.undo();
  684. return;
  685. } else {
  686. return;
  687. }
  688. }
  689. // 保存数据
  690. editor.txt.$txt.html(val);
  691. // 更新数据到textarea(有必要的话)
  692. editor.updateValue();
  693. // onchange 事件
  694. if (editor.onchange && typeof editor.onchange === 'function') {
  695. editor.onchange.call(editor);
  696. }
  697. // ?????
  698. // 注释:$txt 被重新赋值之后,range会被重置,cloneRange() 也不好使
  699. // // 重置选区
  700. // if (range2) {
  701. // editor.restoreSelection(range2);
  702. // }
  703. }
  704. // 记录
  705. E.fn.undoRecord = function () {
  706. var editor = this;
  707. var $txt = editor.txt.$txt;
  708. var val = $txt.html();
  709. var undoList = _getUndoList(editor);
  710. var redoList = _getRedoList(editor);
  711. var currentVal = undoList.length ? undoList[0] : '';
  712. if (val === currentVal.val) {
  713. return;
  714. }
  715. // 清空 redolist
  716. if (redoList.length) {
  717. redoList = [];
  718. }
  719. // 添加数据到 undoList
  720. undoList.unshift({
  721. range: editor.currentRange(), // 将当前的range也记录下
  722. val: val
  723. });
  724. // 限制 undoList 长度
  725. if (undoList.length > length) {
  726. undoList.pop();
  727. }
  728. };
  729. // undo 操作
  730. E.fn.undo = function () {
  731. var editor = this;
  732. var undoList = _getUndoList(editor);
  733. var redoList = _getRedoList(editor);
  734. if (!undoList.length) {
  735. return;
  736. }
  737. // 取出 undolist 第一个值,加入 redolist
  738. var data = undoList.shift();
  739. redoList.unshift(data);
  740. // 并修改编辑器的内容
  741. _handle(this, data, 'undo');
  742. };
  743. // redo 操作
  744. E.fn.redo = function () {
  745. var editor = this;
  746. var undoList = _getUndoList(editor);
  747. var redoList = _getRedoList(editor);
  748. if (!redoList.length) {
  749. return;
  750. }
  751. // 取出 redolist 第一个值,加入 undolist
  752. var data = redoList.shift();
  753. undoList.unshift(data);
  754. // 并修改编辑器的内容
  755. _handle(this, data, 'redo');
  756. };
  757. });
  758. // 暴露给用户的 API
  759. _e(function (E, $) {
  760. // 创建编辑器
  761. E.fn.create = function () {
  762. var editor = this;
  763. // 检查 E.$body 是否有值
  764. // 如果在 body 之前引用了 js 文件,body 尚未加载,可能没有值
  765. if (!E.$body || E.$body.length === 0) {
  766. E.$body = $('body');
  767. E.$document = $(document);
  768. E.$window = $(window);
  769. }
  770. // 执行 addMenus 之前:
  771. // 1. 允许用户修改 editor.UI 自定义配置UI
  772. // 2. 允许用户通过修改 editor.menus 来自定义配置菜单
  773. // 因此要在 create 时执行,而不是 init
  774. editor.addMenus();
  775. // 渲染
  776. editor.renderMenus();
  777. editor.renderMenuContainer();
  778. editor.renderTxt();
  779. editor.renderEditorContainer();
  780. // 绑定事件
  781. editor.eventMenus();
  782. editor.eventMenuContainer();
  783. editor.eventTxt();
  784. // 处理ready事件
  785. editor.readyHeadler();
  786. // 初始化选区
  787. editor.initSelection();
  788. // $txt 快捷方式
  789. editor.$txt = editor.txt.$txt;
  790. // 执行用户自定义事件,通过 E.ready() 添加
  791. var _plugins = E._plugins;
  792. if (_plugins && _plugins.length) {
  793. $.each(_plugins, function (k, val) {
  794. val.call(editor);
  795. });
  796. }
  797. };
  798. // 禁用编辑器
  799. E.fn.disable = function () {
  800. this.txt.$txt.removeAttr('contenteditable');
  801. this.disableMenusExcept();
  802. // 先禁用,再记录状态
  803. this._disabled = true;
  804. };
  805. // 启用编辑器
  806. E.fn.enable = function () {
  807. // 先解除状态记录,再启用
  808. this._disabled = false;
  809. this.txt.$txt.attr('contenteditable', 'true');
  810. this.enableMenusExcept();
  811. };
  812. // 销毁编辑器
  813. E.fn.destroy = function () {
  814. var self = this;
  815. var $valueContainer = self.$valueContainer;
  816. var $editorContainer = self.$editorContainer;
  817. var valueNodeName = self.valueNodeName;
  818. if (valueNodeName === 'div') {
  819. // div 生成的编辑器
  820. $valueContainer.removeAttr('contenteditable');
  821. $editorContainer.after($valueContainer);
  822. $editorContainer.hide();
  823. } else {
  824. // textarea 生成的编辑器
  825. $valueContainer.show();
  826. $editorContainer.hide();
  827. }
  828. };
  829. // 撤销 销毁编辑器
  830. E.fn.undestroy = function () {
  831. var self = this;
  832. var $valueContainer = self.$valueContainer;
  833. var $editorContainer = self.$editorContainer;
  834. var $menuContainer = self.menuContainer.$menuContainer;
  835. var valueNodeName = self.valueNodeName;
  836. if (valueNodeName === 'div') {
  837. // div 生成的编辑器
  838. $valueContainer.attr('contenteditable', 'true');
  839. $menuContainer.after($valueContainer);
  840. $editorContainer.show();
  841. } else {
  842. // textarea 生成的编辑器
  843. $valueContainer.hide();
  844. $editorContainer.show();
  845. }
  846. };
  847. // 清空内容的快捷方式
  848. E.fn.clear = function () {
  849. var editor = this;
  850. var $txt = editor.txt.$txt;
  851. $txt.html('<p><br></p>');
  852. editor.restoreSelectionByElem($txt.find('p').get(0));
  853. };
  854. });
  855. // menuContainer 构造函数
  856. _e(function (E, $) {
  857. // 定义构造函数
  858. var MenuContainer = function (editor) {
  859. this.editor = editor;
  860. this.init();
  861. };
  862. MenuContainer.fn = MenuContainer.prototype;
  863. // 暴露给 E 即 window.wangEditor
  864. E.MenuContainer = MenuContainer;
  865. });
  866. // MenuContainer.fn bind fn
  867. _e(function (E, $) {
  868. var MenuContainer = E.MenuContainer;
  869. // 初始化
  870. MenuContainer.fn.init = function () {
  871. var self = this;
  872. var $menuContainer = $('<div class="wangEditor-menu-container clearfix"></div>');
  873. self.$menuContainer = $menuContainer;
  874. // change shadow
  875. self.changeShadow();
  876. };
  877. // 编辑区域滚动时,增加shadow
  878. MenuContainer.fn.changeShadow = function () {
  879. var $menuContainer = this.$menuContainer;
  880. var editor = this.editor;
  881. var $txt = editor.txt.$txt;
  882. $txt.on('scroll', function () {
  883. if ($txt.scrollTop() > 10) {
  884. $menuContainer.addClass('wangEditor-menu-shadow');
  885. } else {
  886. $menuContainer.removeClass('wangEditor-menu-shadow');
  887. }
  888. });
  889. };
  890. });
  891. // MenuContainer.fn API
  892. _e(function (E, $) {
  893. var MenuContainer = E.MenuContainer;
  894. MenuContainer.fn.render = function () {
  895. var $menuContainer = this.$menuContainer;
  896. var $editorContainer = this.editor.$editorContainer;
  897. $editorContainer.append($menuContainer);
  898. };
  899. // 获取菜单栏的高度
  900. MenuContainer.fn.height = function () {
  901. var $menuContainer = this.$menuContainer;
  902. return $menuContainer.height();
  903. };
  904. // 添加菜单
  905. MenuContainer.fn.appendMenu = function (groupIdx, menu) {
  906. // 判断是否需要新增一个菜单组
  907. this._addGroup(groupIdx);
  908. // 增加菜单(返回 $menuItem)
  909. return this._addOneMenu(menu);
  910. };
  911. MenuContainer.fn._addGroup = function (groupIdx) {
  912. var $menuContainer = this.$menuContainer;
  913. var $menuGroup;
  914. if (!this.$currentGroup || this.currentGroupIdx !== groupIdx) {
  915. $menuGroup = $('<div class="menu-group clearfix"></div>');
  916. $menuContainer.append($menuGroup);
  917. this.$currentGroup = $menuGroup;
  918. this.currentGroupIdx = groupIdx;
  919. }
  920. };
  921. MenuContainer.fn._addOneMenu = function (menu) {
  922. var $menuNormal = menu.$domNormal;
  923. var $menuSelected = menu.$domSelected;
  924. var $menuGroup = this.$currentGroup;
  925. var $item = $('<div class="menu-item clearfix"></div>');
  926. $menuSelected.hide();
  927. $item.append($menuNormal).append($menuSelected);
  928. $menuGroup.append($item);
  929. return $item;
  930. };
  931. });
  932. // menu 构造函数
  933. _e(function (E, $) {
  934. // 定义构造函数
  935. var Menu = function (opt) {
  936. this.editor = opt.editor;
  937. this.id = opt.id;
  938. this.title = opt.title;
  939. this.$domNormal = opt.$domNormal;
  940. this.$domSelected = opt.$domSelected || opt.$domNormal;
  941. // document.execCommand 的参数
  942. this.commandName = opt.commandName;
  943. this.commandValue = opt.commandValue;
  944. this.commandNameSelected = opt.commandNameSelected || opt.commandName;
  945. this.commandValueSelected = opt.commandValueSelected || opt.commandValue;
  946. };
  947. Menu.fn = Menu.prototype;
  948. // 暴露给 E 即 window.wangEditor
  949. E.Menu = Menu;
  950. });
  951. // Menu.fn 初始化绑定的事件
  952. _e(function (E, $) {
  953. var Menu = E.Menu;
  954. // 初始化UI
  955. Menu.fn.initUI = function () {
  956. var editor = this.editor;
  957. var uiConfig = editor.UI.menus;
  958. var menuId = this.id;
  959. var menuUI = uiConfig[menuId];
  960. if (this.$domNormal && this.$domSelected) {
  961. // 自定义的菜单中,已经传入了 $dom 无需从配置文件中查找生成
  962. return;
  963. }
  964. if (menuUI == null) {
  965. E.warn('editor.UI配置中,没有菜单 "' + menuId + '" 的UI配置,只能取默认值');
  966. // 必须写成 uiConfig['default'];
  967. // 写成 uiConfig.default IE8会报错
  968. menuUI = uiConfig['default'];
  969. }
  970. // 正常状态
  971. this.$domNormal = $(menuUI.normal);
  972. // 选中状态
  973. if (/^\./.test(menuUI.selected)) {
  974. // 增加一个样式
  975. this.$domSelected = this.$domNormal.clone().addClass(menuUI.selected.slice(1));
  976. } else {
  977. // 一个新的dom对象
  978. this.$domSelected = $(menuUI.selected);
  979. }
  980. };
  981. });
  982. // Menu.fn API
  983. _e(function (E, $) {
  984. var Menu = E.Menu;
  985. // 渲染菜单
  986. Menu.fn.render = function (groupIdx) {
  987. // 渲染UI
  988. this.initUI();
  989. var editor = this.editor;
  990. var menuContainer = editor.menuContainer;
  991. var $menuItem = menuContainer.appendMenu(groupIdx, this);
  992. var onRender = this.onRender;
  993. // 渲染tip
  994. this._renderTip($menuItem);
  995. // 执行 onRender 函数
  996. if (onRender && typeof onRender === 'function') {
  997. onRender.call(this);
  998. }
  999. };
  1000. Menu.fn._renderTip = function ($menuItem) {
  1001. var self = this;
  1002. var editor = self.editor;
  1003. var title = self.title;
  1004. var $tip = $('<div class="menu-tip"></div>');
  1005. // var $triangle = $('<i class="tip-triangle"></i>'); // 小三角
  1006. // 计算 tip 宽度
  1007. var $tempDiv;
  1008. if (!self.tipWidth) {
  1009. // 设置一个纯透明的 p(absolute;top:-10000px;不会显示在内容区域)
  1010. // 内容赋值为 title ,为了计算tip宽度
  1011. $tempDiv = $('<p style="opacity:0; filter:Alpha(opacity=0); position:absolute;top:-10000px;">' + title + '</p>');
  1012. // 先添加到body,计算完再 remove
  1013. E.$body.append($tempDiv);
  1014. editor.ready(function () {
  1015. var editor = this;
  1016. var titleWidth = $tempDiv.outerWidth() + 5; // 多出 5px 的冗余
  1017. var currentWidth = $tip.outerWidth();
  1018. var currentMarginLeft = parseFloat($tip.css('margin-left'), 10);
  1019. // 计算完,拿到数据,则弃用
  1020. $tempDiv.remove();
  1021. $tempDiv = null;
  1022. // 重新设置样式
  1023. $tip.css({
  1024. width: titleWidth,
  1025. 'margin-left': currentMarginLeft + (currentWidth - titleWidth)/2
  1026. });
  1027. // 存储
  1028. self.tipWidth = titleWidth;
  1029. });
  1030. }
  1031. // $tip.append($triangle);
  1032. $tip.append(title);
  1033. $menuItem.append($tip);
  1034. function show() {
  1035. $tip.show();
  1036. }
  1037. function hide() {
  1038. $tip.hide();
  1039. }
  1040. var timeoutId;
  1041. $menuItem.find('a').on('mouseenter', function (e) {
  1042. if (!self.active() && !self.disabled()) {
  1043. timeoutId = setTimeout(show, 200);
  1044. }
  1045. }).on('mouseleave', function (e) {
  1046. timeoutId && clearTimeout(timeoutId);
  1047. hide();
  1048. }).on('click', hide);
  1049. };
  1050. // 绑定事件
  1051. Menu.fn.bindEvent = function () {
  1052. var self = this;
  1053. var $domNormal = self.$domNormal;
  1054. var $domSelected = self.$domSelected;
  1055. // 试图获取该菜单定义的事件(未selected),没有则自己定义
  1056. var clickEvent = self.clickEvent;
  1057. if (!clickEvent) {
  1058. clickEvent = function (e) {
  1059. // -----------dropPanel dropList modal-----------
  1060. var dropObj = self.dropPanel || self.dropList || self.modal;
  1061. if (dropObj && dropObj.show) {
  1062. if (dropObj.isShowing) {
  1063. dropObj.hide();
  1064. } else {
  1065. dropObj.show();
  1066. }
  1067. return;
  1068. }
  1069. // -----------command-----------
  1070. var editor = self.editor;
  1071. var commandName;
  1072. var commandValue;
  1073. var selected = self.selected;
  1074. if (selected) {
  1075. commandName = self.commandNameSelected;
  1076. commandValue = self.commandValueSelected;
  1077. } else {
  1078. commandName = self.commandName;
  1079. commandValue = self.commandValue;
  1080. }
  1081. if (commandName) {
  1082. // 执行命令
  1083. editor.command(e, commandName, commandValue);
  1084. } else {
  1085. // 提示
  1086. E.warn('菜单 "' + self.id + '" 未定义click事件');
  1087. e.preventDefault();
  1088. }
  1089. };
  1090. }
  1091. // 获取菜单定义的selected情况下的点击事件
  1092. var clickEventSelected = self.clickEventSelected || clickEvent;
  1093. // 将事件绑定到菜单dom上
  1094. $domNormal.click(function (e) {
  1095. if (!self.disabled()) {
  1096. clickEvent.call(self, e);
  1097. self.updateSelected();
  1098. }
  1099. e.preventDefault();
  1100. });
  1101. $domSelected.click(function (e) {
  1102. if (!self.disabled()) {
  1103. clickEventSelected.call(self, e);
  1104. self.updateSelected();
  1105. }
  1106. e.preventDefault();
  1107. });
  1108. };
  1109. // 更新选中状态
  1110. Menu.fn.updateSelected = function () {
  1111. var self = this;
  1112. var editor = self.editor;
  1113. // 试图获取用户自定义的判断事件
  1114. var updateSelectedEvent = self.updateSelectedEvent;
  1115. if (!updateSelectedEvent) {
  1116. // 用户未自定义,则设置默认值
  1117. updateSelectedEvent = function () {
  1118. var self = this;
  1119. var editor = self.editor;
  1120. var commandName = self.commandName;
  1121. var commandValue = self.commandValue;
  1122. if (commandValue) {
  1123. if (editor.queryCommandValue(commandName).toLowerCase() === commandValue.toLowerCase()) {
  1124. return true;
  1125. }
  1126. } else if (editor.queryCommandState(commandName)) {
  1127. return true;
  1128. }
  1129. return false;
  1130. };
  1131. }
  1132. // 获取结果
  1133. var result = updateSelectedEvent.call(self);
  1134. result = !!result;
  1135. // 存储结果、显示效果
  1136. self.changeSelectedState(result);
  1137. };
  1138. // 切换选中状态、显示效果
  1139. Menu.fn.changeSelectedState = function (state) {
  1140. var self = this;
  1141. var selected = self.selected;
  1142. if (state != null && typeof state === 'boolean') {
  1143. if (selected === state) {
  1144. // 计算结果和当前的状态一样
  1145. return;
  1146. }
  1147. // 存储结果
  1148. self.selected = state;
  1149. // 切换菜单的显示
  1150. if (state) {
  1151. // 选中
  1152. self.$domNormal.hide();
  1153. self.$domSelected.show();
  1154. } else {
  1155. // 未选中
  1156. self.$domNormal.show();
  1157. self.$domSelected.hide();
  1158. }
  1159. } // if
  1160. };
  1161. // 点击菜单,显示了 dropPanel modal 时,菜单的状态
  1162. Menu.fn.active = function (active) {
  1163. if (active == null) {
  1164. return this._activeState;
  1165. }
  1166. this._activeState = active;
  1167. };
  1168. Menu.fn.activeStyle = function (active) {
  1169. var selected = this.selected;
  1170. var $dom = this.$domNormal;
  1171. var $domSelected = this.$domSelected;
  1172. if (active) {
  1173. $dom.addClass('active');
  1174. $domSelected.addClass('active');
  1175. } else {
  1176. $dom.removeClass('active');
  1177. $domSelected.removeClass('active');
  1178. }
  1179. // 记录状态 ( menu hover 时会取状态用 )
  1180. this.active(active);
  1181. };
  1182. // 菜单的启用和禁用
  1183. Menu.fn.disabled = function (opt) {
  1184. // 参数为空,取值
  1185. if (opt == null) {
  1186. return !!this._disabled;
  1187. }
  1188. if (this._disabled === opt) {
  1189. // 要设置的参数值和当前参数只一样,无需再次设置
  1190. return;
  1191. }
  1192. var $dom = this.$domNormal;
  1193. var $domSelected = this.$domSelected;
  1194. // 设置样式
  1195. if (opt) {
  1196. $dom.addClass('disable');
  1197. $domSelected.addClass('disable');
  1198. } else {
  1199. $dom.removeClass('disable');
  1200. $domSelected.removeClass('disable');
  1201. }
  1202. // 存储
  1203. this._disabled = opt;
  1204. };
  1205. });
  1206. // dropList 构造函数
  1207. _e(function (E, $) {
  1208. // 定义构造函数
  1209. var DropList = function (editor, menu, opt) {
  1210. this.editor = editor;
  1211. this.menu = menu;
  1212. // list 的数据源,格式 {'commandValue': 'title', ...}
  1213. this.data = opt.data;
  1214. // 要为每个item自定义的模板
  1215. this.tpl = opt.tpl;
  1216. // 为了执行 editor.commandForElem 而传入的elem查询方式
  1217. this.selectorForELemCommand = opt.selectorForELemCommand;
  1218. // 执行事件前后的钩子
  1219. this.beforeEvent = opt.beforeEvent;
  1220. this.afterEvent = opt.afterEvent;
  1221. // 初始化
  1222. this.init();
  1223. };
  1224. DropList.fn = DropList.prototype;
  1225. // 暴露给 E 即 window.wangEditor
  1226. E.DropList = DropList;
  1227. });
  1228. // dropList fn bind
  1229. _e(function (E, $) {
  1230. var DropList = E.DropList;
  1231. // init
  1232. DropList.fn.init = function () {
  1233. var self = this;
  1234. // 生成dom对象
  1235. self.initDOM();
  1236. // 绑定command事件
  1237. self.bindEvent();
  1238. // 声明隐藏的事件
  1239. self.initHideEvent();
  1240. };
  1241. // 初始化dom结构
  1242. DropList.fn.initDOM = function () {
  1243. var self = this;
  1244. var data = self.data;
  1245. var tpl = self.tpl || '<span>{#title}</span>';
  1246. var $list = $('<div class="wangEditor-drop-list clearfix"></div>');
  1247. var itemContent;
  1248. var $item;
  1249. $.each(data, function (commandValue, title) {
  1250. itemContent = tpl.replace(/{#commandValue}/ig, commandValue).replace(/{#title}/ig, title);
  1251. $item = $('<a href="#" commandValue="' + commandValue + '"></a>');
  1252. $item.append(itemContent);
  1253. $list.append($item);
  1254. });
  1255. self.$list = $list;
  1256. };
  1257. // 绑定事件
  1258. DropList.fn.bindEvent = function () {
  1259. var self = this;
  1260. var editor = self.editor;
  1261. var menu = self.menu;
  1262. var commandName = menu.commandName;
  1263. var selectorForELemCommand = self.selectorForELemCommand;
  1264. var $list = self.$list;
  1265. // 执行事件前后的钩子函数
  1266. var beforeEvent = self.beforeEvent;
  1267. var afterEvent = self.afterEvent;
  1268. $list.on('click', 'a[commandValue]', function (e) {
  1269. // 正式命令执行之前
  1270. if (beforeEvent && typeof beforeEvent === 'function') {
  1271. beforeEvent.call(e);
  1272. }
  1273. // 执行命令
  1274. var commandValue = $(e.currentTarget).attr('commandValue');
  1275. if (menu.selected && editor.isRangeEmpty() && selectorForELemCommand) {
  1276. // 当前处于选中状态,并且选中内容为空
  1277. editor.commandForElem(selectorForELemCommand, e, commandName, commandValue);
  1278. } else {
  1279. // 当前未处于选中状态,或者有选中内容。则执行默认命令
  1280. editor.command(e, commandName, commandValue);
  1281. }
  1282. // 正式命令之后的钩子
  1283. if (afterEvent && typeof afterEvent === 'function') {
  1284. afterEvent.call(e);
  1285. }
  1286. });
  1287. };
  1288. // 点击其他地方,立即隐藏 droplist
  1289. DropList.fn.initHideEvent = function () {
  1290. var self = this;
  1291. // 获取 list elem
  1292. var thisList = self.$list.get(0);
  1293. E.$body.on('click', function (e) {
  1294. if (!self.isShowing) {
  1295. return;
  1296. }
  1297. var trigger = e.target;
  1298. // 获取菜单elem
  1299. var menu = self.menu;
  1300. var menuDom;
  1301. if (menu.selected) {
  1302. menuDom = menu.$domSelected.get(0);
  1303. } else {
  1304. menuDom = menu.$domNormal.get(0);
  1305. }
  1306. if (menuDom === trigger || $.contains(menuDom, trigger)) {
  1307. // 说明由本菜单点击触发的
  1308. return;
  1309. }
  1310. if (thisList === trigger || $.contains(thisList, trigger)) {
  1311. // 说明由本list点击触发的
  1312. return;
  1313. }
  1314. // 其他情况,隐藏 list
  1315. self.hide();
  1316. });
  1317. E.$window.scroll(function () {
  1318. self.hide();
  1319. });
  1320. E.$window.on('resize', function () {
  1321. self.hide();
  1322. });
  1323. };
  1324. });
  1325. // dropListfn api
  1326. _e(function (E, $) {
  1327. var DropList = E.DropList;
  1328. // 渲染
  1329. DropList.fn._render = function () {
  1330. var self = this;
  1331. var editor = self.editor;
  1332. var $list = self.$list;
  1333. // 渲染到页面
  1334. editor.$editorContainer.append($list);
  1335. // 记录状态
  1336. self.rendered = true;
  1337. };
  1338. // 定位
  1339. DropList.fn._position = function () {
  1340. var self = this;
  1341. var $list = self.$list;
  1342. var editor = self.editor;
  1343. var menu = self.menu;
  1344. var $menuContainer = editor.menuContainer.$menuContainer;
  1345. var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
  1346. // 注意这里的 offsetParent() 要返回 .menu-item 的 position
  1347. // 因为 .menu-item 是 position:relative
  1348. var menuPosition = $menuDom.offsetParent().position();
  1349. // 取得 menu 的位置、尺寸属性
  1350. var menuTop = menuPosition.top;
  1351. var menuLeft = menuPosition.left;
  1352. var menuHeight = $menuDom.offsetParent().height();
  1353. var menuWidth = $menuDom.offsetParent().width();
  1354. // 取得 list 的尺寸属性
  1355. var listWidth = $list.outerWidth();
  1356. // var listHeight = $list.outerHeight();
  1357. // 取得 $txt 的尺寸
  1358. var txtWidth = editor.txt.$txt.outerWidth();
  1359. // ------------开始计算-------------
  1360. // 初步计算 list 位置属性
  1361. var top = menuTop + menuHeight;
  1362. var left = menuLeft + menuWidth/2;
  1363. var marginLeft = 0 - menuWidth/2;
  1364. // 如果超出了有边界,则要左移(且和右侧有间隙)
  1365. var valWithTxt = (left + listWidth) - txtWidth;
  1366. if (valWithTxt > -10) {
  1367. marginLeft = marginLeft - valWithTxt - 10;
  1368. }
  1369. // 设置样式
  1370. $list.css({
  1371. top: top,
  1372. left: left,
  1373. 'margin-left': marginLeft
  1374. });
  1375. // 如果因为向下滚动而导致菜单fixed,则再加一步处理
  1376. if (editor._isMenufixed) {
  1377. top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $list.offset().top);
  1378. // 重新设置top
  1379. $list.css({
  1380. top: top
  1381. });
  1382. }
  1383. };
  1384. // 显示
  1385. DropList.fn.show = function () {
  1386. var self = this;
  1387. var menu = self.menu;
  1388. if (!self.rendered) {
  1389. // 第一次show之前,先渲染
  1390. self._render();
  1391. }
  1392. if (self.isShowing) {
  1393. return;
  1394. }
  1395. var $list = self.$list;
  1396. $list.show();
  1397. // 定位
  1398. self._position();
  1399. // 记录状态
  1400. self.isShowing = true;
  1401. // 菜单状态
  1402. menu.activeStyle(true);
  1403. };
  1404. // 隐藏
  1405. DropList.fn.hide = function () {
  1406. var self = this;
  1407. var menu = self.menu;
  1408. if (!self.isShowing) {
  1409. return;
  1410. }
  1411. var $list = self.$list;
  1412. $list.hide();
  1413. // 记录状态
  1414. self.isShowing = false;
  1415. // 菜单状态
  1416. menu.activeStyle(false);
  1417. };
  1418. });
  1419. // dropPanel 构造函数
  1420. _e(function (E, $) {
  1421. // 定义构造函数
  1422. var DropPanel = function (editor, menu, opt) {
  1423. this.editor = editor;
  1424. this.menu = menu;
  1425. this.$content = opt.$content;
  1426. this.width = opt.width || 200;
  1427. this.height = opt.height;
  1428. this.onRender = opt.onRender;
  1429. // init
  1430. this.init();
  1431. };
  1432. DropPanel.fn = DropPanel.prototype;
  1433. // 暴露给 E 即 window.wangEditor
  1434. E.DropPanel = DropPanel;
  1435. });
  1436. // dropPanel fn bind
  1437. _e(function (E, $) {
  1438. var DropPanel = E.DropPanel;
  1439. // init
  1440. DropPanel.fn.init = function () {
  1441. var self = this;
  1442. // 生成dom对象
  1443. self.initDOM();
  1444. // 声明隐藏的事件
  1445. self.initHideEvent();
  1446. };
  1447. // init DOM
  1448. DropPanel.fn.initDOM = function () {
  1449. var self = this;
  1450. var $content = self.$content;
  1451. var width = self.width;
  1452. var height = self.height;
  1453. var $panel = $('<div class="wangEditor-drop-panel clearfix"></div>');
  1454. var $triangle = $('<div class="tip-triangle"></div>');
  1455. $panel.css({
  1456. width: width,
  1457. height: height ? height : 'auto'
  1458. });
  1459. $panel.append($triangle);
  1460. $panel.append($content);
  1461. // 添加对象数据
  1462. self.$panel = $panel;
  1463. self.$triangle = $triangle;
  1464. };
  1465. // 点击其他地方,立即隐藏 dropPanel
  1466. DropPanel.fn.initHideEvent = function () {
  1467. var self = this;
  1468. // 获取 panel elem
  1469. var thisPanle = self.$panel.get(0);
  1470. E.$body.on('click', function (e) {
  1471. if (!self.isShowing) {
  1472. return;
  1473. }
  1474. var trigger = e.target;
  1475. // 获取菜单elem
  1476. var menu = self.menu;
  1477. var menuDom;
  1478. if (menu.selected) {
  1479. menuDom = menu.$domSelected.get(0);
  1480. } else {
  1481. menuDom = menu.$domNormal.get(0);
  1482. }
  1483. if (menuDom === trigger || $.contains(menuDom, trigger)) {
  1484. // 说明由本菜单点击触发的
  1485. return;
  1486. }
  1487. if (thisPanle === trigger || $.contains(thisPanle, trigger)) {
  1488. // 说明由本panel点击触发的
  1489. return;
  1490. }
  1491. // 其他情况,隐藏 panel
  1492. self.hide();
  1493. });
  1494. E.$window.scroll(function (e) {
  1495. self.hide();
  1496. });
  1497. E.$window.on('resize', function () {
  1498. self.hide();
  1499. });
  1500. };
  1501. });
  1502. // dropPanel fn api
  1503. _e(function (E, $) {
  1504. var DropPanel = E.DropPanel;
  1505. // 渲染
  1506. DropPanel.fn._render = function () {
  1507. var self = this;
  1508. var onRender = self.onRender;
  1509. var editor = self.editor;
  1510. var $panel = self.$panel;
  1511. // 渲染到页面
  1512. editor.$editorContainer.append($panel);
  1513. // 渲染后的回调事件
  1514. onRender && onRender.call(self);
  1515. // 记录状态
  1516. self.rendered = true;
  1517. };
  1518. // 定位
  1519. DropPanel.fn._position = function () {
  1520. var self = this;
  1521. var $panel = self.$panel;
  1522. var $triangle = self.$triangle;
  1523. var editor = self.editor;
  1524. var $menuContainer = editor.menuContainer.$menuContainer;
  1525. var menu = self.menu;
  1526. var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
  1527. // 注意这里的 offsetParent() 要返回 .menu-item 的 position
  1528. // 因为 .menu-item 是 position:relative
  1529. var menuPosition = $menuDom.offsetParent().position();
  1530. // 取得 menu 的位置、尺寸属性
  1531. var menuTop = menuPosition.top;
  1532. var menuLeft = menuPosition.left;
  1533. var menuHeight = $menuDom.offsetParent().height();
  1534. var menuWidth = $menuDom.offsetParent().width();
  1535. // 取得 panel 的尺寸属性
  1536. var panelWidth = $panel.outerWidth();
  1537. // var panelHeight = $panel.outerHeight();
  1538. // 取得 $txt 的尺寸
  1539. var txtWidth = editor.txt.$txt.outerWidth();
  1540. // ------------开始计算-------------
  1541. // 初步计算 panel 位置属性
  1542. var top = menuTop + menuHeight;
  1543. var left = menuLeft + menuWidth/2;
  1544. var marginLeft = 0 - panelWidth/2;
  1545. var marginLeft2 = marginLeft; // 下文用于和 marginLeft 比较,来设置三角形tip的位置
  1546. // 如果超出了左边界,则移动回来(要和左侧有10px间隙)
  1547. if ((0 - marginLeft) > (left - 10)) {
  1548. marginLeft = 0 - (left - 10);
  1549. }
  1550. // 如果超出了有边界,则要左移(且和右侧有10px间隙)
  1551. var valWithTxt = (left + panelWidth + marginLeft) - txtWidth;
  1552. if (valWithTxt > -10) {
  1553. marginLeft = marginLeft - valWithTxt - 10;
  1554. }
  1555. // 设置样式
  1556. $panel.css({
  1557. top: top,
  1558. left: left,
  1559. 'margin-left': marginLeft
  1560. });
  1561. // 如果因为向下滚动而导致菜单fixed,则再加一步处理
  1562. if (editor._isMenufixed) {
  1563. top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $panel.offset().top);
  1564. // 重新设置top
  1565. $panel.css({
  1566. top: top
  1567. });
  1568. }
  1569. // 设置三角形 tip 的位置
  1570. $triangle.css({
  1571. 'margin-left': marginLeft2 - marginLeft - 5
  1572. });
  1573. };
  1574. // focus 第一个 input
  1575. DropPanel.fn.focusFirstInput = function () {
  1576. var self = this;
  1577. var $panel = self.$panel;
  1578. $panel.find('input[type=text],textarea').each(function () {
  1579. var $input = $(this);
  1580. if ($input.attr('disabled') == null) {
  1581. $input.focus();
  1582. return false;
  1583. }
  1584. });
  1585. };
  1586. // 显示
  1587. DropPanel.fn.show = function () {
  1588. var self = this;
  1589. var menu = self.menu;
  1590. if (!self.rendered) {
  1591. // 第一次show之前,先渲染
  1592. self._render();
  1593. }
  1594. if (self.isShowing) {
  1595. return;
  1596. }
  1597. var $panel = self.$panel;
  1598. $panel.show();
  1599. // 定位
  1600. self._position();
  1601. // 记录状态
  1602. self.isShowing = true;
  1603. // 菜单状态
  1604. menu.activeStyle(true);
  1605. if (E.w3cRange) {
  1606. // 高级浏览器
  1607. self.focusFirstInput();
  1608. } else {
  1609. // 兼容 IE8 input placeholder
  1610. E.placeholderForIE8($panel);
  1611. }
  1612. };
  1613. // 隐藏
  1614. DropPanel.fn.hide = function () {
  1615. var self = this;
  1616. var menu = self.menu;
  1617. if (!self.isShowing) {
  1618. return;
  1619. }
  1620. var $panel = self.$panel;
  1621. $panel.hide();
  1622. // 记录状态
  1623. self.isShowing = false;
  1624. // 菜单状态
  1625. menu.activeStyle(false);
  1626. };
  1627. });
  1628. // modal 构造函数
  1629. _e(function (E, $) {
  1630. // 定义构造函数
  1631. var Modal = function (editor, menu, opt) {
  1632. this.editor = editor;
  1633. this.menu = menu;
  1634. this.$content = opt.$content;
  1635. this.init();
  1636. };
  1637. Modal.fn = Modal.prototype;
  1638. // 暴露给 E 即 window.wangEditor
  1639. E.Modal = Modal;
  1640. });
  1641. // modal fn bind
  1642. _e(function (E, $) {
  1643. var Modal = E.Modal;
  1644. Modal.fn.init = function () {
  1645. var self = this;
  1646. // 初始化dom
  1647. self.initDom();
  1648. // 初始化隐藏事件
  1649. self.initHideEvent();
  1650. };
  1651. // 初始化dom
  1652. Modal.fn.initDom = function () {
  1653. var self = this;
  1654. var $content = self.$content;
  1655. var $modal = $('<div class="wangEditor-modal"></div>');
  1656. var $close = $('<div class="wangEditor-modal-close"><i class="wangeditor-menu-img-cancel-circle"></i></div>');
  1657. $modal.append($close);
  1658. $modal.append($content);
  1659. // 记录数据
  1660. self.$modal = $modal;
  1661. self.$close = $close;
  1662. };
  1663. // 初始化隐藏事件
  1664. Modal.fn.initHideEvent = function () {
  1665. var self = this;
  1666. var $close = self.$close;
  1667. var modal = self.$modal.get(0);
  1668. // 点击 $close 按钮,隐藏
  1669. $close.click(function () {
  1670. self.hide();
  1671. });
  1672. // 点击其他部分,隐藏
  1673. E.$body.on('click', function (e) {
  1674. if (!self.isShowing) {
  1675. return;
  1676. }
  1677. var trigger = e.target;
  1678. // 获取菜单elem
  1679. var menu = self.menu;
  1680. var menuDom;
  1681. if (menu) {
  1682. if (menu.selected) {
  1683. menuDom = menu.$domSelected.get(0);
  1684. } else {
  1685. menuDom = menu.$domNormal.get(0);
  1686. }
  1687. if (menuDom === trigger || $.contains(menuDom, trigger)) {
  1688. // 说明由本菜单点击触发的
  1689. return;
  1690. }
  1691. }
  1692. if (modal === trigger || $.contains(modal, trigger)) {
  1693. // 说明由本panel点击触发的
  1694. return;
  1695. }
  1696. // 其他情况,隐藏 panel
  1697. self.hide();
  1698. });
  1699. };
  1700. });
  1701. // modal fn api
  1702. _e(function (E, $) {
  1703. var Modal = E.Modal;
  1704. // 渲染
  1705. Modal.fn._render = function () {
  1706. var self = this;
  1707. var editor = self.editor;
  1708. var $modal = self.$modal;
  1709. // $modal的z-index,在配置的z-index基础上再 +10
  1710. $modal.css('z-index', editor.config.zindex + 10 + '');
  1711. // 渲染到body最后面
  1712. E.$body.append($modal);
  1713. // 记录状态
  1714. self.rendered = true;
  1715. };
  1716. // 定位
  1717. Modal.fn._position = function () {
  1718. var self = this;
  1719. var $modal = self.$modal;
  1720. var top = $modal.offset().top;
  1721. var width = $modal.outerWidth();
  1722. var height = $modal.outerHeight();
  1723. var marginLeft = 0 - (width / 2);
  1724. var marginTop = 0 - (height / 2);
  1725. var sTop = E.$window.scrollTop();
  1726. // 保证modal最顶部,不超过浏览器上边框
  1727. if ((height / 2) > top) {
  1728. marginTop = 0 - top;
  1729. }
  1730. $modal.css({
  1731. 'margin-left': marginLeft + 'px',
  1732. 'margin-top': (marginTop + sTop) + 'px'
  1733. });
  1734. };
  1735. // 显示
  1736. Modal.fn.show = function () {
  1737. var self = this;
  1738. var menu = self.menu;
  1739. if (!self.rendered) {
  1740. // 第一次show之前,先渲染
  1741. self._render();
  1742. }
  1743. if (self.isShowing) {
  1744. return;
  1745. }
  1746. // 记录状态
  1747. self.isShowing = true;
  1748. var $modal = self.$modal;
  1749. $modal.show();
  1750. // 定位
  1751. self._position();
  1752. // 激活菜单状态
  1753. menu && menu.activeStyle(true);
  1754. };
  1755. // 隐藏
  1756. Modal.fn.hide = function () {
  1757. var self = this;
  1758. var menu = self.menu;
  1759. if (!self.isShowing) {
  1760. return;
  1761. }
  1762. // 记录状态
  1763. self.isShowing = false;
  1764. // 隐藏
  1765. var $modal = self.$modal;
  1766. $modal.hide();
  1767. // 菜单状态
  1768. menu && menu.activeStyle(false);
  1769. };
  1770. });
  1771. // txt 构造函数
  1772. _e(function (E, $) {
  1773. // 定义构造函数
  1774. var Txt = function (editor) {
  1775. this.editor = editor;
  1776. // 初始化
  1777. this.init();
  1778. };
  1779. Txt.fn = Txt.prototype;
  1780. // 暴露给 E 即 window.wangEditor
  1781. E.Txt = Txt;
  1782. });
  1783. // Txt.fn bind fn
  1784. _e(function (E, $) {
  1785. var Txt = E.Txt;
  1786. // 初始化
  1787. Txt.fn.init = function () {
  1788. var self = this;
  1789. var editor = self.editor;
  1790. var $valueContainer = editor.$valueContainer;
  1791. var currentValue = editor.getInitValue();
  1792. var $txt;
  1793. if ($valueContainer.get(0).nodeName === 'DIV') {
  1794. // 如果传入生成编辑器的元素就是div,则直接使用
  1795. $txt = $valueContainer;
  1796. $txt.addClass("wangEditor-txt");
  1797. $txt.attr('contentEditable', 'true');
  1798. } else {
  1799. // 如果不是div(是textarea),则创建一个div
  1800. $txt = $(
  1801. '<div class="wangEditor-txt" contentEditable="true">' +
  1802. currentValue +
  1803. '</div>'
  1804. );
  1805. }
  1806. // 试图最后插入一个空行,ready之后才行
  1807. editor.ready(function () {
  1808. self.insertEmptyP();
  1809. });
  1810. self.$txt = $txt;
  1811. // 删除时,如果没有内容了,就添加一个 <p><br></p>
  1812. self.contentEmptyHandle();
  1813. // enter时,不能使用 div 换行
  1814. self.bindEnterForDiv();
  1815. // enter时,用 p 包裹 text
  1816. self.bindEnterForText();
  1817. // tab 插入4个空格
  1818. self.bindTabEvent();
  1819. // 处理粘贴内容
  1820. self.bindPasteFilter();
  1821. // $txt.formatText() 方法
  1822. self.bindFormatText();
  1823. // 定义 $txt.html() 方法
  1824. self.bindHtml();
  1825. };
  1826. // 删除时,如果没有内容了,就添加一个 <p><br></p>
  1827. Txt.fn.contentEmptyHandle = function () {
  1828. var self = this;
  1829. var editor = self.editor;
  1830. var $txt = self.$txt;
  1831. var $p;
  1832. $txt.on('keydown', function (e) {
  1833. if (e.keyCode !== 8) {
  1834. return;
  1835. }
  1836. var txtHtml = $.trim($txt.html().toLowerCase());
  1837. if (txtHtml === '<p><br></p>') {
  1838. // 如果最后还剩余一个空行,就不再继续删除了
  1839. e.preventDefault();
  1840. return;
  1841. }
  1842. });
  1843. $txt.on('keyup', function (e) {
  1844. if (e.keyCode !== 8) {
  1845. return;
  1846. }
  1847. var txtHtml = $.trim($txt.html().toLowerCase());
  1848. // ff时用 txtHtml === '<br>' 判断,其他用 !txtHtml 判断
  1849. if (!txtHtml || txtHtml === '<br>') {
  1850. // 内容空了
  1851. $p = $('<p><br/></p>');
  1852. $txt.html(''); // 一定要先清空,否则在 ff 下有问题
  1853. $txt.append($p);
  1854. editor.restoreSelectionByElem($p.get(0));
  1855. }
  1856. });
  1857. };
  1858. // enter时,不能使用 div 换行
  1859. Txt.fn.bindEnterForDiv = function () {
  1860. var tags = E.config.legalTags; // 配置中编辑器要求的合法标签,如 p head table blockquote ul ol 等
  1861. var self = this;
  1862. var editor = self.editor;
  1863. var $txt = self.$txt;
  1864. var $keydownDivElem;
  1865. function divHandler() {
  1866. if (!$keydownDivElem) {
  1867. return;
  1868. }
  1869. var $pElem = $('<p>' + $keydownDivElem.html() + '</p>');
  1870. $keydownDivElem.after($pElem);
  1871. $keydownDivElem.remove();
  1872. }
  1873. $txt.on('keydown keyup', function (e) {
  1874. if (e.keyCode !== 13) {
  1875. return;
  1876. }
  1877. // 查找合法标签
  1878. var rangeElem = editor.getRangeElem();
  1879. var targetElem = editor.getLegalTags(rangeElem);
  1880. var $targetElem;
  1881. var $pElem;
  1882. if (!targetElem) {
  1883. // 没找到合法标签,就去查找 div
  1884. targetElem = editor.getSelfOrParentByName(rangeElem, 'div');
  1885. if (!targetElem) {
  1886. return;
  1887. }
  1888. $targetElem = $(targetElem);
  1889. if (e.type === 'keydown') {
  1890. // 异步执行(同步执行会出现问题)
  1891. $keydownDivElem = $targetElem;
  1892. setTimeout(divHandler, 0);
  1893. }
  1894. if (e.type === 'keyup') {
  1895. // 将 div 的内容移动到 p 里面,并移除 div
  1896. $pElem = $('<p>' + $targetElem.html() + '</p>');
  1897. $targetElem.after($pElem);
  1898. $targetElem.remove();
  1899. // 如果是回车结束,将选区定位到行首
  1900. editor.restoreSelectionByElem($pElem.get(0), 'start');
  1901. }
  1902. }
  1903. });
  1904. };
  1905. // enter时,用 p 包裹 text
  1906. Txt.fn.bindEnterForText = function () {
  1907. var self = this;
  1908. var $txt = self.$txt;
  1909. var handle;
  1910. $txt.on('keyup', function (e) {
  1911. if (e.keyCode !== 13) {
  1912. return;
  1913. }
  1914. if (!handle) {
  1915. handle = function() {
  1916. self.wrapImgAndText();
  1917. };
  1918. }
  1919. setTimeout(handle);
  1920. });
  1921. };
  1922. // tab 时,插入4个空格
  1923. Txt.fn.bindTabEvent = function () {
  1924. var self = this;
  1925. var editor = self.editor;
  1926. var $txt = self.$txt;
  1927. $txt.on('keydown', function (e) {
  1928. if (e.keyCode !== 9) {
  1929. // 只监听 tab 按钮
  1930. return;
  1931. }
  1932. // 如果浏览器支持 insertHtml 则插入4个空格。如果不支持,就不管了
  1933. if (editor.queryCommandSupported('insertHtml')) {
  1934. editor.command(e, 'insertHtml', '&nbsp;&nbsp;&nbsp;&nbsp;');
  1935. }
  1936. });
  1937. };
  1938. // 处理粘贴内容
  1939. Txt.fn.bindPasteFilter = function () {
  1940. var self = this;
  1941. var editor = self.editor;
  1942. var resultHtml = ''; //存储最终的结果
  1943. var $txt = self.$txt;
  1944. var legalTags = editor.config.legalTags;
  1945. var legalTagArr = legalTags.split(',');
  1946. $txt.on('paste', function (e) {
  1947. if (!editor.config.pasteFilter) {
  1948. // 配置中取消了粘贴过滤
  1949. return;
  1950. }
  1951. var currentNodeName = editor.getRangeElem().nodeName;
  1952. if (currentNodeName === 'TD' || currentNodeName === 'TH') {
  1953. // 在表格的单元格中粘贴,忽略所有内容。否则会出现异常情况
  1954. return;
  1955. }
  1956. resultHtml = ''; // 先清空 resultHtml
  1957. var pasteHtml, $paste;
  1958. var data = e.clipboardData || e.originalEvent.clipboardData;
  1959. var ieData = window.clipboardData;
  1960. if (editor.config.pasteText) {
  1961. // 只粘贴纯文本
  1962. if (data && data.getData) {
  1963. // w3c
  1964. pasteHtml = data.getData('text/plain');
  1965. } else if (ieData && ieData.getData) {
  1966. // IE
  1967. pasteHtml = ieData.getData('text');
  1968. } else {
  1969. // 其他情况
  1970. return;
  1971. }
  1972. // 拼接为 <p> 标签
  1973. if (pasteHtml) {
  1974. resultHtml = '<p>' + pasteHtml + '</p>';
  1975. }
  1976. } else {
  1977. // 粘贴过滤了样式的、只有标签的 html
  1978. if (data && data.getData) {
  1979. // w3c
  1980. // 获取粘贴过来的html
  1981. pasteHtml = data.getData('text/html');
  1982. if (pasteHtml) {
  1983. // 创建dom
  1984. $paste = $('<div>' + pasteHtml + '</div>');
  1985. // 处理,并将结果存储到 resultHtml 『全局』变量
  1986. handle($paste.get(0));
  1987. } else {
  1988. // 得不到html,试图获取text
  1989. pasteHtml = data.getData('text/plain');
  1990. if (pasteHtml) {
  1991. // 替换特殊字符
  1992. pasteHtml = pasteHtml.replace(/[ ]/g, '&nbsp;')
  1993. .replace(/</g, '&lt;')
  1994. .replace(/>/g, '&gt;')
  1995. .replace(/\n/g, '</p><p>');
  1996. // 拼接
  1997. resultHtml = '<p>' + pasteHtml + '</p>';
  1998. // 查询链接
  1999. resultHtml = resultHtml.replace(/<p>(https?:\/\/.*?)<\/p>/ig, function (match, link) {
  2000. return '<p><a href="' + link + '" target="_blank">' + link + '</p>';
  2001. });
  2002. }
  2003. }
  2004. } else if (ieData && ieData.getData) {
  2005. // IE 直接从剪切板中取出纯文本格式
  2006. resultHtml = ieData.getData('text');
  2007. if (!resultHtml) {
  2008. return;
  2009. }
  2010. // 拼接为 <p> 标签
  2011. resultHtml = '<p>' + resultHtml + '</p>';
  2012. resultHtml = resultHtml.replace(new RegExp('\n', 'g'), '</p><p>');
  2013. } else {
  2014. // 其他情况
  2015. return;
  2016. }
  2017. }
  2018. // 执行命令
  2019. if (resultHtml) {
  2020. editor.command(e, 'insertHtml', resultHtml);
  2021. // 删除内容为空的 p 和嵌套的 p
  2022. self.clearEmptyOrNestP();
  2023. }
  2024. });
  2025. // 处理粘贴的内容
  2026. function handle(elem) {
  2027. if (!elem || !elem.nodeType || !elem.nodeName) {
  2028. return;
  2029. }
  2030. var $elem;
  2031. var nodeName = elem.nodeName.toLowerCase();
  2032. var nodeType = elem.nodeType;
  2033. var childNodesClone;
  2034. // 只处理文本和普通node标签
  2035. if (nodeType !== 3 && nodeType !== 1) {
  2036. return;
  2037. }
  2038. $elem = $(elem);
  2039. // 如果是容器,则继续深度遍历
  2040. if (nodeName === 'div') {
  2041. childNodesClone = [];
  2042. $.each(elem.childNodes, function (index, item) {
  2043. // elem.childNodes 可获取TEXT节点,而 $elem.children() 就获取不到
  2044. // 先将 elem.childNodes 拷贝一份,一面在循环递归过程中 elem 发生变化
  2045. childNodesClone.push(item);
  2046. });
  2047. // 遍历子元素,执行操作
  2048. $.each(childNodesClone, function () {
  2049. handle(this);
  2050. });
  2051. return;
  2052. }
  2053. if (legalTagArr.indexOf(nodeName) >= 0) {
  2054. // 如果是合法标签之内的,则根据元素类型,获取值
  2055. resultHtml += getResult(elem);
  2056. } else if (nodeType === 3) {
  2057. // 如果是文本,则直接插入 p 标签
  2058. resultHtml += '<p>' + elem.textContent + '</p>';
  2059. } else if (nodeName === 'br') {
  2060. // <br>保留
  2061. resultHtml += '<br/>';
  2062. }
  2063. else {
  2064. // 忽略的标签
  2065. if (['meta', 'style', 'script', 'object', 'form', 'iframe', 'hr'].indexOf(nodeName) >= 0) {
  2066. return;
  2067. }
  2068. // 其他标签,移除属性,插入 p 标签
  2069. $elem = $(removeAttrs(elem));
  2070. // 注意,这里的 clone() 是必须的,否则会出错
  2071. resultHtml += $('<div>').append($elem.clone()).html();
  2072. }
  2073. }
  2074. // 获取元素的结果
  2075. function getResult(elem) {
  2076. var nodeName = elem.nodeName.toLowerCase();
  2077. var $elem;
  2078. var htmlForP = '';
  2079. var htmlForLi = '';
  2080. if (['blockquote'].indexOf(nodeName) >= 0) {
  2081. // 直接取出元素text即可
  2082. $elem = $(elem);
  2083. return '<' + nodeName + '>' + $elem.text() + '</' + nodeName + '>';
  2084. } else if (['p', 'h1', 'h2', 'h3', 'h4', 'h5'].indexOf(nodeName) >= 0) {
  2085. //p head 取出 text 和链接
  2086. elem = removeAttrs(elem);
  2087. $elem = $(elem);
  2088. htmlForP = $elem.html();
  2089. // 剔除 a img 之外的元素
  2090. htmlForP = htmlForP.replace(/<.*?>/ig, function (tag) {
  2091. if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
  2092. return tag;
  2093. } else {
  2094. return '';
  2095. }
  2096. });
  2097. return '<' + nodeName + '>' + htmlForP + '</' + nodeName + '>';
  2098. } else if (['ul', 'ol'].indexOf(nodeName) >= 0) {
  2099. // ul ol元素,获取子元素(li元素)的text link img,再拼接
  2100. $elem = $(elem);
  2101. $elem.children().each(function () {
  2102. var $li = $(removeAttrs(this));
  2103. var html = $li.html();
  2104. html = html.replace(/<.*?>/ig, function (tag) {
  2105. if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
  2106. return tag;
  2107. } else {
  2108. return '';
  2109. }
  2110. });
  2111. htmlForLi += '<li>' + html + '</li>';
  2112. });
  2113. return '<' + nodeName + '>' + htmlForLi + '</' + nodeName + '>';
  2114. } else {
  2115. // 其他元素,移除元素属性
  2116. $elem = $(removeAttrs(elem));
  2117. return $('<div>').append($elem).html();
  2118. }
  2119. }
  2120. // 移除一个元素(子元素)的attr
  2121. function removeAttrs(elem) {
  2122. var attrs = elem.attributes || [];
  2123. var attrNames = [];
  2124. var exception = ['href', 'target', 'src', 'alt', 'rowspan', 'colspan']; //例外情况
  2125. // 先存储下elem中所有 attr 的名称
  2126. $.each(attrs, function (key, attr) {
  2127. if (attr && attr.nodeType === 2) {
  2128. attrNames.push(attr.nodeName);
  2129. }
  2130. });
  2131. // 再根据名称删除所有attr
  2132. $.each(attrNames, function (key, attr) {
  2133. if (exception.indexOf(attr) < 0) {
  2134. // 除了 exception 规定的例外情况,删除其他属性
  2135. elem.removeAttribute(attr);
  2136. }
  2137. });
  2138. // 递归子节点
  2139. var children = elem.childNodes;
  2140. if (children.length) {
  2141. $.each(children, function (key, value) {
  2142. removeAttrs(value);
  2143. });
  2144. }
  2145. return elem;
  2146. }
  2147. };
  2148. // 绑定 $txt.formatText() 方法
  2149. Txt.fn.bindFormatText = function () {
  2150. var self = this;
  2151. var editor = self.editor;
  2152. var $txt = self.$txt;
  2153. var legalTags = E.config.legalTags;
  2154. var legalTagArr = legalTags.split(',');
  2155. var length = legalTagArr.length;
  2156. var regArr = [];
  2157. // 将 E.config.legalTags 配置的有效字符,生成正则表达式
  2158. $.each(legalTagArr, function (k, tag) {
  2159. var reg = '\>\\s*\<(' + tag + ')\>';
  2160. regArr.push(new RegExp(reg, 'ig'));
  2161. });
  2162. // 增加 li
  2163. regArr.push(new RegExp('\>\\s*\<(li)\>', 'ig'));
  2164. // 增加 tr
  2165. regArr.push(new RegExp('\>\\s*\<(tr)\>', 'ig'));
  2166. // 增加 code
  2167. regArr.push(new RegExp('\>\\s*\<(code)\>', 'ig'));
  2168. // 生成 formatText 方法
  2169. $txt.formatText = function () {
  2170. var $temp = $('<div>');
  2171. var html = $txt.html();
  2172. // 去除空格
  2173. html = html.replace(/\s*</ig, '<');
  2174. // 段落、表格之间换行
  2175. $.each(regArr, function (k, reg) {
  2176. if (!reg.test(html)) {
  2177. return;
  2178. }
  2179. html = html.replace(reg, function (matchStr, tag) {
  2180. return '>\n<' + tag + '>';
  2181. });
  2182. });
  2183. $temp.html(html);
  2184. return $temp.text();
  2185. };
  2186. };
  2187. // 定制 $txt.html 方法
  2188. Txt.fn.bindHtml = function () {
  2189. var self = this;
  2190. var editor = self.editor;
  2191. var $txt = self.$txt;
  2192. var $valueContainer = editor.$valueContainer;
  2193. var valueNodeName = editor.valueNodeName;
  2194. $txt.html = function (html) {
  2195. var result;
  2196. if (valueNodeName === 'div') {
  2197. // div 生成的编辑器,取值、赋值,都直接触发jquery的html方法
  2198. result = $.fn.html.call($txt, html);
  2199. }
  2200. // textarea 生成的编辑器,则需要考虑赋值时,也给textarea赋值
  2201. if (html === undefined) {
  2202. // 取值,直接触发jquery原生html方法
  2203. result = $.fn.html.call($txt);
  2204. // 替换 html 中,src和href属性中的 & 字符。
  2205. // 因为 .html() 或者 .innerHTML 会把所有的 & 字符都改成 &amp; 但是 src 和 href 中的要保持 &
  2206. result = result.replace(/(href|src)\=\"(.*)\"/igm, function (a, b, c) {
  2207. return b + '="' + c.replace('&amp;', '&') + '"';
  2208. });
  2209. } else {
  2210. // 赋值,需要同时给 textarea 赋值
  2211. result = $.fn.html.call($txt, html);
  2212. $valueContainer.val(html);
  2213. }
  2214. if (html === undefined) {
  2215. return result;
  2216. } else {
  2217. // 手动触发 change 事件,因为 $txt 监控了 change 事件来判断是否需要执行 editor.onchange
  2218. $txt.change();
  2219. }
  2220. };
  2221. };
  2222. });
  2223. // Txt.fn api
  2224. _e(function (E, $) {
  2225. var Txt = E.Txt;
  2226. var txtChangeEventNames = 'propertychange change click keyup input paste';
  2227. // 渲染
  2228. Txt.fn.render = function () {
  2229. var $txt = this.$txt;
  2230. var $editorContainer = this.editor.$editorContainer;
  2231. $editorContainer.append($txt);
  2232. };
  2233. // 计算高度
  2234. Txt.fn.initHeight = function () {
  2235. var editor = this.editor;
  2236. var $txt = this.$txt;
  2237. var valueContainerHeight = editor.$valueContainer.height();
  2238. var menuHeight = editor.menuContainer.height();
  2239. var txtHeight = valueContainerHeight - menuHeight;
  2240. // 限制最小为 50px
  2241. txtHeight = txtHeight < 50 ? 50 : txtHeight;
  2242. $txt.height(txtHeight);
  2243. // 记录原始高度
  2244. editor.valueContainerHeight = valueContainerHeight;
  2245. // 设置 max-height
  2246. this.initMaxHeight(txtHeight, menuHeight);
  2247. };
  2248. // 计算最大高度
  2249. Txt.fn.initMaxHeight = function (txtHeight, menuHeight) {
  2250. var editor = this.editor;
  2251. var $menuContainer = editor.menuContainer.$menuContainer;
  2252. var $txt = this.$txt;
  2253. var $wrap = $('<div>');
  2254. // 需要浏览器支持 max-height,否则不管
  2255. if (window.getComputedStyle && 'max-height'in window.getComputedStyle($txt.get(0))) {
  2256. // 获取 max-height 并判断是否有值
  2257. var maxHeight = parseInt(editor.$valueContainer.css('max-height'));
  2258. if (isNaN(maxHeight)) {
  2259. return;
  2260. }
  2261. // max-height 和『全屏』暂时有冲突
  2262. if (editor.menus.fullscreen) {
  2263. E.warn('max-height和『全屏』菜单一起使用时,会有一些问题尚未解决,请暂时不要两个同时使用');
  2264. return;
  2265. }
  2266. // 标记
  2267. editor.useMaxHeight = true;
  2268. // 设置maxheight
  2269. $wrap.css({
  2270. 'max-height': (maxHeight - menuHeight) + 'px',
  2271. 'overflow-y': 'auto'
  2272. });
  2273. $txt.css({
  2274. 'height': 'auto',
  2275. 'overflow-y': 'visible',
  2276. 'min-height': txtHeight + 'px'
  2277. });
  2278. // 滚动式,菜单阴影
  2279. $wrap.on('scroll', function () {
  2280. if ($txt.parent().scrollTop() > 10) {
  2281. $menuContainer.addClass('wangEditor-menu-shadow');
  2282. } else {
  2283. $menuContainer.removeClass('wangEditor-menu-shadow');
  2284. }
  2285. });
  2286. // 需在编辑器区域外面再包裹一层
  2287. $txt.wrap($wrap);
  2288. }
  2289. };
  2290. // 保存选区
  2291. Txt.fn.saveSelectionEvent = function () {
  2292. var $txt = this.$txt;
  2293. var editor = this.editor;
  2294. var timeoutId;
  2295. var dt = Date.now();
  2296. function save() {
  2297. editor.saveSelection();
  2298. }
  2299. // 同步保存选区
  2300. function saveSync() {
  2301. // 100ms之内,不重复保存
  2302. if (Date.now() - dt < 100) {
  2303. return;
  2304. }
  2305. dt = Date.now();
  2306. save();
  2307. }
  2308. // 异步保存选区
  2309. function saveAync() {
  2310. // 节流,防止高频率重复操作
  2311. if (timeoutId) {
  2312. clearTimeout(timeoutId);
  2313. }
  2314. timeoutId = setTimeout(save, 300);
  2315. }
  2316. // txt change 、focus、blur 时随时保存选区
  2317. $txt.on(txtChangeEventNames + ' focus blur', function (e) {
  2318. // 先同步保存选区,为了让接下来就马上要执行 editor.getRangeElem() 的程序
  2319. // 能够获取到正确的 rangeElem
  2320. saveSync();
  2321. // 再异步保存选区,为了确定更加准确的选区,为后续的操作做准备
  2322. saveAync();
  2323. });
  2324. // 鼠标拖拽选择时,可能会拖拽到编辑器区域外面再松手,此时 $txt 就监听不到 click事件了
  2325. $txt.on('mousedown', function () {
  2326. $txt.on('mouseleave.saveSelection', function (e) {
  2327. // 先同步后异步,如上述注释
  2328. saveSync();
  2329. saveAync();
  2330. // 顺道吧菜单状态也更新了
  2331. editor.updateMenuStyle();
  2332. });
  2333. }).on('mouseup', function () {
  2334. $txt.off('mouseleave.saveSelection');
  2335. });
  2336. };
  2337. // 随时更新 value
  2338. Txt.fn.updateValueEvent = function () {
  2339. var $txt = this.$txt;
  2340. var editor = this.editor;
  2341. var timeoutId, oldValue;
  2342. // 触发 onchange 事件
  2343. function doOnchange() {
  2344. var val = $txt.html();
  2345. if (oldValue === val) {
  2346. // 无变化
  2347. return;
  2348. }
  2349. // 触发 onchange 事件
  2350. if (editor.onchange && typeof editor.onchange === 'function') {
  2351. editor.onchange.call(editor);
  2352. }
  2353. // 更新内容
  2354. editor.updateValue();
  2355. // 记录最新内容
  2356. oldValue = val;
  2357. }
  2358. // txt change 时随时更新内容
  2359. $txt.on(txtChangeEventNames, function (e) {
  2360. // 初始化
  2361. if (oldValue == null) {
  2362. oldValue = $txt.html();
  2363. }
  2364. // 监控内容变化(停止操作 100ms 之后立即执行)
  2365. if (timeoutId) {
  2366. clearTimeout(timeoutId);
  2367. }
  2368. timeoutId = setTimeout(doOnchange, 100);
  2369. });
  2370. };
  2371. // 随时更新 menustyle
  2372. Txt.fn.updateMenuStyleEvent = function () {
  2373. var $txt = this.$txt;
  2374. var editor = this.editor;
  2375. // txt change 时随时更新内容
  2376. $txt.on(txtChangeEventNames, function (e) {
  2377. editor.updateMenuStyle();
  2378. });
  2379. };
  2380. // 最后插入试图插入 <p><br><p>
  2381. Txt.fn.insertEmptyP = function () {
  2382. var $txt = this.$txt;
  2383. var $children = $txt.children();
  2384. if ($children.length === 0) {
  2385. $txt.append($('<p><br></p>'));
  2386. return;
  2387. }
  2388. if ($.trim($children.last().html()).toLowerCase() !== '<br>') {
  2389. $txt.append($('<p><br></p>'));
  2390. }
  2391. };
  2392. // 将编辑器暴露出来的文字和图片,都用 p 来包裹
  2393. Txt.fn.wrapImgAndText = function () {
  2394. var $txt = this.$txt;
  2395. var $imgs = $txt.children('img');
  2396. var txt = $txt[0];
  2397. var childNodes = txt.childNodes;
  2398. var childrenLength = childNodes.length;
  2399. var i, childNode, p;
  2400. // 处理图片
  2401. $imgs.length && $imgs.each(function () {
  2402. $(this).wrap('<p>');
  2403. });
  2404. // 处理文字
  2405. for (i = 0; i < childrenLength; i++) {
  2406. childNode = childNodes[i];
  2407. if (childNode.nodeType === 3 && childNode.textContent && $.trim(childNode.textContent)) {
  2408. $(childNode).wrap('<p>');
  2409. }
  2410. }
  2411. };
  2412. // 清空内容为空的<p>,以及重复包裹的<p>(在windows下的chrome粘贴文字之后,会出现上述情况)
  2413. Txt.fn.clearEmptyOrNestP = function () {
  2414. var $txt = this.$txt;
  2415. var $pList = $txt.find('p');
  2416. $pList.each(function () {
  2417. var $p = $(this);
  2418. var $children = $p.children();
  2419. var childrenLength = $children.length;
  2420. var $firstChild;
  2421. var content = $.trim($p.html());
  2422. // 内容为空的p
  2423. if (!content) {
  2424. $p.remove();
  2425. return;
  2426. }
  2427. // 嵌套的p
  2428. if (childrenLength === 1) {
  2429. $firstChild = $children.first();
  2430. if ($firstChild.get(0) && $firstChild.get(0).nodeName === 'P') {
  2431. $p.html( $firstChild.html() );
  2432. }
  2433. }
  2434. });
  2435. };
  2436. // 获取 scrollTop
  2437. Txt.fn.scrollTop = function (val) {
  2438. var self = this;
  2439. var editor = self.editor;
  2440. var $txt = self.$txt;
  2441. if (editor.useMaxHeight) {
  2442. return $txt.parent().scrollTop(val);
  2443. } else {
  2444. return $txt.scrollTop(val);
  2445. }
  2446. };
  2447. // 鼠标hover时候,显示p、head的高度
  2448. Txt.fn.showHeightOnHover = function () {
  2449. var editor = this.editor;
  2450. var $editorContainer = editor.$editorContainer;
  2451. var menuContainer = editor.menuContainer;
  2452. var $txt = this.$txt;
  2453. var $tip = $('<i class="height-tip"><i>');
  2454. var isTipInTxt = false;
  2455. function addAndShowTip($target) {
  2456. if (!isTipInTxt) {
  2457. $editorContainer.append($tip);
  2458. isTipInTxt = true;
  2459. }
  2460. var txtTop = $txt.position().top;
  2461. var txtHeight = $txt.outerHeight();
  2462. var height = $target.height();
  2463. var top = $target.position().top;
  2464. var marginTop = parseInt($target.css('margin-top'), 10);
  2465. var paddingTop = parseInt($target.css('padding-top'), 10);
  2466. var marginBottom = parseInt($target.css('margin-bottom'), 10);
  2467. var paddingBottom = parseInt($target.css('padding-bottom'), 10);
  2468. // 计算初步的结果
  2469. var resultHeight = height + paddingTop + marginTop + paddingBottom + marginBottom;
  2470. var resultTop = top + menuContainer.height();
  2471. // var spaceValue;
  2472. // // 判断是否超出下边界
  2473. // spaceValue = (resultTop + resultHeight) - (txtTop + txtHeight);
  2474. // if (spaceValue > 0) {
  2475. // resultHeight = resultHeight - spaceValue;
  2476. // }
  2477. // // 判断是否超出了下边界
  2478. // spaceValue = txtTop > resultTop;
  2479. // if (spaceValue) {
  2480. // resultHeight = resultHeight - spaceValue;
  2481. // top = top + spaceValue;
  2482. // }
  2483. // 按照最终结果渲染
  2484. $tip.css({
  2485. height: height + paddingTop + marginTop + paddingBottom + marginBottom,
  2486. top: top + menuContainer.height()
  2487. });
  2488. }
  2489. function removeTip() {
  2490. if (!isTipInTxt) {
  2491. return;
  2492. }
  2493. $tip.remove();
  2494. isTipInTxt = false;
  2495. }
  2496. $txt.on('mouseenter', 'ul,ol,blockquote,p,h1,h2,h3,h4,h5,table,pre', function (e) {
  2497. addAndShowTip($(e.currentTarget));
  2498. }).on('mouseleave', function () {
  2499. removeTip();
  2500. });
  2501. };
  2502. });
  2503. // 工具函数
  2504. _e(function (E, $) {
  2505. // IE8 [].indexOf()
  2506. if(!Array.prototype.indexOf){
  2507. //IE低版本不支持 arr.indexOf
  2508. Array.prototype.indexOf = function(elem){
  2509. var i = 0,
  2510. length = this.length;
  2511. for(; i<length; i++){
  2512. if(this[i] === elem){
  2513. return i;
  2514. }
  2515. }
  2516. return -1;
  2517. };
  2518. //IE低版本不支持 arr.lastIndexOf
  2519. Array.prototype.lastIndexOf = function(elem){
  2520. var length = this.length;
  2521. for(length = length - 1; length >= 0; length--){
  2522. if(this[length] === elem){
  2523. return length;
  2524. }
  2525. }
  2526. return -1;
  2527. };
  2528. }
  2529. // IE8 Date.now()
  2530. if (!Date.now) {
  2531. Date.now = function () {
  2532. return new Date().valueOf();
  2533. };
  2534. }
  2535. // console.log && console.warn && console.error
  2536. var console = window.console;
  2537. var emptyFn = function () {};
  2538. $.each(['info', 'log', 'warn', 'error'], function (key, value) {
  2539. if (console == null) {
  2540. E[value] = emptyFn;
  2541. } else {
  2542. E[value] = function (info) {
  2543. // 通过配置来控制打印输出
  2544. if (E.config && E.config.printLog) {
  2545. console[value]('wangEditor提示: ' + info);
  2546. }
  2547. };
  2548. }
  2549. });
  2550. // 获取随机数
  2551. E.random = function () {
  2552. return Math.random().toString().slice(2);
  2553. };
  2554. // 浏览器是否支持 placeholder
  2555. E.placeholder = 'placeholder' in document.createElement('input');
  2556. // 兼容IE8的 input placeholder
  2557. E.placeholderForIE8 = function ($container) {
  2558. if (E.placeholder) {
  2559. return;
  2560. }
  2561. $container.find('input[placeholder]').each(function () {
  2562. var $input = $(this);
  2563. var placeholder = $input.attr('placeholder');
  2564. if ($input.val() === '') {
  2565. $input.css('color', '#666');
  2566. $input.val(placeholder);
  2567. $input.on('focus.placeholder click.placeholder', function () {
  2568. $input.val('');
  2569. $input.css('color', '#333');
  2570. $input.off('focus.placeholder click.placeholder');
  2571. });
  2572. }
  2573. });
  2574. };
  2575. });
  2576. // 语言包
  2577. _e(function (E, $) {
  2578. E.langs = {};
  2579. // 中文
  2580. E.langs['zh-cn'] = {
  2581. bold: '粗体',
  2582. underline: '下划线',
  2583. italic: '斜体',
  2584. forecolor: '文字颜色',
  2585. bgcolor: '背景色',
  2586. strikethrough: '删除线',
  2587. eraser: '清空格式',
  2588. source: '源码',
  2589. quote: '引用',
  2590. fontfamily: '字体',
  2591. fontsize: '字号',
  2592. head: '标题',
  2593. orderlist: '有序列表',
  2594. unorderlist: '无序列表',
  2595. alignleft: '左对齐',
  2596. aligncenter: '居中',
  2597. alignright: '右对齐',
  2598. link: '链接',
  2599. text: '文本',
  2600. submit: '提交',
  2601. cancel: '取消',
  2602. unlink: '取消链接',
  2603. table: '表格',
  2604. emotion: '表情',
  2605. img: '图片',
  2606. video: '视频',
  2607. 'width': '宽',
  2608. 'height': '高',
  2609. location: '位置',
  2610. loading: '加载中',
  2611. searchlocation: '搜索位置',
  2612. dynamicMap: '动态地图',
  2613. clearLocation: '清除位置',
  2614. langDynamicOneLocation: '动态地图只能显示一个位置',
  2615. insertcode: '插入代码',
  2616. undo: '撤销',
  2617. redo: '重复',
  2618. fullscreen: '全屏',
  2619. openLink: '打开链接'
  2620. };
  2621. // 英文
  2622. E.langs.en = {
  2623. bold: 'Bold',
  2624. underline: 'Underline',
  2625. italic: 'Italic',
  2626. forecolor: 'Color',
  2627. bgcolor: 'Backcolor',
  2628. strikethrough: 'Strikethrough',
  2629. eraser: 'Eraser',
  2630. source: 'Codeview',
  2631. quote: 'Quote',
  2632. fontfamily: 'Font family',
  2633. fontsize: 'Font size',
  2634. head: 'Head',
  2635. orderlist: 'Ordered list',
  2636. unorderlist: 'Unordered list',
  2637. alignleft: 'Align left',
  2638. aligncenter: 'Align center',
  2639. alignright: 'Align right',
  2640. link: 'Insert link',
  2641. text: 'Text',
  2642. submit: 'Submit',
  2643. cancel: 'Cancel',
  2644. unlink: 'Unlink',
  2645. table: 'Table',
  2646. emotion: 'Emotions',
  2647. img: 'Image',
  2648. video: 'Video',
  2649. 'width': 'width',
  2650. 'height': 'height',
  2651. location: 'Location',
  2652. loading: 'Loading',
  2653. searchlocation: 'search',
  2654. dynamicMap: 'Dynamic',
  2655. clearLocation: 'Clear',
  2656. langDynamicOneLocation: 'Only one location in dynamic map',
  2657. insertcode: 'Insert Code',
  2658. undo: 'Undo',
  2659. redo: 'Redo',
  2660. fullscreen: 'Full screnn',
  2661. openLink: 'open link'
  2662. };
  2663. });
  2664. // 全局配置
  2665. _e(function (E, $) {
  2666. E.config = {};
  2667. // 全屏时的 z-index
  2668. E.config.zindex = 10000;
  2669. // 是否打印log
  2670. E.config.printLog = true;
  2671. // 菜单吸顶:false - 不吸顶;number - 吸顶,值为top值
  2672. E.config.menuFixed = 0;
  2673. // 编辑源码时,过滤 javascript
  2674. E.config.jsFilter = true;
  2675. // 编辑器允许的标签
  2676. E.config.legalTags = 'p,h1,h2,h3,h4,h5,h6,blockquote,table,ul,ol,pre';
  2677. // 语言包
  2678. E.config.lang = E.langs['zh-cn'];
  2679. // 菜单配置
  2680. E.config.menus = [
  2681. 'source',
  2682. '|',
  2683. 'bold',
  2684. 'underline',
  2685. 'italic',
  2686. 'strikethrough',
  2687. 'eraser',
  2688. 'forecolor',
  2689. 'bgcolor',
  2690. '|',
  2691. 'quote',
  2692. 'fontfamily',
  2693. 'fontsize',
  2694. 'head',
  2695. 'unorderlist',
  2696. 'orderlist',
  2697. 'alignleft',
  2698. 'aligncenter',
  2699. 'alignright',
  2700. '|',
  2701. 'link',
  2702. 'unlink',
  2703. 'table',
  2704. 'emotion',
  2705. '|',
  2706. 'img',
  2707. 'video',
  2708. // 'location',
  2709. 'insertcode',
  2710. '|',
  2711. 'undo',
  2712. 'redo',
  2713. 'fullscreen'
  2714. ];
  2715. // 颜色配置
  2716. E.config.colors = {
  2717. // 'value': 'title'
  2718. '#880000': '暗红色',
  2719. '#800080': '紫色',
  2720. '#ff0000': '红色',
  2721. '#ff00ff': '鲜粉色',
  2722. '#000080': '深蓝色',
  2723. '#0000ff': '蓝色',
  2724. '#00ffff': '湖蓝色',
  2725. '#008080': '蓝绿色',
  2726. '#008000': '绿色',
  2727. '#808000': '橄榄色',
  2728. '#00ff00': '浅绿色',
  2729. '#ffcc00': '橙黄色',
  2730. '#808080': '灰色',
  2731. '#c0c0c0': '银色',
  2732. '#000000': '黑色',
  2733. '#ffffff': '白色'
  2734. };
  2735. // 字体
  2736. E.config.familys = [
  2737. '宋体', '黑体', '楷体', '微软雅黑',
  2738. 'Arial', 'Verdana', 'Georgia',
  2739. 'Times New Roman', 'Microsoft JhengHei',
  2740. 'Trebuchet MS', 'Courier New', 'Impact', 'Comic Sans MS', 'Consolas'
  2741. ];
  2742. // 字号
  2743. E.config.fontsizes = {
  2744. // 格式:'value': 'title'
  2745. 1: '12px',
  2746. 2: '13px',
  2747. 3: '16px',
  2748. 4: '18px',
  2749. 5: '24px',
  2750. 6: '32px',
  2751. 7: '48px'
  2752. };
  2753. // 表情包
  2754. E.config.emotionsShow = 'icon'; // 显示项,默认为'icon',也可以配置成'value'
  2755. E.config.emotions = {
  2756. // 'default': {
  2757. // title: '默认',
  2758. // data: './emotions.data'
  2759. // },
  2760. 'weibo': {
  2761. title: '微博表情',
  2762. data: [
  2763. {
  2764. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif',
  2765. value: '[草泥马]'
  2766. },
  2767. {
  2768. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif',
  2769. value: '[神马]'
  2770. },
  2771. {
  2772. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif',
  2773. value: '[浮云]'
  2774. },
  2775. {
  2776. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c9/geili_thumb.gif',
  2777. value: '[给力]'
  2778. },
  2779. {
  2780. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f2/wg_thumb.gif',
  2781. value: '[围观]'
  2782. },
  2783. {
  2784. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/70/vw_thumb.gif',
  2785. value: '[威武]'
  2786. },
  2787. {
  2788. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6e/panda_thumb.gif',
  2789. value: '[熊猫]'
  2790. },
  2791. {
  2792. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/81/rabbit_thumb.gif',
  2793. value: '[兔子]'
  2794. },
  2795. {
  2796. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/otm_thumb.gif',
  2797. value: '[奥特曼]'
  2798. },
  2799. {
  2800. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/15/j_thumb.gif',
  2801. value: '[囧]'
  2802. },
  2803. {
  2804. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/89/hufen_thumb.gif',
  2805. value: '[互粉]'
  2806. },
  2807. {
  2808. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c4/liwu_thumb.gif',
  2809. value: '[礼物]'
  2810. },
  2811. {
  2812. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/ac/smilea_thumb.gif',
  2813. value: '[呵呵]'
  2814. },
  2815. {
  2816. icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0b/tootha_thumb.gif',
  2817. value: '[哈哈]'
  2818. }
  2819. ]
  2820. }
  2821. };
  2822. // 百度地图的key
  2823. E.config.mapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
  2824. // 上传图片的配置
  2825. // server地址
  2826. E.config.uploadImgUrl = '';
  2827. // 超时时间
  2828. E.config.uploadTimeout = 20 * 1000;
  2829. // 用于存储上传回调事件
  2830. E.config.uploadImgFns = {};
  2831. // 自定义上传图片的filename
  2832. // E.config.uploadImgFileName = 'customFileName';
  2833. // 自定义上传,设置为 true 之后,显示上传图标
  2834. E.config.customUpload = false;
  2835. // 自定义上传的init事件
  2836. // E.config.customUploadInit = function () {....};
  2837. // 自定义上传时传递的参数(如 token)
  2838. E.config.uploadParams = {
  2839. /* token: 'abcdef12345' */
  2840. };
  2841. // 自定义上传是的header参数
  2842. E.config.uploadHeaders = {
  2843. /* 'Accept' : 'text/x-json' */
  2844. };
  2845. // 隐藏网络图片,默认为 false
  2846. E.config.hideLinkImg = false;
  2847. // 是否过滤粘贴内容
  2848. E.config.pasteFilter = true;
  2849. // 是否粘贴纯文本,当 editor.config.pasteFilter === false 时候,此配置将失效
  2850. E.config.pasteText = false;
  2851. // 插入代码时,默认的语言
  2852. E.config.codeDefaultLang = 'javascript';
  2853. });
  2854. // 全局UI
  2855. _e(function (E, $) {
  2856. E.UI = {};
  2857. // 为菜单自定义配置的UI
  2858. E.UI.menus = {
  2859. // 这个 default 不加引号,在 IE8 会报错
  2860. 'default': {
  2861. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-command"></i></a>',
  2862. selected: '.selected'
  2863. },
  2864. bold: {
  2865. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-bold"></i></a>',
  2866. selected: '.selected'
  2867. },
  2868. underline: {
  2869. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-underline"></i></a>',
  2870. selected: '.selected'
  2871. },
  2872. italic: {
  2873. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-italic"></i></a>',
  2874. selected: '.selected'
  2875. },
  2876. forecolor: {
  2877. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-pencil"></i></a>',
  2878. selected: '.selected'
  2879. },
  2880. bgcolor: {
  2881. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-brush"></i></a>',
  2882. selected: '.selected'
  2883. },
  2884. strikethrough: {
  2885. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-strikethrough"></i></a>',
  2886. selected: '.selected'
  2887. },
  2888. eraser: {
  2889. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-eraser"></i></a>',
  2890. selected: '.selected'
  2891. },
  2892. quote: {
  2893. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-quotes-left"></i></a>',
  2894. selected: '.selected'
  2895. },
  2896. source: {
  2897. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-code"></i></a>',
  2898. selected: '.selected'
  2899. },
  2900. fontfamily: {
  2901. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-font2"></i></a>',
  2902. selected: '.selected'
  2903. },
  2904. fontsize: {
  2905. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-text-height"></i></a>',
  2906. selected: '.selected'
  2907. },
  2908. head: {
  2909. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-header"></i></a>',
  2910. selected: '.selected'
  2911. },
  2912. orderlist: {
  2913. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-numbered"></i></a>',
  2914. selected: '.selected'
  2915. },
  2916. unorderlist: {
  2917. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-bullet"></i></a>',
  2918. selected: '.selected'
  2919. },
  2920. alignleft: {
  2921. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-left"></i></a>',
  2922. selected: '.selected'
  2923. },
  2924. aligncenter: {
  2925. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-center"></i></a>',
  2926. selected: '.selected'
  2927. },
  2928. alignright: {
  2929. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-right"></i></a>',
  2930. selected: '.selected'
  2931. },
  2932. link: {
  2933. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-link"></i></a>',
  2934. selected: '.selected'
  2935. },
  2936. unlink: {
  2937. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-unlink"></i></a>',
  2938. selected: '.selected'
  2939. },
  2940. table: {
  2941. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-table"></i></a>',
  2942. selected: '.selected'
  2943. },
  2944. emotion: {
  2945. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-happy"></i></a>',
  2946. selected: '.selected'
  2947. },
  2948. img: {
  2949. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-picture"></i></a>',
  2950. selected: '.selected'
  2951. },
  2952. video: {
  2953. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-play"></i></a>',
  2954. selected: '.selected'
  2955. },
  2956. location: {
  2957. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-location"></i></a>',
  2958. selected: '.selected'
  2959. },
  2960. insertcode: {
  2961. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-terminal"></i></a>',
  2962. selected: '.selected'
  2963. },
  2964. undo: {
  2965. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-ccw"></i></a>',
  2966. selected: '.selected'
  2967. },
  2968. redo: {
  2969. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-cw"></i></a>',
  2970. selected: '.selected'
  2971. },
  2972. fullscreen: {
  2973. normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-enlarge2"></i></a>',
  2974. selected: '<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-shrink2"></i></a>'
  2975. }
  2976. };
  2977. });
  2978. // 对象配置
  2979. _e(function (E, $) {
  2980. E.fn.initDefaultConfig = function () {
  2981. var editor = this;
  2982. editor.config = $.extend({}, E.config);
  2983. editor.UI = $.extend({}, E.UI);
  2984. };
  2985. });
  2986. // 增加 container
  2987. _e(function (E, $) {
  2988. E.fn.addEditorContainer = function () {
  2989. this.$editorContainer = $('<div class="wangEditor-container"></div>');
  2990. };
  2991. });
  2992. // 增加编辑区域对象
  2993. _e(function (E, $) {
  2994. E.fn.addTxt = function () {
  2995. var editor = this;
  2996. var txt = new E.Txt(editor);
  2997. editor.txt = txt;
  2998. };
  2999. });
  3000. // 增加menuContainer对象
  3001. _e(function (E, $) {
  3002. E.fn.addMenuContainer = function () {
  3003. var editor = this;
  3004. editor.menuContainer = new E.MenuContainer(editor);
  3005. };
  3006. });
  3007. // 增加menus
  3008. _e(function (E, $) {
  3009. // 存储创建菜单的函数
  3010. E.createMenuFns = [];
  3011. E.createMenu = function (fn) {
  3012. E.createMenuFns.push(fn);
  3013. };
  3014. // 创建所有菜单
  3015. E.fn.addMenus = function () {
  3016. var editor = this;
  3017. var menuIds = editor.config.menus;
  3018. // 检验 menuId 是否在配置中存在
  3019. function check(menuId) {
  3020. if (menuIds.indexOf(menuId) >= 0) {
  3021. return true;
  3022. }
  3023. return false;
  3024. }
  3025. // 遍历所有的菜单创建函数,并执行
  3026. $.each(E.createMenuFns, function (k, createMenuFn) {
  3027. createMenuFn.call(editor, check);
  3028. });
  3029. };
  3030. });
  3031. // bold菜单
  3032. _e(function (E, $) {
  3033. E.createMenu(function (check) {
  3034. var menuId = 'bold';
  3035. if (!check(menuId)) {
  3036. return;
  3037. }
  3038. var editor = this;
  3039. var lang = editor.config.lang;
  3040. // 创建 menu 对象
  3041. var menu = new E.Menu({
  3042. editor: editor,
  3043. id: menuId,
  3044. title: lang.bold,
  3045. commandName: 'Bold'
  3046. });
  3047. // 定义选中状态下的click事件
  3048. menu.clickEventSelected = function (e) {
  3049. var isRangeEmpty = editor.isRangeEmpty();
  3050. if (!isRangeEmpty) {
  3051. // 如果选区有内容,则执行基础命令
  3052. editor.command(e, 'Bold');
  3053. } else {
  3054. // 如果选区没有内容
  3055. editor.commandForElem('b,strong,h1,h2,h3,h4,h5', e, 'Bold');
  3056. }
  3057. };
  3058. // 增加到editor对象中
  3059. editor.menus[menuId] = menu;
  3060. });
  3061. });
  3062. // underline菜单
  3063. _e(function (E, $) {
  3064. E.createMenu(function (check) {
  3065. var menuId = 'underline';
  3066. if (!check(menuId)) {
  3067. return;
  3068. }
  3069. var editor = this;
  3070. var lang = editor.config.lang;
  3071. // 创建 menu 对象
  3072. var menu = new E.Menu({
  3073. editor: editor,
  3074. id: menuId,
  3075. title: lang.underline,
  3076. commandName: 'Underline'
  3077. });
  3078. // 定义选中状态下的click事件
  3079. menu.clickEventSelected = function (e) {
  3080. var isRangeEmpty = editor.isRangeEmpty();
  3081. if (!isRangeEmpty) {
  3082. // 如果选区有内容,则执行基础命令
  3083. editor.command(e, 'Underline');
  3084. } else {
  3085. // 如果选区没有内容
  3086. editor.commandForElem('u,a', e, 'Underline');
  3087. }
  3088. };
  3089. // 增加到editor对象中
  3090. editor.menus[menuId] = menu;
  3091. });
  3092. });
  3093. // italic 菜单
  3094. _e(function (E, $) {
  3095. E.createMenu(function (check) {
  3096. var menuId = 'italic';
  3097. if (!check(menuId)) {
  3098. return;
  3099. }
  3100. var editor = this;
  3101. var lang = editor.config.lang;
  3102. // 创建 menu 对象
  3103. var menu = new E.Menu({
  3104. editor: editor,
  3105. id: menuId,
  3106. title: lang.italic,
  3107. commandName: 'Italic'
  3108. });
  3109. // 定义选中状态下的click事件
  3110. menu.clickEventSelected = function (e) {
  3111. var isRangeEmpty = editor.isRangeEmpty();
  3112. if (!isRangeEmpty) {
  3113. // 如果选区有内容,则执行基础命令
  3114. editor.command(e, 'Italic');
  3115. } else {
  3116. // 如果选区没有内容
  3117. editor.commandForElem('i', e, 'Italic');
  3118. }
  3119. };
  3120. // 增加到editor对象中
  3121. editor.menus[menuId] = menu;
  3122. });
  3123. });
  3124. // forecolor 菜单
  3125. _e(function (E, $) {
  3126. E.createMenu(function (check) {
  3127. var menuId = 'forecolor';
  3128. if (!check(menuId)) {
  3129. return;
  3130. }
  3131. var editor = this;
  3132. var lang = editor.config.lang;
  3133. var configColors = editor.config.colors;
  3134. // 创建 menu 对象
  3135. var menu = new E.Menu({
  3136. editor: editor,
  3137. id: menuId,
  3138. title: lang.forecolor
  3139. });
  3140. // 创建 dropPanel
  3141. var $content = $('<div></div>');
  3142. $.each(configColors, function (k, v) {
  3143. $content.append(
  3144. [
  3145. '<a href="#" class="color-item"',
  3146. ' title="' + v + '" commandValue="' + k + '" ',
  3147. ' style="color: ' + k + '" ',
  3148. '><i class="wangeditor-menu-img-pencil"></i></a>'
  3149. ].join('')
  3150. );
  3151. });
  3152. $content.on('click', 'a[commandValue]', function (e) {
  3153. // 执行命令
  3154. var $elem = $(this);
  3155. var commandValue = $elem.attr('commandValue');
  3156. if (menu.selected && editor.isRangeEmpty()) {
  3157. // 当前处于选中状态,并且选中内容为空
  3158. editor.commandForElem('font[color]', e, 'forecolor', commandValue);
  3159. } else {
  3160. // 当前未处于选中状态,或者有选中内容。则执行默认命令
  3161. editor.command(e, 'forecolor', commandValue);
  3162. }
  3163. });
  3164. menu.dropPanel = new E.DropPanel(editor, menu, {
  3165. $content: $content,
  3166. width: 125
  3167. });
  3168. // 定义 update selected 事件
  3169. menu.updateSelectedEvent = function () {
  3170. var rangeElem = editor.getRangeElem();
  3171. rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[color]');
  3172. if (rangeElem) {
  3173. return true;
  3174. }
  3175. return false;
  3176. };
  3177. // 增加到editor对象中
  3178. editor.menus[menuId] = menu;
  3179. });
  3180. });
  3181. // bgcolor 菜单
  3182. _e(function (E, $) {
  3183. E.createMenu(function (check) {
  3184. var menuId = 'bgcolor';
  3185. if (!check(menuId)) {
  3186. return;
  3187. }
  3188. var editor = this;
  3189. var lang = editor.config.lang;
  3190. var configColors = editor.config.colors;
  3191. // 检查元素是否有 background-color: 内联样式
  3192. function checkElemFn(elem) {
  3193. var cssText;
  3194. if (elem && elem.style && elem.style.cssText != null) {
  3195. cssText = elem.style.cssText;
  3196. if (cssText && cssText.indexOf('background-color:') >= 0) {
  3197. return true;
  3198. }
  3199. }
  3200. return false;
  3201. }
  3202. // 创建 menu 对象
  3203. var menu = new E.Menu({
  3204. editor: editor,
  3205. id: menuId,
  3206. title: lang.bgcolor
  3207. });
  3208. // 创建 dropPanel
  3209. var $content = $('<div></div>');
  3210. $.each(configColors, function (k, v) {
  3211. $content.append(
  3212. [
  3213. '<a href="#" class="color-item"',
  3214. ' title="' + v + '" commandValue="' + k + '" ',
  3215. ' style="color: ' + k + '" ',
  3216. '><i class="wangeditor-menu-img-brush"></i></a>'
  3217. ].join('')
  3218. );
  3219. });
  3220. $content.on('click', 'a[commandValue]', function (e) {
  3221. // 执行命令
  3222. var $elem = $(this);
  3223. var commandValue = $elem.attr('commandValue');
  3224. if (menu.selected && editor.isRangeEmpty()) {
  3225. // 当前处于选中状态,并且选中内容为空。使用 commandForElem 执行命令
  3226. editor.commandForElem({
  3227. selector: 'span,font',
  3228. check: checkElemFn
  3229. }, e, 'BackColor', commandValue);
  3230. } else {
  3231. // 当前未处于选中状态,或者有选中内容。则执行默认命令
  3232. editor.command(e, 'BackColor', commandValue);
  3233. }
  3234. });
  3235. menu.dropPanel = new E.DropPanel(editor, menu, {
  3236. $content: $content,
  3237. width: 125
  3238. });
  3239. // 定义 update selected 事件
  3240. menu.updateSelectedEvent = function () {
  3241. var rangeElem = editor.getRangeElem();
  3242. rangeElem = editor.getSelfOrParentByName(rangeElem, 'span,font', checkElemFn);
  3243. if (rangeElem) {
  3244. return true;
  3245. }
  3246. return false;
  3247. };
  3248. // 增加到editor对象中
  3249. editor.menus[menuId] = menu;
  3250. });
  3251. });
  3252. // strikethrough 菜单
  3253. _e(function (E, $) {
  3254. E.createMenu(function (check) {
  3255. var menuId = 'strikethrough';
  3256. if (!check(menuId)) {
  3257. return;
  3258. }
  3259. var editor = this;
  3260. var lang = editor.config.lang;
  3261. // 创建 menu 对象
  3262. var menu = new E.Menu({
  3263. editor: editor,
  3264. id: menuId,
  3265. title: lang.strikethrough,
  3266. commandName: 'StrikeThrough'
  3267. });
  3268. // 定义选中状态下的click事件
  3269. menu.clickEventSelected = function (e) {
  3270. var isRangeEmpty = editor.isRangeEmpty();
  3271. if (!isRangeEmpty) {
  3272. // 如果选区有内容,则执行基础命令
  3273. editor.command(e, 'StrikeThrough');
  3274. } else {
  3275. // 如果选区没有内容
  3276. editor.commandForElem('strike', e, 'StrikeThrough');
  3277. }
  3278. };
  3279. // 增加到editor对象中
  3280. editor.menus[menuId] = menu;
  3281. });
  3282. });
  3283. // eraser 菜单
  3284. _e(function (E, $) {
  3285. E.createMenu(function (check) {
  3286. var menuId = 'eraser';
  3287. if (!check(menuId)) {
  3288. return;
  3289. }
  3290. var editor = this;
  3291. var lang = editor.config.lang;
  3292. // 创建 menu 对象
  3293. var menu = new E.Menu({
  3294. editor: editor,
  3295. id: menuId,
  3296. title: lang.eraser,
  3297. commandName: 'RemoveFormat'
  3298. });
  3299. // 定义点击事件
  3300. menu.clickEvent = function (e) {
  3301. var isRangeEmpty = editor.isRangeEmpty();
  3302. if (!isRangeEmpty) {
  3303. // 选区不是空的,则执行默认命令
  3304. editor.command(e, 'RemoveFormat');
  3305. return;
  3306. }
  3307. var $clearElem;
  3308. // 自定义的命令函数
  3309. function commandFn() {
  3310. var editor = this;
  3311. var rangeElem;
  3312. var pElem, $pElem;
  3313. var quoteElem, $quoteElem;
  3314. var listElem, $listElem;
  3315. // 获取选区 elem
  3316. rangeElem = editor.getRangeElem();
  3317. // 第一步,获取 quote 父元素
  3318. quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3319. if (quoteElem) {
  3320. $quoteElem = $(quoteElem);
  3321. $clearElem = $('<p>' + $quoteElem.text() + '</p>');
  3322. $quoteElem.after($clearElem).remove();
  3323. }
  3324. // 第二步,获取 p h 父元素
  3325. pElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5');
  3326. if (pElem) {
  3327. $pElem = $(pElem);
  3328. $clearElem = $('<p>' + $pElem.text() + '</p>');
  3329. $pElem.after($clearElem).remove();
  3330. }
  3331. // 第三步,获取list
  3332. listElem = editor.getSelfOrParentByName(rangeElem, 'ul,ol');
  3333. if (listElem) {
  3334. $listElem = $(listElem);
  3335. $clearElem = $('<p>' + $listElem.text() + '</p>');
  3336. $listElem.after($clearElem).remove();
  3337. }
  3338. }
  3339. // 自定义 callback 事件
  3340. function callback() {
  3341. // callback中,设置range为clearElem
  3342. var editor = this;
  3343. if ($clearElem) {
  3344. editor.restoreSelectionByElem($clearElem.get(0));
  3345. }
  3346. }
  3347. // 执行自定义命令
  3348. editor.customCommand(e, commandFn, callback);
  3349. };
  3350. // 增加到editor对象中
  3351. editor.menus[menuId] = menu;
  3352. });
  3353. });
  3354. // source 菜单
  3355. _e(function (E, $) {
  3356. E.createMenu(function (check) {
  3357. var menuId = 'source';
  3358. if (!check(menuId)) {
  3359. return;
  3360. }
  3361. var editor = this;
  3362. var lang = editor.config.lang;
  3363. var txtHtml;
  3364. // 创建 menu 对象
  3365. var menu = new E.Menu({
  3366. editor: editor,
  3367. id: menuId,
  3368. title: lang.source
  3369. });
  3370. menu.isShowCode = false;
  3371. // 更新内容
  3372. function updateValue() {
  3373. var $code = menu.$codeTextarea;
  3374. var $txt = editor.txt.$txt;
  3375. var value = $.trim($code.val()); // 取值
  3376. if (!value) {
  3377. value = '<p><br></p>';
  3378. }
  3379. // 过滤js代码
  3380. if (editor.config.jsFilter) {
  3381. value = value.replace(/<script[\s\S]*?<\/script>/ig, '');
  3382. }
  3383. // 赋值
  3384. try {
  3385. $txt.html(value);
  3386. } catch (ex) {
  3387. // 更新 html 源码出错,一般都是取消了 js 过滤之后,js报错导致的
  3388. }
  3389. }
  3390. // 定义click事件
  3391. menu.clickEvent = function (e) {
  3392. var self = this;
  3393. var editor = self.editor;
  3394. var $txt = editor.txt.$txt;
  3395. var txtOuterHeight = $txt.outerHeight();
  3396. var txtHeight = $txt.height();
  3397. if (!self.$codeTextarea) {
  3398. self.$codeTextarea = $('<textarea class="code-textarea"></textarea>');
  3399. }
  3400. var $code = self.$codeTextarea;
  3401. $code.css({
  3402. height: txtHeight,
  3403. 'margin-top': txtOuterHeight - txtHeight
  3404. });
  3405. // 赋值
  3406. $code.val($txt.html());
  3407. // 监控变化
  3408. $code.on('change', function (e) {
  3409. updateValue();
  3410. });
  3411. // 渲染
  3412. $txt.after($code).hide();
  3413. $code.show();
  3414. // 更新状态
  3415. menu.isShowCode = true;
  3416. // 执行 updateSelected 事件
  3417. this.updateSelected();
  3418. // 禁用其他菜单
  3419. editor.disableMenusExcept('source');
  3420. // 记录当前html值
  3421. txtHtml = $txt.html();
  3422. };
  3423. // 定义选中状态下的click事件
  3424. menu.clickEventSelected = function (e) {
  3425. var self = this;
  3426. var editor = self.editor;
  3427. var $txt = editor.txt.$txt;
  3428. var $code = self.$codeTextarea;
  3429. var value;
  3430. if (!$code) {
  3431. return;
  3432. }
  3433. // 更新内容
  3434. updateValue();
  3435. // 渲染
  3436. $code.after($txt).hide();
  3437. $txt.show();
  3438. // 更新状态
  3439. menu.isShowCode = false;
  3440. // 执行 updateSelected 事件
  3441. this.updateSelected();
  3442. // 启用其他菜单
  3443. editor.enableMenusExcept('source');
  3444. // 判断是否执行 onchange 事件
  3445. if ($txt.html() !== txtHtml) {
  3446. if (editor.onchange && typeof editor.onchange === 'function') {
  3447. editor.onchange.call(editor);
  3448. }
  3449. }
  3450. };
  3451. // 定义切换选中状态事件
  3452. menu.updateSelectedEvent = function () {
  3453. return this.isShowCode;
  3454. };
  3455. // 增加到editor对象中
  3456. editor.menus[menuId] = menu;
  3457. });
  3458. });
  3459. // quote 菜单
  3460. _e(function (E, $) {
  3461. E.createMenu(function (check) {
  3462. var menuId = 'quote';
  3463. if (!check(menuId)) {
  3464. return;
  3465. }
  3466. var editor = this;
  3467. var lang = editor.config.lang;
  3468. // 创建 menu 对象
  3469. var menu = new E.Menu({
  3470. editor: editor,
  3471. id: menuId,
  3472. title: lang.quote,
  3473. commandName: 'formatBlock',
  3474. commandValue: 'blockquote'
  3475. });
  3476. // 定义click事件
  3477. menu.clickEvent = function (e) {
  3478. var rangeElem = editor.getRangeElem();
  3479. var $rangeElem;
  3480. if (!rangeElem) {
  3481. e.preventDefault();
  3482. return;
  3483. }
  3484. var currentQuote = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3485. var $quote;
  3486. if (currentQuote) {
  3487. // 说明当前在quote之内,不做任何处理
  3488. e.preventDefault();
  3489. return;
  3490. }
  3491. rangeElem = editor.getLegalTags(rangeElem);
  3492. $rangeElem = $(rangeElem);
  3493. // 无文字,则不允许执行引用
  3494. if (!$rangeElem.text()) {
  3495. return;
  3496. }
  3497. if (!rangeElem) {
  3498. // 执行默认命令
  3499. // IE8 下执行此处(不过,经测试代码无效,也不报错)
  3500. editor.command(e, 'formatBlock', 'blockquote');
  3501. return;
  3502. }
  3503. // 自定义command事件
  3504. function commandFn() {
  3505. $quote = $('<p>' + $rangeElem.text() + '</p>');
  3506. $rangeElem.after($quote).remove();
  3507. $quote.wrap('<blockquote>');
  3508. }
  3509. // 自定义 callback 事件
  3510. function callback() {
  3511. // callback中,设置range为quote
  3512. var editor = this;
  3513. if ($quote) {
  3514. editor.restoreSelectionByElem($quote.get(0));
  3515. }
  3516. }
  3517. // 执行自定义命令
  3518. editor.customCommand(e, commandFn, callback);
  3519. };
  3520. // 定义选中状态下的click事件
  3521. menu.clickEventSelected = function (e) {
  3522. var rangeElem;
  3523. var quoteElem;
  3524. var $lastChild;
  3525. // 获取当前选区的elem,并试图往上找 quote 元素
  3526. rangeElem = editor.getRangeElem();
  3527. quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3528. if (!quoteElem) {
  3529. // 没找到,则返回
  3530. e.preventDefault();
  3531. return;
  3532. }
  3533. // 自定义的command事件
  3534. function commandFn() {
  3535. var $quoteElem;
  3536. var $children;
  3537. $quoteElem = $(quoteElem);
  3538. $children = $quoteElem.children();
  3539. if ($children.length) {
  3540. $children.each(function (k) {
  3541. var $item = $(this);
  3542. if ($item.get(0).nodeName === 'P') {
  3543. $quoteElem.after($item);
  3544. } else {
  3545. $quoteElem.after('<p>' + $item.text() + '</p>');
  3546. }
  3547. $lastChild = $item; // 记录最后一个子元素,用于callback中的range定位
  3548. });
  3549. $quoteElem.remove();
  3550. return;
  3551. }
  3552. }
  3553. // 自定义的callback函数
  3554. function callback() {
  3555. // callback中,设置range为lastChild
  3556. var editor = this;
  3557. if ($lastChild) {
  3558. editor.restoreSelectionByElem($lastChild.get(0));
  3559. }
  3560. }
  3561. // 执行自定义命令
  3562. editor.customCommand(e, commandFn, callback);
  3563. };
  3564. // 定义更新选中状态的事件
  3565. menu.updateSelectedEvent = function () {
  3566. var self = this; //菜单对象
  3567. var editor = self.editor;
  3568. var rangeElem;
  3569. rangeElem = editor.getRangeElem();
  3570. rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3571. if (rangeElem) {
  3572. return true;
  3573. }
  3574. return false;
  3575. };
  3576. // 增加到editor对象中
  3577. editor.menus[menuId] = menu;
  3578. // --------------- 两次点击 enter 跳出引用 ---------------
  3579. editor.ready(function () {
  3580. var editor = this;
  3581. var $txt = editor.txt.$txt;
  3582. var isPrevEnter = false; // 是不是刚刚在quote中按了 enter 键
  3583. $txt.on('keydown', function (e) {
  3584. if (e.keyCode !== 13) {
  3585. // 不是 enter 键
  3586. isPrevEnter = false;
  3587. return;
  3588. }
  3589. var rangeElem = editor.getRangeElem();
  3590. rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3591. if (!rangeElem) {
  3592. // 选区不是 quote
  3593. isPrevEnter = false;
  3594. return;
  3595. }
  3596. if (!isPrevEnter) {
  3597. // 最近没有在qote中按enter键
  3598. isPrevEnter = true;
  3599. return;
  3600. }
  3601. var currentRangeElem = editor.getRangeElem();
  3602. var $currentRangeElem = $(currentRangeElem);
  3603. if ($currentRangeElem.length) {
  3604. $currentRangeElem.parent().after($currentRangeElem);
  3605. }
  3606. // 设置选区
  3607. editor.restoreSelectionByElem(currentRangeElem, 'start');
  3608. isPrevEnter = false;
  3609. // 阻止默认行文
  3610. e.preventDefault();
  3611. });
  3612. }); // editor.ready(
  3613. // --------------- 处理quote中无内容时不能删除的问题 ---------------
  3614. editor.ready(function () {
  3615. var editor = this;
  3616. var $txt = editor.txt.$txt;
  3617. var $rangeElem;
  3618. function commandFn() {
  3619. $rangeElem && $rangeElem.remove();
  3620. }
  3621. function callback() {
  3622. if (!$rangeElem) {
  3623. return;
  3624. }
  3625. var $prev = $rangeElem.prev();
  3626. if ($prev.length) {
  3627. // 有 prev 则定位到 prev 最后
  3628. editor.restoreSelectionByElem($prev.get(0));
  3629. } else {
  3630. // 无 prev 则初始化选区
  3631. editor.initSelection();
  3632. }
  3633. }
  3634. $txt.on('keydown', function (e) {
  3635. if (e.keyCode !== 8) {
  3636. // 不是 backspace 键
  3637. return;
  3638. }
  3639. var rangeElem = editor.getRangeElem();
  3640. rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  3641. if (!rangeElem) {
  3642. // 选区不是 quote
  3643. return;
  3644. }
  3645. $rangeElem = $(rangeElem);
  3646. var text = $rangeElem.text();
  3647. if (text) {
  3648. // quote 中还有内容
  3649. return;
  3650. }
  3651. editor.customCommand(e, commandFn, callback);
  3652. }); // $txt.on
  3653. }); // editor.ready(
  3654. });
  3655. });
  3656. // 字体 菜单
  3657. _e(function (E, $) {
  3658. E.createMenu(function (check) {
  3659. var menuId = 'fontfamily';
  3660. if (!check(menuId)) {
  3661. return;
  3662. }
  3663. var editor = this;
  3664. var lang = editor.config.lang;
  3665. var configFamilys = editor.config.familys;
  3666. // 创建 menu 对象
  3667. var menu = new E.Menu({
  3668. editor: editor,
  3669. id: menuId,
  3670. title: lang.fontfamily,
  3671. commandName: 'fontName'
  3672. });
  3673. // 初始化数据
  3674. var data = {};
  3675. /*
  3676. data 需要的结构
  3677. {
  3678. 'commandValue': 'title'
  3679. ...
  3680. }
  3681. */
  3682. $.each(configFamilys, function (k, v) {
  3683. // configFamilys 是数组,data 是对象
  3684. data[v] = v;
  3685. });
  3686. // 创建droplist
  3687. var tpl = '<span style="font-family:{#commandValue};">{#title}</span>';
  3688. menu.dropList = new E.DropList(editor, menu, {
  3689. data: data,
  3690. tpl: tpl,
  3691. selectorForELemCommand: 'font[face]' // 为了执行 editor.commandForElem 而传入的elem查询方式
  3692. });
  3693. // 定义 update selected 事件
  3694. menu.updateSelectedEvent = function () {
  3695. var rangeElem = editor.getRangeElem();
  3696. rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[face]');
  3697. if (rangeElem) {
  3698. return true;
  3699. }
  3700. return false;
  3701. };
  3702. // 增加到editor对象中
  3703. editor.menus[menuId] = menu;
  3704. });
  3705. });
  3706. // 字号 菜单
  3707. _e(function (E, $) {
  3708. E.createMenu(function (check) {
  3709. var menuId = 'fontsize';
  3710. if (!check(menuId)) {
  3711. return;
  3712. }
  3713. var editor = this;
  3714. var lang = editor.config.lang;
  3715. var configSize = editor.config.fontsizes;
  3716. // 创建 menu 对象
  3717. var menu = new E.Menu({
  3718. editor: editor,
  3719. id: menuId,
  3720. title: lang.fontsize,
  3721. commandName: 'fontSize'
  3722. });
  3723. // 初始化数据
  3724. var data = configSize;
  3725. /*
  3726. data 需要的结构
  3727. {
  3728. 'commandValue': 'title'
  3729. ...
  3730. }
  3731. */
  3732. // 创建droplist
  3733. var tpl = '<span style="font-size:{#title};">{#title}</span>';
  3734. menu.dropList = new E.DropList(editor, menu, {
  3735. data: data,
  3736. tpl: tpl,
  3737. selectorForELemCommand: 'font[size]' // 为了执行 editor.commandForElem 而传入的elem查询方式
  3738. });
  3739. // 定义 update selected 事件
  3740. menu.updateSelectedEvent = function () {
  3741. var rangeElem = editor.getRangeElem();
  3742. rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[size]');
  3743. if (rangeElem) {
  3744. return true;
  3745. }
  3746. return false;
  3747. };
  3748. // 增加到editor对象中
  3749. editor.menus[menuId] = menu;
  3750. });
  3751. });
  3752. // head 菜单
  3753. _e(function (E, $) {
  3754. E.createMenu(function (check) {
  3755. var menuId = 'head';
  3756. if (!check(menuId)) {
  3757. return;
  3758. }
  3759. var editor = this;
  3760. var lang = editor.config.lang;
  3761. // 创建 menu 对象
  3762. var menu = new E.Menu({
  3763. editor: editor,
  3764. id: menuId,
  3765. title: lang.head,
  3766. commandName: 'formatBlock'
  3767. });
  3768. // 初始化数据
  3769. var data = {
  3770. '<h1>': '标题1',
  3771. '<h2>': '标题2',
  3772. '<h3>': '标题3',
  3773. '<h4>': '标题4',
  3774. '<h5>': '标题5'
  3775. };
  3776. /*
  3777. data 需要的结构
  3778. {
  3779. 'commandValue': 'title'
  3780. ...
  3781. }
  3782. */
  3783. var isOrderedList;
  3784. function beforeEvent(e) {
  3785. if (editor.queryCommandState('InsertOrderedList')) {
  3786. isOrderedList = true;
  3787. // 先取消有序列表
  3788. editor.command(e, 'InsertOrderedList');
  3789. } else {
  3790. isOrderedList = false;
  3791. }
  3792. }
  3793. function afterEvent(e) {
  3794. if (isOrderedList) {
  3795. // 再设置有序列表
  3796. editor.command(e, 'InsertOrderedList');
  3797. }
  3798. }
  3799. // 创建droplist
  3800. var tpl = '{#commandValue}{#title}';
  3801. menu.dropList = new E.DropList(editor, menu, {
  3802. data: data,
  3803. tpl: tpl,
  3804. // 对 ol 直接设置 head,会出现每个 li 的 index 都变成 1 的问题,因此要先取消 ol,然后设置 head,最后再增加上 ol
  3805. beforeEvent: beforeEvent,
  3806. afterEvent: afterEvent
  3807. });
  3808. // 定义 update selected 事件
  3809. menu.updateSelectedEvent = function () {
  3810. var rangeElem = editor.getRangeElem();
  3811. rangeElem = editor.getSelfOrParentByName(rangeElem, 'h1,h2,h3,h4,h5');
  3812. if (rangeElem) {
  3813. return true;
  3814. }
  3815. return false;
  3816. };
  3817. // 增加到editor对象中
  3818. editor.menus[menuId] = menu;
  3819. });
  3820. });
  3821. // unorderlist 菜单
  3822. _e(function (E, $) {
  3823. E.createMenu(function (check) {
  3824. var menuId = 'unorderlist';
  3825. if (!check(menuId)) {
  3826. return;
  3827. }
  3828. var editor = this;
  3829. var lang = editor.config.lang;
  3830. // 创建 menu 对象
  3831. var menu = new E.Menu({
  3832. editor: editor,
  3833. id: menuId,
  3834. title: lang.unorderlist,
  3835. commandName: 'InsertUnorderedList'
  3836. });
  3837. // 增加到editor对象中
  3838. editor.menus[menuId] = menu;
  3839. });
  3840. });
  3841. // orderlist 菜单
  3842. _e(function (E, $) {
  3843. E.createMenu(function (check) {
  3844. var menuId = 'orderlist';
  3845. if (!check(menuId)) {
  3846. return;
  3847. }
  3848. var editor = this;
  3849. var lang = editor.config.lang;
  3850. // 创建 menu 对象
  3851. var menu = new E.Menu({
  3852. editor: editor,
  3853. id: menuId,
  3854. title: lang.orderlist,
  3855. commandName: 'InsertOrderedList'
  3856. });
  3857. // 增加到editor对象中
  3858. editor.menus[menuId] = menu;
  3859. });
  3860. });
  3861. // alignleft 菜单
  3862. _e(function (E, $) {
  3863. E.createMenu(function (check) {
  3864. var menuId = 'alignleft';
  3865. if (!check(menuId)) {
  3866. return;
  3867. }
  3868. var editor = this;
  3869. var lang = editor.config.lang;
  3870. // 创建 menu 对象
  3871. var menu = new E.Menu({
  3872. editor: editor,
  3873. id: menuId,
  3874. title: lang.alignleft,
  3875. commandName: 'JustifyLeft'
  3876. });
  3877. // 定义 update selected 事件
  3878. menu.updateSelectedEvent = function () {
  3879. var rangeElem = editor.getRangeElem();
  3880. rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
  3881. var cssText;
  3882. if (elem && elem.style && elem.style.cssText != null) {
  3883. cssText = elem.style.cssText;
  3884. if (cssText && /text-align:\s*left;/.test(cssText)) {
  3885. return true;
  3886. }
  3887. }
  3888. if ($(elem).attr('align') === 'left') {
  3889. // ff 中,设置align-left之后,会是 <p align="left">xxx</p>
  3890. return true;
  3891. }
  3892. return false;
  3893. });
  3894. if (rangeElem) {
  3895. return true;
  3896. }
  3897. return false;
  3898. };
  3899. // 增加到editor对象中
  3900. editor.menus[menuId] = menu;
  3901. });
  3902. });
  3903. // aligncenter 菜单
  3904. _e(function (E, $) {
  3905. E.createMenu(function (check) {
  3906. var menuId = 'aligncenter';
  3907. if (!check(menuId)) {
  3908. return;
  3909. }
  3910. var editor = this;
  3911. var lang = editor.config.lang;
  3912. // 创建 menu 对象
  3913. var menu = new E.Menu({
  3914. editor: editor,
  3915. id: menuId,
  3916. title: lang.aligncenter,
  3917. commandName: 'JustifyCenter'
  3918. });
  3919. // 定义 update selected 事件
  3920. menu.updateSelectedEvent = function () {
  3921. var rangeElem = editor.getRangeElem();
  3922. rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
  3923. var cssText;
  3924. if (elem && elem.style && elem.style.cssText != null) {
  3925. cssText = elem.style.cssText;
  3926. if (cssText && /text-align:\s*center;/.test(cssText)) {
  3927. return true;
  3928. }
  3929. }
  3930. if ($(elem).attr('align') === 'center') {
  3931. // ff 中,设置align-center之后,会是 <p align="center">xxx</p>
  3932. return true;
  3933. }
  3934. return false;
  3935. });
  3936. if (rangeElem) {
  3937. return true;
  3938. }
  3939. return false;
  3940. };
  3941. // 增加到editor对象中
  3942. editor.menus[menuId] = menu;
  3943. });
  3944. });
  3945. // alignright 菜单
  3946. _e(function (E, $) {
  3947. E.createMenu(function (check) {
  3948. var menuId = 'alignright';
  3949. if (!check(menuId)) {
  3950. return;
  3951. }
  3952. var editor = this;
  3953. var lang = editor.config.lang;
  3954. // 创建 menu 对象
  3955. var menu = new E.Menu({
  3956. editor: editor,
  3957. id: menuId,
  3958. title: lang.alignright,
  3959. commandName: 'JustifyRight'
  3960. });
  3961. // 定义 update selected 事件
  3962. menu.updateSelectedEvent = function () {
  3963. var rangeElem = editor.getRangeElem();
  3964. rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
  3965. var cssText;
  3966. if (elem && elem.style && elem.style.cssText != null) {
  3967. cssText = elem.style.cssText;
  3968. if (cssText && /text-align:\s*right;/.test(cssText)) {
  3969. return true;
  3970. }
  3971. }
  3972. if ($(elem).attr('align') === 'right') {
  3973. // ff 中,设置align-right之后,会是 <p align="right">xxx</p>
  3974. return true;
  3975. }
  3976. return false;
  3977. });
  3978. if (rangeElem) {
  3979. return true;
  3980. }
  3981. return false;
  3982. };
  3983. // 增加到editor对象中
  3984. editor.menus[menuId] = menu;
  3985. });
  3986. });
  3987. // link 菜单
  3988. _e(function (E, $) {
  3989. E.createMenu(function (check) {
  3990. var menuId = 'link';
  3991. if (!check(menuId)) {
  3992. return;
  3993. }
  3994. var editor = this;
  3995. var lang = editor.config.lang;
  3996. // 创建 menu 对象
  3997. var menu = new E.Menu({
  3998. editor: editor,
  3999. id: menuId,
  4000. title: lang.link
  4001. });
  4002. // 创建 dropPanel
  4003. var $content = $('<div></div>');
  4004. var $div1 = $('<div style="margin:20px 10px;" class="clearfix"></div>');
  4005. var $div2 = $div1.clone();
  4006. var $div3 = $div1.clone().css('margin', '0 10px');
  4007. var $textInput = $('<input type="text" class="block" placeholder="' + lang.text + '"/>');
  4008. var $urlInput = $('<input type="text" class="block" placeholder="' + lang.link + '"/>');
  4009. var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
  4010. var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  4011. $div1.append($textInput);
  4012. $div2.append($urlInput);
  4013. $div3.append($btnSubmit).append($btnCancel);
  4014. $content.append($div1).append($div2).append($div3);
  4015. menu.dropPanel = new E.DropPanel(editor, menu, {
  4016. $content: $content,
  4017. width: 300
  4018. });
  4019. // 定义click事件
  4020. menu.clickEvent = function (e) {
  4021. var menu = this;
  4022. var dropPanel = menu.dropPanel;
  4023. // -------------隐藏----------------
  4024. if (dropPanel.isShowing) {
  4025. dropPanel.hide();
  4026. return;
  4027. }
  4028. // -------------显示----------------
  4029. // 重置 input
  4030. $textInput.val('');
  4031. $urlInput.val('http://');
  4032. // 获取url
  4033. var url = '';
  4034. var rangeElem = editor.getRangeElem();
  4035. rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
  4036. if (rangeElem) {
  4037. url = rangeElem.href || '';
  4038. }
  4039. // 获取 text
  4040. var text = '';
  4041. var isRangeEmpty = editor.isRangeEmpty();
  4042. if (!isRangeEmpty) {
  4043. // 选区不是空
  4044. text = editor.getRangeText() || '';
  4045. } else if (rangeElem) {
  4046. // 如果选区空,并且在 a 标签之内
  4047. text = rangeElem.textContent || rangeElem.innerHTML;
  4048. }
  4049. // 设置 url 和 text
  4050. url && $urlInput.val(url);
  4051. text && $textInput.val(text);
  4052. // 如果有选区内容,textinput 不能修改
  4053. if (!isRangeEmpty) {
  4054. $textInput.attr('disabled', true);
  4055. } else {
  4056. $textInput.removeAttr('disabled');
  4057. }
  4058. // 显示(要设置好了所有input的值和属性之后再显示)
  4059. dropPanel.show();
  4060. };
  4061. // 定义 update selected 事件
  4062. menu.updateSelectedEvent = function () {
  4063. var rangeElem = editor.getRangeElem();
  4064. rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
  4065. if (rangeElem) {
  4066. return true;
  4067. }
  4068. return false;
  4069. };
  4070. // 『取消』 按钮
  4071. $btnCancel.click(function (e) {
  4072. e.preventDefault();
  4073. menu.dropPanel.hide();
  4074. });
  4075. // 『确定』按钮
  4076. $btnSubmit.click(function (e) {
  4077. e.preventDefault();
  4078. var rangeElem = editor.getRangeElem();
  4079. var targetElem = editor.getSelfOrParentByName(rangeElem, 'a');
  4080. var isRangeEmpty = editor.isRangeEmpty();
  4081. var $linkElem, linkHtml;
  4082. var commandFn, callback;
  4083. var $txt = editor.txt.$txt;
  4084. var $oldLinks, $newLinks;
  4085. var uniqId = 'link' + E.random();
  4086. // 获取数据
  4087. var url = $.trim($urlInput.val());
  4088. var text = $.trim($textInput.val());
  4089. if (!url) {
  4090. menu.dropPanel.focusFirstInput();
  4091. return;
  4092. }
  4093. if (!text) {
  4094. text = url;
  4095. }
  4096. if (!isRangeEmpty) {
  4097. // 选中区域有内容,则执行默认命令
  4098. // 获取目前 txt 内所有链接,并为当前链接做一个标记
  4099. $oldLinks = $txt.find('a');
  4100. $oldLinks.attr(uniqId, '1');
  4101. // 执行命令
  4102. editor.command(e, 'createLink', url);
  4103. // 去的没有标记的链接,即刚刚插入的链接
  4104. $newLinks = $txt.find('a').not('[' + uniqId + ']');
  4105. $newLinks.attr('target', '_blank'); // 增加 _blank
  4106. // 去掉之前做的标记
  4107. $oldLinks.removeAttr(uniqId);
  4108. } else if (targetElem) {
  4109. // 无选中区域,在 a 标签之内,修改该 a 标签的内容和链接
  4110. $linkElem = $(targetElem);
  4111. commandFn = function () {
  4112. $linkElem.attr('href', url);
  4113. $linkElem.text(text);
  4114. };
  4115. callback = function () {
  4116. var editor = this;
  4117. editor.restoreSelectionByElem(targetElem);
  4118. };
  4119. // 执行命令
  4120. editor.customCommand(e, commandFn, callback);
  4121. } else {
  4122. // 无选中区域,不在 a 标签之内,插入新的链接
  4123. linkHtml = '<a href="' + url + '" target="_blank">' + text + '</a>';
  4124. if (E.userAgent.indexOf('Firefox') > 0) {
  4125. linkHtml += '<span>&nbsp;</span>';
  4126. }
  4127. editor.command(e, 'insertHtml', linkHtml);
  4128. }
  4129. });
  4130. // 增加到editor对象中
  4131. editor.menus[menuId] = menu;
  4132. });
  4133. });
  4134. // unlink 菜单
  4135. _e(function (E, $) {
  4136. E.createMenu(function (check) {
  4137. var menuId = 'unlink';
  4138. if (!check(menuId)) {
  4139. return;
  4140. }
  4141. var editor = this;
  4142. var lang = editor.config.lang;
  4143. // 创建 menu 对象
  4144. var menu = new E.Menu({
  4145. editor: editor,
  4146. id: menuId,
  4147. title: lang.unlink,
  4148. commandName: 'unLink'
  4149. });
  4150. // click 事件
  4151. menu.clickEvent = function (e) {
  4152. var isRangeEmpty = editor.isRangeEmpty();
  4153. if (!isRangeEmpty) {
  4154. // 有选中区域,或者IE8,执行默认命令
  4155. editor.command(e, 'unLink');
  4156. return;
  4157. }
  4158. // 无选中区域...
  4159. var rangeElem = editor.getRangeElem();
  4160. var aElem = editor.getSelfOrParentByName(rangeElem, 'a');
  4161. if (!aElem) {
  4162. // 不在 a 之内,返回
  4163. e.preventDefault();
  4164. return;
  4165. }
  4166. // 在 a 之内
  4167. var $a = $(aElem);
  4168. var $span = $('<span>' + $a.text() + '</span>');
  4169. function commandFn() {
  4170. $a.after($span).remove();
  4171. }
  4172. function callback() {
  4173. editor.restoreSelectionByElem($span.get(0));
  4174. }
  4175. editor.customCommand(e, commandFn, callback);
  4176. };
  4177. // 增加到editor对象中
  4178. editor.menus[menuId] = menu;
  4179. });
  4180. });
  4181. // table 菜单
  4182. _e(function (E, $) {
  4183. E.createMenu(function (check) {
  4184. var menuId = 'table';
  4185. if (!check(menuId)) {
  4186. return;
  4187. }
  4188. var editor = this;
  4189. var lang = editor.config.lang;
  4190. // 创建 menu 对象
  4191. var menu = new E.Menu({
  4192. editor: editor,
  4193. id: menuId,
  4194. title: lang.table
  4195. });
  4196. // dropPanel 内容
  4197. var $content = $('<div style="font-size: 14px; color: #666; text-align:right;"></div>');
  4198. var $table = $('<table class="choose-table" style="margin-bottom:10px;margin-top:5px;">');
  4199. var $row = $('<span>0</span>');
  4200. var $rowspan = $('<span> 行 </span>');
  4201. var $col = $('<span>0</span>');
  4202. var $colspan = $('<span> 列</span>');
  4203. var $tr;
  4204. var i, j;
  4205. // 创建一个n行n列的表格
  4206. for (i = 0; i < 15; i++) {
  4207. $tr = $('<tr index="' + (i + 1) + '">');
  4208. for (j = 0; j < 20; j++) {
  4209. $tr.append($('<td index="' + (j + 1) + '">'));
  4210. }
  4211. $table.append($tr);
  4212. }
  4213. $content.append($table);
  4214. $content.append($row).append($rowspan).append($col).append($colspan);
  4215. // 定义table事件
  4216. $table.on('mouseenter', 'td', function (e) {
  4217. var $currentTd = $(e.currentTarget);
  4218. var currentTdIndex = $currentTd.attr('index');
  4219. var $currentTr = $currentTd.parent();
  4220. var currentTrIndex = $currentTr.attr('index');
  4221. // 显示
  4222. $row.text(currentTrIndex);
  4223. $col.text(currentTdIndex);
  4224. // 遍历设置背景颜色
  4225. $table.find('tr').each(function () {
  4226. var $tr = $(this);
  4227. var trIndex = $tr.attr('index');
  4228. if (parseInt(trIndex, 10) <= parseInt(currentTrIndex, 10)) {
  4229. // 该行需要可能需要设置背景色
  4230. $tr.find('td').each(function () {
  4231. var $td = $(this);
  4232. var tdIndex = $td.attr('index');
  4233. if (parseInt(tdIndex, 10) <= parseInt(currentTdIndex, 10)) {
  4234. // 需要设置背景色
  4235. $td.addClass('active');
  4236. } else {
  4237. // 需要移除背景色
  4238. $td.removeClass('active');
  4239. }
  4240. });
  4241. } else {
  4242. // 改行不需要设置背景色
  4243. $tr.find('td').removeClass('active');
  4244. }
  4245. });
  4246. }).on('mouseleave', function (e) {
  4247. // mouseleave 删除背景色
  4248. $table.find('td').removeClass('active');
  4249. $row.text(0);
  4250. $col.text(0);
  4251. });
  4252. // 插入表格
  4253. $table.on('click', 'td', function (e) {
  4254. var $currentTd = $(e.currentTarget);
  4255. var currentTdIndex = $currentTd.attr('index');
  4256. var $currentTr = $currentTd.parent();
  4257. var currentTrIndex = $currentTr.attr('index');
  4258. var rownum = parseInt(currentTrIndex, 10);
  4259. var colnum = parseInt(currentTdIndex, 10);
  4260. // -------- 拼接tabel html --------
  4261. var i, j;
  4262. var tableHtml = '<table>';
  4263. for (i = 0; i < rownum; i++) {
  4264. tableHtml += '<tr>';
  4265. for (j = 0; j < colnum; j++) {
  4266. tableHtml += '<td><span>&nbsp;</span></td>';
  4267. }
  4268. tableHtml += '</tr>';
  4269. }
  4270. tableHtml += '</table>';
  4271. // -------- 执行命令 --------
  4272. editor.command(e, 'insertHtml', tableHtml);
  4273. });
  4274. // 创建 panel
  4275. menu.dropPanel = new E.DropPanel(editor, menu, {
  4276. $content: $content,
  4277. width: 262
  4278. });
  4279. // 增加到editor对象中
  4280. editor.menus[menuId] = menu;
  4281. });
  4282. });
  4283. // emotion 菜单
  4284. _e(function (E, $) {
  4285. E.createMenu(function (check) {
  4286. var menuId = 'emotion';
  4287. if (!check(menuId)) {
  4288. return;
  4289. }
  4290. var editor = this;
  4291. var config = editor.config;
  4292. var lang = config.lang;
  4293. var configEmotions = config.emotions;
  4294. var emotionsShow = config.emotionsShow;
  4295. // 记录每一个表情图片的地址
  4296. editor.emotionUrls = [];
  4297. // 创建 menu 对象
  4298. var menu = new E.Menu({
  4299. editor: editor,
  4300. id: menuId,
  4301. title: lang.emotion
  4302. });
  4303. // 添加表情图片的函数
  4304. function insertEmotionImgs(data, $tabContent) {
  4305. // 添加表情图片
  4306. $.each(data, function (k, emotion) {
  4307. var src = emotion.icon || emotion.url;
  4308. var value = emotion.value || emotion.title;
  4309. // 通过配置 editor.config.emotionsShow 的值来修改插入到编辑器的内容(图片/value)
  4310. var commandValue = emotionsShow === 'icon' ? src : value;
  4311. var $command = $('<a href="#" commandValue="' + commandValue + '"></a>');
  4312. var $img = $('<img>');
  4313. $img.attr('_src', src); // 先将 src 复制到 '_src' 属性,先不加载
  4314. $command.append($img);
  4315. $tabContent.append($command);
  4316. // 记录下每一个表情图片的地址
  4317. editor.emotionUrls.push(src);
  4318. });
  4319. }
  4320. // 拼接 dropPanel 内容
  4321. var $panelContent = $('<div class="panel-tab"></div>');
  4322. var $tabContainer = $('<div class="tab-container"></div>');
  4323. var $contentContainer = $('<div class="content-container emotion-content-container"></div>');
  4324. $.each(configEmotions, function (k, emotion) {
  4325. var title = emotion.title;
  4326. var data = emotion.data;
  4327. E.log('正在处理 ' + title + ' 表情的数据...');
  4328. // 增加该组表情的tab和content
  4329. var $tab = $('<a href="#">' + title +' </a>');
  4330. $tabContainer.append($tab);
  4331. var $tabContent = $('<div class="content"></div>');
  4332. $contentContainer.append($tabContent);
  4333. // tab 切换事件
  4334. $tab.click(function (e) {
  4335. $tabContainer.children().removeClass('selected');
  4336. $contentContainer.children().removeClass('selected');
  4337. $tabContent.addClass('selected');
  4338. $tab.addClass('selected');
  4339. e.preventDefault();
  4340. });
  4341. // 处理data
  4342. if (typeof data === 'string') {
  4343. // url 形式,需要通过ajax从该url获取数据
  4344. E.log('将通过 ' + data + ' 地址ajax下载表情包');
  4345. $.get(data, function (result) {
  4346. result = $.parseJSON(result);
  4347. E.log('下载完毕,得到 ' + result.length + ' 个表情');
  4348. insertEmotionImgs(result, $tabContent);
  4349. });
  4350. } else if ( Object.prototype.toString.call(data).toLowerCase().indexOf('array') > 0 ) {
  4351. // 数组,即 data 直接就是表情包数据
  4352. insertEmotionImgs(data, $tabContent);
  4353. } else {
  4354. // 其他情况,data格式不对
  4355. E.error('data 数据格式错误,请修改为正确格式,参考文档:' + E.docsite);
  4356. return;
  4357. }
  4358. });
  4359. $panelContent.append($tabContainer).append($contentContainer);
  4360. // 默认显示第一个tab
  4361. $tabContainer.children().first().addClass('selected');
  4362. $contentContainer.children().first().addClass('selected');
  4363. // 插入表情command事件
  4364. $contentContainer.on('click', 'a[commandValue]', function (e) {
  4365. var $a = $(e.currentTarget);
  4366. var commandValue = $a.attr('commandValue');
  4367. var img;
  4368. // commandValue 有可能是图片url,也有可能是表情的 value,需要区别对待
  4369. if (emotionsShow === 'icon') {
  4370. // 插入图片
  4371. editor.command(e, 'InsertImage', commandValue);
  4372. } else {
  4373. // 插入value
  4374. editor.command(e, 'insertHtml', '<span>' + commandValue + '</span>');
  4375. }
  4376. e.preventDefault();
  4377. });
  4378. // 添加panel
  4379. menu.dropPanel = new E.DropPanel(editor, menu, {
  4380. $content: $panelContent,
  4381. width: 350
  4382. });
  4383. // 定义click事件(异步加载表情图片)
  4384. menu.clickEvent = function (e) {
  4385. var menu = this;
  4386. var dropPanel = menu.dropPanel;
  4387. // -------------隐藏-------------
  4388. if (dropPanel.isShowing) {
  4389. dropPanel.hide();
  4390. return;
  4391. }
  4392. // -------------显示-------------
  4393. dropPanel.show();
  4394. // 异步加载图片
  4395. if (menu.imgLoaded) {
  4396. return;
  4397. }
  4398. $contentContainer.find('img').each(function () {
  4399. var $img = $(this);
  4400. var _src = $img.attr('_src');
  4401. $img.on('error', function () {
  4402. E.error('加载不出表情图片 ' + _src);
  4403. });
  4404. $img.attr('src', _src);
  4405. $img.removeAttr('_src');
  4406. });
  4407. menu.imgLoaded = true;
  4408. };
  4409. // 增加到editor对象中
  4410. editor.menus[menuId] = menu;
  4411. });
  4412. });
  4413. // img 菜单
  4414. _e(function (E, $) {
  4415. E.createMenu(function (check) {
  4416. var menuId = 'img';
  4417. if (!check(menuId)) {
  4418. return;
  4419. }
  4420. var editor = this;
  4421. var lang = editor.config.lang;
  4422. // 创建 menu 对象
  4423. var menu = new E.Menu({
  4424. editor: editor,
  4425. id: menuId,
  4426. title: lang.img
  4427. });
  4428. // 创建 panel content
  4429. var $panelContent = $('<div class="panel-tab"></div>');
  4430. var $tabContainer = $('<div class="tab-container"></div>');
  4431. var $contentContainer = $('<div class="content-container"></div>');
  4432. $panelContent.append($tabContainer).append($contentContainer);
  4433. // tab
  4434. var $uploadTab = $('<a href="#">上传图片</a>');
  4435. var $linkTab = $('<a href="#">网络图片</a>');
  4436. $tabContainer.append($uploadTab).append($linkTab);
  4437. // 上传图片 content
  4438. var $uploadContent = $('<div class="content"></div>');
  4439. $contentContainer.append($uploadContent);
  4440. // 网络图片 content
  4441. var $linkContent = $('<div class="content"></div>');
  4442. $contentContainer.append($linkContent);
  4443. linkContentHandler(editor, menu, $linkContent);
  4444. // 添加panel
  4445. menu.dropPanel = new E.DropPanel(editor, menu, {
  4446. $content: $panelContent,
  4447. width: 400,
  4448. onRender: function () {
  4449. // 渲染后的回调事件,用于执行自定义上传的init
  4450. // 因为渲染之后,上传面板的dom才会被渲染到页面,才能让第三方空间获取到
  4451. var init = editor.config.customUploadInit;
  4452. init && init.call(editor);
  4453. }
  4454. });
  4455. // 增加到editor对象中
  4456. editor.menus[menuId] = menu;
  4457. // tab 切换事件
  4458. function tabToggle() {
  4459. $uploadTab.click(function (e) {
  4460. $tabContainer.children().removeClass('selected');
  4461. $contentContainer.children().removeClass('selected');
  4462. $uploadContent.addClass('selected');
  4463. $uploadTab.addClass('selected');
  4464. e.preventDefault();
  4465. });
  4466. $linkTab.click(function (e) {
  4467. $tabContainer.children().removeClass('selected');
  4468. $contentContainer.children().removeClass('selected');
  4469. $linkContent.addClass('selected');
  4470. $linkTab.addClass('selected');
  4471. e.preventDefault();
  4472. // focus input
  4473. if (E.placeholder) {
  4474. $linkContent.find('input[type=text]').focus();
  4475. }
  4476. });
  4477. // 默认情况
  4478. // $uploadTab.addClass('selected');
  4479. // $uploadContent.addClass('selected');
  4480. $uploadTab.click();
  4481. }
  4482. // 隐藏上传图片
  4483. function hideUploadImg() {
  4484. $tabContainer.remove();
  4485. $uploadContent.remove();
  4486. $linkContent.addClass('selected');
  4487. }
  4488. // 隐藏网络图片
  4489. function hideLinkImg() {
  4490. $tabContainer.remove();
  4491. $linkContent.remove();
  4492. $uploadContent.addClass('selected');
  4493. }
  4494. // 判断用户是否配置了上传图片
  4495. editor.ready(function () {
  4496. var editor = this;
  4497. var config = editor.config;
  4498. var uploadImgUrl = config.uploadImgUrl;
  4499. var customUpload = config.customUpload;
  4500. var linkImg = config.hideLinkImg;
  4501. var $uploadImgPanel;
  4502. if (uploadImgUrl || customUpload) {
  4503. // 第一,暴露出 $uploadContent 以便用户自定义 !!!重要
  4504. editor.$uploadContent = $uploadContent;
  4505. // 第二,绑定tab切换事件
  4506. tabToggle();
  4507. if (linkImg) {
  4508. // 隐藏网络图片
  4509. hideLinkImg();
  4510. }
  4511. } else {
  4512. // 未配置上传图片功能
  4513. hideUploadImg();
  4514. }
  4515. // 点击 $uploadContent 立即隐藏 dropPanel
  4516. // 为了兼容IE8、9的上传,因为IE8、9使用 modal 上传
  4517. // 这里使用异步,为了不妨碍高级浏览器通过点击 $uploadContent 选择文件
  4518. function hidePanel() {
  4519. menu.dropPanel.hide();
  4520. }
  4521. $uploadContent.click(function () {
  4522. setTimeout(hidePanel);
  4523. });
  4524. });
  4525. });
  4526. // --------------- 处理网络图片content ---------------
  4527. function linkContentHandler (editor, menu, $linkContent) {
  4528. var lang = editor.config.lang;
  4529. var $urlContainer = $('<div style="margin:20px 10px 10px 10px;"></div>');
  4530. var $urlInput = $('<input type="text" class="block" placeholder="http://"/>');
  4531. $urlContainer.append($urlInput);
  4532. var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
  4533. var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  4534. $linkContent.append($urlContainer).append($btnSubmit).append($btnCancel);
  4535. // 取消
  4536. $btnCancel.click(function (e) {
  4537. e.preventDefault();
  4538. menu.dropPanel.hide();
  4539. });
  4540. // callback
  4541. function callback() {
  4542. $urlInput.val('');
  4543. }
  4544. // 确定
  4545. $btnSubmit.click(function (e) {
  4546. e.preventDefault();
  4547. var url = $.trim($urlInput.val());
  4548. if (!url) {
  4549. // 无内容
  4550. $urlInput.focus();
  4551. return;
  4552. }
  4553. var imgHtml = '<img style="max-width:100%;" src="' + url + '"/>';
  4554. editor.command(e, 'insertHtml', imgHtml, callback);
  4555. });
  4556. }
  4557. });
  4558. // video 菜单
  4559. _e(function (E, $) {
  4560. E.createMenu(function (check) {
  4561. var menuId = 'video';
  4562. if (!check(menuId)) {
  4563. return;
  4564. }
  4565. var editor = this;
  4566. var lang = editor.config.lang;
  4567. var reg = /^<(iframe)|(embed)/i; // <iframe... 或者 <embed... 格式
  4568. // 创建 menu 对象
  4569. var menu = new E.Menu({
  4570. editor: editor,
  4571. id: menuId,
  4572. title: lang.video
  4573. });
  4574. // 创建 panel 内容
  4575. var $content = $('<div></div>');
  4576. var $linkInputContainer = $('<div style="margin:20px 10px;"></div>');
  4577. var $linkInput = $('<input type="text" class="block" placeholder=\'格式如:<iframe src="..." frameborder=0 allowfullscreen></iframe>\'/>');
  4578. $linkInputContainer.append($linkInput);
  4579. var $sizeContainer = $('<div style="margin:20px 10px;"></div>');
  4580. var $widthInput = $('<input type="text" value="640" style="width:50px;text-align:center;"/>');
  4581. var $heightInput = $('<input type="text" value="498" style="width:50px;text-align:center;"/>');
  4582. $sizeContainer.append('<span> ' + lang.width + ' </span>')
  4583. .append($widthInput)
  4584. .append('<span> px &nbsp;&nbsp;&nbsp;</span>')
  4585. .append('<span> ' + lang.height + ' </span>')
  4586. .append($heightInput)
  4587. .append('<span> px </span>');
  4588. var $btnContainer = $('<div></div>');
  4589. var $howToCopy = $('<a href="http://www.kancloud.cn/wangfupeng/wangeditor2/134973" target="_blank" style="display:inline-block;margin-top:10px;margin-left:10px;color:#999;">如何复制视频链接?</a>');
  4590. var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
  4591. var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  4592. $btnContainer.append($howToCopy).append($btnSubmit).append($btnCancel);
  4593. $content.append($linkInputContainer).append($sizeContainer).append($btnContainer);
  4594. // 取消按钮
  4595. $btnCancel.click(function (e) {
  4596. e.preventDefault();
  4597. $linkInput.val('');
  4598. menu.dropPanel.hide();
  4599. });
  4600. // 确定按钮
  4601. $btnSubmit.click(function (e) {
  4602. e.preventDefault();
  4603. var link = $.trim($linkInput.val());
  4604. var $link;
  4605. var width = parseInt($widthInput.val());
  4606. var height = parseInt($heightInput.val());
  4607. var $div = $('<div>');
  4608. var html = '<p>{content}</p>';
  4609. // 验证数据
  4610. if (!link) {
  4611. menu.dropPanel.focusFirstInput();
  4612. return;
  4613. }
  4614. if (!reg.test(link)) {
  4615. alert('视频链接格式错误!');
  4616. menu.dropPanel.focusFirstInput();
  4617. return;
  4618. }
  4619. if (isNaN(width) || isNaN(height)) {
  4620. alert('宽度或高度不是数字!');
  4621. return;
  4622. }
  4623. $link = $(link);
  4624. // 设置高度和宽度
  4625. $link.attr('width', width)
  4626. .attr('height', height);
  4627. // 拼接字符串
  4628. html = html.replace('{content}', $div.append($link).html());
  4629. // 执行命令
  4630. editor.command(e, 'insertHtml', html);
  4631. $linkInput.val('');
  4632. });
  4633. // 创建panel
  4634. menu.dropPanel = new E.DropPanel(editor, menu, {
  4635. $content: $content,
  4636. width: 400
  4637. });
  4638. // 增加到editor对象中
  4639. editor.menus[menuId] = menu;
  4640. });
  4641. });
  4642. // location 菜单
  4643. _e(function (E, $) {
  4644. // 判断浏览器的 input 是否支持 keyup
  4645. var inputKeyup = (function (input) {
  4646. return 'onkeyup' in input;
  4647. })(document.createElement('input'));
  4648. // 百度地图的key
  4649. E.baiduMapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
  4650. // 一个页面中,如果有多个编辑器,地图会出现问题。这个参数记录一下,如果超过 1 就提示
  4651. E.numberOfLocation = 0;
  4652. E.createMenu(function (check) {
  4653. var menuId = 'location';
  4654. if (!check(menuId)) {
  4655. return;
  4656. }
  4657. if (++E.numberOfLocation > 1) {
  4658. E.error('目前不支持在一个页面多个编辑器上同时使用地图,可通过自定义菜单配置去掉地图菜单');
  4659. return;
  4660. }
  4661. var editor = this;
  4662. var config = editor.config;
  4663. var lang = config.lang;
  4664. var ak = config.mapAk;
  4665. // 地图的变量存储到这个地方
  4666. editor.mapData = {};
  4667. var mapData = editor.mapData;
  4668. // ---------- 地图事件 ----------
  4669. mapData.markers = [];
  4670. mapData.mapContainerId = 'map' + E.random();
  4671. mapData.clearLocations = function () {
  4672. var map = mapData.map;
  4673. if (!map) {
  4674. return;
  4675. }
  4676. map.clearOverlays();
  4677. //同时,清空marker数组
  4678. mapData.markers = [];
  4679. };
  4680. mapData.searchMap = function () {
  4681. var map = mapData.map;
  4682. if (!map) {
  4683. return;
  4684. }
  4685. var BMap = window.BMap;
  4686. var cityName = $cityInput.val();
  4687. var locationName = $searchInput.val();
  4688. var myGeo, marker;
  4689. if(cityName !== ''){
  4690. if(!locationName || locationName === ''){
  4691. map.centerAndZoom(cityName, 11);
  4692. }
  4693. //地址解析
  4694. if(locationName && locationName !== ''){
  4695. myGeo = new BMap.Geocoder();
  4696. // 将地址解析结果显示在地图上,并调整地图视野
  4697. myGeo.getPoint(locationName, function(point){
  4698. if (point) {
  4699. map.centerAndZoom(point, 13);
  4700. marker = new BMap.Marker(point);
  4701. map.addOverlay(marker);
  4702. marker.enableDragging(); //允许拖拽
  4703. mapData.markers.push(marker); //将marker加入到数组中
  4704. }else{
  4705. // alert('未找到');
  4706. map.centerAndZoom(cityName, 11); //找不到则重新定位到城市
  4707. }
  4708. }, cityName);
  4709. }
  4710. } // if(cityName !== '')
  4711. };
  4712. // load script 之后的 callback
  4713. var hasCallback = false;
  4714. window.baiduMapCallBack = function(){
  4715. // 避免重复加载
  4716. if (hasCallback) {
  4717. return;
  4718. } else {
  4719. hasCallback = true;
  4720. }
  4721. var BMap = window.BMap;
  4722. if (!mapData.map) {
  4723. // 创建Map实例
  4724. mapData.map = new BMap.Map(mapData.mapContainerId);
  4725. }
  4726. var map = mapData.map;
  4727. map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 初始化地图,设置中心点坐标和地图级别
  4728. map.addControl(new BMap.MapTypeControl()); //添加地图类型控件
  4729. map.setCurrentCity("北京"); // 设置地图显示的城市 此项是必须设置的
  4730. map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
  4731. //根据IP定位
  4732. function locationFun(result){
  4733. var cityName = result.name;
  4734. map.setCenter(cityName);
  4735. // 设置城市名称
  4736. $cityInput.val(cityName);
  4737. if (E.placeholder) {
  4738. $searchInput.focus();
  4739. }
  4740. var timeoutId, searchFn;
  4741. if (inputKeyup) {
  4742. // 并绑定搜索事件 - input 支持 keyup
  4743. searchFn = function (e) {
  4744. if (e.type === 'keyup' && e.keyCode === 13) {
  4745. e.preventDefault();
  4746. }
  4747. if (timeoutId) {
  4748. clearTimeout(timeoutId);
  4749. }
  4750. timeoutId = setTimeout(mapData.searchMap, 500);
  4751. };
  4752. $cityInput.on('keyup change paste', searchFn);
  4753. $searchInput.on('keyup change paste', searchFn);
  4754. } else {
  4755. // 并绑定搜索事件 - input 不支持 keyup
  4756. searchFn = function () {
  4757. if (!$content.is(':visible')) {
  4758. // panel 不显示了,就不用再监控了
  4759. clearTimeout(timeoutId);
  4760. return;
  4761. }
  4762. var currentCity = '';
  4763. var currentSearch = '';
  4764. var city = $cityInput.val();
  4765. var search = $searchInput.val();
  4766. if (city !== currentCity || search !== currentSearch) {
  4767. // 刚获取的数据和之前的数据不一致,执行查询
  4768. mapData.searchMap();
  4769. // 更新数据
  4770. currentCity = city;
  4771. currentSearch = search;
  4772. }
  4773. // 继续监控
  4774. if (timeoutId) {
  4775. clearTimeout(timeoutId);
  4776. }
  4777. timeoutId = setTimeout(searchFn, 1000);
  4778. };
  4779. // 开始监控
  4780. timeoutId = setTimeout(searchFn, 1000);
  4781. }
  4782. }
  4783. var myCity = new BMap.LocalCity();
  4784. myCity.get(locationFun);
  4785. //鼠标点击,创建位置
  4786. map.addEventListener("click", function(e){
  4787. var marker = new BMap.Marker(new BMap.Point(e.point.lng, e.point.lat));
  4788. map.addOverlay(marker);
  4789. marker.enableDragging();
  4790. mapData.markers.push(marker); //加入到数组中
  4791. }, false);
  4792. };
  4793. mapData.loadMapScript = function () {
  4794. var script = document.createElement("script");
  4795. script.type = "text/javascript";
  4796. script.src = "https://api.map.baidu.com/api?v=2.0&ak=" + ak + "&s=1&callback=baiduMapCallBack"; // baiduMapCallBack是一个本地函数
  4797. try {
  4798. // IE10- 报错
  4799. document.body.appendChild(script);
  4800. } catch (ex) {
  4801. E.error('加载地图过程中发生错误');
  4802. }
  4803. };
  4804. // 初始化地图
  4805. mapData.initMap = function () {
  4806. if (window.BMap) {
  4807. // 不是第一次,直接处理地图即可
  4808. window.baiduMapCallBack();
  4809. } else {
  4810. // 第一次,先加载地图 script,再处理地图(script加载完自动执行处理)
  4811. mapData.loadMapScript();
  4812. }
  4813. };
  4814. // ---------- 创建 menu 对象 ----------
  4815. var menu = new E.Menu({
  4816. editor: editor,
  4817. id: menuId,
  4818. title: lang.location
  4819. });
  4820. editor.menus[menuId] = menu;
  4821. // ---------- 构建UI ----------
  4822. // panel content
  4823. var $content = $('<div></div>');
  4824. // 搜索框
  4825. var $inputContainer = $('<div style="margin:10px 0;"></div>');
  4826. var $cityInput = $('<input type="text"/>');
  4827. $cityInput.css({
  4828. width: '80px',
  4829. 'text-align': 'center'
  4830. });
  4831. var $searchInput = $('<input type="text"/>');
  4832. $searchInput.css({
  4833. width: '300px',
  4834. 'margin-left': '10px'
  4835. }).attr('placeholder', lang.searchlocation);
  4836. var $clearBtn = $('<button class="right link">' + lang.clearLocation + '</button>');
  4837. $inputContainer.append($clearBtn)
  4838. .append($cityInput)
  4839. .append($searchInput);
  4840. $content.append($inputContainer);
  4841. // 清除位置按钮
  4842. $clearBtn.click(function (e) {
  4843. $searchInput.val('');
  4844. $searchInput.focus();
  4845. mapData.clearLocations();
  4846. e.preventDefault();
  4847. });
  4848. // 地图
  4849. var $map = $('<div id="' + mapData.mapContainerId + '"></div>');
  4850. $map.css({
  4851. height: '260px',
  4852. width: '100%',
  4853. position: 'relative',
  4854. 'margin-top': '10px',
  4855. border: '1px solid #f1f1f1'
  4856. });
  4857. var $mapLoading = $('<span>' + lang.loading + '</span>');
  4858. $mapLoading.css({
  4859. position: 'absolute',
  4860. width: '100px',
  4861. 'text-align': 'center',
  4862. top: '45%',
  4863. left: '50%',
  4864. 'margin-left': '-50px'
  4865. });
  4866. $map.append($mapLoading);
  4867. $content.append($map);
  4868. // 按钮
  4869. var $btnContainer = $('<div style="margin:10px 0;"></div>');
  4870. var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
  4871. var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  4872. var $checkLabel = $('<label style="display:inline-block;margin-top:10px;color:#666;"></label>');
  4873. var $check = $('<input type="checkbox">');
  4874. $checkLabel.append($check).append('<span style="display:inline-block;margin-left:5px;"> ' + lang.dynamicMap + '</span>');
  4875. $btnContainer.append($checkLabel)
  4876. .append($btnSubmit)
  4877. .append($btnCancel);
  4878. $content.append($btnContainer);
  4879. function callback() {
  4880. $searchInput.val('');
  4881. }
  4882. // 『取消』按钮事件
  4883. $btnCancel.click(function (e) {
  4884. e.preventDefault();
  4885. callback();
  4886. menu.dropPanel.hide();
  4887. });
  4888. // 『确定』按钮事件
  4889. $btnSubmit.click(function (e) {
  4890. e.preventDefault();
  4891. var map = mapData.map,
  4892. isDynamic = $check.is(':checked'),
  4893. markers = mapData.markers,
  4894. center = map.getCenter(),
  4895. centerLng = center.lng,
  4896. centerLat = center.lat,
  4897. zoom = map.getZoom(),
  4898. size = map.getSize(),
  4899. sizeWidth = size.width,
  4900. sizeHeight = size.height,
  4901. position,
  4902. src,
  4903. iframe;
  4904. if(isDynamic){
  4905. //动态地址
  4906. src = 'http://ueditor.baidu.com/ueditor/dialogs/map/show.html#';
  4907. }else{
  4908. //静态地址
  4909. src = 'http://api.map.baidu.com/staticimage?';
  4910. }
  4911. //src参数
  4912. src = src +'center=' + centerLng + ',' + centerLat +
  4913. '&zoom=' + zoom +
  4914. '&width=' + sizeWidth +
  4915. '&height=' + sizeHeight;
  4916. if(markers.length > 0){
  4917. src = src + '&markers=';
  4918. //添加所有的marker
  4919. $.each(markers, function(key, value){
  4920. position = value.getPosition();
  4921. if(key > 0){
  4922. src = src + '|';
  4923. }
  4924. src = src + position.lng + ',' + position.lat;
  4925. });
  4926. }
  4927. if(isDynamic){
  4928. if(markers.length > 1){
  4929. alert( lang.langDynamicOneLocation );
  4930. return;
  4931. }
  4932. src += '&markerStyles=l,A';
  4933. //插入iframe
  4934. iframe = '<iframe class="ueditor_baidumap" src="{src}" frameborder="0" width="' + sizeWidth + '" height="' + sizeHeight + '"></iframe>';
  4935. iframe = iframe.replace('{src}', src);
  4936. editor.command(e, 'insertHtml', iframe, callback);
  4937. }else{
  4938. //插入图片
  4939. editor.command(e, 'insertHtml', '<img style="max-width:100%;" src="' + src + '"/>', callback);
  4940. }
  4941. });
  4942. // 根据 UI 创建菜单 panel
  4943. menu.dropPanel = new E.DropPanel(editor, menu, {
  4944. $content: $content,
  4945. width: 500
  4946. });
  4947. // ---------- 事件 ----------
  4948. // render 时执行事件
  4949. menu.onRender = function () {
  4950. if (ak === E.baiduMapAk) {
  4951. E.warn('建议在配置中自定义百度地图的mapAk,否则可能影响地图功能,文档:' + E.docsite);
  4952. }
  4953. };
  4954. // click 事件
  4955. menu.clickEvent = function (e) {
  4956. var menu = this;
  4957. var dropPanel = menu.dropPanel;
  4958. var firstTime = false;
  4959. // -------------隐藏-------------
  4960. if (dropPanel.isShowing) {
  4961. dropPanel.hide();
  4962. return;
  4963. }
  4964. // -------------显示-------------
  4965. if (!mapData.map) {
  4966. // 第一次,先加载地图
  4967. firstTime = true;
  4968. }
  4969. dropPanel.show();
  4970. mapData.initMap();
  4971. if (!firstTime) {
  4972. $searchInput.focus();
  4973. }
  4974. };
  4975. });
  4976. });
  4977. // insertcode 菜单
  4978. _e(function (E, $) {
  4979. // 加载 highlightjs 代码
  4980. function loadHljs() {
  4981. if (E.userAgent.indexOf('MSIE 8') > 0) {
  4982. // 不支持 IE8
  4983. return;
  4984. }
  4985. if (window.hljs) {
  4986. // 不要重复加载
  4987. return;
  4988. }
  4989. var script = document.createElement("script");
  4990. script.type = "text/javascript";
  4991. script.src = "//cdn.bootcss.com/highlight.js/9.2.0/highlight.min.js";
  4992. document.body.appendChild(script);
  4993. }
  4994. E.createMenu(function (check) {
  4995. var menuId = 'insertcode';
  4996. if (!check(menuId)) {
  4997. return;
  4998. }
  4999. // 加载 highlightjs 代码
  5000. setTimeout(loadHljs, 0);
  5001. var editor = this;
  5002. var config = editor.config;
  5003. var lang = config.lang;
  5004. var $txt = editor.txt.$txt;
  5005. // 创建 menu 对象
  5006. var menu = new E.Menu({
  5007. editor: editor,
  5008. id: menuId,
  5009. title: lang.insertcode
  5010. });
  5011. // click 事件
  5012. menu.clickEvent = function (e) {
  5013. var menu = this;
  5014. var dropPanel = menu.dropPanel;
  5015. // 隐藏
  5016. if (dropPanel.isShowing) {
  5017. dropPanel.hide();
  5018. return;
  5019. }
  5020. // 显示
  5021. $textarea.val('');
  5022. dropPanel.show();
  5023. // highlightjs 语言列表
  5024. var hljs = window.hljs;
  5025. if (hljs && hljs.listLanguages) {
  5026. if ($langSelect.children().length !== 0) {
  5027. return;
  5028. }
  5029. $langSelect.css({
  5030. 'margin-top': '9px',
  5031. 'margin-left': '5px'
  5032. });
  5033. $.each(hljs.listLanguages(), function (key, lang) {
  5034. if (lang === 'xml') {
  5035. lang = 'html';
  5036. }
  5037. if (lang === config.codeDefaultLang) {
  5038. $langSelect.append('<option value="' + lang + '" selected="selected">' + lang + '</option>');
  5039. } else {
  5040. $langSelect.append('<option value="' + lang + '">' + lang + '</option>');
  5041. }
  5042. });
  5043. } else {
  5044. $langSelect.hide();
  5045. }
  5046. };
  5047. // 选中状态下的 click 事件
  5048. menu.clickEventSelected = function (e) {
  5049. var menu = this;
  5050. var dropPanel = menu.dropPanel;
  5051. // 隐藏
  5052. if (dropPanel.isShowing) {
  5053. dropPanel.hide();
  5054. return;
  5055. }
  5056. // 显示
  5057. dropPanel.show();
  5058. var rangeElem = editor.getRangeElem();
  5059. var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
  5060. var $targetElem;
  5061. var className;
  5062. if (targetElem) {
  5063. // 确定找到 pre 之后,再找 code
  5064. targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
  5065. }
  5066. if (!targetElem) {
  5067. return;
  5068. }
  5069. $targetElem = $(targetElem);
  5070. // 赋值内容
  5071. $textarea.val($targetElem.text());
  5072. if ($langSelect) {
  5073. // 赋值语言
  5074. className = $targetElem.attr('class');
  5075. if (className) {
  5076. $langSelect.val(className.split(' ')[0]);
  5077. }
  5078. }
  5079. };
  5080. // 定义更新选中状态的事件
  5081. menu.updateSelectedEvent = function () {
  5082. var self = this; //菜单对象
  5083. var editor = self.editor;
  5084. var rangeElem;
  5085. rangeElem = editor.getRangeElem();
  5086. rangeElem = editor.getSelfOrParentByName(rangeElem, 'pre');
  5087. if (rangeElem) {
  5088. return true;
  5089. }
  5090. return false;
  5091. };
  5092. // 创建 panel
  5093. var $content = $('<div></div>');
  5094. var $textarea = $('<textarea></textarea>');
  5095. var $langSelect = $('<select></select>');
  5096. contentHandle($content);
  5097. menu.dropPanel = new E.DropPanel(editor, menu, {
  5098. $content: $content,
  5099. width: 500
  5100. });
  5101. // 增加到editor对象中
  5102. editor.menus[menuId] = menu;
  5103. // ------ 增加 content 内容 ------
  5104. function contentHandle($content) {
  5105. // textarea 区域
  5106. var $textareaContainer = $('<div></div>');
  5107. $textareaContainer.css({
  5108. margin: '15px 5px 5px 5px',
  5109. height: '160px',
  5110. 'text-align': 'center'
  5111. });
  5112. $textarea.css({
  5113. width: '100%',
  5114. height: '100%',
  5115. padding: '10px'
  5116. });
  5117. $textarea.on('keydown', function (e) {
  5118. // 取消 tab 键默认行为
  5119. if (e.keyCode === 9) {
  5120. e.preventDefault();
  5121. }
  5122. });
  5123. $textareaContainer.append($textarea);
  5124. $content.append($textareaContainer);
  5125. // 按钮区域
  5126. var $btnContainer = $('<div></div>');
  5127. var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
  5128. var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  5129. $btnContainer.append($btnSubmit).append($btnCancel).append($langSelect);
  5130. $content.append($btnContainer);
  5131. // 取消按钮
  5132. $btnCancel.click(function (e) {
  5133. e.preventDefault();
  5134. menu.dropPanel.hide();
  5135. });
  5136. // 确定按钮
  5137. var codeTpl = '<pre style="max-width:100%;overflow-x:auto;"><code{#langClass}>{#content}</code></pre>';
  5138. $btnSubmit.click(function (e) {
  5139. e.preventDefault();
  5140. var val = $textarea.val();
  5141. if (!val) {
  5142. // 无内容
  5143. $textarea.focus();
  5144. return;
  5145. }
  5146. var rangeElem = editor.getRangeElem();
  5147. if ($.trim($(rangeElem).text()) && codeTpl.indexOf('<p><br></p>') !== 0) {
  5148. codeTpl = '<p><br></p>' + codeTpl;
  5149. }
  5150. var lang = $langSelect ? $langSelect.val() : ''; // 获取高亮语言
  5151. var langClass = '';
  5152. var doHightlight = function () {
  5153. $txt.find('pre code').each(function (i, block) {
  5154. var $block = $(block);
  5155. if ($block.attr('codemark')) {
  5156. // 有 codemark 标记的代码块,就不再重新格式化了
  5157. return;
  5158. } else if (window.hljs) {
  5159. // 新代码块,格式化之后,立即标记 codemark
  5160. window.hljs.highlightBlock(block);
  5161. $block.attr('codemark', '1');
  5162. }
  5163. });
  5164. };
  5165. // 语言高亮样式
  5166. if (lang) {
  5167. langClass = ' class="' + lang + ' hljs"';
  5168. }
  5169. // 替换标签
  5170. val = val.replace(/&/gm, '&amp;')
  5171. .replace(/</gm, '&lt;')
  5172. .replace(/>/gm, '&gt;');
  5173. // ---- menu 未选中状态 ----
  5174. if (!menu.selected) {
  5175. // 拼接html
  5176. var html = codeTpl.replace('{#langClass}', langClass).replace('{#content}', val);
  5177. editor.command(e, 'insertHtml', html, doHightlight);
  5178. return;
  5179. }
  5180. // ---- menu 选中状态 ----
  5181. var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
  5182. var $targetElem;
  5183. if (targetElem) {
  5184. // 确定找到 pre 之后,再找 code
  5185. targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
  5186. }
  5187. if (!targetElem) {
  5188. return;
  5189. }
  5190. $targetElem = $(targetElem);
  5191. function commandFn() {
  5192. var className;
  5193. if (lang) {
  5194. className = $targetElem.attr('class');
  5195. if (className !== lang + ' hljs') {
  5196. $targetElem.attr('class', lang + ' hljs');
  5197. }
  5198. }
  5199. $targetElem.html(val);
  5200. }
  5201. function callback() {
  5202. editor.restoreSelectionByElem(targetElem);
  5203. doHightlight();
  5204. }
  5205. editor.customCommand(e, commandFn, callback);
  5206. });
  5207. }
  5208. // ------ enter 时,不另起标签,只换行 ------
  5209. $txt.on('keydown', function (e) {
  5210. if (e.keyCode !== 13) {
  5211. return;
  5212. }
  5213. var rangeElem = editor.getRangeElem();
  5214. var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
  5215. if (!targetElem) {
  5216. return;
  5217. }
  5218. editor.command(e, 'insertHtml', '\n');
  5219. });
  5220. // ------ 点击时,禁用其他标签 ------
  5221. function updateMenu() {
  5222. var rangeElem = editor.getRangeElem();
  5223. var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
  5224. if (targetElem) {
  5225. // 在 code 之内,禁用其他菜单
  5226. editor.disableMenusExcept('insertcode');
  5227. } else {
  5228. // 不是在 code 之内,启用其他菜单
  5229. editor.enableMenusExcept('insertcode');
  5230. }
  5231. }
  5232. $txt.on('keydown click', function (e) {
  5233. // 此处必须使用 setTimeout 异步处理,否则不对
  5234. setTimeout(updateMenu);
  5235. });
  5236. });
  5237. });
  5238. // undo 菜单
  5239. _e(function (E, $) {
  5240. E.createMenu(function (check) {
  5241. var menuId = 'undo';
  5242. if (!check(menuId)) {
  5243. return;
  5244. }
  5245. var editor = this;
  5246. var lang = editor.config.lang;
  5247. // 创建 menu 对象
  5248. var menu = new E.Menu({
  5249. editor: editor,
  5250. id: menuId,
  5251. title: lang.undo
  5252. });
  5253. // click 事件
  5254. menu.clickEvent = function (e) {
  5255. editor.undo();
  5256. };
  5257. // 增加到editor对象中
  5258. editor.menus[menuId] = menu;
  5259. // ------------ 初始化时、enter 时、打字中断时,做记录 ------------
  5260. // ------------ ctrl + z 是调用记录撤销,而不是使用浏览器默认的撤销 ------------
  5261. editor.ready(function () {
  5262. var editor = this;
  5263. var $txt = editor.txt.$txt;
  5264. var timeoutId;
  5265. // 执行undo记录
  5266. function undo() {
  5267. editor.undoRecord();
  5268. }
  5269. $txt.on('keydown', function (e) {
  5270. var keyCode = e.keyCode;
  5271. // 撤销 ctrl + z
  5272. if (e.ctrlKey && keyCode === 90) {
  5273. editor.undo();
  5274. return;
  5275. }
  5276. if (keyCode === 13) {
  5277. // enter 做记录
  5278. undo();
  5279. } else {
  5280. // keyup 之后 1s 之内不操作,则做一次记录
  5281. if (timeoutId) {
  5282. clearTimeout(timeoutId);
  5283. }
  5284. timeoutId = setTimeout(undo, 1000);
  5285. }
  5286. });
  5287. // 初始化做记录
  5288. editor.undoRecord();
  5289. });
  5290. });
  5291. });
  5292. // redo 菜单
  5293. _e(function (E, $) {
  5294. E.createMenu(function (check) {
  5295. var menuId = 'redo';
  5296. if (!check(menuId)) {
  5297. return;
  5298. }
  5299. var editor = this;
  5300. var lang = editor.config.lang;
  5301. // 创建 menu 对象
  5302. var menu = new E.Menu({
  5303. editor: editor,
  5304. id: menuId,
  5305. title: lang.redo
  5306. });
  5307. // click 事件
  5308. menu.clickEvent = function (e) {
  5309. editor.redo();
  5310. };
  5311. // 增加到editor对象中
  5312. editor.menus[menuId] = menu;
  5313. });
  5314. });
  5315. // 全屏 菜单
  5316. _e(function (E, $) {
  5317. // 记录全屏时的scrollTop
  5318. var scrollTopWhenFullScreen;
  5319. E.createMenu(function (check) {
  5320. var menuId = 'fullscreen';
  5321. if (!check(menuId)) {
  5322. return;
  5323. }
  5324. var editor = this;
  5325. var $txt = editor.txt.$txt;
  5326. var config = editor.config;
  5327. var zIndexConfig = config.zindex || 10000;
  5328. var lang = config.lang;
  5329. var isSelected = false;
  5330. var zIndex;
  5331. var maxHeight;
  5332. // 创建 menu 对象
  5333. var menu = new E.Menu({
  5334. editor: editor,
  5335. id: menuId,
  5336. title: lang.fullscreen
  5337. });
  5338. // 定义click事件
  5339. menu.clickEvent = function (e) {
  5340. // 增加样式
  5341. var $editorContainer = editor.$editorContainer;
  5342. $editorContainer.addClass('wangEditor-fullscreen');
  5343. // (先保存当前的)再设置z-index
  5344. zIndex = $editorContainer.css('z-index');
  5345. $editorContainer.css('z-index', zIndexConfig);
  5346. var $wrapper;
  5347. var txtHeight = $txt.height();
  5348. var txtOuterHeight = $txt.outerHeight();
  5349. if (editor.useMaxHeight) {
  5350. // 记录 max-height,并暂时去掉maxheight
  5351. maxHeight = $txt.css('max-height');
  5352. $txt.css('max-height', 'none');
  5353. // 如果使用了maxHeight, 将$txt从它的父元素中移出来
  5354. $wrapper = $txt.parent();
  5355. $wrapper.after($txt);
  5356. $wrapper.remove();
  5357. $txt.css('overflow-y', 'auto');
  5358. }
  5359. // 设置高度到全屏
  5360. var menuContainer = editor.menuContainer;
  5361. $txt.height(
  5362. E.$window.height() -
  5363. menuContainer.height() -
  5364. (txtOuterHeight - txtHeight) // 去掉内边距和外边距
  5365. );
  5366. // 取消menuContainer的内联样式(menu吸顶时,会为 menuContainer 设置一些内联样式)
  5367. editor.menuContainer.$menuContainer.attr('style', '');
  5368. // 保存状态
  5369. isSelected = true;
  5370. // 记录编辑器是否全屏
  5371. editor.isFullScreen = true;
  5372. // 记录设置全屏时的高度
  5373. scrollTopWhenFullScreen = E.$window.scrollTop();
  5374. };
  5375. // 定义选中状态的 click 事件
  5376. menu.clickEventSelected = function (e) {
  5377. // 取消样式
  5378. var $editorContainer = editor.$editorContainer;
  5379. $editorContainer.removeClass('wangEditor-fullscreen');
  5380. $editorContainer.css('z-index', zIndex);
  5381. // 还原height
  5382. if (editor.useMaxHeight) {
  5383. $txt.css('max-height', maxHeight);
  5384. } else {
  5385. // editor.valueContainerHeight 在 editor.txt.initHeight() 中事先保存了
  5386. editor.$valueContainer.css('height', editor.valueContainerHeight);
  5387. }
  5388. // 重新计算高度
  5389. editor.txt.initHeight();
  5390. // 保存状态
  5391. isSelected = false;
  5392. // 记录编辑器是否全屏
  5393. editor.isFullScreen = false;
  5394. // 还原scrollTop
  5395. if (scrollTopWhenFullScreen != null) {
  5396. E.$window.scrollTop(scrollTopWhenFullScreen);
  5397. }
  5398. };
  5399. // 定义选中事件
  5400. menu.updateSelectedEvent = function (e) {
  5401. return isSelected;
  5402. };
  5403. // 增加到editor对象中
  5404. editor.menus[menuId] = menu;
  5405. });
  5406. });
  5407. // 渲染menus
  5408. _e(function (E, $) {
  5409. E.fn.renderMenus = function () {
  5410. var editor = this;
  5411. var menus = editor.menus;
  5412. var menuIds = editor.config.menus;
  5413. var menuContainer = editor.menuContainer;
  5414. var menu;
  5415. var groupIdx = 0;
  5416. $.each(menuIds, function (k, v) {
  5417. if (v === '|') {
  5418. groupIdx++;
  5419. return;
  5420. }
  5421. menu = menus[v];
  5422. if (menu) {
  5423. menu.render(groupIdx);
  5424. }
  5425. });
  5426. };
  5427. });
  5428. // 渲染menus
  5429. _e(function (E, $) {
  5430. E.fn.renderMenuContainer = function () {
  5431. var editor = this;
  5432. var menuContainer = editor.menuContainer;
  5433. var $editorContainer = editor.$editorContainer;
  5434. menuContainer.render();
  5435. };
  5436. });
  5437. // 渲染 txt
  5438. _e(function (E, $) {
  5439. E.fn.renderTxt = function () {
  5440. var editor = this;
  5441. var txt = editor.txt;
  5442. txt.render();
  5443. // ready 时候,计算txt的高度
  5444. editor.ready(function () {
  5445. txt.initHeight();
  5446. });
  5447. };
  5448. });
  5449. // 渲染 container
  5450. _e(function (E, $) {
  5451. E.fn.renderEditorContainer = function () {
  5452. var editor = this;
  5453. var $valueContainer = editor.$valueContainer;
  5454. var $editorContainer = editor.$editorContainer;
  5455. var $txt = editor.txt.$txt;
  5456. var $prev, $parent;
  5457. // 将编辑器渲染到页面中
  5458. if ($valueContainer === $txt) {
  5459. $prev = editor.$prev;
  5460. $parent = editor.$parent;
  5461. if ($prev && $prev.length) {
  5462. // 有前置节点,就插入到前置节点的后面
  5463. $prev.after($editorContainer);
  5464. } else {
  5465. // 没有前置节点,就直接插入到父元素
  5466. $parent.prepend($editorContainer);
  5467. }
  5468. } else {
  5469. $valueContainer.after($editorContainer);
  5470. $valueContainer.hide();
  5471. }
  5472. // 设置宽度(这样设置宽度有问题)
  5473. // $editorContainer.css('width', $valueContainer.css('width'));
  5474. };
  5475. });
  5476. // 菜单事件
  5477. _e(function (E, $) {
  5478. // 绑定每个菜单的click事件
  5479. E.fn.eventMenus = function () {
  5480. var menus = this.menus;
  5481. // 绑定菜单的点击事件
  5482. $.each(menus, function (k, v) {
  5483. v.bindEvent();
  5484. });
  5485. };
  5486. });
  5487. // 菜单container事件
  5488. _e(function (E, $) {
  5489. E.fn.eventMenuContainer = function () {
  5490. };
  5491. });
  5492. // 编辑区域事件
  5493. _e(function (E, $) {
  5494. E.fn.eventTxt = function () {
  5495. var txt = this.txt;
  5496. // txt内容变化时,保存选区
  5497. txt.saveSelectionEvent();
  5498. // txt内容变化时,随时更新 value
  5499. txt.updateValueEvent();
  5500. // txt内容变化时,随时更新 menu style
  5501. txt.updateMenuStyleEvent();
  5502. // // 鼠标hover时,显示 p head 高度(暂时关闭这个功能)
  5503. // if (!/ie/i.test(E.userAgent)) {
  5504. // // 暂时不支持IE
  5505. // txt.showHeightOnHover();
  5506. // }
  5507. };
  5508. });
  5509. // 上传图片事件
  5510. _e(function (E, $) {
  5511. E.plugin(function () {
  5512. var editor = this;
  5513. var fns = editor.config.uploadImgFns; // editor.config.uploadImgFns = {} 在config文件中定义了
  5514. // -------- 定义load函数 --------
  5515. fns.onload || (fns.onload = function (resultText, xhr) {
  5516. E.log('上传结束,返回结果为 ' + resultText);
  5517. var editor = this;
  5518. var originalName = editor.uploadImgOriginalName || ''; // 上传图片时,已经将图片的名字存在 editor.uploadImgOriginalName
  5519. var img;
  5520. if (resultText.indexOf('error|') === 0) {
  5521. // 提示错误
  5522. E.warn('上传失败:' + resultText.split('|')[1]);
  5523. alert(resultText.split('|')[1]);
  5524. } else {
  5525. E.log('上传成功,即将插入编辑区域,结果为:' + resultText);
  5526. // 将结果插入编辑器
  5527. img = document.createElement('img');
  5528. img.onload = function () {
  5529. var html = '<img src="' + resultText + '" alt="' + originalName + '" style="max-width:100%;"/>';
  5530. editor.command(null, 'insertHtml', html);
  5531. E.log('已插入图片,地址 ' + resultText);
  5532. img = null;
  5533. };
  5534. img.onerror = function () {
  5535. E.error('使用返回的结果获取图片,发生错误。请确认以下结果是否正确:' + resultText);
  5536. img = null;
  5537. };
  5538. img.src = resultText;
  5539. }
  5540. });
  5541. // -------- 定义tiemout函数 --------
  5542. fns.ontimeout || (fns.ontimeout = function (xhr) {
  5543. E.error('上传图片超时');
  5544. alert('上传图片超时');
  5545. });
  5546. // -------- 定义error函数 --------
  5547. fns.onerror || (fns.onerror = function (xhr) {
  5548. E.error('上传上图片发生错误');
  5549. alert('上传上图片发生错误');
  5550. });
  5551. });
  5552. });
  5553. // xhr 上传图片
  5554. _e(function (E, $) {
  5555. if (!window.FileReader || !window.FormData) {
  5556. // 如果不支持html5的文档操作,直接返回
  5557. return;
  5558. }
  5559. E.plugin(function () {
  5560. var editor = this;
  5561. var config = editor.config;
  5562. var uploadImgUrl = config.uploadImgUrl;
  5563. var uploadTimeout = config.uploadTimeout;
  5564. // 获取配置中的上传事件
  5565. var uploadImgFns = config.uploadImgFns;
  5566. var onload = uploadImgFns.onload;
  5567. var ontimeout = uploadImgFns.ontimeout;
  5568. var onerror = uploadImgFns.onerror;
  5569. if (!uploadImgUrl) {
  5570. return;
  5571. }
  5572. // -------- 将以base64的图片url数据转换为Blob --------
  5573. function convertBase64UrlToBlob(urlData, filetype){
  5574. //去掉url的头,并转换为byte
  5575. var bytes = window.atob(urlData.split(',')[1]);
  5576. //处理异常,将ascii码小于0的转换为大于0
  5577. var ab = new ArrayBuffer(bytes.length);
  5578. var ia = new Uint8Array(ab);
  5579. var i;
  5580. for (i = 0; i < bytes.length; i++) {
  5581. ia[i] = bytes.charCodeAt(i);
  5582. }
  5583. return new Blob([ab], {type : filetype});
  5584. }
  5585. // -------- 插入图片的方法 --------
  5586. function insertImg(src, event) {
  5587. var img = document.createElement('img');
  5588. img.onload = function () {
  5589. var html = '<img src="' + src + '" style="max-width:100%;"/>';
  5590. editor.command(event, 'insertHtml', html);
  5591. E.log('已插入图片,地址 ' + src);
  5592. img = null;
  5593. };
  5594. img.onerror = function () {
  5595. E.error('使用返回的结果获取图片,发生错误。请确认以下结果是否正确:' + src);
  5596. img = null;
  5597. };
  5598. img.src = src;
  5599. }
  5600. // -------- onprogress 事件 --------
  5601. function updateProgress(e) {
  5602. if (e.lengthComputable) {
  5603. var percentComplete = e.loaded / e.total;
  5604. editor.showUploadProgress(percentComplete * 100);
  5605. }
  5606. }
  5607. // -------- xhr 上传图片 --------
  5608. editor.xhrUploadImg = function (opt) {
  5609. // opt 数据
  5610. var event = opt.event;
  5611. var fileName = opt.filename || '';
  5612. var base64 = opt.base64;
  5613. var fileType = opt.fileType || 'image/png'; // 无扩展名则默认使用 png
  5614. var name = opt.name || 'wangEditor_upload_file';
  5615. var loadfn = opt.loadfn || onload;
  5616. var errorfn = opt.errorfn || onerror;
  5617. var timeoutfn = opt.timeoutfn || ontimeout;
  5618. // 上传参数(如 token)
  5619. var params = editor.config.uploadParams || {};
  5620. // headers
  5621. var headers = editor.config.uploadHeaders || {};
  5622. // 获取文件扩展名
  5623. var fileExt = 'png'; // 默认为 png
  5624. if (fileName.indexOf('.') > 0) {
  5625. // 原来的文件名有扩展名
  5626. fileExt = fileName.slice(fileName.lastIndexOf('.') - fileName.length + 1);
  5627. } else if (fileType.indexOf('/') > 0 && fileType.split('/')[1]) {
  5628. // 文件名没有扩展名,通过类型获取,如从 'image/png' 取 'png'
  5629. fileExt = fileType.split('/')[1];
  5630. }
  5631. // ------------ begin 预览模拟上传 ------------
  5632. if (E.isOnWebsite) {
  5633. E.log('预览模拟上传');
  5634. insertImg(base64, event);
  5635. return;
  5636. }
  5637. // ------------ end 预览模拟上传 ------------
  5638. // 变量声明
  5639. var xhr = new XMLHttpRequest();
  5640. var timeoutId;
  5641. var src;
  5642. var formData = new FormData();
  5643. // 超时处理
  5644. function timeoutCallback() {
  5645. if (timeoutId) {
  5646. clearTimeout(timeoutId);
  5647. }
  5648. if (xhr && xhr.abort) {
  5649. xhr.abort();
  5650. }
  5651. // 超时了就阻止默认行为
  5652. event.preventDefault();
  5653. // 执行回调函数,提示什么内容,都应该在回调函数中定义
  5654. timeoutfn && timeoutfn.call(editor, xhr);
  5655. // 隐藏进度条
  5656. editor.hideUploadProgress();
  5657. }
  5658. xhr.onload = function () {
  5659. if (timeoutId) {
  5660. clearTimeout(timeoutId);
  5661. }
  5662. // 记录文件名到 editor.uploadImgOriginalName ,插入图片时,可做 alt 属性用
  5663. editor.uploadImgOriginalName = fileName;
  5664. if (fileName.indexOf('.') > 0) {
  5665. editor.uploadImgOriginalName = fileName.split('.')[0];
  5666. }
  5667. // 执行load函数,任何操作,都应该在load函数中定义
  5668. loadfn && loadfn.call(editor, xhr.responseText, xhr);
  5669. // 隐藏进度条
  5670. editor.hideUploadProgress();
  5671. };
  5672. xhr.onerror = function () {
  5673. if (timeoutId) {
  5674. clearTimeout(timeoutId);
  5675. }
  5676. // 超时了就阻止默认行为
  5677. event.preventDefault();
  5678. // 执行error函数,错误提示,应该在error函数中定义
  5679. errorfn && errorfn.call(editor, xhr);
  5680. // 隐藏进度条
  5681. editor.hideUploadProgress();
  5682. };
  5683. // xhr.onprogress = updateProgress;
  5684. xhr.upload.onprogress = updateProgress;
  5685. // 填充数据
  5686. formData.append(name, convertBase64UrlToBlob(base64, fileType), E.random() + '.' + fileExt);
  5687. // 添加参数
  5688. $.each(params, function (key, value) {
  5689. formData.append(key, value);
  5690. });
  5691. // 开始上传
  5692. xhr.open('POST', uploadImgUrl, true);
  5693. // xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // 将参数解析成传统form的方式上传
  5694. // 修改自定义配置的headers
  5695. $.each(headers, function (key, value) {
  5696. xhr.setRequestHeader(key, value);
  5697. });
  5698. // 跨域上传时,传cookie
  5699. xhr.withCredentials = true;
  5700. // 发送数据
  5701. xhr.send(formData);
  5702. timeoutId = setTimeout(timeoutCallback, uploadTimeout);
  5703. E.log('开始上传...并开始超时计算');
  5704. };
  5705. });
  5706. });
  5707. // 进度条
  5708. _e(function (E, $) {
  5709. E.plugin(function () {
  5710. var editor = this;
  5711. var menuContainer = editor.menuContainer;
  5712. var menuHeight = menuContainer.height();
  5713. var $editorContainer = editor.$editorContainer;
  5714. var width = $editorContainer.width();
  5715. var $progress = $('<div class="wangEditor-upload-progress"></div>');
  5716. // 渲染事件
  5717. var isRender = false;
  5718. function render() {
  5719. if (isRender) {
  5720. return;
  5721. }
  5722. isRender = true;
  5723. $progress.css({
  5724. top: menuHeight + 'px'
  5725. });
  5726. $editorContainer.append($progress);
  5727. }
  5728. // ------ 显示进度 ------
  5729. editor.showUploadProgress = function (progress) {
  5730. if (timeoutId) {
  5731. clearTimeout(timeoutId);
  5732. }
  5733. // 显示之前,先判断是否渲染
  5734. render();
  5735. $progress.show();
  5736. $progress.width(progress * width / 100);
  5737. };
  5738. // ------ 隐藏进度条 ------
  5739. var timeoutId;
  5740. function hideProgress() {
  5741. $progress.hide();
  5742. timeoutId = null;
  5743. }
  5744. editor.hideUploadProgress = function (time) {
  5745. if (timeoutId) {
  5746. clearTimeout(timeoutId);
  5747. }
  5748. time = time || 750;
  5749. timeoutId = setTimeout(hideProgress, time);
  5750. };
  5751. });
  5752. });
  5753. // upload img 插件
  5754. _e(function (E, $) {
  5755. E.plugin(function () {
  5756. var editor = this;
  5757. var config = editor.config;
  5758. var uploadImgUrl = config.uploadImgUrl;
  5759. var uploadTimeout = config.uploadTimeout;
  5760. var event;
  5761. if (!uploadImgUrl) {
  5762. return;
  5763. }
  5764. // 获取editor的上传dom
  5765. var $uploadContent = editor.$uploadContent;
  5766. if (!$uploadContent) {
  5767. return;
  5768. }
  5769. // 自定义UI,并添加到上传dom节点上
  5770. var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
  5771. $uploadContent.append($uploadIcon);
  5772. // ---------- 构建上传对象 ----------
  5773. var upfile = new E.UploadFile({
  5774. editor: editor,
  5775. uploadUrl: uploadImgUrl,
  5776. timeout: uploadTimeout,
  5777. fileAccept: 'image/jpg,image/jpeg,image/png,image/gif,image/bmp' // 只允许选择图片
  5778. });
  5779. // 选择本地文件,上传
  5780. $uploadIcon.click(function (e) {
  5781. event = e;
  5782. upfile.selectFiles();
  5783. });
  5784. });
  5785. });
  5786. // h5 方式上传图片
  5787. _e(function (E, $) {
  5788. if (!window.FileReader || !window.FormData) {
  5789. // 如果不支持html5的文档操作,直接返回
  5790. return;
  5791. }
  5792. // 构造函数
  5793. var UploadFile = function (opt) {
  5794. this.editor = opt.editor;
  5795. this.uploadUrl = opt.uploadUrl;
  5796. this.timeout = opt.timeout;
  5797. this.fileAccept = opt.fileAccept;
  5798. this.multiple = true;
  5799. };
  5800. UploadFile.fn = UploadFile.prototype;
  5801. // clear
  5802. UploadFile.fn.clear = function () {
  5803. this.$input.val('');
  5804. E.log('input value 已清空');
  5805. };
  5806. // 渲染
  5807. UploadFile.fn.render = function () {
  5808. var self = this;
  5809. if (self._hasRender) {
  5810. // 不要重复渲染
  5811. return;
  5812. }
  5813. E.log('渲染dom');
  5814. var fileAccept = self.fileAccept;
  5815. var acceptTpl = fileAccept ? 'accept="' + fileAccept + '"' : '';
  5816. var multiple = self.multiple;
  5817. var multipleTpl = multiple ? 'multiple="multiple"' : '';
  5818. var $input = $('<input type="file" ' + acceptTpl + ' ' + multipleTpl + '/>');
  5819. var $container = $('<div style="visibility:hidden;"></div>');
  5820. $container.append($input);
  5821. E.$body.append($container);
  5822. // onchange 事件
  5823. $input.on('change', function (e) {
  5824. self.selected(e, $input.get(0));
  5825. });
  5826. // 记录对象数据
  5827. self.$input = $input;
  5828. // 记录
  5829. self._hasRender = true;
  5830. };
  5831. // 选择
  5832. UploadFile.fn.selectFiles = function () {
  5833. var self = this;
  5834. E.log('使用 html5 方式上传');
  5835. // 先渲染
  5836. self.render();
  5837. // 选择
  5838. E.log('选择文件');
  5839. self.$input.click();
  5840. };
  5841. // 选中文件之后
  5842. UploadFile.fn.selected = function (e, input) {
  5843. var self = this;
  5844. var files = input.files || [];
  5845. if (files.length === 0) {
  5846. return;
  5847. }
  5848. E.log('选中 ' + files.length + ' 个文件');
  5849. // 遍历选中的文件,预览、上传
  5850. $.each(files, function (key, value) {
  5851. self.upload(value);
  5852. });
  5853. };
  5854. // 上传单个文件
  5855. UploadFile.fn.upload = function (file) {
  5856. var self = this;
  5857. var editor = self.editor;
  5858. var filename = file.name || '';
  5859. var fileType = file.type || '';
  5860. var uploadImgFns = editor.config.uploadImgFns;
  5861. var uploadFileName = editor.config.uploadImgFileName || 'wangEditorH5File';
  5862. var onload = uploadImgFns.onload;
  5863. var ontimeout = uploadImgFns.ontimeout;
  5864. var onerror = uploadImgFns.onerror;
  5865. var reader = new FileReader();
  5866. if (!onload || !ontimeout || !onerror) {
  5867. E.error('请为编辑器配置上传图片的 onload ontimeout onerror 回调事件');
  5868. return;
  5869. }
  5870. E.log('开始执行 ' + filename + ' 文件的上传');
  5871. // 清空 input 数据
  5872. function clearInput() {
  5873. self.clear();
  5874. }
  5875. // onload事件
  5876. reader.onload = function (e) {
  5877. E.log('已读取' + filename + '文件');
  5878. var base64 = e.target.result || this.result;
  5879. editor.xhrUploadImg({
  5880. event: e,
  5881. filename: filename,
  5882. base64: base64,
  5883. fileType: fileType,
  5884. name: uploadFileName,
  5885. loadfn: function (resultText, xhr) {
  5886. clearInput();
  5887. // 执行配置中的方法
  5888. var editor = this;
  5889. onload.call(editor, resultText, xhr);
  5890. },
  5891. errorfn: function (xhr) {
  5892. clearInput();
  5893. if (E.isOnWebsite) {
  5894. alert('wangEditor官网暂时没有服务端,因此报错。实际项目中不会发生');
  5895. }
  5896. // 执行配置中的方法
  5897. var editor = this;
  5898. onerror.call(editor, xhr);
  5899. },
  5900. timeoutfn: function (xhr) {
  5901. clearInput();
  5902. if (E.isOnWebsite) {
  5903. alert('wangEditor官网暂时没有服务端,因此超时。实际项目中不会发生');
  5904. }
  5905. // 执行配置中的方法
  5906. var editor = this;
  5907. ontimeout(editor, xhr);
  5908. }
  5909. });
  5910. };
  5911. // 开始取文件
  5912. reader.readAsDataURL(file);
  5913. };
  5914. // 暴露给 E
  5915. E.UploadFile = UploadFile;
  5916. });
  5917. // form方式上传图片
  5918. _e(function (E, $) {
  5919. if (window.FileReader && window.FormData) {
  5920. // 如果支持 html5 上传,则返回
  5921. return;
  5922. }
  5923. // 构造函数
  5924. var UploadFile = function (opt) {
  5925. this.editor = opt.editor;
  5926. this.uploadUrl = opt.uploadUrl;
  5927. this.timeout = opt.timeout;
  5928. this.fileAccept = opt.fileAccept;
  5929. this.multiple = false;
  5930. };
  5931. UploadFile.fn = UploadFile.prototype;
  5932. // clear
  5933. UploadFile.fn.clear = function () {
  5934. this.$input.val('');
  5935. E.log('input value 已清空');
  5936. };
  5937. // 隐藏modal
  5938. UploadFile.fn.hideModal = function () {
  5939. this.modal.hide();
  5940. };
  5941. // 渲染
  5942. UploadFile.fn.render = function () {
  5943. var self = this;
  5944. var editor = self.editor;
  5945. var uploadFileName = editor.config.uploadImgFileName || 'wangEditorFormFile';
  5946. if (self._hasRender) {
  5947. // 不要重复渲染
  5948. return;
  5949. }
  5950. // 服务器端路径
  5951. var uploadUrl = self.uploadUrl;
  5952. E.log('渲染dom');
  5953. // 创建 form 和 iframe
  5954. var iframeId = 'iframe' + E.random();
  5955. var $iframe = $('<iframe name="' + iframeId + '" id="' + iframeId + '" frameborder="0" width="0" height="0"></iframe>');
  5956. var multiple = self.multiple;
  5957. var multipleTpl = multiple ? 'multiple="multiple"' : '';
  5958. var $p = $('<p>选择图片并上传</p>');
  5959. var $input = $('<input type="file" ' + multipleTpl + ' name="' + uploadFileName + '"/>');
  5960. var $btn = $('<input type="submit" value="上传"/>');
  5961. var $form = $('<form enctype="multipart/form-data" method="post" action="' + uploadUrl + '" target="' + iframeId + '"></form>');
  5962. var $container = $('<div style="margin:10px 20px;"></div>');
  5963. $form.append($p).append($input).append($btn);
  5964. // 增加用户配置的参数,如 token
  5965. $.each(editor.config.uploadParams, function (key, value) {
  5966. $form.append( $('<input type="hidden" name="' + key + '" value="' + value + '"/>') );
  5967. });
  5968. $container.append($form);
  5969. $container.append($iframe);
  5970. self.$input = $input;
  5971. self.$iframe = $iframe;
  5972. // 生成 modal
  5973. var modal = new E.Modal(editor, undefined, {
  5974. $content: $container
  5975. });
  5976. self.modal = modal;
  5977. // 记录
  5978. self._hasRender = true;
  5979. };
  5980. // 绑定 iframe load 事件
  5981. UploadFile.fn.bindLoadEvent = function () {
  5982. var self = this;
  5983. if (self._hasBindLoad) {
  5984. // 不要重复绑定
  5985. return;
  5986. }
  5987. var editor = self.editor;
  5988. var $iframe = self.$iframe;
  5989. var iframe = $iframe.get(0);
  5990. var iframeWindow = iframe.contentWindow;
  5991. var onload = editor.config.uploadImgFns.onload;
  5992. // 定义load事件
  5993. function onloadFn() {
  5994. var resultText = $.trim(iframeWindow.document.body.innerHTML);
  5995. if (!resultText) {
  5996. return;
  5997. }
  5998. // 获取文件名
  5999. var fileFullName = self.$input.val(); // 结果如 C:\folder\abc.png 格式
  6000. var fileOriginalName = fileFullName;
  6001. if (fileFullName.lastIndexOf('\\') >= 0) {
  6002. // 获取 abc.png 格式
  6003. fileOriginalName = fileFullName.slice(fileFullName.lastIndexOf('\\') + 1);
  6004. if (fileOriginalName.indexOf('.') > 0) {
  6005. // 获取 abc (即不带扩展名的文件名)
  6006. fileOriginalName = fileOriginalName.split('.')[0];
  6007. }
  6008. }
  6009. // 将文件名暂存到 editor.uploadImgOriginalName ,插入图片时,可作为 alt 属性来用
  6010. editor.uploadImgOriginalName = fileOriginalName;
  6011. // 执行load函数,插入图片的操作,应该在load函数中执行
  6012. onload.call(editor, resultText);
  6013. // 清空 input 数据
  6014. self.clear();
  6015. // 隐藏modal
  6016. self.hideModal();
  6017. }
  6018. // 绑定 load 事件
  6019. if (iframe.attachEvent) {
  6020. iframe.attachEvent('onload', onloadFn);
  6021. } else {
  6022. iframe.onload = onloadFn;
  6023. }
  6024. // 记录
  6025. self._hasBindLoad = true;
  6026. };
  6027. UploadFile.fn.show = function () {
  6028. var self = this;
  6029. var modal = self.modal;
  6030. function show() {
  6031. modal.show();
  6032. self.bindLoadEvent();
  6033. }
  6034. setTimeout(show);
  6035. };
  6036. // 选择
  6037. UploadFile.fn.selectFiles = function () {
  6038. var self = this;
  6039. E.log('使用 form 方式上传');
  6040. // 先渲染
  6041. self.render();
  6042. // 先清空
  6043. self.clear();
  6044. // 显示
  6045. self.show();
  6046. };
  6047. // 暴露给 E
  6048. E.UploadFile = UploadFile;
  6049. });
  6050. // upload img 插件 粘贴图片
  6051. _e(function (E, $) {
  6052. E.plugin(function () {
  6053. var editor = this;
  6054. var txt = editor.txt;
  6055. var $txt = txt.$txt;
  6056. var config = editor.config;
  6057. var uploadImgUrl = config.uploadImgUrl;
  6058. var uploadFileName = config.uploadImgFileName || 'wangEditorPasteFile';
  6059. var pasteEvent;
  6060. var $imgsBeforePaste;
  6061. // 未配置上传图片url,则忽略
  6062. if (!uploadImgUrl) {
  6063. return;
  6064. }
  6065. // -------- 非 chrome 下,通过查找粘贴的图片的方式上传 --------
  6066. function findPasteImgAndUpload() {
  6067. var reg = /^data:(image\/\w+);base64/;
  6068. var $imgs = $txt.find('img');
  6069. E.log('粘贴后,检查到编辑器有' + $imgs.length + '个图片。开始遍历图片,试图找到刚刚粘贴过来的图片');
  6070. $.each($imgs, function () {
  6071. var img = this;
  6072. var $img = $(img);
  6073. var flag;
  6074. var base64 = $img.attr('src');
  6075. var type;
  6076. // 判断当前图片是否是粘贴之前的
  6077. $imgsBeforePaste.each(function () {
  6078. if (img === this) {
  6079. // 当前图片是粘贴之前的
  6080. flag = true;
  6081. return false;
  6082. }
  6083. });
  6084. // 当前图片是粘贴之前的,则忽略
  6085. if (flag) {
  6086. return;
  6087. }
  6088. E.log('找到一个粘贴过来的图片');
  6089. if (reg.test(base64)) {
  6090. // 得到的粘贴的图片是 base64 格式,符合要求
  6091. E.log('src 是 base64 格式,可以上传');
  6092. type = base64.match(reg)[1];
  6093. editor.xhrUploadImg({
  6094. event: pasteEvent,
  6095. base64: base64,
  6096. fileType: type,
  6097. name: uploadFileName
  6098. });
  6099. } else {
  6100. E.log('src 为 ' + base64 + ' ,不是 base64 格式,暂时不支持上传');
  6101. }
  6102. // 最终移除原图片
  6103. $img.remove();
  6104. });
  6105. E.log('遍历结束');
  6106. }
  6107. // 开始监控粘贴事件
  6108. $txt.on('paste', function (e) {
  6109. pasteEvent = e;
  6110. var data = pasteEvent.clipboardData || pasteEvent.originalEvent.clipboardData;
  6111. var text;
  6112. var items;
  6113. // -------- 试图获取剪切板中的文字,有文字的情况下,就不处理图片粘贴 --------
  6114. if (data == null) {
  6115. text = window.clipboardData && window.clipboardData.getData('text');
  6116. } else {
  6117. text = data.getData('text/plain') || data.getData('text/html');
  6118. }
  6119. if (text) {
  6120. return;
  6121. }
  6122. items = data && data.items;
  6123. if (items) {
  6124. // -------- chrome 可以用 data.items 取出图片 -----
  6125. E.log('通过 data.items 得到了数据');
  6126. $.each(items, function (key, value) {
  6127. var fileType = value.type || '';
  6128. if(fileType.indexOf('image') < 0) {
  6129. // 不是图片
  6130. return;
  6131. }
  6132. var file = value.getAsFile();
  6133. var reader = new FileReader();
  6134. E.log('得到一个粘贴图片');
  6135. reader.onload = function (e) {
  6136. E.log('读取到粘贴的图片');
  6137. // 执行上传
  6138. var base64 = e.target.result || this.result;
  6139. editor.xhrUploadImg({
  6140. event: pasteEvent,
  6141. base64: base64,
  6142. fileType: fileType,
  6143. name: uploadFileName
  6144. });
  6145. };
  6146. //读取粘贴的文件
  6147. reader.readAsDataURL(file);
  6148. });
  6149. } else {
  6150. // -------- 非 chrome 不能用 data.items 取图片 -----
  6151. E.log('未从 data.items 得到数据,使用检测粘贴图片的方式');
  6152. // 获取
  6153. $imgsBeforePaste = $txt.find('img');
  6154. E.log('粘贴前,检查到编辑器有' + $imgsBeforePaste.length + '个图片');
  6155. // 异步上传找到的图片
  6156. setTimeout(findPasteImgAndUpload, 0);
  6157. }
  6158. });
  6159. });
  6160. });
  6161. // 拖拽上传图片 插件
  6162. _e(function (E, $) {
  6163. E.plugin(function () {
  6164. var editor = this;
  6165. var txt = editor.txt;
  6166. var $txt = txt.$txt;
  6167. var config = editor.config;
  6168. var uploadImgUrl = config.uploadImgUrl;
  6169. var uploadFileName = config.uploadImgFileName || 'wangEditorDragFile';
  6170. // 未配置上传图片url,则忽略
  6171. if (!uploadImgUrl) {
  6172. return;
  6173. }
  6174. // 阻止浏览器默认行为
  6175. E.$document.on('dragleave drop dragenter dragover', function (e) {
  6176. e.preventDefault();
  6177. });
  6178. // 监控 $txt drop 事件
  6179. $txt.on('drop', function (dragEvent) {
  6180. dragEvent.preventDefault();
  6181. var originalEvent = dragEvent.originalEvent;
  6182. var files = originalEvent.dataTransfer && originalEvent.dataTransfer.files;
  6183. if (!files || !files.length) {
  6184. return;
  6185. }
  6186. $.each(files, function (k, file) {
  6187. var type = file.type;
  6188. var name = file.name;
  6189. if (type.indexOf('image/') < 0) {
  6190. // 只接收图片
  6191. return;
  6192. }
  6193. E.log('得到图片 ' + name);
  6194. var reader = new FileReader();
  6195. reader.onload = function (e) {
  6196. E.log('读取到图片 ' + name);
  6197. // 执行上传
  6198. var base64 = e.target.result || this.result;
  6199. editor.xhrUploadImg({
  6200. event: dragEvent,
  6201. base64: base64,
  6202. fileType: type,
  6203. name: uploadFileName
  6204. });
  6205. };
  6206. //读取粘贴的文件
  6207. reader.readAsDataURL(file);
  6208. });
  6209. });
  6210. });
  6211. });
  6212. // 编辑器区域 table toolbar
  6213. _e(function (E, $) {
  6214. E.plugin(function () {
  6215. var editor = this;
  6216. var txt = editor.txt;
  6217. var $txt = txt.$txt;
  6218. var html = '';
  6219. // 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
  6220. var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
  6221. var $currentTable;
  6222. // 用到的dom节点
  6223. var isRendered = false;
  6224. var $toolbar = $('<div class="txt-toolbar"></div>');
  6225. var $triangle = $('<div class="tip-triangle"></div>');
  6226. var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
  6227. var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
  6228. var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
  6229. // 渲染到页面
  6230. function render() {
  6231. if (isRendered) {
  6232. return;
  6233. }
  6234. // 绑定事件
  6235. bindEvent();
  6236. // 拼接 渲染到页面上
  6237. $toolbar.append($triangle)
  6238. .append($delete)
  6239. .append($zoomSmall)
  6240. .append($zoomBig);
  6241. editor.$editorContainer.append($toolbar);
  6242. isRendered = true;
  6243. }
  6244. // 绑定事件
  6245. function bindEvent() {
  6246. // 统一执行命令的方法
  6247. var commandFn;
  6248. function command(e, callback) {
  6249. // 执行命令之前,先存储html内容
  6250. html = $txt.html();
  6251. // 监控内容变化
  6252. var cb = function () {
  6253. if (callback) {
  6254. callback();
  6255. }
  6256. if (html !== $txt.html()) {
  6257. $txt.change();
  6258. }
  6259. };
  6260. // 执行命令
  6261. if (commandFn) {
  6262. editor.customCommand(e, commandFn, cb);
  6263. }
  6264. }
  6265. // 删除
  6266. $delete.click(function (e) {
  6267. commandFn = function () {
  6268. $currentTable.remove();
  6269. };
  6270. command(e, function () {
  6271. setTimeout(hide, 100);
  6272. });
  6273. });
  6274. // 放大
  6275. $zoomBig.click(function (e) {
  6276. commandFn = function () {
  6277. $currentTable.css({
  6278. width: '100%'
  6279. });
  6280. };
  6281. command(e, function () {
  6282. setTimeout(show);
  6283. });
  6284. });
  6285. // 缩小
  6286. $zoomSmall.click(function (e) {
  6287. commandFn = function () {
  6288. $currentTable.css({
  6289. width: 'auto'
  6290. });
  6291. };
  6292. command(e, function () {
  6293. setTimeout(show);
  6294. });
  6295. });
  6296. }
  6297. // 显示 toolbar
  6298. function show() {
  6299. if (editor._disabled) {
  6300. // 编辑器已经被禁用,则不让显示
  6301. return;
  6302. }
  6303. if ($currentTable == null) {
  6304. return;
  6305. }
  6306. $currentTable.addClass('clicked');
  6307. var tablePosition = $currentTable.position();
  6308. var tableTop = tablePosition.top;
  6309. var tableLeft = tablePosition.left;
  6310. var tableHeight = $currentTable.outerHeight();
  6311. var tableWidth = $currentTable.outerWidth();
  6312. // --- 定位 toolbar ---
  6313. // 计算初步结果
  6314. var top = tableTop + tableHeight;
  6315. var left = tableLeft;
  6316. var marginLeft = 0;
  6317. var txtTop = $currentTxt.position().top;
  6318. var txtHeight = $currentTxt.outerHeight();
  6319. if (top > (txtTop + txtHeight)) {
  6320. // top 不得超出编辑范围
  6321. top = txtTop + txtHeight;
  6322. }
  6323. // 显示(方便计算 margin)
  6324. $toolbar.show();
  6325. // 计算 margin
  6326. var width = $toolbar.outerWidth();
  6327. marginLeft = tableWidth / 2 - width / 2;
  6328. // 定位
  6329. $toolbar.css({
  6330. top: top + 5,
  6331. left: left,
  6332. 'margin-left': marginLeft
  6333. });
  6334. // 如果定位太靠左了
  6335. if (marginLeft < 0) {
  6336. // 得到三角形的margin-left
  6337. $toolbar.css('margin-left', '0');
  6338. $triangle.hide();
  6339. } else {
  6340. $triangle.show();
  6341. }
  6342. }
  6343. // 隐藏 toolbar
  6344. function hide() {
  6345. if ($currentTable == null) {
  6346. return;
  6347. }
  6348. $currentTable.removeClass('clicked');
  6349. $currentTable = null;
  6350. $toolbar.hide();
  6351. }
  6352. // click table 事件
  6353. $currentTxt.on('click', 'table', function (e) {
  6354. var $table = $(e.currentTarget);
  6355. // 渲染
  6356. render();
  6357. if ($currentTable && ($currentTable.get(0) === $table.get(0))) {
  6358. setTimeout(hide, 100);
  6359. return;
  6360. }
  6361. // 显示 toolbar
  6362. $currentTable = $table;
  6363. show();
  6364. // 阻止冒泡
  6365. e.preventDefault();
  6366. e.stopPropagation();
  6367. }).on('click keydown scroll', function (e) {
  6368. setTimeout(hide, 100);
  6369. });
  6370. E.$body.on('click keydown scroll', function (e) {
  6371. setTimeout(hide, 100);
  6372. });
  6373. });
  6374. });
  6375. // 编辑器区域 img toolbar
  6376. _e(function (E, $) {
  6377. if (E.userAgent.indexOf('MSIE 8') > 0) {
  6378. return;
  6379. }
  6380. E.plugin(function () {
  6381. var editor = this;
  6382. var lang = editor.config.lang;
  6383. var txt = editor.txt;
  6384. var $txt = txt.$txt;
  6385. var html = '';
  6386. // 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
  6387. var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
  6388. var $editorContainer = editor.$editorContainer;
  6389. var $currentImg;
  6390. var currentLink = '';
  6391. // 用到的dom节点
  6392. var isRendered = false;
  6393. var $dragPoint = $('<div class="img-drag-point"></div>');
  6394. var $toolbar = $('<div class="txt-toolbar"></div>');
  6395. var $triangle = $('<div class="tip-triangle"></div>');
  6396. var $menuContainer = $('<div></div>');
  6397. var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
  6398. var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
  6399. var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
  6400. // var $floatLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
  6401. // var $noFloat = $('<a href="#"><i class="wangeditor-menu-img-align-justify"></i></a>');
  6402. // var $floatRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
  6403. var $alignLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
  6404. var $alignCenter = $('<a href="#"><i class="wangeditor-menu-img-align-center"></i></a>');
  6405. var $alignRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
  6406. var $link = $('<a href="#"><i class="wangeditor-menu-img-link"></i></a>');
  6407. var $unLink = $('<a href="#"><i class="wangeditor-menu-img-unlink"></i></a>');
  6408. var $linkInputContainer = $('<div style="display:none;"></div>');
  6409. var $linkInput = $('<input type="text" style="height:26px; margin-left:10px; width:200px;"/>');
  6410. var $linkBtnSubmit = $('<button class="right">' + lang.submit + '</button>');
  6411. var $linkBtnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  6412. // 记录是否正在拖拽
  6413. var isOnDrag = false;
  6414. // 获取 / 设置 链接
  6415. function imgLink(e, url) {
  6416. if (!$currentImg) {
  6417. return;
  6418. }
  6419. var commandFn;
  6420. var callback = function () {
  6421. // 及时保存currentLink
  6422. if (url != null) {
  6423. currentLink = url;
  6424. }
  6425. if (html !== $txt.html()) {
  6426. $txt.change();
  6427. }
  6428. };
  6429. var $link;
  6430. var inLink = false;
  6431. var $parent = $currentImg.parent();
  6432. if ($parent.get(0).nodeName.toLowerCase() === 'a') {
  6433. // 父元素就是图片链接
  6434. $link = $parent;
  6435. inLink = true;
  6436. } else {
  6437. // 父元素不是图片链接,则重新创建一个链接
  6438. $link = $('<a target="_blank"></a>');
  6439. }
  6440. if (url == null) {
  6441. // url 无值,是获取链接
  6442. return $link.attr('href') || '';
  6443. } else if (url === '') {
  6444. // url 是空字符串,是取消链接
  6445. if (inLink) {
  6446. commandFn = function () {
  6447. $currentImg.unwrap();
  6448. };
  6449. }
  6450. } else {
  6451. // url 有值,是设置链接
  6452. if (url === currentLink) {
  6453. return;
  6454. }
  6455. commandFn = function () {
  6456. $link.attr('href', url);
  6457. if (!inLink) {
  6458. // 当前图片未包含在链接中,则包含进来
  6459. $currentImg.wrap($link);
  6460. }
  6461. };
  6462. }
  6463. // 执行命令
  6464. if (commandFn) {
  6465. // 记录下执行命令之前的html内容
  6466. html = $txt.html();
  6467. // 执行命令
  6468. editor.customCommand(e, commandFn, callback);
  6469. }
  6470. }
  6471. // 渲染到页面
  6472. function render() {
  6473. if (isRendered) {
  6474. return;
  6475. }
  6476. // 绑定事件
  6477. bindToolbarEvent();
  6478. bindDragEvent();
  6479. // 菜单放入 container
  6480. $menuContainer.append($delete)
  6481. .append($zoomSmall)
  6482. .append($zoomBig)
  6483. // .append($floatLeft)
  6484. // .append($noFloat)
  6485. // .append($floatRight);
  6486. .append($alignLeft)
  6487. .append($alignCenter)
  6488. .append($alignRight)
  6489. .append($link)
  6490. .append($unLink);
  6491. // 链接input放入container
  6492. $linkInputContainer.append($linkInput)
  6493. .append($linkBtnCancel)
  6494. .append($linkBtnSubmit);
  6495. // 拼接 渲染到页面上
  6496. $toolbar.append($triangle)
  6497. .append($menuContainer)
  6498. .append($linkInputContainer);
  6499. editor.$editorContainer.append($toolbar).append($dragPoint);
  6500. isRendered = true;
  6501. }
  6502. // 绑定toolbar事件
  6503. function bindToolbarEvent() {
  6504. // 统一执行命令的方法
  6505. var commandFn;
  6506. function customCommand(e, callback) {
  6507. var cb;
  6508. // 记录下执行命令之前的html内容
  6509. html = $txt.html();
  6510. cb = function () {
  6511. if (callback) {
  6512. callback();
  6513. }
  6514. if (html !== $txt.html()) {
  6515. $txt.change();
  6516. }
  6517. };
  6518. // 执行命令
  6519. if (commandFn) {
  6520. editor.customCommand(e, commandFn, cb);
  6521. }
  6522. }
  6523. // 删除
  6524. $delete.click(function (e) {
  6525. // 删除之前先unlink
  6526. imgLink(e, '');
  6527. // 删除图片
  6528. commandFn = function () {
  6529. $currentImg.remove();
  6530. };
  6531. customCommand(e, function () {
  6532. setTimeout(hide, 100);
  6533. });
  6534. });
  6535. // 放大
  6536. $zoomBig.click(function (e) {
  6537. commandFn = function () {
  6538. var img = $currentImg.get(0);
  6539. var width = img.width;
  6540. var height = img.height;
  6541. width = width * 1.1;
  6542. height = height * 1.1;
  6543. $currentImg.css({
  6544. width: width + 'px',
  6545. height: height + 'px'
  6546. });
  6547. };
  6548. customCommand(e, function () {
  6549. setTimeout(show);
  6550. });
  6551. });
  6552. // 缩小
  6553. $zoomSmall.click(function (e) {
  6554. commandFn = function () {
  6555. var img = $currentImg.get(0);
  6556. var width = img.width;
  6557. var height = img.height;
  6558. width = width * 0.9;
  6559. height = height * 0.9;
  6560. $currentImg.css({
  6561. width: width + 'px',
  6562. height: height + 'px'
  6563. });
  6564. };
  6565. customCommand(e, function () {
  6566. setTimeout(show);
  6567. });
  6568. });
  6569. // // 左浮动
  6570. // $floatLeft.click(function (e) {
  6571. // commandFn = function () {
  6572. // $currentImg.css({
  6573. // float: 'left'
  6574. // });
  6575. // };
  6576. // customCommand(e, function () {
  6577. // setTimeout(hide, 100);
  6578. // });
  6579. // });
  6580. // alignLeft
  6581. $alignLeft.click(function (e) {
  6582. commandFn = function () {
  6583. // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
  6584. $currentImg.parents('p').css({
  6585. 'text-align': 'left'
  6586. }).attr('align', 'left');
  6587. };
  6588. customCommand(e, function () {
  6589. setTimeout(hide, 100);
  6590. });
  6591. });
  6592. // // 右浮动
  6593. // $floatRight.click(function (e) {
  6594. // commandFn = function () {
  6595. // $currentImg.css({
  6596. // float: 'right'
  6597. // });
  6598. // };
  6599. // customCommand(e, function () {
  6600. // setTimeout(hide, 100);
  6601. // });
  6602. // });
  6603. // alignRight
  6604. $alignRight.click(function (e) {
  6605. commandFn = function () {
  6606. // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
  6607. $currentImg.parents('p').css({
  6608. 'text-align': 'right'
  6609. }).attr('align', 'right');
  6610. };
  6611. customCommand(e, function () {
  6612. setTimeout(hide, 100);
  6613. });
  6614. });
  6615. // // 无浮动
  6616. // $noFloat.click(function (e) {
  6617. // commandFn = function () {
  6618. // $currentImg.css({
  6619. // float: 'none'
  6620. // });
  6621. // };
  6622. // customCommand(e, function () {
  6623. // setTimeout(hide, 100);
  6624. // });
  6625. // });
  6626. // alignCenter
  6627. $alignCenter.click(function (e) {
  6628. commandFn = function () {
  6629. // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
  6630. $currentImg.parents('p').css({
  6631. 'text-align': 'center'
  6632. }).attr('align', 'center');
  6633. };
  6634. customCommand(e, function () {
  6635. setTimeout(hide, 100);
  6636. });
  6637. });
  6638. // link
  6639. // 显示链接input
  6640. $link.click(function (e) {
  6641. e.preventDefault();
  6642. // 获取当前链接,并显示
  6643. currentLink = imgLink(e);
  6644. $linkInput.val(currentLink);
  6645. $menuContainer.hide();
  6646. $linkInputContainer.show();
  6647. });
  6648. // 设置链接
  6649. $linkBtnSubmit.click(function (e) {
  6650. e.preventDefault();
  6651. var url = $.trim($linkInput.val());
  6652. if (url) {
  6653. // 设置链接,同时会自动更新 currentLink 的值
  6654. imgLink(e, url);
  6655. }
  6656. // 隐藏 toolbar
  6657. setTimeout(hide);
  6658. });
  6659. // 取消设置链接
  6660. $linkBtnCancel.click(function (e) {
  6661. e.preventDefault();
  6662. // 重置链接 input
  6663. $linkInput.val(currentLink);
  6664. $menuContainer.show();
  6665. $linkInputContainer.hide();
  6666. });
  6667. // unlink
  6668. $unLink.click(function (e) {
  6669. e.preventDefault();
  6670. // 执行 unlink
  6671. imgLink(e, '');
  6672. // 隐藏 toolbar
  6673. setTimeout(hide);
  6674. });
  6675. }
  6676. // 绑定drag事件
  6677. function bindDragEvent() {
  6678. var _x, _y;
  6679. var dragMarginLeft, dragMarginTop;
  6680. var imgWidth, imgHeight;
  6681. function mousemove (e) {
  6682. var diffX, diffY;
  6683. // 计算差额
  6684. diffX = e.pageX - _x;
  6685. diffY = e.pageY - _y;
  6686. // --------- 计算拖拽点的位置 ---------
  6687. var currentDragMarginLeft = dragMarginLeft + diffX;
  6688. var currentDragMarginTop = dragMarginTop + diffY;
  6689. $dragPoint.css({
  6690. 'margin-left': currentDragMarginLeft,
  6691. 'margin-top': currentDragMarginTop
  6692. });
  6693. // --------- 计算图片的大小 ---------
  6694. var currentImgWidth = imgWidth + diffX;
  6695. var currentImggHeight = imgHeight + diffY;
  6696. $currentImg && $currentImg.css({
  6697. width: currentImgWidth,
  6698. height: currentImggHeight
  6699. });
  6700. }
  6701. $dragPoint.on('mousedown', function(e){
  6702. if (!$currentImg) {
  6703. return;
  6704. }
  6705. // 当前鼠标位置
  6706. _x = e.pageX;
  6707. _y = e.pageY;
  6708. // 当前拖拽点的位置
  6709. dragMarginLeft = parseFloat($dragPoint.css('margin-left'), 10);
  6710. dragMarginTop = parseFloat($dragPoint.css('margin-top'), 10);
  6711. // 当前图片的大小
  6712. imgWidth = $currentImg.width();
  6713. imgHeight = $currentImg.height();
  6714. // 隐藏 $toolbar
  6715. $toolbar.hide();
  6716. // 绑定计算事件
  6717. E.$document.on('mousemove._dragResizeImg', mousemove);
  6718. E.$document.on('mouseup._dragResizeImg', function (e) {
  6719. // 取消绑定
  6720. E.$document.off('mousemove._dragResizeImg');
  6721. E.$document.off('mouseup._dragResizeImg');
  6722. // 隐藏,并还原拖拽点的位置
  6723. hide();
  6724. $dragPoint.css({
  6725. 'margin-left': dragMarginLeft,
  6726. 'margin-top': dragMarginTop
  6727. });
  6728. // 记录
  6729. isOnDrag = false;
  6730. });
  6731. // 记录
  6732. isOnDrag = true;
  6733. });
  6734. }
  6735. // 显示 toolbar
  6736. function show() {
  6737. if (editor._disabled) {
  6738. // 编辑器已经被禁用,则不让显示
  6739. return;
  6740. }
  6741. if ($currentImg == null) {
  6742. return;
  6743. }
  6744. $currentImg.addClass('clicked');
  6745. var imgPosition = $currentImg.position();
  6746. var imgTop = imgPosition.top;
  6747. var imgLeft = imgPosition.left;
  6748. var imgHeight = $currentImg.outerHeight();
  6749. var imgWidth = $currentImg.outerWidth();
  6750. // --- 定位 dragpoint ---
  6751. $dragPoint.css({
  6752. top: imgTop + imgHeight,
  6753. left: imgLeft + imgWidth
  6754. });
  6755. // --- 定位 toolbar ---
  6756. // 计算初步结果
  6757. var top = imgTop + imgHeight;
  6758. var left = imgLeft;
  6759. var marginLeft = 0;
  6760. var txtTop = $currentTxt.position().top;
  6761. var txtHeight = $currentTxt.outerHeight();
  6762. if (top > (txtTop + txtHeight)) {
  6763. // top 不得超出编辑范围
  6764. top = txtTop + txtHeight;
  6765. } else {
  6766. // top 超出编辑范围,dragPoint就不显示了
  6767. $dragPoint.show();
  6768. }
  6769. // 显示(方便计算 margin)
  6770. $toolbar.show();
  6771. // 计算 margin
  6772. var width = $toolbar.outerWidth();
  6773. marginLeft = imgWidth / 2 - width / 2;
  6774. // 定位
  6775. $toolbar.css({
  6776. top: top + 5,
  6777. left: left,
  6778. 'margin-left': marginLeft
  6779. });
  6780. // 如果定位太靠左了
  6781. if (marginLeft < 0) {
  6782. // 得到三角形的margin-left
  6783. $toolbar.css('margin-left', '0');
  6784. $triangle.hide();
  6785. } else {
  6786. $triangle.show();
  6787. }
  6788. // disable 菜单
  6789. editor.disableMenusExcept();
  6790. }
  6791. // 隐藏 toolbar
  6792. function hide() {
  6793. if ($currentImg == null) {
  6794. return;
  6795. }
  6796. $currentImg.removeClass('clicked');
  6797. $currentImg = null;
  6798. $toolbar.hide();
  6799. $dragPoint.hide();
  6800. // enable 菜单
  6801. editor.enableMenusExcept();
  6802. }
  6803. // 判断img是否是一个表情
  6804. function isEmotion(imgSrc) {
  6805. var result = false;
  6806. if (!editor.emotionUrls) {
  6807. return result;
  6808. }
  6809. $.each(editor.emotionUrls, function (index, url) {
  6810. var flag = false;
  6811. if (imgSrc === url) {
  6812. result = true;
  6813. flag = true;
  6814. }
  6815. if (flag) {
  6816. return false; // break 循环
  6817. }
  6818. });
  6819. return result;
  6820. }
  6821. // click img 事件
  6822. $currentTxt.on('mousedown', 'img', function (e) {
  6823. e.preventDefault();
  6824. }).on('click', 'img', function (e) {
  6825. var $img = $(e.currentTarget);
  6826. var src = $img.attr('src');
  6827. if (!src || isEmotion(src)) {
  6828. // 是一个表情图标
  6829. return;
  6830. }
  6831. // ---------- 不是表情图标 ----------
  6832. // 渲染
  6833. render();
  6834. if ($currentImg && ($currentImg.get(0) === $img.get(0))) {
  6835. setTimeout(hide, 100);
  6836. return;
  6837. }
  6838. // 显示 toolbar
  6839. $currentImg = $img;
  6840. show();
  6841. // 默认显示menuContainer,其他默认隐藏
  6842. $menuContainer.show();
  6843. $linkInputContainer.hide();
  6844. // 阻止冒泡
  6845. e.preventDefault();
  6846. e.stopPropagation();
  6847. }).on('click keydown scroll', function (e) {
  6848. if (!isOnDrag) {
  6849. setTimeout(hide, 100);
  6850. }
  6851. });
  6852. });
  6853. });
  6854. // 编辑区域 link toolbar
  6855. _e(function (E, $) {
  6856. E.plugin(function () {
  6857. var editor = this;
  6858. var lang = editor.config.lang;
  6859. var $txt = editor.txt.$txt;
  6860. // 当前命中的链接
  6861. var $currentLink;
  6862. var $toolbar = $('<div class="txt-toolbar"></div>');
  6863. var $triangle = $('<div class="tip-triangle"></div>');
  6864. var $triggerLink = $('<a href="#" target="_blank"><i class="wangeditor-menu-img-link"></i> ' + lang.openLink + '</a>');
  6865. var isRendered;
  6866. // 记录当前的显示/隐藏状态
  6867. var isShow = false;
  6868. var showTimeoutId, hideTimeoutId;
  6869. var showTimeoutIdByToolbar, hideTimeoutIdByToolbar;
  6870. // 渲染 dom
  6871. function render() {
  6872. if (isRendered) {
  6873. return;
  6874. }
  6875. $toolbar.append($triangle)
  6876. .append($triggerLink);
  6877. editor.$editorContainer.append($toolbar);
  6878. isRendered = true;
  6879. }
  6880. // 定位
  6881. function setPosition() {
  6882. if (!$currentLink) {
  6883. return;
  6884. }
  6885. var position = $currentLink.position();
  6886. var left = position.left;
  6887. var top = position.top;
  6888. var height = $currentLink.height();
  6889. // 初步计算top值
  6890. var topResult = top + height + 5;
  6891. // 判断 toolbar 是否超过了编辑器区域的下边界
  6892. var menuHeight = editor.menuContainer.height();
  6893. var txtHeight = editor.txt.$txt.outerHeight();
  6894. if (topResult > menuHeight + txtHeight) {
  6895. topResult = menuHeight + txtHeight + 5;
  6896. }
  6897. // 最终设置
  6898. $toolbar.css({
  6899. top: topResult,
  6900. left: left
  6901. });
  6902. }
  6903. // 显示 toolbar
  6904. function show() {
  6905. if (isShow) {
  6906. return;
  6907. }
  6908. if (!$currentLink) {
  6909. return;
  6910. }
  6911. render();
  6912. $toolbar.show();
  6913. // 设置链接
  6914. var href = $currentLink.attr('href');
  6915. $triggerLink.attr('href', href);
  6916. // 定位
  6917. setPosition();
  6918. isShow = true;
  6919. }
  6920. // 隐藏 toolbar
  6921. function hide() {
  6922. if (!isShow) {
  6923. return;
  6924. }
  6925. if (!$currentLink) {
  6926. return;
  6927. }
  6928. $toolbar.hide();
  6929. isShow = false;
  6930. }
  6931. // $txt 绑定事件
  6932. $txt.on('mouseenter', 'a', function (e) {
  6933. // 延时 500ms 显示toolbar
  6934. if (showTimeoutId) {
  6935. clearTimeout(showTimeoutId);
  6936. }
  6937. showTimeoutId = setTimeout(function () {
  6938. var a = e.currentTarget;
  6939. var $a = $(a);
  6940. $currentLink = $a;
  6941. var $img = $a.children('img');
  6942. if ($img.length) {
  6943. // 该链接下包含一个图片
  6944. // 图片点击时,隐藏toolbar
  6945. $img.click(function (e) {
  6946. hide();
  6947. });
  6948. if ($img.hasClass('clicked')) {
  6949. // 图片还处于clicked状态,则不显示toolbar
  6950. return;
  6951. }
  6952. }
  6953. // 显示toolbar
  6954. show();
  6955. }, 500);
  6956. }).on('mouseleave', 'a', function (e) {
  6957. // 延时 500ms 隐藏toolbar
  6958. if (hideTimeoutId) {
  6959. clearTimeout(hideTimeoutId);
  6960. }
  6961. hideTimeoutId = setTimeout(hide, 500);
  6962. }).on('click keydown scroll', function (e) {
  6963. setTimeout(hide, 100);
  6964. });
  6965. // $toolbar 绑定事件
  6966. $toolbar.on('mouseenter', function (e) {
  6967. // 先中断掉 $txt.mouseleave 导致的隐藏
  6968. if (hideTimeoutId) {
  6969. clearTimeout(hideTimeoutId);
  6970. }
  6971. }).on('mouseleave', function (e) {
  6972. // 延时 500ms 显示toolbar
  6973. if (showTimeoutIdByToolbar) {
  6974. clearTimeout(showTimeoutIdByToolbar);
  6975. }
  6976. showTimeoutIdByToolbar = setTimeout(hide, 500);
  6977. });
  6978. });
  6979. });
  6980. // menu吸顶
  6981. _e(function (E, $) {
  6982. E.plugin(function () {
  6983. var editor = this;
  6984. var menuFixed = editor.config.menuFixed;
  6985. if (menuFixed === false || typeof menuFixed !== 'number') {
  6986. // 没有配置菜单吸顶
  6987. return;
  6988. }
  6989. var bodyMarginTop = parseFloat(E.$body.css('margin-top'), 10);
  6990. if (isNaN(bodyMarginTop)) {
  6991. bodyMarginTop = 0;
  6992. }
  6993. var $editorContainer = editor.$editorContainer;
  6994. var editorTop = $editorContainer.offset().top;
  6995. var editorHeight = $editorContainer.outerHeight();
  6996. var $menuContainer = editor.menuContainer.$menuContainer;
  6997. var menuCssPosition = $menuContainer.css('position');
  6998. var menuCssTop = $menuContainer.css('top');
  6999. var menuTop = $menuContainer.offset().top;
  7000. var menuHeight = $menuContainer.outerHeight();
  7001. var $txt = editor.txt.$txt;
  7002. E.$window.scroll(function () {
  7003. //全屏模式不支持
  7004. if (editor.isFullScreen) {
  7005. return;
  7006. }
  7007. var sTop = E.$window.scrollTop();
  7008. // 需要重新计算宽度,因为浏览器可能此时出现滚动条
  7009. var menuWidth = $menuContainer.width();
  7010. // 如果 menuTop === 0 说明此前编辑器一直隐藏,后来显示出来了,要重新计算相关数据
  7011. if (menuTop === 0) {
  7012. menuTop = $menuContainer.offset().top;
  7013. editorTop = $editorContainer.offset().top;
  7014. editorHeight = $editorContainer.outerHeight();
  7015. menuHeight = $menuContainer.outerHeight();
  7016. }
  7017. if (sTop >= menuTop && sTop + menuFixed + menuHeight + 30 < editorTop + editorHeight) {
  7018. // 吸顶
  7019. $menuContainer.css({
  7020. position: 'fixed',
  7021. top: menuFixed
  7022. });
  7023. // 固定宽度
  7024. $menuContainer.width(menuWidth);
  7025. // 增加body margin-top
  7026. E.$body.css({
  7027. 'margin-top': bodyMarginTop + menuHeight
  7028. });
  7029. // 记录
  7030. if (!editor._isMenufixed) {
  7031. editor._isMenufixed = true;
  7032. }
  7033. } else {
  7034. // 取消吸顶
  7035. $menuContainer.css({
  7036. position: menuCssPosition,
  7037. top: menuCssTop
  7038. });
  7039. // 取消宽度固定
  7040. $menuContainer.css('width', '100%');
  7041. // 还原 body margin-top
  7042. E.$body.css({
  7043. 'margin-top': bodyMarginTop
  7044. });
  7045. // 撤销记录
  7046. if (editor._isMenufixed) {
  7047. editor._isMenufixed = false;
  7048. }
  7049. }
  7050. });
  7051. });
  7052. });
  7053. // 缩进 菜单插件
  7054. _e(function (E, $) {
  7055. // 用 createMenu 方法创建菜单
  7056. E.createMenu(function (check) {
  7057. // 定义菜单id,不要和其他菜单id重复。编辑器自带的所有菜单id,可通过『参数配置-自定义菜单』一节查看
  7058. var menuId = 'indent';
  7059. // check将检查菜单配置(『参数配置-自定义菜单』一节描述)中是否该菜单id,如果没有,则忽略下面的代码。
  7060. if (!check(menuId)) {
  7061. return;
  7062. }
  7063. // this 指向 editor 对象自身
  7064. var editor = this;
  7065. // 创建 menu 对象
  7066. var menu = new E.Menu({
  7067. editor: editor, // 编辑器对象
  7068. id: menuId, // 菜单id
  7069. title: '缩进', // 菜单标题
  7070. // 正常状态和选中装下的dom对象,样式需要自定义
  7071. $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-indent-left"></i></a>'),
  7072. $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-indent-left"></i></a>')
  7073. });
  7074. // 菜单正常状态下,点击将触发该事件
  7075. menu.clickEvent = function (e) {
  7076. var elem = editor.getRangeElem();
  7077. var p = editor.getSelfOrParentByName(elem, 'p');
  7078. var $p;
  7079. if (!p) {
  7080. // 未找到 p 元素,则忽略
  7081. return e.preventDefault();
  7082. }
  7083. $p = $(p);
  7084. // 使用自定义命令
  7085. function commandFn() {
  7086. $p.css('text-indent', '2em');
  7087. }
  7088. editor.customCommand(e, commandFn);
  7089. };
  7090. // 菜单选中状态下,点击将触发该事件
  7091. menu.clickEventSelected = function (e) {
  7092. var elem = editor.getRangeElem();
  7093. var p = editor.getSelfOrParentByName(elem, 'p');
  7094. var $p;
  7095. if (!p) {
  7096. // 未找到 p 元素,则忽略
  7097. return e.preventDefault();
  7098. }
  7099. $p = $(p);
  7100. // 使用自定义命令
  7101. function commandFn() {
  7102. $p.css('text-indent', '0');
  7103. }
  7104. editor.customCommand(e, commandFn);
  7105. };
  7106. // 根据当前选区,自定义更新菜单的选中状态或者正常状态
  7107. menu.updateSelectedEvent = function () {
  7108. // 获取当前选区所在的父元素
  7109. var elem = editor.getRangeElem();
  7110. var p = editor.getSelfOrParentByName(elem, 'p');
  7111. var $p;
  7112. var indent;
  7113. if (!p) {
  7114. // 未找到 p 元素,则标记为未处于选中状态
  7115. return false;
  7116. }
  7117. $p = $(p);
  7118. indent = $p.css('text-indent');
  7119. if (!indent || indent === '0px') {
  7120. // 得到的p,text-indent 属性是 0,则标记为未处于选中状态
  7121. return false;
  7122. }
  7123. // 找到 p 元素,并且 text-indent 不是 0,则标记为选中状态
  7124. return true;
  7125. };
  7126. // 增加到editor对象中
  7127. editor.menus[menuId] = menu;
  7128. });
  7129. });
  7130. // 行高 菜单插件
  7131. _e(function (E, $) {
  7132. // 用 createMenu 方法创建菜单
  7133. E.createMenu(function (check) {
  7134. // 定义菜单id,不要和其他菜单id重复。编辑器自带的所有菜单id,可通过『参数配置-自定义菜单』一节查看
  7135. var menuId = 'lineheight';
  7136. // check将检查菜单配置(『参数配置-自定义菜单』一节描述)中是否该菜单id,如果没有,则忽略下面的代码。
  7137. if (!check(menuId)) {
  7138. return;
  7139. }
  7140. // this 指向 editor 对象自身
  7141. var editor = this;
  7142. // 由于浏览器自身不支持 lineHeight 命令,因此要做一个hook
  7143. editor.commandHooks.lineHeight = function (value) {
  7144. var rangeElem = editor.getRangeElem();
  7145. var targetElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,pre');
  7146. if (!targetElem) {
  7147. return;
  7148. }
  7149. $(targetElem).css('line-height', value + '');
  7150. };
  7151. // 创建 menu 对象
  7152. var menu = new E.Menu({
  7153. editor: editor, // 编辑器对象
  7154. id: menuId, // 菜单id
  7155. title: '行高', // 菜单标题
  7156. commandName: 'lineHeight', // 命令名称
  7157. // 正常状态和选中装下的dom对象,样式需要自定义
  7158. $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-arrows-v"></i></a>'),
  7159. $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-arrows-v"></i></a>')
  7160. });
  7161. // 数据源
  7162. var data = {
  7163. // 格式: 'value' : 'title'
  7164. '1.0': '1.0倍',
  7165. '1.5': '1.5倍',
  7166. '1.8': '1.8倍',
  7167. '2.0': '2.0倍',
  7168. '2.5': '2.5倍',
  7169. '3.0': '3.0倍'
  7170. };
  7171. // 为menu创建droplist对象
  7172. var tpl = '<span style="line-height:{#commandValue}">{#title}</span>';
  7173. menu.dropList = new E.DropList(editor, menu, {
  7174. data: data, // 传入数据源
  7175. tpl: tpl // 传入模板
  7176. });
  7177. // 增加到editor对象中
  7178. editor.menus[menuId] = menu;
  7179. });
  7180. });
  7181. // 自定义上传
  7182. _e(function (E, $) {
  7183. E.plugin(function () {
  7184. var editor = this;
  7185. var customUpload = editor.config.customUpload;
  7186. if (!customUpload) {
  7187. return;
  7188. } else if (editor.config.uploadImgUrl) {
  7189. alert('自定义上传无效,详看浏览器日志console.log');
  7190. E.error('已经配置了 uploadImgUrl ,就不能再配置 customUpload ,两者冲突。将导致自定义上传无效。');
  7191. return;
  7192. }
  7193. var $uploadContent = editor.$uploadContent;
  7194. if (!$uploadContent) {
  7195. E.error('自定义上传,无法获取 editor.$uploadContent');
  7196. }
  7197. // UI
  7198. var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
  7199. $uploadContent.append($uploadIcon);
  7200. // 设置id,并暴露
  7201. var btnId = 'upload' + E.random();
  7202. var containerId = 'upload' + E.random();
  7203. $uploadIcon.attr('id', btnId);
  7204. $uploadContent.attr('id', containerId);
  7205. editor.customUploadBtnId = btnId;
  7206. editor.customUploadContainerId = containerId;
  7207. });
  7208. });
  7209. // 版权提示
  7210. _e(function (E, $) {
  7211. E.info();
  7212. });
  7213. // 最终返回wangEditor构造函数
  7214. return window.wangEditor;
  7215. });