| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571 |
- (function (factory) {
- if (typeof window.define === 'function') {
- if (window.define.amd) {
- // AMD模式
- window.define('wangEditor', ["jquery"], factory);
- } else if (window.define.cmd) {
- // CMD模式
- window.define(function (require, exports, module) {
- return factory;
- });
- } else {
- // 全局模式
- factory(window.jQuery);
- }
- } else if (typeof module === "object" && typeof module.exports === "object") {
- // commonjs
- // 引用 css —— webapck
- window.wangEditorCssPath ? require(window.wangEditorCssPath) : require('../css/wangEditor.css');
- module.exports = factory(
- // 传入 jquery ,支持使用 npm 方式或者自己定义jquery的路径
- window.wangEditorJQueryPath ? require(window.wangEditorJQueryPath) : require('jquery')
- );
- } else {
- // 全局模式
- factory(window.jQuery);
- }
- })(function($){
- // 验证是否引用jquery
- // if (!$ || !$.fn || !$.fn.jquery) {
- // alert('在引用wangEditor.js之前,先引用jQuery,否则无法使用 wangEditor');
- // return;
- // }
- // 定义扩展函数
- var _e = function (fn) {
- var E = window.wangEditor;
- if (E) {
- // 执行传入的函数
- fn(E, $);
- }
- };
- // 定义构造函数
- (function (window, $) {
- // if (window.wangEditor) {
- // // 重复引用
- // alert('一个页面不能重复引用 wangEditor.js 或 wangEditor.min.js !!!');
- // return;
- // }
- // 编辑器(整体)构造函数
- var E = function (elem) {
- // 支持 id 和 element 两种形式
- if (typeof elem === 'string') {
- elem = '#' + elem;
- }
- // ---------------获取基本节点------------------
- var $elem = $(elem);
- if ($elem.length !== 1) {
- return;
- }
- var nodeName = $elem[0].nodeName;
- if (nodeName !== 'TEXTAREA' && nodeName !== 'DIV') {
- // 只能是 textarea 和 div ,其他类型的元素不行
- return;
- }
- this.valueNodeName = nodeName.toLowerCase();
- this.$valueContainer = $elem;
- // 记录 elem 的 prev 和 parent(最后渲染 editor 要用到)
- this.$prev = $elem.prev();
- this.$parent = $elem.parent();
- // ------------------初始化------------------
- this.init();
- };
- E.fn = E.prototype;
- E.$body = $('body');
- E.$document = $(document);
- E.$window = $(window);
- E.userAgent = navigator.userAgent;
- E.getComputedStyle = window.getComputedStyle;
- E.w3cRange = typeof document.createRange === 'function';
- E.hostname = location.hostname.toLowerCase();
- E.websiteHost = 'wangeditor.github.io|www.wangeditor.com|wangeditor.coding.me';
- E.isOnWebsite = E.websiteHost.indexOf(E.hostname) >= 0;
- E.docsite = 'http://www.kancloud.cn/wangfupeng/wangeditor2/113961';
- // 暴露给全局对象
- window.wangEditor = E;
- // 注册 plugin 事件,用于用户自定义插件
- // 用户在引用 wangEditor.js 之后,还可以通过 E.plugin() 注入自定义函数,
- // 该函数将会在 editor.create() 方法的最后一步执行
- E.plugin = function (fn) {
- if (!E._plugins) {
- E._plugins = [];
- }
- if (typeof fn === 'function') {
- E._plugins.push(fn);
- }
- };
- })(window, $);
- // editor 绑定事件
- _e(function (E, $) {
- E.fn.init = function () {
- // 初始化 editor 默认配置
- this.initDefaultConfig();
- // 增加container
- this.addEditorContainer();
- // 增加编辑区域
- this.addTxt();
- // 增加menuContainer
- this.addMenuContainer();
- // 初始化菜单集合
- this.menus = {};
- // 初始化commandHooks
- this.commandHooks();
- };
- });
- // editor api
- _e(function (E, $) {
- // 预定义 ready 事件
- E.fn.ready = function (fn) {
- if (!this.readyFns) {
- this.readyFns = [];
- }
- this.readyFns.push(fn);
- };
- // 处理ready事件
- E.fn.readyHeadler = function () {
- var fns = this.readyFns;
- while (fns.length) {
- fns.shift().call(this);
- }
- };
- // 更新内容到 $valueContainer
- E.fn.updateValue = function () {
- var editor = this;
- var $valueContainer = editor.$valueContainer;
- var $txt = editor.txt.$txt;
- if ($valueContainer === $txt) {
- // 传入生成编辑器的div,即是编辑区域
- return;
- }
- var value = $txt.html();
- $valueContainer.val(value);
- };
- // 获取初始化的内容
- E.fn.getInitValue = function () {
- var editor = this;
- var $valueContainer = editor.$valueContainer;
- var currentValue = '';
- var nodeName = editor.valueNodeName;
- if (nodeName === 'div') {
- currentValue = $valueContainer.html();
- } else if (nodeName === 'textarea') {
- currentValue = $valueContainer.val();
- }
- return currentValue;
- };
- // 触发菜单updatestyle
- E.fn.updateMenuStyle = function () {
- var menus = this.menus;
- $.each(menus, function (k, menu) {
- menu.updateSelected();
- });
- };
- // 除了传入的 menuIds,其他全部启用
- E.fn.enableMenusExcept = function (menuIds) {
- if (this._disabled) {
- // 编辑器处于禁用状态,则不执行改操作
- return;
- }
- // menuIds参数:支持数组和字符串
- menuIds = menuIds || [];
- if (typeof menuIds === 'string') {
- menuIds = [menuIds];
- }
- $.each(this.menus, function (k, menu) {
- if (menuIds.indexOf(k) >= 0) {
- return;
- }
- menu.disabled(false);
- });
- };
- // 除了传入的 menuIds,其他全部禁用
- E.fn.disableMenusExcept = function (menuIds) {
- if (this._disabled) {
- // 编辑器处于禁用状态,则不执行改操作
- return;
- }
- // menuIds参数:支持数组和字符串
- menuIds = menuIds || [];
- if (typeof menuIds === 'string') {
- menuIds = [menuIds];
- }
- $.each(this.menus, function (k, menu) {
- if (menuIds.indexOf(k) >= 0) {
- return;
- }
- menu.disabled(true);
- });
- };
- // 隐藏所有 dropPanel droplist modal
- E.fn.hideDropPanelAndModal = function () {
- var menus = this.menus;
- $.each(menus, function (k, menu) {
- var m = menu.dropPanel || menu.dropList || menu.modal;
- if (m && m.hide) {
- m.hide();
- }
- });
- };
- });
- // selection range API
- _e(function (E, $) {
- // 用到 w3c range 的函数,如果检测到浏览器不支持 w3c range,则赋值为空函数
- var ieRange = !E.w3cRange;
- function emptyFn() {}
- // 设置或读取当前的range
- E.fn.currentRange = function (cr){
- if (cr) {
- this._rangeData = cr;
- } else {
- return this._rangeData;
- }
- };
- // 将当前选区折叠
- E.fn.collapseRange = function (range, opt) {
- // opt 参数说明:'start'-折叠到开始; 'end'-折叠到结束
- opt = opt || 'end';
- opt = opt === 'start' ? true : false;
- range = range || this.currentRange();
- if (range) {
- // 合并,保存
- range.collapse(opt);
- this.currentRange(range);
- }
- };
- // 获取选区的文字
- E.fn.getRangeText = ieRange ? emptyFn : function (range) {
- range = range || this.currentRange();
- if (!range) {
- return;
- }
- return range.toString();
- };
- // 获取选区对应的DOM对象
- E.fn.getRangeElem = ieRange ? emptyFn : function (range) {
- range = range || this.currentRange();
- var dom = range.commonAncestorContainer;
- if (dom.nodeType === 1) {
- return dom;
- } else {
- return dom.parentNode;
- }
- };
- // 选区内容是否为空?
- E.fn.isRangeEmpty = ieRange ? emptyFn : function (range) {
- range = range || this.currentRange();
- if (range && range.startContainer) {
- if (range.startContainer === range.endContainer) {
- if (range.startOffset === range.endOffset) {
- return true;
- }
- }
- }
- return false;
- };
- // 保存选区数据
- E.fn.saveSelection = ieRange ? emptyFn : function (range) {
- var self = this,
- _parentElem,
- selection,
- txt = self.txt.$txt.get(0);
- if (range) {
- _parentElem = range.commonAncestorContainer;
- } else {
- selection = document.getSelection();
- if (selection.getRangeAt && selection.rangeCount) {
- range = document.getSelection().getRangeAt(0);
- _parentElem = range.commonAncestorContainer;
- }
- }
- // 确定父元素一定要包含在编辑器区域内
- if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
- // 保存选择区域
- self.currentRange(range);
- }
- };
- // 恢复选中区域
- E.fn.restoreSelection = ieRange ? emptyFn : function (range) {
- var selection;
- range = range || this.currentRange();
- if (!range) {
- return;
- }
- // 使用 try catch 来防止 IE 某些情况报错
- try {
- selection = document.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- } catch (ex) {
- E.error('执行 editor.restoreSelection 时,IE可能会有异常,不影响使用');
- }
- };
- // 根据elem恢复选区
- E.fn.restoreSelectionByElem = ieRange ? emptyFn : function (elem, opt) {
- // opt参数说明:'start'-折叠到开始,'end'-折叠到结束,'all'-全部选中
- if (!elem) {
- return;
- }
- opt = opt || 'end'; // 默认为折叠到结束
- // 根据elem获取选区
- this.setRangeByElem(elem);
- // 根据 opt 折叠选区
- if (opt === 'start') {
- this.collapseRange(this.currentRange(), 'start');
- }
- if (opt === 'end') {
- this.collapseRange(this.currentRange(), 'end');
- }
- // 恢复选区
- this.restoreSelection();
- };
- // 初始化选区
- E.fn.initSelection = ieRange ? emptyFn : function () {
- var editor = this;
- if( editor.currentRange() ){
- //如果currentRange有值,则不用再初始化
- return;
- }
- var range;
- var $txt = editor.txt.$txt;
- var $firstChild = $txt.children().first();
- if ($firstChild.length) {
- editor.restoreSelectionByElem($firstChild.get(0));
- }
- };
- // 根据元素创建选区
- E.fn.setRangeByElem = ieRange ? emptyFn : function (elem) {
- var editor = this;
- var txtElem = editor.txt.$txt.get(0);
- if (!elem || !$.contains(txtElem, elem)) {
- return;
- }
- // 找到elem的第一个 textNode 和 最后一个 textNode
- var firstTextNode = elem.firstChild;
- while (firstTextNode) {
- if (firstTextNode.nodeType === 3) {
- break;
- }
- // 继续向下
- firstTextNode = firstTextNode.firstChild;
- }
- var lastTextNode = elem.lastChild;
- while (lastTextNode) {
- if (lastTextNode.nodeType === 3) {
- break;
- }
- // 继续向下
- lastTextNode = lastTextNode.lastChild;
- }
- var range = document.createRange();
- if (firstTextNode && lastTextNode) {
- // 说明 elem 有内容,能取到子元素
- range.setStart(firstTextNode, 0);
- range.setEnd(lastTextNode, lastTextNode.textContent.length);
- } else {
- // 说明 elem 无内容
- range.setStart(elem, 0);
- range.setEnd(elem, 0);
- }
- // 保存选区
- editor.saveSelection(range);
- };
- });
- // selection range API - IE8及以下
- _e(function (E, $) {
- if (E.w3cRange) {
- // 说明支持 W3C 的range方法
- return;
- }
- // -----------------IE8时,需要重写以下方法-------------------
- // 获取选区的文字
- E.fn.getRangeText = function (range) {
- range = range || this.currentRange();
- if (!range) {
- return;
- }
- return range.text;
- };
- // 获取选区对应的DOM对象
- E.fn.getRangeElem = function (range) {
- range = range || this.currentRange();
- if (!range) {
- return;
- }
- var dom = range.parentElement();
- if (dom.nodeType === 1) {
- return dom;
- } else {
- return dom.parentNode;
- }
- };
- // 选区内容是否为空?
- E.fn.isRangeEmpty = function (range) {
- range = range || this.currentRange();
- if (range && range.text) {
- return false;
- }
- return true;
- };
- // 保存选区数据
- E.fn.saveSelection = function (range) {
- var self = this,
- _parentElem,
- selection,
- txt = self.txt.$txt.get(0);
- if (range) {
- _parentElem = range.parentElement();
- } else {
- range = document.selection.createRange();
- if(typeof range.parentElement === 'undefined'){
- //IE6、7中,insertImage后会执行此处
- //由于找不到range.parentElement,所以干脆将_parentElem赋值为null
- _parentElem = null;
- }else{
- _parentElem = range.parentElement();
- }
- }
- // 确定父元素一定要包含在编辑器区域内
- if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
- // 保存选择区域
- self.currentRange(range);
- }
- };
- // 恢复选中区域
- E.fn.restoreSelection = function (currentRange){
- var editor = this,
- selection,
- range;
- currentRange = currentRange || editor.currentRange();
- if(!currentRange){
- return;
- }
- range = document.selection.createRange();
- try {
- // 此处,plupload上传上传图片时,IE8-会报一个『参数无效』的错误
- range.setEndPoint('EndToEnd', currentRange);
- } catch (ex) {
- }
- if(currentRange.text.length === 0){
- try {
- // IE8 插入表情会报错
- range.collapse(false);
- } catch (ex) {
- }
- }else{
- range.setEndPoint('StartToStart', currentRange);
- }
- range.select();
- };
- });
- // editor command hooks
- _e(function (E, $) {
- E.fn.commandHooks = function () {
- var editor = this;
- var commandHooks = {};
- // insertHtml
- commandHooks.insertHtml = function (html) {
- var $elem = $(html);
- var rangeElem = editor.getRangeElem();
- var targetElem;
- targetElem = editor.getLegalTags(rangeElem);
- if (!targetElem) {
- return;
- }
- $(targetElem).after($elem);
- };
- // 保存到对象
- editor.commandHooks = commandHooks;
- };
- });
- // editor command API
- _e(function (E, $) {
- // 基本命令
- E.fn.command = function (e, commandName, commandValue, callback) {
- var editor = this;
- var hooks;
- function commandFn() {
- if (!commandName) {
- return;
- }
- if (editor.queryCommandSupported(commandName)) {
- // 默认命令
- document.execCommand(commandName, false, commandValue);
- } else {
- // hooks 命令
- hooks = editor.commandHooks;
- if (commandName in hooks) {
- hooks[commandName](commandValue);
- }
- }
- }
- this.customCommand(e, commandFn, callback);
- };
- // 针对一个elem对象执行基础命令
- E.fn.commandForElem = function (elemOpt, e, commandName, commandValue, callback) {
- // 取得查询elem的查询条件和验证函数
- var selector;
- var check;
- if (typeof elemOpt === 'string') {
- selector = elemOpt;
- } else {
- selector = elemOpt.selector;
- check = elemOpt.check;
- }
- // 查询elem
- var rangeElem = this.getRangeElem();
- rangeElem = this.getSelfOrParentByName(rangeElem, selector, check);
- // 根据elem设置range
- if (rangeElem) {
- this.setRangeByElem(rangeElem);
- }
- // 然后执行基础命令
- this.command(e, commandName, commandValue, callback);
- };
- // 自定义命令
- E.fn.customCommand = function (e, commandFn, callback) {
- var editor = this;
- var range = editor.currentRange();
- if (!range) {
- // 目前没有选区,则无法执行命令
- e && e.preventDefault();
- return;
- }
- // 记录内容,以便撤销(执行命令之前就要记录)
- editor.undoRecord();
- // 恢复选区(有 range 参数)
- this.restoreSelection(range);
- // 执行命令事件
- commandFn.call(editor);
- // 保存选区(无参数,要从浏览器直接获取range信息)
- this.saveSelection();
- // 重新恢复选区(无参数,要取得刚刚从浏览器得到的range信息)
- this.restoreSelection();
- // 执行 callback
- if (callback && typeof callback === 'function') {
- callback.call(editor);
- }
- // 最后插入空行
- editor.txt.insertEmptyP();
- // 包裹暴露的img和text
- editor.txt.wrapImgAndText();
- // 更新内容
- editor.updateValue();
- // 更新菜单样式
- editor.updateMenuStyle();
- // 隐藏 dropPanel dropList modal 设置 200ms 间隔
- function hidePanelAndModal() {
- editor.hideDropPanelAndModal();
- }
- setTimeout(hidePanelAndModal, 200);
- if (e) {
- e.preventDefault();
- }
- };
- // 封装 document.queryCommandValue 函数
- // IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
- E.fn.queryCommandValue = function (commandName) {
- var result = '';
- try {
- result = document.queryCommandValue(commandName);
- } catch (ex) {
- }
- return result;
- };
- // 封装 document.queryCommandState 函数
- // IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
- E.fn.queryCommandState = function (commandName) {
- var result = false;
- try {
- result = document.queryCommandState(commandName);
- } catch (ex) {
- }
- return result;
- };
- // 封装 document.queryCommandSupported 函数
- E.fn.queryCommandSupported = function (commandName) {
- var result = false;
- try {
- result = document.queryCommandSupported(commandName);
- } catch (ex) {
- }
- return result;
- };
- });
- // dom selector
- _e(function (E, $) {
- var matchesSelector;
- // matchesSelector hook
- function _matchesSelectorForIE(selector) {
- var elem = this;
- var $elems = $(selector);
- var result = false;
- // 用jquery查找 selector 所有对象,如果其中有一个和传入 elem 相同,则证明 elem 符合 selector
- $elems.each(function () {
- if (this === elem) {
- result = true;
- return false;
- }
- });
- return result;
- }
- // 从当前的elem,往上去查找合法标签 如 p head table blockquote ul ol 等
- E.fn.getLegalTags = function (elem) {
- var legalTags = this.config.legalTags;
- if (!legalTags) {
- E.error('配置项中缺少 legalTags 的配置');
- return;
- }
- return this.getSelfOrParentByName(elem, legalTags);
- };
- // 根据条件,查询自身或者父元素,符合即返回
- E.fn.getSelfOrParentByName = function (elem, selector, check) {
- if (!elem || !selector) {
- return;
- }
- if (!matchesSelector) {
- // 定义 matchesSelector 函数
- matchesSelector = elem.webkitMatchesSelector ||
- elem.mozMatchesSelector ||
- elem.oMatchesSelector ||
- elem.matchesSelector;
- }
- if (!matchesSelector) {
- // 如果浏览器本身不支持 matchesSelector 则使用自定义的hook
- matchesSelector = _matchesSelectorForIE;
- }
- var txt = this.txt.$txt.get(0);
- while (elem && txt !== elem && $.contains(txt, elem)) {
- if (matchesSelector.call(elem, selector)) {
- // 符合 selector 查询条件
- if (!check) {
- // 没有 check 验证函数,直接返回即可
- return elem;
- }
- if (check(elem)) {
- // 如果有 check 验证函数,还需 check 函数的确认
- return elem;
- }
- }
- // 如果上一步没经过验证,则将跳转到父元素
- elem = elem.parentNode;
- }
- return;
- };
- });
- // undo redo
- _e(function (E, $) {
- var length = 20; // 缓存的最大长度
- function _getRedoList(editor) {
- if (editor._redoList == null) {
- editor._redoList = [];
- }
- return editor._redoList;
- }
- function _getUndoList(editor) {
- if (editor._undoList == null) {
- editor._undoList = [];
- }
- return editor._undoList;
- }
- // 数据处理
- function _handle(editor, data, type) {
- // var range = data.range;
- // var range2 = range.cloneRange && range.cloneRange();
- var val = data.val;
- var html = editor.txt.$txt.html();
- if(val == null) {
- return;
- }
- if (val === html) {
- if (type === 'redo') {
- editor.redo();
- return;
- } else if (type === 'undo') {
- editor.undo();
- return;
- } else {
- return;
- }
- }
- // 保存数据
- editor.txt.$txt.html(val);
- // 更新数据到textarea(有必要的话)
- editor.updateValue();
- // onchange 事件
- if (editor.onchange && typeof editor.onchange === 'function') {
- editor.onchange.call(editor);
- }
- // ?????
- // 注释:$txt 被重新赋值之后,range会被重置,cloneRange() 也不好使
- // // 重置选区
- // if (range2) {
- // editor.restoreSelection(range2);
- // }
- }
- // 记录
- E.fn.undoRecord = function () {
- var editor = this;
- var $txt = editor.txt.$txt;
- var val = $txt.html();
- var undoList = _getUndoList(editor);
- var redoList = _getRedoList(editor);
- var currentVal = undoList.length ? undoList[0] : '';
- if (val === currentVal.val) {
- return;
- }
- // 清空 redolist
- if (redoList.length) {
- redoList = [];
- }
- // 添加数据到 undoList
- undoList.unshift({
- range: editor.currentRange(), // 将当前的range也记录下
- val: val
- });
- // 限制 undoList 长度
- if (undoList.length > length) {
- undoList.pop();
- }
- };
- // undo 操作
- E.fn.undo = function () {
- var editor = this;
- var undoList = _getUndoList(editor);
- var redoList = _getRedoList(editor);
- if (!undoList.length) {
- return;
- }
- // 取出 undolist 第一个值,加入 redolist
- var data = undoList.shift();
- redoList.unshift(data);
- // 并修改编辑器的内容
- _handle(this, data, 'undo');
- };
- // redo 操作
- E.fn.redo = function () {
- var editor = this;
- var undoList = _getUndoList(editor);
- var redoList = _getRedoList(editor);
- if (!redoList.length) {
- return;
- }
- // 取出 redolist 第一个值,加入 undolist
- var data = redoList.shift();
- undoList.unshift(data);
- // 并修改编辑器的内容
- _handle(this, data, 'redo');
- };
- });
- // 暴露给用户的 API
- _e(function (E, $) {
- // 创建编辑器
- E.fn.create = function () {
- var editor = this;
- // 检查 E.$body 是否有值
- // 如果在 body 之前引用了 js 文件,body 尚未加载,可能没有值
- if (!E.$body || E.$body.length === 0) {
- E.$body = $('body');
- E.$document = $(document);
- E.$window = $(window);
- }
- // 执行 addMenus 之前:
- // 1. 允许用户修改 editor.UI 自定义配置UI
- // 2. 允许用户通过修改 editor.menus 来自定义配置菜单
- // 因此要在 create 时执行,而不是 init
- editor.addMenus();
- // 渲染
- editor.renderMenus();
- editor.renderMenuContainer();
- editor.renderTxt();
- editor.renderEditorContainer();
- // 绑定事件
- editor.eventMenus();
- editor.eventMenuContainer();
- editor.eventTxt();
- // 处理ready事件
- editor.readyHeadler();
- // 初始化选区
- editor.initSelection();
- // $txt 快捷方式
- editor.$txt = editor.txt.$txt;
- // 执行用户自定义事件,通过 E.ready() 添加
- var _plugins = E._plugins;
- if (_plugins && _plugins.length) {
- $.each(_plugins, function (k, val) {
- val.call(editor);
- });
- }
- };
- // 禁用编辑器
- E.fn.disable = function () {
- this.txt.$txt.removeAttr('contenteditable');
- this.disableMenusExcept();
- // 先禁用,再记录状态
- this._disabled = true;
- };
- // 启用编辑器
- E.fn.enable = function () {
- // 先解除状态记录,再启用
- this._disabled = false;
- this.txt.$txt.attr('contenteditable', 'true');
- this.enableMenusExcept();
- };
- // 销毁编辑器
- E.fn.destroy = function () {
- var self = this;
- var $valueContainer = self.$valueContainer;
- var $editorContainer = self.$editorContainer;
- var valueNodeName = self.valueNodeName;
- if (valueNodeName === 'div') {
- // div 生成的编辑器
- $valueContainer.removeAttr('contenteditable');
- $editorContainer.after($valueContainer);
- $editorContainer.hide();
- } else {
- // textarea 生成的编辑器
- $valueContainer.show();
- $editorContainer.hide();
- }
- };
- // 撤销 销毁编辑器
- E.fn.undestroy = function () {
- var self = this;
- var $valueContainer = self.$valueContainer;
- var $editorContainer = self.$editorContainer;
- var $menuContainer = self.menuContainer.$menuContainer;
- var valueNodeName = self.valueNodeName;
- if (valueNodeName === 'div') {
- // div 生成的编辑器
- $valueContainer.attr('contenteditable', 'true');
- $menuContainer.after($valueContainer);
- $editorContainer.show();
- } else {
- // textarea 生成的编辑器
- $valueContainer.hide();
- $editorContainer.show();
- }
- };
- // 清空内容的快捷方式
- E.fn.clear = function () {
- var editor = this;
- var $txt = editor.txt.$txt;
- $txt.html('<p><br></p>');
- editor.restoreSelectionByElem($txt.find('p').get(0));
- };
- });
- // menuContainer 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var MenuContainer = function (editor) {
- this.editor = editor;
- this.init();
- };
- MenuContainer.fn = MenuContainer.prototype;
- // 暴露给 E 即 window.wangEditor
- E.MenuContainer = MenuContainer;
- });
- // MenuContainer.fn bind fn
- _e(function (E, $) {
- var MenuContainer = E.MenuContainer;
- // 初始化
- MenuContainer.fn.init = function () {
- var self = this;
- var $menuContainer = $('<div class="wangEditor-menu-container clearfix"></div>');
- self.$menuContainer = $menuContainer;
- // change shadow
- self.changeShadow();
- };
- // 编辑区域滚动时,增加shadow
- MenuContainer.fn.changeShadow = function () {
- var $menuContainer = this.$menuContainer;
- var editor = this.editor;
- var $txt = editor.txt.$txt;
- $txt.on('scroll', function () {
- if ($txt.scrollTop() > 10) {
- $menuContainer.addClass('wangEditor-menu-shadow');
- } else {
- $menuContainer.removeClass('wangEditor-menu-shadow');
- }
- });
- };
- });
- // MenuContainer.fn API
- _e(function (E, $) {
- var MenuContainer = E.MenuContainer;
- MenuContainer.fn.render = function () {
- var $menuContainer = this.$menuContainer;
- var $editorContainer = this.editor.$editorContainer;
- $editorContainer.append($menuContainer);
- };
- // 获取菜单栏的高度
- MenuContainer.fn.height = function () {
- var $menuContainer = this.$menuContainer;
- return $menuContainer.height();
- };
- // 添加菜单
- MenuContainer.fn.appendMenu = function (groupIdx, menu) {
- // 判断是否需要新增一个菜单组
- this._addGroup(groupIdx);
- // 增加菜单(返回 $menuItem)
- return this._addOneMenu(menu);
- };
- MenuContainer.fn._addGroup = function (groupIdx) {
- var $menuContainer = this.$menuContainer;
- var $menuGroup;
- if (!this.$currentGroup || this.currentGroupIdx !== groupIdx) {
- $menuGroup = $('<div class="menu-group clearfix"></div>');
- $menuContainer.append($menuGroup);
- this.$currentGroup = $menuGroup;
- this.currentGroupIdx = groupIdx;
- }
- };
- MenuContainer.fn._addOneMenu = function (menu) {
- var $menuNormal = menu.$domNormal;
- var $menuSelected = menu.$domSelected;
- var $menuGroup = this.$currentGroup;
- var $item = $('<div class="menu-item clearfix"></div>');
- $menuSelected.hide();
- $item.append($menuNormal).append($menuSelected);
- $menuGroup.append($item);
- return $item;
- };
- });
- // menu 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var Menu = function (opt) {
- this.editor = opt.editor;
- this.id = opt.id;
- this.title = opt.title;
- this.$domNormal = opt.$domNormal;
- this.$domSelected = opt.$domSelected || opt.$domNormal;
- // document.execCommand 的参数
- this.commandName = opt.commandName;
- this.commandValue = opt.commandValue;
- this.commandNameSelected = opt.commandNameSelected || opt.commandName;
- this.commandValueSelected = opt.commandValueSelected || opt.commandValue;
- };
- Menu.fn = Menu.prototype;
- // 暴露给 E 即 window.wangEditor
- E.Menu = Menu;
- });
- // Menu.fn 初始化绑定的事件
- _e(function (E, $) {
- var Menu = E.Menu;
- // 初始化UI
- Menu.fn.initUI = function () {
- var editor = this.editor;
- var uiConfig = editor.UI.menus;
- var menuId = this.id;
- var menuUI = uiConfig[menuId];
- if (this.$domNormal && this.$domSelected) {
- // 自定义的菜单中,已经传入了 $dom 无需从配置文件中查找生成
- return;
- }
- if (menuUI == null) {
- E.warn('editor.UI配置中,没有菜单 "' + menuId + '" 的UI配置,只能取默认值');
- // 必须写成 uiConfig['default'];
- // 写成 uiConfig.default IE8会报错
- menuUI = uiConfig['default'];
- }
- // 正常状态
- this.$domNormal = $(menuUI.normal);
- // 选中状态
- if (/^\./.test(menuUI.selected)) {
- // 增加一个样式
- this.$domSelected = this.$domNormal.clone().addClass(menuUI.selected.slice(1));
- } else {
- // 一个新的dom对象
- this.$domSelected = $(menuUI.selected);
- }
- };
- });
- // Menu.fn API
- _e(function (E, $) {
- var Menu = E.Menu;
- // 渲染菜单
- Menu.fn.render = function (groupIdx) {
- // 渲染UI
- this.initUI();
- var editor = this.editor;
- var menuContainer = editor.menuContainer;
- var $menuItem = menuContainer.appendMenu(groupIdx, this);
- var onRender = this.onRender;
- // 渲染tip
- this._renderTip($menuItem);
- // 执行 onRender 函数
- if (onRender && typeof onRender === 'function') {
- onRender.call(this);
- }
- };
- Menu.fn._renderTip = function ($menuItem) {
- var self = this;
- var editor = self.editor;
- var title = self.title;
- var $tip = $('<div class="menu-tip"></div>');
- // var $triangle = $('<i class="tip-triangle"></i>'); // 小三角
- // 计算 tip 宽度
- var $tempDiv;
- if (!self.tipWidth) {
- // 设置一个纯透明的 p(absolute;top:-10000px;不会显示在内容区域)
- // 内容赋值为 title ,为了计算tip宽度
- $tempDiv = $('<p style="opacity:0; filter:Alpha(opacity=0); position:absolute;top:-10000px;">' + title + '</p>');
- // 先添加到body,计算完再 remove
- E.$body.append($tempDiv);
- editor.ready(function () {
- var editor = this;
- var titleWidth = $tempDiv.outerWidth() + 5; // 多出 5px 的冗余
- var currentWidth = $tip.outerWidth();
- var currentMarginLeft = parseFloat($tip.css('margin-left'), 10);
- // 计算完,拿到数据,则弃用
- $tempDiv.remove();
- $tempDiv = null;
- // 重新设置样式
- $tip.css({
- width: titleWidth,
- 'margin-left': currentMarginLeft + (currentWidth - titleWidth)/2
- });
- // 存储
- self.tipWidth = titleWidth;
- });
- }
- // $tip.append($triangle);
- $tip.append(title);
- $menuItem.append($tip);
- function show() {
- $tip.show();
- }
- function hide() {
- $tip.hide();
- }
- var timeoutId;
- $menuItem.find('a').on('mouseenter', function (e) {
- if (!self.active() && !self.disabled()) {
- timeoutId = setTimeout(show, 200);
- }
- }).on('mouseleave', function (e) {
- timeoutId && clearTimeout(timeoutId);
- hide();
- }).on('click', hide);
- };
- // 绑定事件
- Menu.fn.bindEvent = function () {
- var self = this;
- var $domNormal = self.$domNormal;
- var $domSelected = self.$domSelected;
- // 试图获取该菜单定义的事件(未selected),没有则自己定义
- var clickEvent = self.clickEvent;
- if (!clickEvent) {
- clickEvent = function (e) {
- // -----------dropPanel dropList modal-----------
- var dropObj = self.dropPanel || self.dropList || self.modal;
- if (dropObj && dropObj.show) {
- if (dropObj.isShowing) {
- dropObj.hide();
- } else {
- dropObj.show();
- }
- return;
- }
- // -----------command-----------
- var editor = self.editor;
- var commandName;
- var commandValue;
- var selected = self.selected;
- if (selected) {
- commandName = self.commandNameSelected;
- commandValue = self.commandValueSelected;
- } else {
- commandName = self.commandName;
- commandValue = self.commandValue;
- }
- if (commandName) {
- // 执行命令
- editor.command(e, commandName, commandValue);
- } else {
- // 提示
- E.warn('菜单 "' + self.id + '" 未定义click事件');
- e.preventDefault();
- }
- };
- }
- // 获取菜单定义的selected情况下的点击事件
- var clickEventSelected = self.clickEventSelected || clickEvent;
- // 将事件绑定到菜单dom上
- $domNormal.click(function (e) {
- if (!self.disabled()) {
- clickEvent.call(self, e);
- self.updateSelected();
- }
- e.preventDefault();
- });
- $domSelected.click(function (e) {
- if (!self.disabled()) {
- clickEventSelected.call(self, e);
- self.updateSelected();
- }
- e.preventDefault();
- });
- };
- // 更新选中状态
- Menu.fn.updateSelected = function () {
- var self = this;
- var editor = self.editor;
- // 试图获取用户自定义的判断事件
- var updateSelectedEvent = self.updateSelectedEvent;
- if (!updateSelectedEvent) {
- // 用户未自定义,则设置默认值
- updateSelectedEvent = function () {
- var self = this;
- var editor = self.editor;
- var commandName = self.commandName;
- var commandValue = self.commandValue;
- if (commandValue) {
- if (editor.queryCommandValue(commandName).toLowerCase() === commandValue.toLowerCase()) {
- return true;
- }
- } else if (editor.queryCommandState(commandName)) {
- return true;
- }
- return false;
- };
- }
- // 获取结果
- var result = updateSelectedEvent.call(self);
- result = !!result;
- // 存储结果、显示效果
- self.changeSelectedState(result);
- };
- // 切换选中状态、显示效果
- Menu.fn.changeSelectedState = function (state) {
- var self = this;
- var selected = self.selected;
- if (state != null && typeof state === 'boolean') {
- if (selected === state) {
- // 计算结果和当前的状态一样
- return;
- }
- // 存储结果
- self.selected = state;
- // 切换菜单的显示
- if (state) {
- // 选中
- self.$domNormal.hide();
- self.$domSelected.show();
- } else {
- // 未选中
- self.$domNormal.show();
- self.$domSelected.hide();
- }
- } // if
- };
- // 点击菜单,显示了 dropPanel modal 时,菜单的状态
- Menu.fn.active = function (active) {
- if (active == null) {
- return this._activeState;
- }
- this._activeState = active;
- };
- Menu.fn.activeStyle = function (active) {
- var selected = this.selected;
- var $dom = this.$domNormal;
- var $domSelected = this.$domSelected;
- if (active) {
- $dom.addClass('active');
- $domSelected.addClass('active');
- } else {
- $dom.removeClass('active');
- $domSelected.removeClass('active');
- }
- // 记录状态 ( menu hover 时会取状态用 )
- this.active(active);
- };
- // 菜单的启用和禁用
- Menu.fn.disabled = function (opt) {
- // 参数为空,取值
- if (opt == null) {
- return !!this._disabled;
- }
- if (this._disabled === opt) {
- // 要设置的参数值和当前参数只一样,无需再次设置
- return;
- }
- var $dom = this.$domNormal;
- var $domSelected = this.$domSelected;
- // 设置样式
- if (opt) {
- $dom.addClass('disable');
- $domSelected.addClass('disable');
- } else {
- $dom.removeClass('disable');
- $domSelected.removeClass('disable');
- }
- // 存储
- this._disabled = opt;
- };
- });
- // dropList 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var DropList = function (editor, menu, opt) {
- this.editor = editor;
- this.menu = menu;
- // list 的数据源,格式 {'commandValue': 'title', ...}
- this.data = opt.data;
- // 要为每个item自定义的模板
- this.tpl = opt.tpl;
- // 为了执行 editor.commandForElem 而传入的elem查询方式
- this.selectorForELemCommand = opt.selectorForELemCommand;
- // 执行事件前后的钩子
- this.beforeEvent = opt.beforeEvent;
- this.afterEvent = opt.afterEvent;
- // 初始化
- this.init();
- };
- DropList.fn = DropList.prototype;
- // 暴露给 E 即 window.wangEditor
- E.DropList = DropList;
- });
- // dropList fn bind
- _e(function (E, $) {
- var DropList = E.DropList;
- // init
- DropList.fn.init = function () {
- var self = this;
- // 生成dom对象
- self.initDOM();
- // 绑定command事件
- self.bindEvent();
- // 声明隐藏的事件
- self.initHideEvent();
- };
- // 初始化dom结构
- DropList.fn.initDOM = function () {
- var self = this;
- var data = self.data;
- var tpl = self.tpl || '<span>{#title}</span>';
- var $list = $('<div class="wangEditor-drop-list clearfix"></div>');
- var itemContent;
- var $item;
- $.each(data, function (commandValue, title) {
- itemContent = tpl.replace(/{#commandValue}/ig, commandValue).replace(/{#title}/ig, title);
- $item = $('<a href="#" commandValue="' + commandValue + '"></a>');
- $item.append(itemContent);
- $list.append($item);
- });
- self.$list = $list;
- };
- // 绑定事件
- DropList.fn.bindEvent = function () {
- var self = this;
- var editor = self.editor;
- var menu = self.menu;
- var commandName = menu.commandName;
- var selectorForELemCommand = self.selectorForELemCommand;
- var $list = self.$list;
- // 执行事件前后的钩子函数
- var beforeEvent = self.beforeEvent;
- var afterEvent = self.afterEvent;
- $list.on('click', 'a[commandValue]', function (e) {
- // 正式命令执行之前
- if (beforeEvent && typeof beforeEvent === 'function') {
- beforeEvent.call(e);
- }
- // 执行命令
- var commandValue = $(e.currentTarget).attr('commandValue');
- if (menu.selected && editor.isRangeEmpty() && selectorForELemCommand) {
- // 当前处于选中状态,并且选中内容为空
- editor.commandForElem(selectorForELemCommand, e, commandName, commandValue);
- } else {
- // 当前未处于选中状态,或者有选中内容。则执行默认命令
- editor.command(e, commandName, commandValue);
- }
- // 正式命令之后的钩子
- if (afterEvent && typeof afterEvent === 'function') {
- afterEvent.call(e);
- }
- });
- };
- // 点击其他地方,立即隐藏 droplist
- DropList.fn.initHideEvent = function () {
- var self = this;
- // 获取 list elem
- var thisList = self.$list.get(0);
- E.$body.on('click', function (e) {
- if (!self.isShowing) {
- return;
- }
- var trigger = e.target;
- // 获取菜单elem
- var menu = self.menu;
- var menuDom;
- if (menu.selected) {
- menuDom = menu.$domSelected.get(0);
- } else {
- menuDom = menu.$domNormal.get(0);
- }
- if (menuDom === trigger || $.contains(menuDom, trigger)) {
- // 说明由本菜单点击触发的
- return;
- }
- if (thisList === trigger || $.contains(thisList, trigger)) {
- // 说明由本list点击触发的
- return;
- }
- // 其他情况,隐藏 list
- self.hide();
- });
- E.$window.scroll(function () {
- self.hide();
- });
- E.$window.on('resize', function () {
- self.hide();
- });
- };
- });
- // dropListfn api
- _e(function (E, $) {
- var DropList = E.DropList;
- // 渲染
- DropList.fn._render = function () {
- var self = this;
- var editor = self.editor;
- var $list = self.$list;
- // 渲染到页面
- editor.$editorContainer.append($list);
- // 记录状态
- self.rendered = true;
- };
- // 定位
- DropList.fn._position = function () {
- var self = this;
- var $list = self.$list;
- var editor = self.editor;
- var menu = self.menu;
- var $menuContainer = editor.menuContainer.$menuContainer;
- var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
- // 注意这里的 offsetParent() 要返回 .menu-item 的 position
- // 因为 .menu-item 是 position:relative
- var menuPosition = $menuDom.offsetParent().position();
- // 取得 menu 的位置、尺寸属性
- var menuTop = menuPosition.top;
- var menuLeft = menuPosition.left;
- var menuHeight = $menuDom.offsetParent().height();
- var menuWidth = $menuDom.offsetParent().width();
- // 取得 list 的尺寸属性
- var listWidth = $list.outerWidth();
- // var listHeight = $list.outerHeight();
- // 取得 $txt 的尺寸
- var txtWidth = editor.txt.$txt.outerWidth();
- // ------------开始计算-------------
- // 初步计算 list 位置属性
- var top = menuTop + menuHeight;
- var left = menuLeft + menuWidth/2;
- var marginLeft = 0 - menuWidth/2;
- // 如果超出了有边界,则要左移(且和右侧有间隙)
- var valWithTxt = (left + listWidth) - txtWidth;
- if (valWithTxt > -10) {
- marginLeft = marginLeft - valWithTxt - 10;
- }
- // 设置样式
- $list.css({
- top: top,
- left: left,
- 'margin-left': marginLeft
- });
- // 如果因为向下滚动而导致菜单fixed,则再加一步处理
- if (editor._isMenufixed) {
- top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $list.offset().top);
- // 重新设置top
- $list.css({
- top: top
- });
- }
- };
- // 显示
- DropList.fn.show = function () {
- var self = this;
- var menu = self.menu;
- if (!self.rendered) {
- // 第一次show之前,先渲染
- self._render();
- }
- if (self.isShowing) {
- return;
- }
- var $list = self.$list;
- $list.show();
- // 定位
- self._position();
- // 记录状态
- self.isShowing = true;
- // 菜单状态
- menu.activeStyle(true);
- };
- // 隐藏
- DropList.fn.hide = function () {
- var self = this;
- var menu = self.menu;
- if (!self.isShowing) {
- return;
- }
- var $list = self.$list;
- $list.hide();
- // 记录状态
- self.isShowing = false;
- // 菜单状态
- menu.activeStyle(false);
- };
- });
- // dropPanel 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var DropPanel = function (editor, menu, opt) {
- this.editor = editor;
- this.menu = menu;
- this.$content = opt.$content;
- this.width = opt.width || 200;
- this.height = opt.height;
- this.onRender = opt.onRender;
- // init
- this.init();
- };
- DropPanel.fn = DropPanel.prototype;
- // 暴露给 E 即 window.wangEditor
- E.DropPanel = DropPanel;
- });
- // dropPanel fn bind
- _e(function (E, $) {
- var DropPanel = E.DropPanel;
- // init
- DropPanel.fn.init = function () {
- var self = this;
- // 生成dom对象
- self.initDOM();
- // 声明隐藏的事件
- self.initHideEvent();
- };
- // init DOM
- DropPanel.fn.initDOM = function () {
- var self = this;
- var $content = self.$content;
- var width = self.width;
- var height = self.height;
- var $panel = $('<div class="wangEditor-drop-panel clearfix"></div>');
- var $triangle = $('<div class="tip-triangle"></div>');
- $panel.css({
- width: width,
- height: height ? height : 'auto'
- });
- $panel.append($triangle);
- $panel.append($content);
- // 添加对象数据
- self.$panel = $panel;
- self.$triangle = $triangle;
- };
- // 点击其他地方,立即隐藏 dropPanel
- DropPanel.fn.initHideEvent = function () {
- var self = this;
- // 获取 panel elem
- var thisPanle = self.$panel.get(0);
- E.$body.on('click', function (e) {
- if (!self.isShowing) {
- return;
- }
- var trigger = e.target;
- // 获取菜单elem
- var menu = self.menu;
- var menuDom;
- if (menu.selected) {
- menuDom = menu.$domSelected.get(0);
- } else {
- menuDom = menu.$domNormal.get(0);
- }
- if (menuDom === trigger || $.contains(menuDom, trigger)) {
- // 说明由本菜单点击触发的
- return;
- }
- if (thisPanle === trigger || $.contains(thisPanle, trigger)) {
- // 说明由本panel点击触发的
- return;
- }
- // 其他情况,隐藏 panel
- self.hide();
- });
- E.$window.scroll(function (e) {
- self.hide();
- });
- E.$window.on('resize', function () {
- self.hide();
- });
- };
- });
- // dropPanel fn api
- _e(function (E, $) {
- var DropPanel = E.DropPanel;
- // 渲染
- DropPanel.fn._render = function () {
- var self = this;
- var onRender = self.onRender;
- var editor = self.editor;
- var $panel = self.$panel;
- // 渲染到页面
- editor.$editorContainer.append($panel);
- // 渲染后的回调事件
- onRender && onRender.call(self);
- // 记录状态
- self.rendered = true;
- };
- // 定位
- DropPanel.fn._position = function () {
- var self = this;
- var $panel = self.$panel;
- var $triangle = self.$triangle;
- var editor = self.editor;
- var $menuContainer = editor.menuContainer.$menuContainer;
- var menu = self.menu;
- var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
- // 注意这里的 offsetParent() 要返回 .menu-item 的 position
- // 因为 .menu-item 是 position:relative
- var menuPosition = $menuDom.offsetParent().position();
- // 取得 menu 的位置、尺寸属性
- var menuTop = menuPosition.top;
- var menuLeft = menuPosition.left;
- var menuHeight = $menuDom.offsetParent().height();
- var menuWidth = $menuDom.offsetParent().width();
- // 取得 panel 的尺寸属性
- var panelWidth = $panel.outerWidth();
- // var panelHeight = $panel.outerHeight();
- // 取得 $txt 的尺寸
- var txtWidth = editor.txt.$txt.outerWidth();
- // ------------开始计算-------------
- // 初步计算 panel 位置属性
- var top = menuTop + menuHeight;
- var left = menuLeft + menuWidth/2;
- var marginLeft = 0 - panelWidth/2;
- var marginLeft2 = marginLeft; // 下文用于和 marginLeft 比较,来设置三角形tip的位置
- // 如果超出了左边界,则移动回来(要和左侧有10px间隙)
- if ((0 - marginLeft) > (left - 10)) {
- marginLeft = 0 - (left - 10);
- }
- // 如果超出了有边界,则要左移(且和右侧有10px间隙)
- var valWithTxt = (left + panelWidth + marginLeft) - txtWidth;
- if (valWithTxt > -10) {
- marginLeft = marginLeft - valWithTxt - 10;
- }
- // 设置样式
- $panel.css({
- top: top,
- left: left,
- 'margin-left': marginLeft
- });
- // 如果因为向下滚动而导致菜单fixed,则再加一步处理
- if (editor._isMenufixed) {
- top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $panel.offset().top);
- // 重新设置top
- $panel.css({
- top: top
- });
- }
- // 设置三角形 tip 的位置
- $triangle.css({
- 'margin-left': marginLeft2 - marginLeft - 5
- });
- };
- // focus 第一个 input
- DropPanel.fn.focusFirstInput = function () {
- var self = this;
- var $panel = self.$panel;
- $panel.find('input[type=text],textarea').each(function () {
- var $input = $(this);
- if ($input.attr('disabled') == null) {
- $input.focus();
- return false;
- }
- });
- };
- // 显示
- DropPanel.fn.show = function () {
- var self = this;
- var menu = self.menu;
- if (!self.rendered) {
- // 第一次show之前,先渲染
- self._render();
- }
- if (self.isShowing) {
- return;
- }
- var $panel = self.$panel;
- $panel.show();
- // 定位
- self._position();
- // 记录状态
- self.isShowing = true;
- // 菜单状态
- menu.activeStyle(true);
- if (E.w3cRange) {
- // 高级浏览器
- self.focusFirstInput();
- } else {
- // 兼容 IE8 input placeholder
- E.placeholderForIE8($panel);
- }
- };
- // 隐藏
- DropPanel.fn.hide = function () {
- var self = this;
- var menu = self.menu;
- if (!self.isShowing) {
- return;
- }
- var $panel = self.$panel;
- $panel.hide();
- // 记录状态
- self.isShowing = false;
- // 菜单状态
- menu.activeStyle(false);
- };
- });
- // modal 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var Modal = function (editor, menu, opt) {
- this.editor = editor;
- this.menu = menu;
- this.$content = opt.$content;
- this.init();
- };
- Modal.fn = Modal.prototype;
- // 暴露给 E 即 window.wangEditor
- E.Modal = Modal;
- });
- // modal fn bind
- _e(function (E, $) {
- var Modal = E.Modal;
- Modal.fn.init = function () {
- var self = this;
- // 初始化dom
- self.initDom();
- // 初始化隐藏事件
- self.initHideEvent();
- };
- // 初始化dom
- Modal.fn.initDom = function () {
- var self = this;
- var $content = self.$content;
- var $modal = $('<div class="wangEditor-modal"></div>');
- var $close = $('<div class="wangEditor-modal-close"><i class="wangeditor-menu-img-cancel-circle"></i></div>');
- $modal.append($close);
- $modal.append($content);
- // 记录数据
- self.$modal = $modal;
- self.$close = $close;
- };
- // 初始化隐藏事件
- Modal.fn.initHideEvent = function () {
- var self = this;
- var $close = self.$close;
- var modal = self.$modal.get(0);
- // 点击 $close 按钮,隐藏
- $close.click(function () {
- self.hide();
- });
- // 点击其他部分,隐藏
- E.$body.on('click', function (e) {
- if (!self.isShowing) {
- return;
- }
- var trigger = e.target;
- // 获取菜单elem
- var menu = self.menu;
- var menuDom;
- if (menu) {
- if (menu.selected) {
- menuDom = menu.$domSelected.get(0);
- } else {
- menuDom = menu.$domNormal.get(0);
- }
- if (menuDom === trigger || $.contains(menuDom, trigger)) {
- // 说明由本菜单点击触发的
- return;
- }
- }
- if (modal === trigger || $.contains(modal, trigger)) {
- // 说明由本panel点击触发的
- return;
- }
- // 其他情况,隐藏 panel
- self.hide();
- });
- };
- });
- // modal fn api
- _e(function (E, $) {
- var Modal = E.Modal;
- // 渲染
- Modal.fn._render = function () {
- var self = this;
- var editor = self.editor;
- var $modal = self.$modal;
- // $modal的z-index,在配置的z-index基础上再 +10
- $modal.css('z-index', editor.config.zindex + 10 + '');
- // 渲染到body最后面
- E.$body.append($modal);
- // 记录状态
- self.rendered = true;
- };
- // 定位
- Modal.fn._position = function () {
- var self = this;
- var $modal = self.$modal;
- var top = $modal.offset().top;
- var width = $modal.outerWidth();
- var height = $modal.outerHeight();
- var marginLeft = 0 - (width / 2);
- var marginTop = 0 - (height / 2);
- var sTop = E.$window.scrollTop();
- // 保证modal最顶部,不超过浏览器上边框
- if ((height / 2) > top) {
- marginTop = 0 - top;
- }
- $modal.css({
- 'margin-left': marginLeft + 'px',
- 'margin-top': (marginTop + sTop) + 'px'
- });
- };
- // 显示
- Modal.fn.show = function () {
- var self = this;
- var menu = self.menu;
- if (!self.rendered) {
- // 第一次show之前,先渲染
- self._render();
- }
- if (self.isShowing) {
- return;
- }
- // 记录状态
- self.isShowing = true;
- var $modal = self.$modal;
- $modal.show();
- // 定位
- self._position();
- // 激活菜单状态
- menu && menu.activeStyle(true);
- };
- // 隐藏
- Modal.fn.hide = function () {
- var self = this;
- var menu = self.menu;
- if (!self.isShowing) {
- return;
- }
- // 记录状态
- self.isShowing = false;
- // 隐藏
- var $modal = self.$modal;
- $modal.hide();
- // 菜单状态
- menu && menu.activeStyle(false);
- };
- });
- // txt 构造函数
- _e(function (E, $) {
- // 定义构造函数
- var Txt = function (editor) {
- this.editor = editor;
- // 初始化
- this.init();
- };
- Txt.fn = Txt.prototype;
- // 暴露给 E 即 window.wangEditor
- E.Txt = Txt;
- });
- // Txt.fn bind fn
- _e(function (E, $) {
- var Txt = E.Txt;
- // 初始化
- Txt.fn.init = function () {
- var self = this;
- var editor = self.editor;
- var $valueContainer = editor.$valueContainer;
- var currentValue = editor.getInitValue();
- var $txt;
- if ($valueContainer.get(0).nodeName === 'DIV') {
- // 如果传入生成编辑器的元素就是div,则直接使用
- $txt = $valueContainer;
- $txt.addClass("wangEditor-txt");
- $txt.attr('contentEditable', 'true');
- } else {
- // 如果不是div(是textarea),则创建一个div
- $txt = $(
- '<div class="wangEditor-txt" contentEditable="true">' +
- currentValue +
- '</div>'
- );
- }
- // 试图最后插入一个空行,ready之后才行
- editor.ready(function () {
- self.insertEmptyP();
- });
- self.$txt = $txt;
- // 删除时,如果没有内容了,就添加一个 <p><br></p>
- self.contentEmptyHandle();
- // enter时,不能使用 div 换行
- self.bindEnterForDiv();
- // enter时,用 p 包裹 text
- self.bindEnterForText();
- // tab 插入4个空格
- self.bindTabEvent();
- // 处理粘贴内容
- self.bindPasteFilter();
- // $txt.formatText() 方法
- self.bindFormatText();
- // 定义 $txt.html() 方法
- self.bindHtml();
- };
- // 删除时,如果没有内容了,就添加一个 <p><br></p>
- Txt.fn.contentEmptyHandle = function () {
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- var $p;
- $txt.on('keydown', function (e) {
- if (e.keyCode !== 8) {
- return;
- }
- var txtHtml = $.trim($txt.html().toLowerCase());
- if (txtHtml === '<p><br></p>') {
- // 如果最后还剩余一个空行,就不再继续删除了
- e.preventDefault();
- return;
- }
- });
- $txt.on('keyup', function (e) {
- if (e.keyCode !== 8) {
- return;
- }
- var txtHtml = $.trim($txt.html().toLowerCase());
- // ff时用 txtHtml === '<br>' 判断,其他用 !txtHtml 判断
- if (!txtHtml || txtHtml === '<br>') {
- // 内容空了
- $p = $('<p><br/></p>');
- $txt.html(''); // 一定要先清空,否则在 ff 下有问题
- $txt.append($p);
- editor.restoreSelectionByElem($p.get(0));
- }
- });
- };
- // enter时,不能使用 div 换行
- Txt.fn.bindEnterForDiv = function () {
- var tags = E.config.legalTags; // 配置中编辑器要求的合法标签,如 p head table blockquote ul ol 等
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- var $keydownDivElem;
- function divHandler() {
- if (!$keydownDivElem) {
- return;
- }
- var $pElem = $('<p>' + $keydownDivElem.html() + '</p>');
- $keydownDivElem.after($pElem);
- $keydownDivElem.remove();
- }
- $txt.on('keydown keyup', function (e) {
- if (e.keyCode !== 13) {
- return;
- }
- // 查找合法标签
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getLegalTags(rangeElem);
- var $targetElem;
- var $pElem;
- if (!targetElem) {
- // 没找到合法标签,就去查找 div
- targetElem = editor.getSelfOrParentByName(rangeElem, 'div');
- if (!targetElem) {
- return;
- }
- $targetElem = $(targetElem);
- if (e.type === 'keydown') {
- // 异步执行(同步执行会出现问题)
- $keydownDivElem = $targetElem;
- setTimeout(divHandler, 0);
- }
- if (e.type === 'keyup') {
- // 将 div 的内容移动到 p 里面,并移除 div
- $pElem = $('<p>' + $targetElem.html() + '</p>');
- $targetElem.after($pElem);
- $targetElem.remove();
- // 如果是回车结束,将选区定位到行首
- editor.restoreSelectionByElem($pElem.get(0), 'start');
- }
- }
- });
- };
- // enter时,用 p 包裹 text
- Txt.fn.bindEnterForText = function () {
- var self = this;
- var $txt = self.$txt;
- var handle;
- $txt.on('keyup', function (e) {
- if (e.keyCode !== 13) {
- return;
- }
- if (!handle) {
- handle = function() {
- self.wrapImgAndText();
- };
- }
- setTimeout(handle);
- });
- };
- // tab 时,插入4个空格
- Txt.fn.bindTabEvent = function () {
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- $txt.on('keydown', function (e) {
- if (e.keyCode !== 9) {
- // 只监听 tab 按钮
- return;
- }
- // 如果浏览器支持 insertHtml 则插入4个空格。如果不支持,就不管了
- if (editor.queryCommandSupported('insertHtml')) {
- editor.command(e, 'insertHtml', ' ');
- }
- });
- };
- // 处理粘贴内容
- Txt.fn.bindPasteFilter = function () {
- var self = this;
- var editor = self.editor;
- var resultHtml = ''; //存储最终的结果
- var $txt = self.$txt;
- var legalTags = editor.config.legalTags;
- var legalTagArr = legalTags.split(',');
- $txt.on('paste', function (e) {
- if (!editor.config.pasteFilter) {
- // 配置中取消了粘贴过滤
- return;
- }
- var currentNodeName = editor.getRangeElem().nodeName;
- if (currentNodeName === 'TD' || currentNodeName === 'TH') {
- // 在表格的单元格中粘贴,忽略所有内容。否则会出现异常情况
- return;
- }
- resultHtml = ''; // 先清空 resultHtml
- var pasteHtml, $paste;
- var data = e.clipboardData || e.originalEvent.clipboardData;
- var ieData = window.clipboardData;
- if (editor.config.pasteText) {
- // 只粘贴纯文本
- if (data && data.getData) {
- // w3c
- pasteHtml = data.getData('text/plain');
- } else if (ieData && ieData.getData) {
- // IE
- pasteHtml = ieData.getData('text');
- } else {
- // 其他情况
- return;
- }
- // 拼接为 <p> 标签
- if (pasteHtml) {
- resultHtml = '<p>' + pasteHtml + '</p>';
- }
- } else {
- // 粘贴过滤了样式的、只有标签的 html
- if (data && data.getData) {
- // w3c
- // 获取粘贴过来的html
- pasteHtml = data.getData('text/html');
- if (pasteHtml) {
- // 创建dom
- $paste = $('<div>' + pasteHtml + '</div>');
- // 处理,并将结果存储到 resultHtml 『全局』变量
- handle($paste.get(0));
- } else {
- // 得不到html,试图获取text
- pasteHtml = data.getData('text/plain');
- if (pasteHtml) {
- // 替换特殊字符
- pasteHtml = pasteHtml.replace(/[ ]/g, ' ')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/\n/g, '</p><p>');
- // 拼接
- resultHtml = '<p>' + pasteHtml + '</p>';
- // 查询链接
- resultHtml = resultHtml.replace(/<p>(https?:\/\/.*?)<\/p>/ig, function (match, link) {
- return '<p><a href="' + link + '" target="_blank">' + link + '</p>';
- });
- }
- }
- } else if (ieData && ieData.getData) {
- // IE 直接从剪切板中取出纯文本格式
- resultHtml = ieData.getData('text');
- if (!resultHtml) {
- return;
- }
- // 拼接为 <p> 标签
- resultHtml = '<p>' + resultHtml + '</p>';
- resultHtml = resultHtml.replace(new RegExp('\n', 'g'), '</p><p>');
- } else {
- // 其他情况
- return;
- }
- }
- // 执行命令
- if (resultHtml) {
- editor.command(e, 'insertHtml', resultHtml);
- // 删除内容为空的 p 和嵌套的 p
- self.clearEmptyOrNestP();
- }
- });
- // 处理粘贴的内容
- function handle(elem) {
- if (!elem || !elem.nodeType || !elem.nodeName) {
- return;
- }
- var $elem;
- var nodeName = elem.nodeName.toLowerCase();
- var nodeType = elem.nodeType;
- var childNodesClone;
- // 只处理文本和普通node标签
- if (nodeType !== 3 && nodeType !== 1) {
- return;
- }
- $elem = $(elem);
- // 如果是容器,则继续深度遍历
- if (nodeName === 'div') {
- childNodesClone = [];
- $.each(elem.childNodes, function (index, item) {
- // elem.childNodes 可获取TEXT节点,而 $elem.children() 就获取不到
- // 先将 elem.childNodes 拷贝一份,一面在循环递归过程中 elem 发生变化
- childNodesClone.push(item);
- });
- // 遍历子元素,执行操作
- $.each(childNodesClone, function () {
- handle(this);
- });
- return;
- }
- if (legalTagArr.indexOf(nodeName) >= 0) {
- // 如果是合法标签之内的,则根据元素类型,获取值
- resultHtml += getResult(elem);
- } else if (nodeType === 3) {
- // 如果是文本,则直接插入 p 标签
- resultHtml += '<p>' + elem.textContent + '</p>';
- } else if (nodeName === 'br') {
- // <br>保留
- resultHtml += '<br/>';
- }
- else {
- // 忽略的标签
- if (['meta', 'style', 'script', 'object', 'form', 'iframe', 'hr'].indexOf(nodeName) >= 0) {
- return;
- }
- // 其他标签,移除属性,插入 p 标签
- $elem = $(removeAttrs(elem));
- // 注意,这里的 clone() 是必须的,否则会出错
- resultHtml += $('<div>').append($elem.clone()).html();
- }
- }
- // 获取元素的结果
- function getResult(elem) {
- var nodeName = elem.nodeName.toLowerCase();
- var $elem;
- var htmlForP = '';
- var htmlForLi = '';
- if (['blockquote'].indexOf(nodeName) >= 0) {
- // 直接取出元素text即可
- $elem = $(elem);
- return '<' + nodeName + '>' + $elem.text() + '</' + nodeName + '>';
- } else if (['p', 'h1', 'h2', 'h3', 'h4', 'h5'].indexOf(nodeName) >= 0) {
- //p head 取出 text 和链接
- elem = removeAttrs(elem);
- $elem = $(elem);
- htmlForP = $elem.html();
- // 剔除 a img 之外的元素
- htmlForP = htmlForP.replace(/<.*?>/ig, function (tag) {
- if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
- return tag;
- } else {
- return '';
- }
- });
- return '<' + nodeName + '>' + htmlForP + '</' + nodeName + '>';
- } else if (['ul', 'ol'].indexOf(nodeName) >= 0) {
- // ul ol元素,获取子元素(li元素)的text link img,再拼接
- $elem = $(elem);
- $elem.children().each(function () {
- var $li = $(removeAttrs(this));
- var html = $li.html();
- html = html.replace(/<.*?>/ig, function (tag) {
- if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
- return tag;
- } else {
- return '';
- }
- });
- htmlForLi += '<li>' + html + '</li>';
- });
- return '<' + nodeName + '>' + htmlForLi + '</' + nodeName + '>';
- } else {
- // 其他元素,移除元素属性
- $elem = $(removeAttrs(elem));
- return $('<div>').append($elem).html();
- }
- }
- // 移除一个元素(子元素)的attr
- function removeAttrs(elem) {
- var attrs = elem.attributes || [];
- var attrNames = [];
- var exception = ['href', 'target', 'src', 'alt', 'rowspan', 'colspan']; //例外情况
- // 先存储下elem中所有 attr 的名称
- $.each(attrs, function (key, attr) {
- if (attr && attr.nodeType === 2) {
- attrNames.push(attr.nodeName);
- }
- });
- // 再根据名称删除所有attr
- $.each(attrNames, function (key, attr) {
- if (exception.indexOf(attr) < 0) {
- // 除了 exception 规定的例外情况,删除其他属性
- elem.removeAttribute(attr);
- }
- });
- // 递归子节点
- var children = elem.childNodes;
- if (children.length) {
- $.each(children, function (key, value) {
- removeAttrs(value);
- });
- }
- return elem;
- }
- };
- // 绑定 $txt.formatText() 方法
- Txt.fn.bindFormatText = function () {
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- var legalTags = E.config.legalTags;
- var legalTagArr = legalTags.split(',');
- var length = legalTagArr.length;
- var regArr = [];
- // 将 E.config.legalTags 配置的有效字符,生成正则表达式
- $.each(legalTagArr, function (k, tag) {
- var reg = '\>\\s*\<(' + tag + ')\>';
- regArr.push(new RegExp(reg, 'ig'));
- });
- // 增加 li
- regArr.push(new RegExp('\>\\s*\<(li)\>', 'ig'));
- // 增加 tr
- regArr.push(new RegExp('\>\\s*\<(tr)\>', 'ig'));
- // 增加 code
- regArr.push(new RegExp('\>\\s*\<(code)\>', 'ig'));
- // 生成 formatText 方法
- $txt.formatText = function () {
- var $temp = $('<div>');
- var html = $txt.html();
- // 去除空格
- html = html.replace(/\s*</ig, '<');
- // 段落、表格之间换行
- $.each(regArr, function (k, reg) {
- if (!reg.test(html)) {
- return;
- }
- html = html.replace(reg, function (matchStr, tag) {
- return '>\n<' + tag + '>';
- });
- });
- $temp.html(html);
- return $temp.text();
- };
- };
- // 定制 $txt.html 方法
- Txt.fn.bindHtml = function () {
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- var $valueContainer = editor.$valueContainer;
- var valueNodeName = editor.valueNodeName;
- $txt.html = function (html) {
- var result;
- if (valueNodeName === 'div') {
- // div 生成的编辑器,取值、赋值,都直接触发jquery的html方法
- result = $.fn.html.call($txt, html);
- }
- // textarea 生成的编辑器,则需要考虑赋值时,也给textarea赋值
- if (html === undefined) {
- // 取值,直接触发jquery原生html方法
- result = $.fn.html.call($txt);
- // 替换 html 中,src和href属性中的 & 字符。
- // 因为 .html() 或者 .innerHTML 会把所有的 & 字符都改成 & 但是 src 和 href 中的要保持 &
- result = result.replace(/(href|src)\=\"(.*)\"/igm, function (a, b, c) {
- return b + '="' + c.replace('&', '&') + '"';
- });
- } else {
- // 赋值,需要同时给 textarea 赋值
- result = $.fn.html.call($txt, html);
- $valueContainer.val(html);
- }
- if (html === undefined) {
- return result;
- } else {
- // 手动触发 change 事件,因为 $txt 监控了 change 事件来判断是否需要执行 editor.onchange
- $txt.change();
- }
- };
- };
- });
- // Txt.fn api
- _e(function (E, $) {
- var Txt = E.Txt;
- var txtChangeEventNames = 'propertychange change click keyup input paste';
- // 渲染
- Txt.fn.render = function () {
- var $txt = this.$txt;
- var $editorContainer = this.editor.$editorContainer;
- $editorContainer.append($txt);
- };
- // 计算高度
- Txt.fn.initHeight = function () {
- var editor = this.editor;
- var $txt = this.$txt;
- var valueContainerHeight = editor.$valueContainer.height();
- var menuHeight = editor.menuContainer.height();
- var txtHeight = valueContainerHeight - menuHeight;
- // 限制最小为 50px
- txtHeight = txtHeight < 50 ? 50 : txtHeight;
- $txt.height(txtHeight);
- // 记录原始高度
- editor.valueContainerHeight = valueContainerHeight;
- // 设置 max-height
- this.initMaxHeight(txtHeight, menuHeight);
- };
- // 计算最大高度
- Txt.fn.initMaxHeight = function (txtHeight, menuHeight) {
- var editor = this.editor;
- var $menuContainer = editor.menuContainer.$menuContainer;
- var $txt = this.$txt;
- var $wrap = $('<div>');
- // 需要浏览器支持 max-height,否则不管
- if (window.getComputedStyle && 'max-height'in window.getComputedStyle($txt.get(0))) {
- // 获取 max-height 并判断是否有值
- var maxHeight = parseInt(editor.$valueContainer.css('max-height'));
- if (isNaN(maxHeight)) {
- return;
- }
- // max-height 和『全屏』暂时有冲突
- if (editor.menus.fullscreen) {
- E.warn('max-height和『全屏』菜单一起使用时,会有一些问题尚未解决,请暂时不要两个同时使用');
- return;
- }
- // 标记
- editor.useMaxHeight = true;
- // 设置maxheight
- $wrap.css({
- 'max-height': (maxHeight - menuHeight) + 'px',
- 'overflow-y': 'auto'
- });
- $txt.css({
- 'height': 'auto',
- 'overflow-y': 'visible',
- 'min-height': txtHeight + 'px'
- });
- // 滚动式,菜单阴影
- $wrap.on('scroll', function () {
- if ($txt.parent().scrollTop() > 10) {
- $menuContainer.addClass('wangEditor-menu-shadow');
- } else {
- $menuContainer.removeClass('wangEditor-menu-shadow');
- }
- });
- // 需在编辑器区域外面再包裹一层
- $txt.wrap($wrap);
- }
- };
- // 保存选区
- Txt.fn.saveSelectionEvent = function () {
- var $txt = this.$txt;
- var editor = this.editor;
- var timeoutId;
- var dt = Date.now();
- function save() {
- editor.saveSelection();
- }
- // 同步保存选区
- function saveSync() {
- // 100ms之内,不重复保存
- if (Date.now() - dt < 100) {
- return;
- }
- dt = Date.now();
- save();
- }
- // 异步保存选区
- function saveAync() {
- // 节流,防止高频率重复操作
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(save, 300);
- }
- // txt change 、focus、blur 时随时保存选区
- $txt.on(txtChangeEventNames + ' focus blur', function (e) {
- // 先同步保存选区,为了让接下来就马上要执行 editor.getRangeElem() 的程序
- // 能够获取到正确的 rangeElem
- saveSync();
- // 再异步保存选区,为了确定更加准确的选区,为后续的操作做准备
- saveAync();
- });
- // 鼠标拖拽选择时,可能会拖拽到编辑器区域外面再松手,此时 $txt 就监听不到 click事件了
- $txt.on('mousedown', function () {
- $txt.on('mouseleave.saveSelection', function (e) {
- // 先同步后异步,如上述注释
- saveSync();
- saveAync();
- // 顺道吧菜单状态也更新了
- editor.updateMenuStyle();
- });
- }).on('mouseup', function () {
- $txt.off('mouseleave.saveSelection');
- });
- };
- // 随时更新 value
- Txt.fn.updateValueEvent = function () {
- var $txt = this.$txt;
- var editor = this.editor;
- var timeoutId, oldValue;
- // 触发 onchange 事件
- function doOnchange() {
- var val = $txt.html();
- if (oldValue === val) {
- // 无变化
- return;
- }
- // 触发 onchange 事件
- if (editor.onchange && typeof editor.onchange === 'function') {
- editor.onchange.call(editor);
- }
- // 更新内容
- editor.updateValue();
- // 记录最新内容
- oldValue = val;
- }
- // txt change 时随时更新内容
- $txt.on(txtChangeEventNames, function (e) {
- // 初始化
- if (oldValue == null) {
- oldValue = $txt.html();
- }
- // 监控内容变化(停止操作 100ms 之后立即执行)
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(doOnchange, 100);
- });
- };
- // 随时更新 menustyle
- Txt.fn.updateMenuStyleEvent = function () {
- var $txt = this.$txt;
- var editor = this.editor;
- // txt change 时随时更新内容
- $txt.on(txtChangeEventNames, function (e) {
- editor.updateMenuStyle();
- });
- };
- // 最后插入试图插入 <p><br><p>
- Txt.fn.insertEmptyP = function () {
- var $txt = this.$txt;
- var $children = $txt.children();
- if ($children.length === 0) {
- $txt.append($('<p><br></p>'));
- return;
- }
- if ($.trim($children.last().html()).toLowerCase() !== '<br>') {
- $txt.append($('<p><br></p>'));
- }
- };
- // 将编辑器暴露出来的文字和图片,都用 p 来包裹
- Txt.fn.wrapImgAndText = function () {
- var $txt = this.$txt;
- var $imgs = $txt.children('img');
- var txt = $txt[0];
- var childNodes = txt.childNodes;
- var childrenLength = childNodes.length;
- var i, childNode, p;
- // 处理图片
- $imgs.length && $imgs.each(function () {
- $(this).wrap('<p>');
- });
- // 处理文字
- for (i = 0; i < childrenLength; i++) {
- childNode = childNodes[i];
- if (childNode.nodeType === 3 && childNode.textContent && $.trim(childNode.textContent)) {
- $(childNode).wrap('<p>');
- }
- }
- };
- // 清空内容为空的<p>,以及重复包裹的<p>(在windows下的chrome粘贴文字之后,会出现上述情况)
- Txt.fn.clearEmptyOrNestP = function () {
- var $txt = this.$txt;
- var $pList = $txt.find('p');
- $pList.each(function () {
- var $p = $(this);
- var $children = $p.children();
- var childrenLength = $children.length;
- var $firstChild;
- var content = $.trim($p.html());
- // 内容为空的p
- if (!content) {
- $p.remove();
- return;
- }
- // 嵌套的p
- if (childrenLength === 1) {
- $firstChild = $children.first();
- if ($firstChild.get(0) && $firstChild.get(0).nodeName === 'P') {
- $p.html( $firstChild.html() );
- }
- }
- });
- };
- // 获取 scrollTop
- Txt.fn.scrollTop = function (val) {
- var self = this;
- var editor = self.editor;
- var $txt = self.$txt;
- if (editor.useMaxHeight) {
- return $txt.parent().scrollTop(val);
- } else {
- return $txt.scrollTop(val);
- }
- };
- // 鼠标hover时候,显示p、head的高度
- Txt.fn.showHeightOnHover = function () {
- var editor = this.editor;
- var $editorContainer = editor.$editorContainer;
- var menuContainer = editor.menuContainer;
- var $txt = this.$txt;
- var $tip = $('<i class="height-tip"><i>');
- var isTipInTxt = false;
- function addAndShowTip($target) {
- if (!isTipInTxt) {
- $editorContainer.append($tip);
- isTipInTxt = true;
- }
- var txtTop = $txt.position().top;
- var txtHeight = $txt.outerHeight();
- var height = $target.height();
- var top = $target.position().top;
- var marginTop = parseInt($target.css('margin-top'), 10);
- var paddingTop = parseInt($target.css('padding-top'), 10);
- var marginBottom = parseInt($target.css('margin-bottom'), 10);
- var paddingBottom = parseInt($target.css('padding-bottom'), 10);
- // 计算初步的结果
- var resultHeight = height + paddingTop + marginTop + paddingBottom + marginBottom;
- var resultTop = top + menuContainer.height();
- // var spaceValue;
- // // 判断是否超出下边界
- // spaceValue = (resultTop + resultHeight) - (txtTop + txtHeight);
- // if (spaceValue > 0) {
- // resultHeight = resultHeight - spaceValue;
- // }
- // // 判断是否超出了下边界
- // spaceValue = txtTop > resultTop;
- // if (spaceValue) {
- // resultHeight = resultHeight - spaceValue;
- // top = top + spaceValue;
- // }
- // 按照最终结果渲染
- $tip.css({
- height: height + paddingTop + marginTop + paddingBottom + marginBottom,
- top: top + menuContainer.height()
- });
- }
- function removeTip() {
- if (!isTipInTxt) {
- return;
- }
- $tip.remove();
- isTipInTxt = false;
- }
- $txt.on('mouseenter', 'ul,ol,blockquote,p,h1,h2,h3,h4,h5,table,pre', function (e) {
- addAndShowTip($(e.currentTarget));
- }).on('mouseleave', function () {
- removeTip();
- });
- };
- });
- // 工具函数
- _e(function (E, $) {
- // IE8 [].indexOf()
- if(!Array.prototype.indexOf){
- //IE低版本不支持 arr.indexOf
- Array.prototype.indexOf = function(elem){
- var i = 0,
- length = this.length;
- for(; i<length; i++){
- if(this[i] === elem){
- return i;
- }
- }
- return -1;
- };
- //IE低版本不支持 arr.lastIndexOf
- Array.prototype.lastIndexOf = function(elem){
- var length = this.length;
- for(length = length - 1; length >= 0; length--){
- if(this[length] === elem){
- return length;
- }
- }
- return -1;
- };
- }
- // IE8 Date.now()
- if (!Date.now) {
- Date.now = function () {
- return new Date().valueOf();
- };
- }
- // console.log && console.warn && console.error
- var console = window.console;
- var emptyFn = function () {};
- $.each(['info', 'log', 'warn', 'error'], function (key, value) {
- if (console == null) {
- E[value] = emptyFn;
- } else {
- E[value] = function (info) {
- // 通过配置来控制打印输出
- if (E.config && E.config.printLog) {
- console[value]('wangEditor提示: ' + info);
- }
- };
- }
- });
- // 获取随机数
- E.random = function () {
- return Math.random().toString().slice(2);
- };
- // 浏览器是否支持 placeholder
- E.placeholder = 'placeholder' in document.createElement('input');
- // 兼容IE8的 input placeholder
- E.placeholderForIE8 = function ($container) {
- if (E.placeholder) {
- return;
- }
- $container.find('input[placeholder]').each(function () {
- var $input = $(this);
- var placeholder = $input.attr('placeholder');
- if ($input.val() === '') {
- $input.css('color', '#666');
- $input.val(placeholder);
- $input.on('focus.placeholder click.placeholder', function () {
- $input.val('');
- $input.css('color', '#333');
- $input.off('focus.placeholder click.placeholder');
- });
- }
- });
- };
- });
- // 语言包
- _e(function (E, $) {
- E.langs = {};
- // 中文
- E.langs['zh-cn'] = {
- bold: '粗体',
- underline: '下划线',
- italic: '斜体',
- forecolor: '文字颜色',
- bgcolor: '背景色',
- strikethrough: '删除线',
- eraser: '清空格式',
- source: '源码',
- quote: '引用',
- fontfamily: '字体',
- fontsize: '字号',
- head: '标题',
- orderlist: '有序列表',
- unorderlist: '无序列表',
- alignleft: '左对齐',
- aligncenter: '居中',
- alignright: '右对齐',
- link: '链接',
- text: '文本',
- submit: '提交',
- cancel: '取消',
- unlink: '取消链接',
- table: '表格',
- emotion: '表情',
- img: '图片',
- video: '视频',
- 'width': '宽',
- 'height': '高',
- location: '位置',
- loading: '加载中',
- searchlocation: '搜索位置',
- dynamicMap: '动态地图',
- clearLocation: '清除位置',
- langDynamicOneLocation: '动态地图只能显示一个位置',
- insertcode: '插入代码',
- undo: '撤销',
- redo: '重复',
- fullscreen: '全屏',
- openLink: '打开链接'
- };
- // 英文
- E.langs.en = {
- bold: 'Bold',
- underline: 'Underline',
- italic: 'Italic',
- forecolor: 'Color',
- bgcolor: 'Backcolor',
- strikethrough: 'Strikethrough',
- eraser: 'Eraser',
- source: 'Codeview',
- quote: 'Quote',
- fontfamily: 'Font family',
- fontsize: 'Font size',
- head: 'Head',
- orderlist: 'Ordered list',
- unorderlist: 'Unordered list',
- alignleft: 'Align left',
- aligncenter: 'Align center',
- alignright: 'Align right',
- link: 'Insert link',
- text: 'Text',
- submit: 'Submit',
- cancel: 'Cancel',
- unlink: 'Unlink',
- table: 'Table',
- emotion: 'Emotions',
- img: 'Image',
- video: 'Video',
- 'width': 'width',
- 'height': 'height',
- location: 'Location',
- loading: 'Loading',
- searchlocation: 'search',
- dynamicMap: 'Dynamic',
- clearLocation: 'Clear',
- langDynamicOneLocation: 'Only one location in dynamic map',
- insertcode: 'Insert Code',
- undo: 'Undo',
- redo: 'Redo',
- fullscreen: 'Full screnn',
- openLink: 'open link'
- };
- });
- // 全局配置
- _e(function (E, $) {
- E.config = {};
- // 全屏时的 z-index
- E.config.zindex = 10000;
- // 是否打印log
- E.config.printLog = true;
- // 菜单吸顶:false - 不吸顶;number - 吸顶,值为top值
- E.config.menuFixed = 0;
- // 编辑源码时,过滤 javascript
- E.config.jsFilter = true;
- // 编辑器允许的标签
- E.config.legalTags = 'p,h1,h2,h3,h4,h5,h6,blockquote,table,ul,ol,pre';
- // 语言包
- E.config.lang = E.langs['zh-cn'];
- // 菜单配置
- E.config.menus = [
- 'source',
- '|',
- 'bold',
- 'underline',
- 'italic',
- 'strikethrough',
- 'eraser',
- 'forecolor',
- 'bgcolor',
- '|',
- 'quote',
- 'fontfamily',
- 'fontsize',
- 'head',
- 'unorderlist',
- 'orderlist',
- 'alignleft',
- 'aligncenter',
- 'alignright',
- '|',
- 'link',
- 'unlink',
- 'table',
- 'emotion',
- '|',
- 'img',
- 'video',
- // 'location',
- 'insertcode',
- '|',
- 'undo',
- 'redo',
- 'fullscreen'
- ];
- // 颜色配置
- E.config.colors = {
- // 'value': 'title'
- '#880000': '暗红色',
- '#800080': '紫色',
- '#ff0000': '红色',
- '#ff00ff': '鲜粉色',
- '#000080': '深蓝色',
- '#0000ff': '蓝色',
- '#00ffff': '湖蓝色',
- '#008080': '蓝绿色',
- '#008000': '绿色',
- '#808000': '橄榄色',
- '#00ff00': '浅绿色',
- '#ffcc00': '橙黄色',
- '#808080': '灰色',
- '#c0c0c0': '银色',
- '#000000': '黑色',
- '#ffffff': '白色'
- };
- // 字体
- E.config.familys = [
- '宋体', '黑体', '楷体', '微软雅黑',
- 'Arial', 'Verdana', 'Georgia',
- 'Times New Roman', 'Microsoft JhengHei',
- 'Trebuchet MS', 'Courier New', 'Impact', 'Comic Sans MS', 'Consolas'
- ];
- // 字号
- E.config.fontsizes = {
- // 格式:'value': 'title'
- 1: '12px',
- 2: '13px',
- 3: '16px',
- 4: '18px',
- 5: '24px',
- 6: '32px',
- 7: '48px'
- };
- // 表情包
- E.config.emotionsShow = 'icon'; // 显示项,默认为'icon',也可以配置成'value'
- E.config.emotions = {
- // 'default': {
- // title: '默认',
- // data: './emotions.data'
- // },
- 'weibo': {
- title: '微博表情',
- data: [
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif',
- value: '[草泥马]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif',
- value: '[神马]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif',
- value: '[浮云]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c9/geili_thumb.gif',
- value: '[给力]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f2/wg_thumb.gif',
- value: '[围观]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/70/vw_thumb.gif',
- value: '[威武]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6e/panda_thumb.gif',
- value: '[熊猫]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/81/rabbit_thumb.gif',
- value: '[兔子]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/otm_thumb.gif',
- value: '[奥特曼]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/15/j_thumb.gif',
- value: '[囧]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/89/hufen_thumb.gif',
- value: '[互粉]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c4/liwu_thumb.gif',
- value: '[礼物]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/ac/smilea_thumb.gif',
- value: '[呵呵]'
- },
- {
- icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0b/tootha_thumb.gif',
- value: '[哈哈]'
- }
- ]
- }
- };
- // 百度地图的key
- E.config.mapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
- // 上传图片的配置
- // server地址
- E.config.uploadImgUrl = '';
- // 超时时间
- E.config.uploadTimeout = 20 * 1000;
- // 用于存储上传回调事件
- E.config.uploadImgFns = {};
- // 自定义上传图片的filename
- // E.config.uploadImgFileName = 'customFileName';
- // 自定义上传,设置为 true 之后,显示上传图标
- E.config.customUpload = false;
- // 自定义上传的init事件
- // E.config.customUploadInit = function () {....};
- // 自定义上传时传递的参数(如 token)
- E.config.uploadParams = {
- /* token: 'abcdef12345' */
- };
- // 自定义上传是的header参数
- E.config.uploadHeaders = {
- /* 'Accept' : 'text/x-json' */
- };
- // 隐藏网络图片,默认为 false
- E.config.hideLinkImg = false;
- // 是否过滤粘贴内容
- E.config.pasteFilter = true;
- // 是否粘贴纯文本,当 editor.config.pasteFilter === false 时候,此配置将失效
- E.config.pasteText = false;
- // 插入代码时,默认的语言
- E.config.codeDefaultLang = 'javascript';
- });
- // 全局UI
- _e(function (E, $) {
- E.UI = {};
- // 为菜单自定义配置的UI
- E.UI.menus = {
- // 这个 default 不加引号,在 IE8 会报错
- 'default': {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-command"></i></a>',
- selected: '.selected'
- },
- bold: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-bold"></i></a>',
- selected: '.selected'
- },
- underline: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-underline"></i></a>',
- selected: '.selected'
- },
- italic: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-italic"></i></a>',
- selected: '.selected'
- },
- forecolor: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-pencil"></i></a>',
- selected: '.selected'
- },
- bgcolor: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-brush"></i></a>',
- selected: '.selected'
- },
- strikethrough: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-strikethrough"></i></a>',
- selected: '.selected'
- },
- eraser: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-eraser"></i></a>',
- selected: '.selected'
- },
- quote: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-quotes-left"></i></a>',
- selected: '.selected'
- },
- source: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-code"></i></a>',
- selected: '.selected'
- },
- fontfamily: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-font2"></i></a>',
- selected: '.selected'
- },
- fontsize: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-text-height"></i></a>',
- selected: '.selected'
- },
- head: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-header"></i></a>',
- selected: '.selected'
- },
- orderlist: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-numbered"></i></a>',
- selected: '.selected'
- },
- unorderlist: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-bullet"></i></a>',
- selected: '.selected'
- },
- alignleft: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-left"></i></a>',
- selected: '.selected'
- },
- aligncenter: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-center"></i></a>',
- selected: '.selected'
- },
- alignright: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-right"></i></a>',
- selected: '.selected'
- },
- link: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-link"></i></a>',
- selected: '.selected'
- },
- unlink: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-unlink"></i></a>',
- selected: '.selected'
- },
- table: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-table"></i></a>',
- selected: '.selected'
- },
- emotion: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-happy"></i></a>',
- selected: '.selected'
- },
- img: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-picture"></i></a>',
- selected: '.selected'
- },
- video: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-play"></i></a>',
- selected: '.selected'
- },
- location: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-location"></i></a>',
- selected: '.selected'
- },
- insertcode: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-terminal"></i></a>',
- selected: '.selected'
- },
- undo: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-ccw"></i></a>',
- selected: '.selected'
- },
- redo: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-cw"></i></a>',
- selected: '.selected'
- },
- fullscreen: {
- normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-enlarge2"></i></a>',
- selected: '<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-shrink2"></i></a>'
- }
- };
- });
- // 对象配置
- _e(function (E, $) {
- E.fn.initDefaultConfig = function () {
- var editor = this;
- editor.config = $.extend({}, E.config);
- editor.UI = $.extend({}, E.UI);
- };
- });
- // 增加 container
- _e(function (E, $) {
- E.fn.addEditorContainer = function () {
- this.$editorContainer = $('<div class="wangEditor-container"></div>');
- };
- });
- // 增加编辑区域对象
- _e(function (E, $) {
- E.fn.addTxt = function () {
- var editor = this;
- var txt = new E.Txt(editor);
- editor.txt = txt;
- };
- });
- // 增加menuContainer对象
- _e(function (E, $) {
- E.fn.addMenuContainer = function () {
- var editor = this;
- editor.menuContainer = new E.MenuContainer(editor);
- };
- });
- // 增加menus
- _e(function (E, $) {
- // 存储创建菜单的函数
- E.createMenuFns = [];
- E.createMenu = function (fn) {
- E.createMenuFns.push(fn);
- };
- // 创建所有菜单
- E.fn.addMenus = function () {
- var editor = this;
- var menuIds = editor.config.menus;
- // 检验 menuId 是否在配置中存在
- function check(menuId) {
- if (menuIds.indexOf(menuId) >= 0) {
- return true;
- }
- return false;
- }
- // 遍历所有的菜单创建函数,并执行
- $.each(E.createMenuFns, function (k, createMenuFn) {
- createMenuFn.call(editor, check);
- });
- };
- });
- // bold菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'bold';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.bold,
- commandName: 'Bold'
- });
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 如果选区有内容,则执行基础命令
- editor.command(e, 'Bold');
- } else {
- // 如果选区没有内容
- editor.commandForElem('b,strong,h1,h2,h3,h4,h5', e, 'Bold');
- }
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // underline菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'underline';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.underline,
- commandName: 'Underline'
- });
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 如果选区有内容,则执行基础命令
- editor.command(e, 'Underline');
- } else {
- // 如果选区没有内容
- editor.commandForElem('u,a', e, 'Underline');
- }
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // italic 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'italic';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.italic,
- commandName: 'Italic'
- });
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 如果选区有内容,则执行基础命令
- editor.command(e, 'Italic');
- } else {
- // 如果选区没有内容
- editor.commandForElem('i', e, 'Italic');
- }
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // forecolor 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'forecolor';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var configColors = editor.config.colors;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.forecolor
- });
- // 创建 dropPanel
- var $content = $('<div></div>');
- $.each(configColors, function (k, v) {
- $content.append(
- [
- '<a href="#" class="color-item"',
- ' title="' + v + '" commandValue="' + k + '" ',
- ' style="color: ' + k + '" ',
- '><i class="wangeditor-menu-img-pencil"></i></a>'
- ].join('')
- );
- });
- $content.on('click', 'a[commandValue]', function (e) {
- // 执行命令
- var $elem = $(this);
- var commandValue = $elem.attr('commandValue');
- if (menu.selected && editor.isRangeEmpty()) {
- // 当前处于选中状态,并且选中内容为空
- editor.commandForElem('font[color]', e, 'forecolor', commandValue);
- } else {
- // 当前未处于选中状态,或者有选中内容。则执行默认命令
- editor.command(e, 'forecolor', commandValue);
- }
- });
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 125
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[color]');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // bgcolor 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'bgcolor';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var configColors = editor.config.colors;
- // 检查元素是否有 background-color: 内联样式
- function checkElemFn(elem) {
- var cssText;
- if (elem && elem.style && elem.style.cssText != null) {
- cssText = elem.style.cssText;
- if (cssText && cssText.indexOf('background-color:') >= 0) {
- return true;
- }
- }
- return false;
- }
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.bgcolor
- });
- // 创建 dropPanel
- var $content = $('<div></div>');
- $.each(configColors, function (k, v) {
- $content.append(
- [
- '<a href="#" class="color-item"',
- ' title="' + v + '" commandValue="' + k + '" ',
- ' style="color: ' + k + '" ',
- '><i class="wangeditor-menu-img-brush"></i></a>'
- ].join('')
- );
- });
- $content.on('click', 'a[commandValue]', function (e) {
- // 执行命令
- var $elem = $(this);
- var commandValue = $elem.attr('commandValue');
- if (menu.selected && editor.isRangeEmpty()) {
- // 当前处于选中状态,并且选中内容为空。使用 commandForElem 执行命令
- editor.commandForElem({
- selector: 'span,font',
- check: checkElemFn
- }, e, 'BackColor', commandValue);
- } else {
- // 当前未处于选中状态,或者有选中内容。则执行默认命令
- editor.command(e, 'BackColor', commandValue);
- }
- });
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 125
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'span,font', checkElemFn);
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // strikethrough 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'strikethrough';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.strikethrough,
- commandName: 'StrikeThrough'
- });
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 如果选区有内容,则执行基础命令
- editor.command(e, 'StrikeThrough');
- } else {
- // 如果选区没有内容
- editor.commandForElem('strike', e, 'StrikeThrough');
- }
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // eraser 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'eraser';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.eraser,
- commandName: 'RemoveFormat'
- });
- // 定义点击事件
- menu.clickEvent = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 选区不是空的,则执行默认命令
- editor.command(e, 'RemoveFormat');
- return;
- }
- var $clearElem;
- // 自定义的命令函数
- function commandFn() {
- var editor = this;
- var rangeElem;
- var pElem, $pElem;
- var quoteElem, $quoteElem;
- var listElem, $listElem;
- // 获取选区 elem
- rangeElem = editor.getRangeElem();
- // 第一步,获取 quote 父元素
- quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- if (quoteElem) {
- $quoteElem = $(quoteElem);
- $clearElem = $('<p>' + $quoteElem.text() + '</p>');
- $quoteElem.after($clearElem).remove();
- }
- // 第二步,获取 p h 父元素
- pElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5');
- if (pElem) {
- $pElem = $(pElem);
- $clearElem = $('<p>' + $pElem.text() + '</p>');
- $pElem.after($clearElem).remove();
- }
- // 第三步,获取list
- listElem = editor.getSelfOrParentByName(rangeElem, 'ul,ol');
- if (listElem) {
- $listElem = $(listElem);
- $clearElem = $('<p>' + $listElem.text() + '</p>');
- $listElem.after($clearElem).remove();
- }
- }
- // 自定义 callback 事件
- function callback() {
- // callback中,设置range为clearElem
- var editor = this;
- if ($clearElem) {
- editor.restoreSelectionByElem($clearElem.get(0));
- }
- }
- // 执行自定义命令
- editor.customCommand(e, commandFn, callback);
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // source 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'source';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var txtHtml;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.source
- });
- menu.isShowCode = false;
- // 更新内容
- function updateValue() {
- var $code = menu.$codeTextarea;
- var $txt = editor.txt.$txt;
- var value = $.trim($code.val()); // 取值
- if (!value) {
- value = '<p><br></p>';
- }
- // 过滤js代码
- if (editor.config.jsFilter) {
- value = value.replace(/<script[\s\S]*?<\/script>/ig, '');
- }
- // 赋值
- try {
- $txt.html(value);
- } catch (ex) {
- // 更新 html 源码出错,一般都是取消了 js 过滤之后,js报错导致的
- }
- }
- // 定义click事件
- menu.clickEvent = function (e) {
- var self = this;
- var editor = self.editor;
- var $txt = editor.txt.$txt;
- var txtOuterHeight = $txt.outerHeight();
- var txtHeight = $txt.height();
- if (!self.$codeTextarea) {
- self.$codeTextarea = $('<textarea class="code-textarea"></textarea>');
- }
- var $code = self.$codeTextarea;
- $code.css({
- height: txtHeight,
- 'margin-top': txtOuterHeight - txtHeight
- });
- // 赋值
- $code.val($txt.html());
- // 监控变化
- $code.on('change', function (e) {
- updateValue();
- });
- // 渲染
- $txt.after($code).hide();
- $code.show();
- // 更新状态
- menu.isShowCode = true;
- // 执行 updateSelected 事件
- this.updateSelected();
- // 禁用其他菜单
- editor.disableMenusExcept('source');
- // 记录当前html值
- txtHtml = $txt.html();
- };
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var self = this;
- var editor = self.editor;
- var $txt = editor.txt.$txt;
- var $code = self.$codeTextarea;
- var value;
- if (!$code) {
- return;
- }
- // 更新内容
- updateValue();
- // 渲染
- $code.after($txt).hide();
- $txt.show();
- // 更新状态
- menu.isShowCode = false;
- // 执行 updateSelected 事件
- this.updateSelected();
- // 启用其他菜单
- editor.enableMenusExcept('source');
- // 判断是否执行 onchange 事件
- if ($txt.html() !== txtHtml) {
- if (editor.onchange && typeof editor.onchange === 'function') {
- editor.onchange.call(editor);
- }
- }
- };
- // 定义切换选中状态事件
- menu.updateSelectedEvent = function () {
- return this.isShowCode;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // quote 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'quote';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.quote,
- commandName: 'formatBlock',
- commandValue: 'blockquote'
- });
- // 定义click事件
- menu.clickEvent = function (e) {
- var rangeElem = editor.getRangeElem();
- var $rangeElem;
- if (!rangeElem) {
- e.preventDefault();
- return;
- }
- var currentQuote = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- var $quote;
- if (currentQuote) {
- // 说明当前在quote之内,不做任何处理
- e.preventDefault();
- return;
- }
- rangeElem = editor.getLegalTags(rangeElem);
- $rangeElem = $(rangeElem);
- // 无文字,则不允许执行引用
- if (!$rangeElem.text()) {
- return;
- }
- if (!rangeElem) {
- // 执行默认命令
- // IE8 下执行此处(不过,经测试代码无效,也不报错)
- editor.command(e, 'formatBlock', 'blockquote');
- return;
- }
- // 自定义command事件
- function commandFn() {
- $quote = $('<p>' + $rangeElem.text() + '</p>');
- $rangeElem.after($quote).remove();
- $quote.wrap('<blockquote>');
- }
- // 自定义 callback 事件
- function callback() {
- // callback中,设置range为quote
- var editor = this;
- if ($quote) {
- editor.restoreSelectionByElem($quote.get(0));
- }
- }
- // 执行自定义命令
- editor.customCommand(e, commandFn, callback);
- };
- // 定义选中状态下的click事件
- menu.clickEventSelected = function (e) {
- var rangeElem;
- var quoteElem;
- var $lastChild;
- // 获取当前选区的elem,并试图往上找 quote 元素
- rangeElem = editor.getRangeElem();
- quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- if (!quoteElem) {
- // 没找到,则返回
- e.preventDefault();
- return;
- }
- // 自定义的command事件
- function commandFn() {
- var $quoteElem;
- var $children;
- $quoteElem = $(quoteElem);
- $children = $quoteElem.children();
- if ($children.length) {
- $children.each(function (k) {
- var $item = $(this);
- if ($item.get(0).nodeName === 'P') {
- $quoteElem.after($item);
- } else {
- $quoteElem.after('<p>' + $item.text() + '</p>');
- }
- $lastChild = $item; // 记录最后一个子元素,用于callback中的range定位
- });
- $quoteElem.remove();
- return;
- }
- }
- // 自定义的callback函数
- function callback() {
- // callback中,设置range为lastChild
- var editor = this;
- if ($lastChild) {
- editor.restoreSelectionByElem($lastChild.get(0));
- }
- }
- // 执行自定义命令
- editor.customCommand(e, commandFn, callback);
- };
- // 定义更新选中状态的事件
- menu.updateSelectedEvent = function () {
- var self = this; //菜单对象
- var editor = self.editor;
- var rangeElem;
- rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- // --------------- 两次点击 enter 跳出引用 ---------------
- editor.ready(function () {
- var editor = this;
- var $txt = editor.txt.$txt;
- var isPrevEnter = false; // 是不是刚刚在quote中按了 enter 键
- $txt.on('keydown', function (e) {
- if (e.keyCode !== 13) {
- // 不是 enter 键
- isPrevEnter = false;
- return;
- }
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- if (!rangeElem) {
- // 选区不是 quote
- isPrevEnter = false;
- return;
- }
- if (!isPrevEnter) {
- // 最近没有在qote中按enter键
- isPrevEnter = true;
- return;
- }
- var currentRangeElem = editor.getRangeElem();
- var $currentRangeElem = $(currentRangeElem);
- if ($currentRangeElem.length) {
- $currentRangeElem.parent().after($currentRangeElem);
- }
- // 设置选区
- editor.restoreSelectionByElem(currentRangeElem, 'start');
- isPrevEnter = false;
- // 阻止默认行文
- e.preventDefault();
- });
- }); // editor.ready(
- // --------------- 处理quote中无内容时不能删除的问题 ---------------
- editor.ready(function () {
- var editor = this;
- var $txt = editor.txt.$txt;
- var $rangeElem;
- function commandFn() {
- $rangeElem && $rangeElem.remove();
- }
- function callback() {
- if (!$rangeElem) {
- return;
- }
- var $prev = $rangeElem.prev();
- if ($prev.length) {
- // 有 prev 则定位到 prev 最后
- editor.restoreSelectionByElem($prev.get(0));
- } else {
- // 无 prev 则初始化选区
- editor.initSelection();
- }
- }
- $txt.on('keydown', function (e) {
- if (e.keyCode !== 8) {
- // 不是 backspace 键
- return;
- }
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
- if (!rangeElem) {
- // 选区不是 quote
- return;
- }
- $rangeElem = $(rangeElem);
- var text = $rangeElem.text();
- if (text) {
- // quote 中还有内容
- return;
- }
- editor.customCommand(e, commandFn, callback);
- }); // $txt.on
- }); // editor.ready(
- });
- });
- // 字体 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'fontfamily';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var configFamilys = editor.config.familys;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.fontfamily,
- commandName: 'fontName'
- });
- // 初始化数据
- var data = {};
- /*
- data 需要的结构
- {
- 'commandValue': 'title'
- ...
- }
- */
- $.each(configFamilys, function (k, v) {
- // configFamilys 是数组,data 是对象
- data[v] = v;
- });
- // 创建droplist
- var tpl = '<span style="font-family:{#commandValue};">{#title}</span>';
- menu.dropList = new E.DropList(editor, menu, {
- data: data,
- tpl: tpl,
- selectorForELemCommand: 'font[face]' // 为了执行 editor.commandForElem 而传入的elem查询方式
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[face]');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // 字号 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'fontsize';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var configSize = editor.config.fontsizes;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.fontsize,
- commandName: 'fontSize'
- });
- // 初始化数据
- var data = configSize;
- /*
- data 需要的结构
- {
- 'commandValue': 'title'
- ...
- }
- */
- // 创建droplist
- var tpl = '<span style="font-size:{#title};">{#title}</span>';
- menu.dropList = new E.DropList(editor, menu, {
- data: data,
- tpl: tpl,
- selectorForELemCommand: 'font[size]' // 为了执行 editor.commandForElem 而传入的elem查询方式
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[size]');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // head 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'head';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.head,
- commandName: 'formatBlock'
- });
- // 初始化数据
- var data = {
- '<h1>': '标题1',
- '<h2>': '标题2',
- '<h3>': '标题3',
- '<h4>': '标题4',
- '<h5>': '标题5'
- };
- /*
- data 需要的结构
- {
- 'commandValue': 'title'
- ...
- }
- */
- var isOrderedList;
- function beforeEvent(e) {
- if (editor.queryCommandState('InsertOrderedList')) {
- isOrderedList = true;
- // 先取消有序列表
- editor.command(e, 'InsertOrderedList');
- } else {
- isOrderedList = false;
- }
- }
- function afterEvent(e) {
- if (isOrderedList) {
- // 再设置有序列表
- editor.command(e, 'InsertOrderedList');
- }
- }
- // 创建droplist
- var tpl = '{#commandValue}{#title}';
- menu.dropList = new E.DropList(editor, menu, {
- data: data,
- tpl: tpl,
- // 对 ol 直接设置 head,会出现每个 li 的 index 都变成 1 的问题,因此要先取消 ol,然后设置 head,最后再增加上 ol
- beforeEvent: beforeEvent,
- afterEvent: afterEvent
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'h1,h2,h3,h4,h5');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // unorderlist 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'unorderlist';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.unorderlist,
- commandName: 'InsertUnorderedList'
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // orderlist 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'orderlist';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.orderlist,
- commandName: 'InsertOrderedList'
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // alignleft 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'alignleft';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.alignleft,
- commandName: 'JustifyLeft'
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
- var cssText;
- if (elem && elem.style && elem.style.cssText != null) {
- cssText = elem.style.cssText;
- if (cssText && /text-align:\s*left;/.test(cssText)) {
- return true;
- }
- }
- if ($(elem).attr('align') === 'left') {
- // ff 中,设置align-left之后,会是 <p align="left">xxx</p>
- return true;
- }
- return false;
- });
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // aligncenter 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'aligncenter';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.aligncenter,
- commandName: 'JustifyCenter'
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
- var cssText;
- if (elem && elem.style && elem.style.cssText != null) {
- cssText = elem.style.cssText;
- if (cssText && /text-align:\s*center;/.test(cssText)) {
- return true;
- }
- }
- if ($(elem).attr('align') === 'center') {
- // ff 中,设置align-center之后,会是 <p align="center">xxx</p>
- return true;
- }
- return false;
- });
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // alignright 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'alignright';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.alignright,
- commandName: 'JustifyRight'
- });
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
- var cssText;
- if (elem && elem.style && elem.style.cssText != null) {
- cssText = elem.style.cssText;
- if (cssText && /text-align:\s*right;/.test(cssText)) {
- return true;
- }
- }
- if ($(elem).attr('align') === 'right') {
- // ff 中,设置align-right之后,会是 <p align="right">xxx</p>
- return true;
- }
- return false;
- });
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // link 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'link';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.link
- });
- // 创建 dropPanel
- var $content = $('<div></div>');
- var $div1 = $('<div style="margin:20px 10px;" class="clearfix"></div>');
- var $div2 = $div1.clone();
- var $div3 = $div1.clone().css('margin', '0 10px');
- var $textInput = $('<input type="text" class="block" placeholder="' + lang.text + '"/>');
- var $urlInput = $('<input type="text" class="block" placeholder="' + lang.link + '"/>');
- var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- $div1.append($textInput);
- $div2.append($urlInput);
- $div3.append($btnSubmit).append($btnCancel);
- $content.append($div1).append($div2).append($div3);
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 300
- });
- // 定义click事件
- menu.clickEvent = function (e) {
- var menu = this;
- var dropPanel = menu.dropPanel;
- // -------------隐藏----------------
- if (dropPanel.isShowing) {
- dropPanel.hide();
- return;
- }
- // -------------显示----------------
- // 重置 input
- $textInput.val('');
- $urlInput.val('http://');
- // 获取url
- var url = '';
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
- if (rangeElem) {
- url = rangeElem.href || '';
- }
- // 获取 text
- var text = '';
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 选区不是空
- text = editor.getRangeText() || '';
- } else if (rangeElem) {
- // 如果选区空,并且在 a 标签之内
- text = rangeElem.textContent || rangeElem.innerHTML;
- }
- // 设置 url 和 text
- url && $urlInput.val(url);
- text && $textInput.val(text);
- // 如果有选区内容,textinput 不能修改
- if (!isRangeEmpty) {
- $textInput.attr('disabled', true);
- } else {
- $textInput.removeAttr('disabled');
- }
- // 显示(要设置好了所有input的值和属性之后再显示)
- dropPanel.show();
- };
- // 定义 update selected 事件
- menu.updateSelectedEvent = function () {
- var rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 『取消』 按钮
- $btnCancel.click(function (e) {
- e.preventDefault();
- menu.dropPanel.hide();
- });
- // 『确定』按钮
- $btnSubmit.click(function (e) {
- e.preventDefault();
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'a');
- var isRangeEmpty = editor.isRangeEmpty();
- var $linkElem, linkHtml;
- var commandFn, callback;
- var $txt = editor.txt.$txt;
- var $oldLinks, $newLinks;
- var uniqId = 'link' + E.random();
- // 获取数据
- var url = $.trim($urlInput.val());
- var text = $.trim($textInput.val());
- if (!url) {
- menu.dropPanel.focusFirstInput();
- return;
- }
- if (!text) {
- text = url;
- }
- if (!isRangeEmpty) {
- // 选中区域有内容,则执行默认命令
- // 获取目前 txt 内所有链接,并为当前链接做一个标记
- $oldLinks = $txt.find('a');
- $oldLinks.attr(uniqId, '1');
- // 执行命令
- editor.command(e, 'createLink', url);
- // 去的没有标记的链接,即刚刚插入的链接
- $newLinks = $txt.find('a').not('[' + uniqId + ']');
- $newLinks.attr('target', '_blank'); // 增加 _blank
- // 去掉之前做的标记
- $oldLinks.removeAttr(uniqId);
- } else if (targetElem) {
- // 无选中区域,在 a 标签之内,修改该 a 标签的内容和链接
- $linkElem = $(targetElem);
- commandFn = function () {
- $linkElem.attr('href', url);
- $linkElem.text(text);
- };
- callback = function () {
- var editor = this;
- editor.restoreSelectionByElem(targetElem);
- };
- // 执行命令
- editor.customCommand(e, commandFn, callback);
- } else {
- // 无选中区域,不在 a 标签之内,插入新的链接
- linkHtml = '<a href="' + url + '" target="_blank">' + text + '</a>';
- if (E.userAgent.indexOf('Firefox') > 0) {
- linkHtml += '<span> </span>';
- }
- editor.command(e, 'insertHtml', linkHtml);
- }
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // unlink 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'unlink';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.unlink,
- commandName: 'unLink'
- });
- // click 事件
- menu.clickEvent = function (e) {
- var isRangeEmpty = editor.isRangeEmpty();
- if (!isRangeEmpty) {
- // 有选中区域,或者IE8,执行默认命令
- editor.command(e, 'unLink');
- return;
- }
- // 无选中区域...
- var rangeElem = editor.getRangeElem();
- var aElem = editor.getSelfOrParentByName(rangeElem, 'a');
- if (!aElem) {
- // 不在 a 之内,返回
- e.preventDefault();
- return;
- }
- // 在 a 之内
- var $a = $(aElem);
- var $span = $('<span>' + $a.text() + '</span>');
- function commandFn() {
- $a.after($span).remove();
- }
- function callback() {
- editor.restoreSelectionByElem($span.get(0));
- }
- editor.customCommand(e, commandFn, callback);
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // table 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'table';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.table
- });
- // dropPanel 内容
- var $content = $('<div style="font-size: 14px; color: #666; text-align:right;"></div>');
- var $table = $('<table class="choose-table" style="margin-bottom:10px;margin-top:5px;">');
- var $row = $('<span>0</span>');
- var $rowspan = $('<span> 行 </span>');
- var $col = $('<span>0</span>');
- var $colspan = $('<span> 列</span>');
- var $tr;
- var i, j;
- // 创建一个n行n列的表格
- for (i = 0; i < 15; i++) {
- $tr = $('<tr index="' + (i + 1) + '">');
- for (j = 0; j < 20; j++) {
- $tr.append($('<td index="' + (j + 1) + '">'));
- }
- $table.append($tr);
- }
- $content.append($table);
- $content.append($row).append($rowspan).append($col).append($colspan);
- // 定义table事件
- $table.on('mouseenter', 'td', function (e) {
- var $currentTd = $(e.currentTarget);
- var currentTdIndex = $currentTd.attr('index');
- var $currentTr = $currentTd.parent();
- var currentTrIndex = $currentTr.attr('index');
- // 显示
- $row.text(currentTrIndex);
- $col.text(currentTdIndex);
- // 遍历设置背景颜色
- $table.find('tr').each(function () {
- var $tr = $(this);
- var trIndex = $tr.attr('index');
- if (parseInt(trIndex, 10) <= parseInt(currentTrIndex, 10)) {
- // 该行需要可能需要设置背景色
- $tr.find('td').each(function () {
- var $td = $(this);
- var tdIndex = $td.attr('index');
- if (parseInt(tdIndex, 10) <= parseInt(currentTdIndex, 10)) {
- // 需要设置背景色
- $td.addClass('active');
- } else {
- // 需要移除背景色
- $td.removeClass('active');
- }
- });
- } else {
- // 改行不需要设置背景色
- $tr.find('td').removeClass('active');
- }
- });
- }).on('mouseleave', function (e) {
- // mouseleave 删除背景色
- $table.find('td').removeClass('active');
- $row.text(0);
- $col.text(0);
- });
- // 插入表格
- $table.on('click', 'td', function (e) {
- var $currentTd = $(e.currentTarget);
- var currentTdIndex = $currentTd.attr('index');
- var $currentTr = $currentTd.parent();
- var currentTrIndex = $currentTr.attr('index');
- var rownum = parseInt(currentTrIndex, 10);
- var colnum = parseInt(currentTdIndex, 10);
- // -------- 拼接tabel html --------
- var i, j;
- var tableHtml = '<table>';
- for (i = 0; i < rownum; i++) {
- tableHtml += '<tr>';
- for (j = 0; j < colnum; j++) {
- tableHtml += '<td><span> </span></td>';
- }
- tableHtml += '</tr>';
- }
- tableHtml += '</table>';
- // -------- 执行命令 --------
- editor.command(e, 'insertHtml', tableHtml);
- });
- // 创建 panel
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 262
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // emotion 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'emotion';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var config = editor.config;
- var lang = config.lang;
- var configEmotions = config.emotions;
- var emotionsShow = config.emotionsShow;
- // 记录每一个表情图片的地址
- editor.emotionUrls = [];
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.emotion
- });
- // 添加表情图片的函数
- function insertEmotionImgs(data, $tabContent) {
- // 添加表情图片
- $.each(data, function (k, emotion) {
- var src = emotion.icon || emotion.url;
- var value = emotion.value || emotion.title;
- // 通过配置 editor.config.emotionsShow 的值来修改插入到编辑器的内容(图片/value)
- var commandValue = emotionsShow === 'icon' ? src : value;
- var $command = $('<a href="#" commandValue="' + commandValue + '"></a>');
- var $img = $('<img>');
- $img.attr('_src', src); // 先将 src 复制到 '_src' 属性,先不加载
- $command.append($img);
- $tabContent.append($command);
- // 记录下每一个表情图片的地址
- editor.emotionUrls.push(src);
- });
- }
- // 拼接 dropPanel 内容
- var $panelContent = $('<div class="panel-tab"></div>');
- var $tabContainer = $('<div class="tab-container"></div>');
- var $contentContainer = $('<div class="content-container emotion-content-container"></div>');
- $.each(configEmotions, function (k, emotion) {
- var title = emotion.title;
- var data = emotion.data;
- E.log('正在处理 ' + title + ' 表情的数据...');
- // 增加该组表情的tab和content
- var $tab = $('<a href="#">' + title +' </a>');
- $tabContainer.append($tab);
- var $tabContent = $('<div class="content"></div>');
- $contentContainer.append($tabContent);
- // tab 切换事件
- $tab.click(function (e) {
- $tabContainer.children().removeClass('selected');
- $contentContainer.children().removeClass('selected');
- $tabContent.addClass('selected');
- $tab.addClass('selected');
- e.preventDefault();
- });
- // 处理data
- if (typeof data === 'string') {
- // url 形式,需要通过ajax从该url获取数据
- E.log('将通过 ' + data + ' 地址ajax下载表情包');
- $.get(data, function (result) {
- result = $.parseJSON(result);
- E.log('下载完毕,得到 ' + result.length + ' 个表情');
- insertEmotionImgs(result, $tabContent);
- });
- } else if ( Object.prototype.toString.call(data).toLowerCase().indexOf('array') > 0 ) {
- // 数组,即 data 直接就是表情包数据
- insertEmotionImgs(data, $tabContent);
- } else {
- // 其他情况,data格式不对
- E.error('data 数据格式错误,请修改为正确格式,参考文档:' + E.docsite);
- return;
- }
- });
- $panelContent.append($tabContainer).append($contentContainer);
- // 默认显示第一个tab
- $tabContainer.children().first().addClass('selected');
- $contentContainer.children().first().addClass('selected');
- // 插入表情command事件
- $contentContainer.on('click', 'a[commandValue]', function (e) {
- var $a = $(e.currentTarget);
- var commandValue = $a.attr('commandValue');
- var img;
- // commandValue 有可能是图片url,也有可能是表情的 value,需要区别对待
- if (emotionsShow === 'icon') {
- // 插入图片
- editor.command(e, 'InsertImage', commandValue);
- } else {
- // 插入value
- editor.command(e, 'insertHtml', '<span>' + commandValue + '</span>');
- }
- e.preventDefault();
- });
- // 添加panel
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $panelContent,
- width: 350
- });
- // 定义click事件(异步加载表情图片)
- menu.clickEvent = function (e) {
- var menu = this;
- var dropPanel = menu.dropPanel;
- // -------------隐藏-------------
- if (dropPanel.isShowing) {
- dropPanel.hide();
- return;
- }
- // -------------显示-------------
- dropPanel.show();
- // 异步加载图片
- if (menu.imgLoaded) {
- return;
- }
- $contentContainer.find('img').each(function () {
- var $img = $(this);
- var _src = $img.attr('_src');
- $img.on('error', function () {
- E.error('加载不出表情图片 ' + _src);
- });
- $img.attr('src', _src);
- $img.removeAttr('_src');
- });
- menu.imgLoaded = true;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // img 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'img';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.img
- });
- // 创建 panel content
- var $panelContent = $('<div class="panel-tab"></div>');
- var $tabContainer = $('<div class="tab-container"></div>');
- var $contentContainer = $('<div class="content-container"></div>');
- $panelContent.append($tabContainer).append($contentContainer);
- // tab
- var $uploadTab = $('<a href="#">上传图片</a>');
- var $linkTab = $('<a href="#">网络图片</a>');
- $tabContainer.append($uploadTab).append($linkTab);
- // 上传图片 content
- var $uploadContent = $('<div class="content"></div>');
- $contentContainer.append($uploadContent);
- // 网络图片 content
- var $linkContent = $('<div class="content"></div>');
- $contentContainer.append($linkContent);
- linkContentHandler(editor, menu, $linkContent);
- // 添加panel
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $panelContent,
- width: 400,
- onRender: function () {
- // 渲染后的回调事件,用于执行自定义上传的init
- // 因为渲染之后,上传面板的dom才会被渲染到页面,才能让第三方空间获取到
- var init = editor.config.customUploadInit;
- init && init.call(editor);
- }
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- // tab 切换事件
- function tabToggle() {
- $uploadTab.click(function (e) {
- $tabContainer.children().removeClass('selected');
- $contentContainer.children().removeClass('selected');
- $uploadContent.addClass('selected');
- $uploadTab.addClass('selected');
- e.preventDefault();
- });
- $linkTab.click(function (e) {
- $tabContainer.children().removeClass('selected');
- $contentContainer.children().removeClass('selected');
- $linkContent.addClass('selected');
- $linkTab.addClass('selected');
- e.preventDefault();
- // focus input
- if (E.placeholder) {
- $linkContent.find('input[type=text]').focus();
- }
- });
- // 默认情况
- // $uploadTab.addClass('selected');
- // $uploadContent.addClass('selected');
- $uploadTab.click();
- }
- // 隐藏上传图片
- function hideUploadImg() {
- $tabContainer.remove();
- $uploadContent.remove();
- $linkContent.addClass('selected');
- }
- // 隐藏网络图片
- function hideLinkImg() {
- $tabContainer.remove();
- $linkContent.remove();
- $uploadContent.addClass('selected');
- }
- // 判断用户是否配置了上传图片
- editor.ready(function () {
- var editor = this;
- var config = editor.config;
- var uploadImgUrl = config.uploadImgUrl;
- var customUpload = config.customUpload;
- var linkImg = config.hideLinkImg;
- var $uploadImgPanel;
- if (uploadImgUrl || customUpload) {
- // 第一,暴露出 $uploadContent 以便用户自定义 !!!重要
- editor.$uploadContent = $uploadContent;
- // 第二,绑定tab切换事件
- tabToggle();
- if (linkImg) {
- // 隐藏网络图片
- hideLinkImg();
- }
- } else {
- // 未配置上传图片功能
- hideUploadImg();
- }
- // 点击 $uploadContent 立即隐藏 dropPanel
- // 为了兼容IE8、9的上传,因为IE8、9使用 modal 上传
- // 这里使用异步,为了不妨碍高级浏览器通过点击 $uploadContent 选择文件
- function hidePanel() {
- menu.dropPanel.hide();
- }
- $uploadContent.click(function () {
- setTimeout(hidePanel);
- });
- });
- });
- // --------------- 处理网络图片content ---------------
- function linkContentHandler (editor, menu, $linkContent) {
- var lang = editor.config.lang;
- var $urlContainer = $('<div style="margin:20px 10px 10px 10px;"></div>');
- var $urlInput = $('<input type="text" class="block" placeholder="http://"/>');
- $urlContainer.append($urlInput);
- var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- $linkContent.append($urlContainer).append($btnSubmit).append($btnCancel);
- // 取消
- $btnCancel.click(function (e) {
- e.preventDefault();
- menu.dropPanel.hide();
- });
- // callback
- function callback() {
- $urlInput.val('');
- }
- // 确定
- $btnSubmit.click(function (e) {
- e.preventDefault();
- var url = $.trim($urlInput.val());
- if (!url) {
- // 无内容
- $urlInput.focus();
- return;
- }
- var imgHtml = '<img style="max-width:100%;" src="' + url + '"/>';
- editor.command(e, 'insertHtml', imgHtml, callback);
- });
- }
- });
- // video 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'video';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- var reg = /^<(iframe)|(embed)/i; // <iframe... 或者 <embed... 格式
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.video
- });
- // 创建 panel 内容
- var $content = $('<div></div>');
- var $linkInputContainer = $('<div style="margin:20px 10px;"></div>');
- var $linkInput = $('<input type="text" class="block" placeholder=\'格式如:<iframe src="..." frameborder=0 allowfullscreen></iframe>\'/>');
- $linkInputContainer.append($linkInput);
- var $sizeContainer = $('<div style="margin:20px 10px;"></div>');
- var $widthInput = $('<input type="text" value="640" style="width:50px;text-align:center;"/>');
- var $heightInput = $('<input type="text" value="498" style="width:50px;text-align:center;"/>');
- $sizeContainer.append('<span> ' + lang.width + ' </span>')
- .append($widthInput)
- .append('<span> px </span>')
- .append('<span> ' + lang.height + ' </span>')
- .append($heightInput)
- .append('<span> px </span>');
- var $btnContainer = $('<div></div>');
- 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>');
- var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- $btnContainer.append($howToCopy).append($btnSubmit).append($btnCancel);
- $content.append($linkInputContainer).append($sizeContainer).append($btnContainer);
- // 取消按钮
- $btnCancel.click(function (e) {
- e.preventDefault();
- $linkInput.val('');
- menu.dropPanel.hide();
- });
- // 确定按钮
- $btnSubmit.click(function (e) {
- e.preventDefault();
- var link = $.trim($linkInput.val());
- var $link;
- var width = parseInt($widthInput.val());
- var height = parseInt($heightInput.val());
- var $div = $('<div>');
- var html = '<p>{content}</p>';
- // 验证数据
- if (!link) {
- menu.dropPanel.focusFirstInput();
- return;
- }
- if (!reg.test(link)) {
- alert('视频链接格式错误!');
- menu.dropPanel.focusFirstInput();
- return;
- }
- if (isNaN(width) || isNaN(height)) {
- alert('宽度或高度不是数字!');
- return;
- }
- $link = $(link);
- // 设置高度和宽度
- $link.attr('width', width)
- .attr('height', height);
- // 拼接字符串
- html = html.replace('{content}', $div.append($link).html());
- // 执行命令
- editor.command(e, 'insertHtml', html);
- $linkInput.val('');
- });
- // 创建panel
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 400
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // location 菜单
- _e(function (E, $) {
- // 判断浏览器的 input 是否支持 keyup
- var inputKeyup = (function (input) {
- return 'onkeyup' in input;
- })(document.createElement('input'));
- // 百度地图的key
- E.baiduMapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
- // 一个页面中,如果有多个编辑器,地图会出现问题。这个参数记录一下,如果超过 1 就提示
- E.numberOfLocation = 0;
- E.createMenu(function (check) {
- var menuId = 'location';
- if (!check(menuId)) {
- return;
- }
- if (++E.numberOfLocation > 1) {
- E.error('目前不支持在一个页面多个编辑器上同时使用地图,可通过自定义菜单配置去掉地图菜单');
- return;
- }
- var editor = this;
- var config = editor.config;
- var lang = config.lang;
- var ak = config.mapAk;
- // 地图的变量存储到这个地方
- editor.mapData = {};
- var mapData = editor.mapData;
- // ---------- 地图事件 ----------
- mapData.markers = [];
- mapData.mapContainerId = 'map' + E.random();
- mapData.clearLocations = function () {
- var map = mapData.map;
- if (!map) {
- return;
- }
- map.clearOverlays();
- //同时,清空marker数组
- mapData.markers = [];
- };
- mapData.searchMap = function () {
- var map = mapData.map;
- if (!map) {
- return;
- }
- var BMap = window.BMap;
- var cityName = $cityInput.val();
- var locationName = $searchInput.val();
- var myGeo, marker;
- if(cityName !== ''){
- if(!locationName || locationName === ''){
- map.centerAndZoom(cityName, 11);
- }
- //地址解析
- if(locationName && locationName !== ''){
- myGeo = new BMap.Geocoder();
- // 将地址解析结果显示在地图上,并调整地图视野
- myGeo.getPoint(locationName, function(point){
- if (point) {
- map.centerAndZoom(point, 13);
- marker = new BMap.Marker(point);
- map.addOverlay(marker);
- marker.enableDragging(); //允许拖拽
- mapData.markers.push(marker); //将marker加入到数组中
- }else{
- // alert('未找到');
- map.centerAndZoom(cityName, 11); //找不到则重新定位到城市
- }
- }, cityName);
- }
- } // if(cityName !== '')
- };
- // load script 之后的 callback
- var hasCallback = false;
- window.baiduMapCallBack = function(){
- // 避免重复加载
- if (hasCallback) {
- return;
- } else {
- hasCallback = true;
- }
- var BMap = window.BMap;
- if (!mapData.map) {
- // 创建Map实例
- mapData.map = new BMap.Map(mapData.mapContainerId);
- }
- var map = mapData.map;
- map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 初始化地图,设置中心点坐标和地图级别
- map.addControl(new BMap.MapTypeControl()); //添加地图类型控件
- map.setCurrentCity("北京"); // 设置地图显示的城市 此项是必须设置的
- map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
- //根据IP定位
- function locationFun(result){
- var cityName = result.name;
- map.setCenter(cityName);
- // 设置城市名称
- $cityInput.val(cityName);
- if (E.placeholder) {
- $searchInput.focus();
- }
- var timeoutId, searchFn;
- if (inputKeyup) {
- // 并绑定搜索事件 - input 支持 keyup
- searchFn = function (e) {
- if (e.type === 'keyup' && e.keyCode === 13) {
- e.preventDefault();
- }
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(mapData.searchMap, 500);
- };
- $cityInput.on('keyup change paste', searchFn);
- $searchInput.on('keyup change paste', searchFn);
- } else {
- // 并绑定搜索事件 - input 不支持 keyup
- searchFn = function () {
- if (!$content.is(':visible')) {
- // panel 不显示了,就不用再监控了
- clearTimeout(timeoutId);
- return;
- }
- var currentCity = '';
- var currentSearch = '';
- var city = $cityInput.val();
- var search = $searchInput.val();
- if (city !== currentCity || search !== currentSearch) {
- // 刚获取的数据和之前的数据不一致,执行查询
- mapData.searchMap();
- // 更新数据
- currentCity = city;
- currentSearch = search;
- }
- // 继续监控
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(searchFn, 1000);
- };
- // 开始监控
- timeoutId = setTimeout(searchFn, 1000);
- }
- }
- var myCity = new BMap.LocalCity();
- myCity.get(locationFun);
- //鼠标点击,创建位置
- map.addEventListener("click", function(e){
- var marker = new BMap.Marker(new BMap.Point(e.point.lng, e.point.lat));
- map.addOverlay(marker);
- marker.enableDragging();
- mapData.markers.push(marker); //加入到数组中
- }, false);
- };
- mapData.loadMapScript = function () {
- var script = document.createElement("script");
- script.type = "text/javascript";
- script.src = "https://api.map.baidu.com/api?v=2.0&ak=" + ak + "&s=1&callback=baiduMapCallBack"; // baiduMapCallBack是一个本地函数
- try {
- // IE10- 报错
- document.body.appendChild(script);
- } catch (ex) {
- E.error('加载地图过程中发生错误');
- }
- };
- // 初始化地图
- mapData.initMap = function () {
- if (window.BMap) {
- // 不是第一次,直接处理地图即可
- window.baiduMapCallBack();
- } else {
- // 第一次,先加载地图 script,再处理地图(script加载完自动执行处理)
- mapData.loadMapScript();
- }
- };
- // ---------- 创建 menu 对象 ----------
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.location
- });
- editor.menus[menuId] = menu;
- // ---------- 构建UI ----------
- // panel content
- var $content = $('<div></div>');
- // 搜索框
- var $inputContainer = $('<div style="margin:10px 0;"></div>');
- var $cityInput = $('<input type="text"/>');
- $cityInput.css({
- width: '80px',
- 'text-align': 'center'
- });
- var $searchInput = $('<input type="text"/>');
- $searchInput.css({
- width: '300px',
- 'margin-left': '10px'
- }).attr('placeholder', lang.searchlocation);
- var $clearBtn = $('<button class="right link">' + lang.clearLocation + '</button>');
- $inputContainer.append($clearBtn)
- .append($cityInput)
- .append($searchInput);
- $content.append($inputContainer);
- // 清除位置按钮
- $clearBtn.click(function (e) {
- $searchInput.val('');
- $searchInput.focus();
- mapData.clearLocations();
- e.preventDefault();
- });
- // 地图
- var $map = $('<div id="' + mapData.mapContainerId + '"></div>');
- $map.css({
- height: '260px',
- width: '100%',
- position: 'relative',
- 'margin-top': '10px',
- border: '1px solid #f1f1f1'
- });
- var $mapLoading = $('<span>' + lang.loading + '</span>');
- $mapLoading.css({
- position: 'absolute',
- width: '100px',
- 'text-align': 'center',
- top: '45%',
- left: '50%',
- 'margin-left': '-50px'
- });
- $map.append($mapLoading);
- $content.append($map);
- // 按钮
- var $btnContainer = $('<div style="margin:10px 0;"></div>');
- var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- var $checkLabel = $('<label style="display:inline-block;margin-top:10px;color:#666;"></label>');
- var $check = $('<input type="checkbox">');
- $checkLabel.append($check).append('<span style="display:inline-block;margin-left:5px;"> ' + lang.dynamicMap + '</span>');
- $btnContainer.append($checkLabel)
- .append($btnSubmit)
- .append($btnCancel);
- $content.append($btnContainer);
- function callback() {
- $searchInput.val('');
- }
- // 『取消』按钮事件
- $btnCancel.click(function (e) {
- e.preventDefault();
- callback();
- menu.dropPanel.hide();
- });
- // 『确定』按钮事件
- $btnSubmit.click(function (e) {
- e.preventDefault();
- var map = mapData.map,
- isDynamic = $check.is(':checked'),
- markers = mapData.markers,
- center = map.getCenter(),
- centerLng = center.lng,
- centerLat = center.lat,
- zoom = map.getZoom(),
- size = map.getSize(),
- sizeWidth = size.width,
- sizeHeight = size.height,
- position,
- src,
- iframe;
- if(isDynamic){
- //动态地址
- src = 'http://ueditor.baidu.com/ueditor/dialogs/map/show.html#';
- }else{
- //静态地址
- src = 'http://api.map.baidu.com/staticimage?';
- }
- //src参数
- src = src +'center=' + centerLng + ',' + centerLat +
- '&zoom=' + zoom +
- '&width=' + sizeWidth +
- '&height=' + sizeHeight;
- if(markers.length > 0){
- src = src + '&markers=';
- //添加所有的marker
- $.each(markers, function(key, value){
- position = value.getPosition();
- if(key > 0){
- src = src + '|';
- }
- src = src + position.lng + ',' + position.lat;
- });
- }
- if(isDynamic){
- if(markers.length > 1){
- alert( lang.langDynamicOneLocation );
- return;
- }
- src += '&markerStyles=l,A';
- //插入iframe
- iframe = '<iframe class="ueditor_baidumap" src="{src}" frameborder="0" width="' + sizeWidth + '" height="' + sizeHeight + '"></iframe>';
- iframe = iframe.replace('{src}', src);
- editor.command(e, 'insertHtml', iframe, callback);
- }else{
- //插入图片
- editor.command(e, 'insertHtml', '<img style="max-width:100%;" src="' + src + '"/>', callback);
- }
- });
- // 根据 UI 创建菜单 panel
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 500
- });
- // ---------- 事件 ----------
- // render 时执行事件
- menu.onRender = function () {
- if (ak === E.baiduMapAk) {
- E.warn('建议在配置中自定义百度地图的mapAk,否则可能影响地图功能,文档:' + E.docsite);
- }
- };
- // click 事件
- menu.clickEvent = function (e) {
- var menu = this;
- var dropPanel = menu.dropPanel;
- var firstTime = false;
- // -------------隐藏-------------
- if (dropPanel.isShowing) {
- dropPanel.hide();
- return;
- }
- // -------------显示-------------
- if (!mapData.map) {
- // 第一次,先加载地图
- firstTime = true;
- }
- dropPanel.show();
- mapData.initMap();
- if (!firstTime) {
- $searchInput.focus();
- }
- };
- });
- });
- // insertcode 菜单
- _e(function (E, $) {
- // 加载 highlightjs 代码
- function loadHljs() {
- if (E.userAgent.indexOf('MSIE 8') > 0) {
- // 不支持 IE8
- return;
- }
- if (window.hljs) {
- // 不要重复加载
- return;
- }
- var script = document.createElement("script");
- script.type = "text/javascript";
- script.src = "//cdn.bootcss.com/highlight.js/9.2.0/highlight.min.js";
- document.body.appendChild(script);
- }
- E.createMenu(function (check) {
- var menuId = 'insertcode';
- if (!check(menuId)) {
- return;
- }
- // 加载 highlightjs 代码
- setTimeout(loadHljs, 0);
- var editor = this;
- var config = editor.config;
- var lang = config.lang;
- var $txt = editor.txt.$txt;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.insertcode
- });
- // click 事件
- menu.clickEvent = function (e) {
- var menu = this;
- var dropPanel = menu.dropPanel;
- // 隐藏
- if (dropPanel.isShowing) {
- dropPanel.hide();
- return;
- }
- // 显示
- $textarea.val('');
- dropPanel.show();
- // highlightjs 语言列表
- var hljs = window.hljs;
- if (hljs && hljs.listLanguages) {
- if ($langSelect.children().length !== 0) {
- return;
- }
- $langSelect.css({
- 'margin-top': '9px',
- 'margin-left': '5px'
- });
- $.each(hljs.listLanguages(), function (key, lang) {
- if (lang === 'xml') {
- lang = 'html';
- }
- if (lang === config.codeDefaultLang) {
- $langSelect.append('<option value="' + lang + '" selected="selected">' + lang + '</option>');
- } else {
- $langSelect.append('<option value="' + lang + '">' + lang + '</option>');
- }
- });
- } else {
- $langSelect.hide();
- }
- };
- // 选中状态下的 click 事件
- menu.clickEventSelected = function (e) {
- var menu = this;
- var dropPanel = menu.dropPanel;
- // 隐藏
- if (dropPanel.isShowing) {
- dropPanel.hide();
- return;
- }
- // 显示
- dropPanel.show();
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
- var $targetElem;
- var className;
- if (targetElem) {
- // 确定找到 pre 之后,再找 code
- targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
- }
- if (!targetElem) {
- return;
- }
- $targetElem = $(targetElem);
- // 赋值内容
- $textarea.val($targetElem.text());
- if ($langSelect) {
- // 赋值语言
- className = $targetElem.attr('class');
- if (className) {
- $langSelect.val(className.split(' ')[0]);
- }
- }
- };
- // 定义更新选中状态的事件
- menu.updateSelectedEvent = function () {
- var self = this; //菜单对象
- var editor = self.editor;
- var rangeElem;
- rangeElem = editor.getRangeElem();
- rangeElem = editor.getSelfOrParentByName(rangeElem, 'pre');
- if (rangeElem) {
- return true;
- }
- return false;
- };
- // 创建 panel
- var $content = $('<div></div>');
- var $textarea = $('<textarea></textarea>');
- var $langSelect = $('<select></select>');
- contentHandle($content);
- menu.dropPanel = new E.DropPanel(editor, menu, {
- $content: $content,
- width: 500
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- // ------ 增加 content 内容 ------
- function contentHandle($content) {
- // textarea 区域
- var $textareaContainer = $('<div></div>');
- $textareaContainer.css({
- margin: '15px 5px 5px 5px',
- height: '160px',
- 'text-align': 'center'
- });
- $textarea.css({
- width: '100%',
- height: '100%',
- padding: '10px'
- });
- $textarea.on('keydown', function (e) {
- // 取消 tab 键默认行为
- if (e.keyCode === 9) {
- e.preventDefault();
- }
- });
- $textareaContainer.append($textarea);
- $content.append($textareaContainer);
- // 按钮区域
- var $btnContainer = $('<div></div>');
- var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- $btnContainer.append($btnSubmit).append($btnCancel).append($langSelect);
- $content.append($btnContainer);
- // 取消按钮
- $btnCancel.click(function (e) {
- e.preventDefault();
- menu.dropPanel.hide();
- });
- // 确定按钮
- var codeTpl = '<pre style="max-width:100%;overflow-x:auto;"><code{#langClass}>{#content}</code></pre>';
- $btnSubmit.click(function (e) {
- e.preventDefault();
- var val = $textarea.val();
- if (!val) {
- // 无内容
- $textarea.focus();
- return;
- }
- var rangeElem = editor.getRangeElem();
- if ($.trim($(rangeElem).text()) && codeTpl.indexOf('<p><br></p>') !== 0) {
- codeTpl = '<p><br></p>' + codeTpl;
- }
- var lang = $langSelect ? $langSelect.val() : ''; // 获取高亮语言
- var langClass = '';
- var doHightlight = function () {
- $txt.find('pre code').each(function (i, block) {
- var $block = $(block);
- if ($block.attr('codemark')) {
- // 有 codemark 标记的代码块,就不再重新格式化了
- return;
- } else if (window.hljs) {
- // 新代码块,格式化之后,立即标记 codemark
- window.hljs.highlightBlock(block);
- $block.attr('codemark', '1');
- }
- });
- };
- // 语言高亮样式
- if (lang) {
- langClass = ' class="' + lang + ' hljs"';
- }
- // 替换标签
- val = val.replace(/&/gm, '&')
- .replace(/</gm, '<')
- .replace(/>/gm, '>');
- // ---- menu 未选中状态 ----
- if (!menu.selected) {
- // 拼接html
- var html = codeTpl.replace('{#langClass}', langClass).replace('{#content}', val);
- editor.command(e, 'insertHtml', html, doHightlight);
- return;
- }
- // ---- menu 选中状态 ----
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
- var $targetElem;
- if (targetElem) {
- // 确定找到 pre 之后,再找 code
- targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
- }
- if (!targetElem) {
- return;
- }
- $targetElem = $(targetElem);
- function commandFn() {
- var className;
- if (lang) {
- className = $targetElem.attr('class');
- if (className !== lang + ' hljs') {
- $targetElem.attr('class', lang + ' hljs');
- }
- }
- $targetElem.html(val);
- }
- function callback() {
- editor.restoreSelectionByElem(targetElem);
- doHightlight();
- }
- editor.customCommand(e, commandFn, callback);
- });
- }
- // ------ enter 时,不另起标签,只换行 ------
- $txt.on('keydown', function (e) {
- if (e.keyCode !== 13) {
- return;
- }
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
- if (!targetElem) {
- return;
- }
- editor.command(e, 'insertHtml', '\n');
- });
- // ------ 点击时,禁用其他标签 ------
- function updateMenu() {
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
- if (targetElem) {
- // 在 code 之内,禁用其他菜单
- editor.disableMenusExcept('insertcode');
- } else {
- // 不是在 code 之内,启用其他菜单
- editor.enableMenusExcept('insertcode');
- }
- }
- $txt.on('keydown click', function (e) {
- // 此处必须使用 setTimeout 异步处理,否则不对
- setTimeout(updateMenu);
- });
- });
- });
- // undo 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'undo';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.undo
- });
- // click 事件
- menu.clickEvent = function (e) {
- editor.undo();
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- // ------------ 初始化时、enter 时、打字中断时,做记录 ------------
- // ------------ ctrl + z 是调用记录撤销,而不是使用浏览器默认的撤销 ------------
- editor.ready(function () {
- var editor = this;
- var $txt = editor.txt.$txt;
- var timeoutId;
- // 执行undo记录
- function undo() {
- editor.undoRecord();
- }
- $txt.on('keydown', function (e) {
- var keyCode = e.keyCode;
- // 撤销 ctrl + z
- if (e.ctrlKey && keyCode === 90) {
- editor.undo();
- return;
- }
- if (keyCode === 13) {
- // enter 做记录
- undo();
- } else {
- // keyup 之后 1s 之内不操作,则做一次记录
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(undo, 1000);
- }
- });
- // 初始化做记录
- editor.undoRecord();
- });
- });
- });
- // redo 菜单
- _e(function (E, $) {
- E.createMenu(function (check) {
- var menuId = 'redo';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var lang = editor.config.lang;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.redo
- });
- // click 事件
- menu.clickEvent = function (e) {
- editor.redo();
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // 全屏 菜单
- _e(function (E, $) {
- // 记录全屏时的scrollTop
- var scrollTopWhenFullScreen;
- E.createMenu(function (check) {
- var menuId = 'fullscreen';
- if (!check(menuId)) {
- return;
- }
- var editor = this;
- var $txt = editor.txt.$txt;
- var config = editor.config;
- var zIndexConfig = config.zindex || 10000;
- var lang = config.lang;
- var isSelected = false;
- var zIndex;
- var maxHeight;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor,
- id: menuId,
- title: lang.fullscreen
- });
- // 定义click事件
- menu.clickEvent = function (e) {
- // 增加样式
- var $editorContainer = editor.$editorContainer;
- $editorContainer.addClass('wangEditor-fullscreen');
- // (先保存当前的)再设置z-index
- zIndex = $editorContainer.css('z-index');
- $editorContainer.css('z-index', zIndexConfig);
- var $wrapper;
- var txtHeight = $txt.height();
- var txtOuterHeight = $txt.outerHeight();
- if (editor.useMaxHeight) {
- // 记录 max-height,并暂时去掉maxheight
- maxHeight = $txt.css('max-height');
- $txt.css('max-height', 'none');
- // 如果使用了maxHeight, 将$txt从它的父元素中移出来
- $wrapper = $txt.parent();
- $wrapper.after($txt);
- $wrapper.remove();
- $txt.css('overflow-y', 'auto');
- }
- // 设置高度到全屏
- var menuContainer = editor.menuContainer;
- $txt.height(
- E.$window.height() -
- menuContainer.height() -
- (txtOuterHeight - txtHeight) // 去掉内边距和外边距
- );
- // 取消menuContainer的内联样式(menu吸顶时,会为 menuContainer 设置一些内联样式)
- editor.menuContainer.$menuContainer.attr('style', '');
- // 保存状态
- isSelected = true;
- // 记录编辑器是否全屏
- editor.isFullScreen = true;
- // 记录设置全屏时的高度
- scrollTopWhenFullScreen = E.$window.scrollTop();
- };
- // 定义选中状态的 click 事件
- menu.clickEventSelected = function (e) {
- // 取消样式
- var $editorContainer = editor.$editorContainer;
- $editorContainer.removeClass('wangEditor-fullscreen');
- $editorContainer.css('z-index', zIndex);
- // 还原height
- if (editor.useMaxHeight) {
- $txt.css('max-height', maxHeight);
- } else {
- // editor.valueContainerHeight 在 editor.txt.initHeight() 中事先保存了
- editor.$valueContainer.css('height', editor.valueContainerHeight);
- }
- // 重新计算高度
- editor.txt.initHeight();
- // 保存状态
- isSelected = false;
- // 记录编辑器是否全屏
- editor.isFullScreen = false;
- // 还原scrollTop
- if (scrollTopWhenFullScreen != null) {
- E.$window.scrollTop(scrollTopWhenFullScreen);
- }
- };
- // 定义选中事件
- menu.updateSelectedEvent = function (e) {
- return isSelected;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // 渲染menus
- _e(function (E, $) {
- E.fn.renderMenus = function () {
- var editor = this;
- var menus = editor.menus;
- var menuIds = editor.config.menus;
- var menuContainer = editor.menuContainer;
- var menu;
- var groupIdx = 0;
- $.each(menuIds, function (k, v) {
- if (v === '|') {
- groupIdx++;
- return;
- }
- menu = menus[v];
- if (menu) {
- menu.render(groupIdx);
- }
- });
- };
- });
- // 渲染menus
- _e(function (E, $) {
- E.fn.renderMenuContainer = function () {
- var editor = this;
- var menuContainer = editor.menuContainer;
- var $editorContainer = editor.$editorContainer;
- menuContainer.render();
- };
- });
- // 渲染 txt
- _e(function (E, $) {
- E.fn.renderTxt = function () {
- var editor = this;
- var txt = editor.txt;
- txt.render();
- // ready 时候,计算txt的高度
- editor.ready(function () {
- txt.initHeight();
- });
- };
- });
- // 渲染 container
- _e(function (E, $) {
- E.fn.renderEditorContainer = function () {
- var editor = this;
- var $valueContainer = editor.$valueContainer;
- var $editorContainer = editor.$editorContainer;
- var $txt = editor.txt.$txt;
- var $prev, $parent;
- // 将编辑器渲染到页面中
- if ($valueContainer === $txt) {
- $prev = editor.$prev;
- $parent = editor.$parent;
- if ($prev && $prev.length) {
- // 有前置节点,就插入到前置节点的后面
- $prev.after($editorContainer);
- } else {
- // 没有前置节点,就直接插入到父元素
- $parent.prepend($editorContainer);
- }
- } else {
- $valueContainer.after($editorContainer);
- $valueContainer.hide();
- }
- // 设置宽度(这样设置宽度有问题)
- // $editorContainer.css('width', $valueContainer.css('width'));
- };
- });
- // 菜单事件
- _e(function (E, $) {
- // 绑定每个菜单的click事件
- E.fn.eventMenus = function () {
- var menus = this.menus;
- // 绑定菜单的点击事件
- $.each(menus, function (k, v) {
- v.bindEvent();
- });
- };
- });
- // 菜单container事件
- _e(function (E, $) {
- E.fn.eventMenuContainer = function () {
- };
- });
- // 编辑区域事件
- _e(function (E, $) {
- E.fn.eventTxt = function () {
- var txt = this.txt;
- // txt内容变化时,保存选区
- txt.saveSelectionEvent();
- // txt内容变化时,随时更新 value
- txt.updateValueEvent();
- // txt内容变化时,随时更新 menu style
- txt.updateMenuStyleEvent();
- // // 鼠标hover时,显示 p head 高度(暂时关闭这个功能)
- // if (!/ie/i.test(E.userAgent)) {
- // // 暂时不支持IE
- // txt.showHeightOnHover();
- // }
- };
- });
- // 上传图片事件
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var fns = editor.config.uploadImgFns; // editor.config.uploadImgFns = {} 在config文件中定义了
- // -------- 定义load函数 --------
- fns.onload || (fns.onload = function (resultText, xhr) {
- E.log('上传结束,返回结果为 ' + resultText);
- var editor = this;
- var originalName = editor.uploadImgOriginalName || ''; // 上传图片时,已经将图片的名字存在 editor.uploadImgOriginalName
- var img;
- if (resultText.indexOf('error|') === 0) {
- // 提示错误
- E.warn('上传失败:' + resultText.split('|')[1]);
- alert(resultText.split('|')[1]);
- } else {
- E.log('上传成功,即将插入编辑区域,结果为:' + resultText);
- // 将结果插入编辑器
- img = document.createElement('img');
- img.onload = function () {
- var html = '<img src="' + resultText + '" alt="' + originalName + '" style="max-width:100%;"/>';
- editor.command(null, 'insertHtml', html);
- E.log('已插入图片,地址 ' + resultText);
- img = null;
- };
- img.onerror = function () {
- E.error('使用返回的结果获取图片,发生错误。请确认以下结果是否正确:' + resultText);
- img = null;
- };
- img.src = resultText;
- }
- });
- // -------- 定义tiemout函数 --------
- fns.ontimeout || (fns.ontimeout = function (xhr) {
- E.error('上传图片超时');
- alert('上传图片超时');
- });
- // -------- 定义error函数 --------
- fns.onerror || (fns.onerror = function (xhr) {
- E.error('上传上图片发生错误');
- alert('上传上图片发生错误');
- });
- });
- });
- // xhr 上传图片
- _e(function (E, $) {
- if (!window.FileReader || !window.FormData) {
- // 如果不支持html5的文档操作,直接返回
- return;
- }
- E.plugin(function () {
- var editor = this;
- var config = editor.config;
- var uploadImgUrl = config.uploadImgUrl;
- var uploadTimeout = config.uploadTimeout;
- // 获取配置中的上传事件
- var uploadImgFns = config.uploadImgFns;
- var onload = uploadImgFns.onload;
- var ontimeout = uploadImgFns.ontimeout;
- var onerror = uploadImgFns.onerror;
- if (!uploadImgUrl) {
- return;
- }
- // -------- 将以base64的图片url数据转换为Blob --------
- function convertBase64UrlToBlob(urlData, filetype){
- //去掉url的头,并转换为byte
- var bytes = window.atob(urlData.split(',')[1]);
- //处理异常,将ascii码小于0的转换为大于0
- var ab = new ArrayBuffer(bytes.length);
- var ia = new Uint8Array(ab);
- var i;
- for (i = 0; i < bytes.length; i++) {
- ia[i] = bytes.charCodeAt(i);
- }
- return new Blob([ab], {type : filetype});
- }
- // -------- 插入图片的方法 --------
- function insertImg(src, event) {
- var img = document.createElement('img');
- img.onload = function () {
- var html = '<img src="' + src + '" style="max-width:100%;"/>';
- editor.command(event, 'insertHtml', html);
- E.log('已插入图片,地址 ' + src);
- img = null;
- };
- img.onerror = function () {
- E.error('使用返回的结果获取图片,发生错误。请确认以下结果是否正确:' + src);
- img = null;
- };
- img.src = src;
- }
- // -------- onprogress 事件 --------
- function updateProgress(e) {
- if (e.lengthComputable) {
- var percentComplete = e.loaded / e.total;
- editor.showUploadProgress(percentComplete * 100);
- }
- }
- // -------- xhr 上传图片 --------
- editor.xhrUploadImg = function (opt) {
- // opt 数据
- var event = opt.event;
- var fileName = opt.filename || '';
- var base64 = opt.base64;
- var fileType = opt.fileType || 'image/png'; // 无扩展名则默认使用 png
- var name = opt.name || 'wangEditor_upload_file';
- var loadfn = opt.loadfn || onload;
- var errorfn = opt.errorfn || onerror;
- var timeoutfn = opt.timeoutfn || ontimeout;
- // 上传参数(如 token)
- var params = editor.config.uploadParams || {};
- // headers
- var headers = editor.config.uploadHeaders || {};
- // 获取文件扩展名
- var fileExt = 'png'; // 默认为 png
- if (fileName.indexOf('.') > 0) {
- // 原来的文件名有扩展名
- fileExt = fileName.slice(fileName.lastIndexOf('.') - fileName.length + 1);
- } else if (fileType.indexOf('/') > 0 && fileType.split('/')[1]) {
- // 文件名没有扩展名,通过类型获取,如从 'image/png' 取 'png'
- fileExt = fileType.split('/')[1];
- }
- // ------------ begin 预览模拟上传 ------------
- if (E.isOnWebsite) {
- E.log('预览模拟上传');
- insertImg(base64, event);
- return;
- }
- // ------------ end 预览模拟上传 ------------
- // 变量声明
- var xhr = new XMLHttpRequest();
- var timeoutId;
- var src;
- var formData = new FormData();
- // 超时处理
- function timeoutCallback() {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- if (xhr && xhr.abort) {
- xhr.abort();
- }
- // 超时了就阻止默认行为
- event.preventDefault();
- // 执行回调函数,提示什么内容,都应该在回调函数中定义
- timeoutfn && timeoutfn.call(editor, xhr);
- // 隐藏进度条
- editor.hideUploadProgress();
- }
- xhr.onload = function () {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- // 记录文件名到 editor.uploadImgOriginalName ,插入图片时,可做 alt 属性用
- editor.uploadImgOriginalName = fileName;
- if (fileName.indexOf('.') > 0) {
- editor.uploadImgOriginalName = fileName.split('.')[0];
- }
- // 执行load函数,任何操作,都应该在load函数中定义
- loadfn && loadfn.call(editor, xhr.responseText, xhr);
- // 隐藏进度条
- editor.hideUploadProgress();
- };
- xhr.onerror = function () {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- // 超时了就阻止默认行为
- event.preventDefault();
- // 执行error函数,错误提示,应该在error函数中定义
- errorfn && errorfn.call(editor, xhr);
- // 隐藏进度条
- editor.hideUploadProgress();
- };
- // xhr.onprogress = updateProgress;
- xhr.upload.onprogress = updateProgress;
- // 填充数据
- formData.append(name, convertBase64UrlToBlob(base64, fileType), E.random() + '.' + fileExt);
- // 添加参数
- $.each(params, function (key, value) {
- formData.append(key, value);
- });
- // 开始上传
- xhr.open('POST', uploadImgUrl, true);
- // xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // 将参数解析成传统form的方式上传
- // 修改自定义配置的headers
- $.each(headers, function (key, value) {
- xhr.setRequestHeader(key, value);
- });
- // 跨域上传时,传cookie
- xhr.withCredentials = true;
- // 发送数据
- xhr.send(formData);
- timeoutId = setTimeout(timeoutCallback, uploadTimeout);
- E.log('开始上传...并开始超时计算');
- };
- });
- });
- // 进度条
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var menuContainer = editor.menuContainer;
- var menuHeight = menuContainer.height();
- var $editorContainer = editor.$editorContainer;
- var width = $editorContainer.width();
- var $progress = $('<div class="wangEditor-upload-progress"></div>');
- // 渲染事件
- var isRender = false;
- function render() {
- if (isRender) {
- return;
- }
- isRender = true;
- $progress.css({
- top: menuHeight + 'px'
- });
- $editorContainer.append($progress);
- }
- // ------ 显示进度 ------
- editor.showUploadProgress = function (progress) {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- // 显示之前,先判断是否渲染
- render();
- $progress.show();
- $progress.width(progress * width / 100);
- };
- // ------ 隐藏进度条 ------
- var timeoutId;
- function hideProgress() {
- $progress.hide();
- timeoutId = null;
- }
- editor.hideUploadProgress = function (time) {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- time = time || 750;
- timeoutId = setTimeout(hideProgress, time);
- };
- });
- });
- // upload img 插件
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var config = editor.config;
- var uploadImgUrl = config.uploadImgUrl;
- var uploadTimeout = config.uploadTimeout;
- var event;
- if (!uploadImgUrl) {
- return;
- }
- // 获取editor的上传dom
- var $uploadContent = editor.$uploadContent;
- if (!$uploadContent) {
- return;
- }
- // 自定义UI,并添加到上传dom节点上
- var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
- $uploadContent.append($uploadIcon);
- // ---------- 构建上传对象 ----------
- var upfile = new E.UploadFile({
- editor: editor,
- uploadUrl: uploadImgUrl,
- timeout: uploadTimeout,
- fileAccept: 'image/jpg,image/jpeg,image/png,image/gif,image/bmp' // 只允许选择图片
- });
- // 选择本地文件,上传
- $uploadIcon.click(function (e) {
- event = e;
- upfile.selectFiles();
- });
- });
- });
- // h5 方式上传图片
- _e(function (E, $) {
- if (!window.FileReader || !window.FormData) {
- // 如果不支持html5的文档操作,直接返回
- return;
- }
- // 构造函数
- var UploadFile = function (opt) {
- this.editor = opt.editor;
- this.uploadUrl = opt.uploadUrl;
- this.timeout = opt.timeout;
- this.fileAccept = opt.fileAccept;
- this.multiple = true;
- };
- UploadFile.fn = UploadFile.prototype;
- // clear
- UploadFile.fn.clear = function () {
- this.$input.val('');
- E.log('input value 已清空');
- };
- // 渲染
- UploadFile.fn.render = function () {
- var self = this;
- if (self._hasRender) {
- // 不要重复渲染
- return;
- }
- E.log('渲染dom');
- var fileAccept = self.fileAccept;
- var acceptTpl = fileAccept ? 'accept="' + fileAccept + '"' : '';
- var multiple = self.multiple;
- var multipleTpl = multiple ? 'multiple="multiple"' : '';
- var $input = $('<input type="file" ' + acceptTpl + ' ' + multipleTpl + '/>');
- var $container = $('<div style="visibility:hidden;"></div>');
- $container.append($input);
- E.$body.append($container);
- // onchange 事件
- $input.on('change', function (e) {
- self.selected(e, $input.get(0));
- });
- // 记录对象数据
- self.$input = $input;
- // 记录
- self._hasRender = true;
- };
- // 选择
- UploadFile.fn.selectFiles = function () {
- var self = this;
- E.log('使用 html5 方式上传');
- // 先渲染
- self.render();
- // 选择
- E.log('选择文件');
- self.$input.click();
- };
- // 选中文件之后
- UploadFile.fn.selected = function (e, input) {
- var self = this;
- var files = input.files || [];
- if (files.length === 0) {
- return;
- }
- E.log('选中 ' + files.length + ' 个文件');
- // 遍历选中的文件,预览、上传
- $.each(files, function (key, value) {
- self.upload(value);
- });
- };
- // 上传单个文件
- UploadFile.fn.upload = function (file) {
- var self = this;
- var editor = self.editor;
- var filename = file.name || '';
- var fileType = file.type || '';
- var uploadImgFns = editor.config.uploadImgFns;
- var uploadFileName = editor.config.uploadImgFileName || 'wangEditorH5File';
- var onload = uploadImgFns.onload;
- var ontimeout = uploadImgFns.ontimeout;
- var onerror = uploadImgFns.onerror;
- var reader = new FileReader();
- if (!onload || !ontimeout || !onerror) {
- E.error('请为编辑器配置上传图片的 onload ontimeout onerror 回调事件');
- return;
- }
- E.log('开始执行 ' + filename + ' 文件的上传');
- // 清空 input 数据
- function clearInput() {
- self.clear();
- }
- // onload事件
- reader.onload = function (e) {
- E.log('已读取' + filename + '文件');
- var base64 = e.target.result || this.result;
- editor.xhrUploadImg({
- event: e,
- filename: filename,
- base64: base64,
- fileType: fileType,
- name: uploadFileName,
- loadfn: function (resultText, xhr) {
- clearInput();
- // 执行配置中的方法
- var editor = this;
- onload.call(editor, resultText, xhr);
- },
- errorfn: function (xhr) {
- clearInput();
- if (E.isOnWebsite) {
- alert('wangEditor官网暂时没有服务端,因此报错。实际项目中不会发生');
- }
- // 执行配置中的方法
- var editor = this;
- onerror.call(editor, xhr);
- },
- timeoutfn: function (xhr) {
- clearInput();
- if (E.isOnWebsite) {
- alert('wangEditor官网暂时没有服务端,因此超时。实际项目中不会发生');
- }
- // 执行配置中的方法
- var editor = this;
- ontimeout(editor, xhr);
- }
- });
- };
- // 开始取文件
- reader.readAsDataURL(file);
- };
- // 暴露给 E
- E.UploadFile = UploadFile;
- });
- // form方式上传图片
- _e(function (E, $) {
- if (window.FileReader && window.FormData) {
- // 如果支持 html5 上传,则返回
- return;
- }
- // 构造函数
- var UploadFile = function (opt) {
- this.editor = opt.editor;
- this.uploadUrl = opt.uploadUrl;
- this.timeout = opt.timeout;
- this.fileAccept = opt.fileAccept;
- this.multiple = false;
- };
- UploadFile.fn = UploadFile.prototype;
- // clear
- UploadFile.fn.clear = function () {
- this.$input.val('');
- E.log('input value 已清空');
- };
- // 隐藏modal
- UploadFile.fn.hideModal = function () {
- this.modal.hide();
- };
- // 渲染
- UploadFile.fn.render = function () {
- var self = this;
- var editor = self.editor;
- var uploadFileName = editor.config.uploadImgFileName || 'wangEditorFormFile';
- if (self._hasRender) {
- // 不要重复渲染
- return;
- }
- // 服务器端路径
- var uploadUrl = self.uploadUrl;
- E.log('渲染dom');
- // 创建 form 和 iframe
- var iframeId = 'iframe' + E.random();
- var $iframe = $('<iframe name="' + iframeId + '" id="' + iframeId + '" frameborder="0" width="0" height="0"></iframe>');
- var multiple = self.multiple;
- var multipleTpl = multiple ? 'multiple="multiple"' : '';
- var $p = $('<p>选择图片并上传</p>');
- var $input = $('<input type="file" ' + multipleTpl + ' name="' + uploadFileName + '"/>');
- var $btn = $('<input type="submit" value="上传"/>');
- var $form = $('<form enctype="multipart/form-data" method="post" action="' + uploadUrl + '" target="' + iframeId + '"></form>');
- var $container = $('<div style="margin:10px 20px;"></div>');
- $form.append($p).append($input).append($btn);
- // 增加用户配置的参数,如 token
- $.each(editor.config.uploadParams, function (key, value) {
- $form.append( $('<input type="hidden" name="' + key + '" value="' + value + '"/>') );
- });
- $container.append($form);
- $container.append($iframe);
- self.$input = $input;
- self.$iframe = $iframe;
- // 生成 modal
- var modal = new E.Modal(editor, undefined, {
- $content: $container
- });
- self.modal = modal;
- // 记录
- self._hasRender = true;
- };
- // 绑定 iframe load 事件
- UploadFile.fn.bindLoadEvent = function () {
- var self = this;
- if (self._hasBindLoad) {
- // 不要重复绑定
- return;
- }
- var editor = self.editor;
- var $iframe = self.$iframe;
- var iframe = $iframe.get(0);
- var iframeWindow = iframe.contentWindow;
- var onload = editor.config.uploadImgFns.onload;
- // 定义load事件
- function onloadFn() {
- var resultText = $.trim(iframeWindow.document.body.innerHTML);
- if (!resultText) {
- return;
- }
- // 获取文件名
- var fileFullName = self.$input.val(); // 结果如 C:\folder\abc.png 格式
- var fileOriginalName = fileFullName;
- if (fileFullName.lastIndexOf('\\') >= 0) {
- // 获取 abc.png 格式
- fileOriginalName = fileFullName.slice(fileFullName.lastIndexOf('\\') + 1);
- if (fileOriginalName.indexOf('.') > 0) {
- // 获取 abc (即不带扩展名的文件名)
- fileOriginalName = fileOriginalName.split('.')[0];
- }
- }
- // 将文件名暂存到 editor.uploadImgOriginalName ,插入图片时,可作为 alt 属性来用
- editor.uploadImgOriginalName = fileOriginalName;
- // 执行load函数,插入图片的操作,应该在load函数中执行
- onload.call(editor, resultText);
- // 清空 input 数据
- self.clear();
- // 隐藏modal
- self.hideModal();
- }
- // 绑定 load 事件
- if (iframe.attachEvent) {
- iframe.attachEvent('onload', onloadFn);
- } else {
- iframe.onload = onloadFn;
- }
- // 记录
- self._hasBindLoad = true;
- };
- UploadFile.fn.show = function () {
- var self = this;
- var modal = self.modal;
- function show() {
- modal.show();
- self.bindLoadEvent();
- }
- setTimeout(show);
- };
- // 选择
- UploadFile.fn.selectFiles = function () {
- var self = this;
- E.log('使用 form 方式上传');
- // 先渲染
- self.render();
- // 先清空
- self.clear();
- // 显示
- self.show();
- };
- // 暴露给 E
- E.UploadFile = UploadFile;
- });
- // upload img 插件 粘贴图片
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var txt = editor.txt;
- var $txt = txt.$txt;
- var config = editor.config;
- var uploadImgUrl = config.uploadImgUrl;
- var uploadFileName = config.uploadImgFileName || 'wangEditorPasteFile';
- var pasteEvent;
- var $imgsBeforePaste;
- // 未配置上传图片url,则忽略
- if (!uploadImgUrl) {
- return;
- }
- // -------- 非 chrome 下,通过查找粘贴的图片的方式上传 --------
- function findPasteImgAndUpload() {
- var reg = /^data:(image\/\w+);base64/;
- var $imgs = $txt.find('img');
- E.log('粘贴后,检查到编辑器有' + $imgs.length + '个图片。开始遍历图片,试图找到刚刚粘贴过来的图片');
- $.each($imgs, function () {
- var img = this;
- var $img = $(img);
- var flag;
- var base64 = $img.attr('src');
- var type;
- // 判断当前图片是否是粘贴之前的
- $imgsBeforePaste.each(function () {
- if (img === this) {
- // 当前图片是粘贴之前的
- flag = true;
- return false;
- }
- });
- // 当前图片是粘贴之前的,则忽略
- if (flag) {
- return;
- }
- E.log('找到一个粘贴过来的图片');
- if (reg.test(base64)) {
- // 得到的粘贴的图片是 base64 格式,符合要求
- E.log('src 是 base64 格式,可以上传');
- type = base64.match(reg)[1];
- editor.xhrUploadImg({
- event: pasteEvent,
- base64: base64,
- fileType: type,
- name: uploadFileName
- });
- } else {
- E.log('src 为 ' + base64 + ' ,不是 base64 格式,暂时不支持上传');
- }
- // 最终移除原图片
- $img.remove();
- });
- E.log('遍历结束');
- }
- // 开始监控粘贴事件
- $txt.on('paste', function (e) {
- pasteEvent = e;
- var data = pasteEvent.clipboardData || pasteEvent.originalEvent.clipboardData;
- var text;
- var items;
- // -------- 试图获取剪切板中的文字,有文字的情况下,就不处理图片粘贴 --------
- if (data == null) {
- text = window.clipboardData && window.clipboardData.getData('text');
- } else {
- text = data.getData('text/plain') || data.getData('text/html');
- }
- if (text) {
- return;
- }
- items = data && data.items;
- if (items) {
- // -------- chrome 可以用 data.items 取出图片 -----
- E.log('通过 data.items 得到了数据');
- $.each(items, function (key, value) {
- var fileType = value.type || '';
- if(fileType.indexOf('image') < 0) {
- // 不是图片
- return;
- }
- var file = value.getAsFile();
- var reader = new FileReader();
- E.log('得到一个粘贴图片');
- reader.onload = function (e) {
- E.log('读取到粘贴的图片');
- // 执行上传
- var base64 = e.target.result || this.result;
- editor.xhrUploadImg({
- event: pasteEvent,
- base64: base64,
- fileType: fileType,
- name: uploadFileName
- });
- };
- //读取粘贴的文件
- reader.readAsDataURL(file);
- });
- } else {
- // -------- 非 chrome 不能用 data.items 取图片 -----
- E.log('未从 data.items 得到数据,使用检测粘贴图片的方式');
- // 获取
- $imgsBeforePaste = $txt.find('img');
- E.log('粘贴前,检查到编辑器有' + $imgsBeforePaste.length + '个图片');
- // 异步上传找到的图片
- setTimeout(findPasteImgAndUpload, 0);
- }
- });
- });
- });
- // 拖拽上传图片 插件
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var txt = editor.txt;
- var $txt = txt.$txt;
- var config = editor.config;
- var uploadImgUrl = config.uploadImgUrl;
- var uploadFileName = config.uploadImgFileName || 'wangEditorDragFile';
- // 未配置上传图片url,则忽略
- if (!uploadImgUrl) {
- return;
- }
- // 阻止浏览器默认行为
- E.$document.on('dragleave drop dragenter dragover', function (e) {
- e.preventDefault();
- });
- // 监控 $txt drop 事件
- $txt.on('drop', function (dragEvent) {
- dragEvent.preventDefault();
- var originalEvent = dragEvent.originalEvent;
- var files = originalEvent.dataTransfer && originalEvent.dataTransfer.files;
- if (!files || !files.length) {
- return;
- }
- $.each(files, function (k, file) {
- var type = file.type;
- var name = file.name;
- if (type.indexOf('image/') < 0) {
- // 只接收图片
- return;
- }
- E.log('得到图片 ' + name);
- var reader = new FileReader();
- reader.onload = function (e) {
- E.log('读取到图片 ' + name);
- // 执行上传
- var base64 = e.target.result || this.result;
- editor.xhrUploadImg({
- event: dragEvent,
- base64: base64,
- fileType: type,
- name: uploadFileName
- });
- };
- //读取粘贴的文件
- reader.readAsDataURL(file);
- });
- });
- });
- });
- // 编辑器区域 table toolbar
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var txt = editor.txt;
- var $txt = txt.$txt;
- var html = '';
- // 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
- var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
- var $currentTable;
- // 用到的dom节点
- var isRendered = false;
- var $toolbar = $('<div class="txt-toolbar"></div>');
- var $triangle = $('<div class="tip-triangle"></div>');
- var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
- var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
- var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
- // 渲染到页面
- function render() {
- if (isRendered) {
- return;
- }
- // 绑定事件
- bindEvent();
- // 拼接 渲染到页面上
- $toolbar.append($triangle)
- .append($delete)
- .append($zoomSmall)
- .append($zoomBig);
- editor.$editorContainer.append($toolbar);
- isRendered = true;
- }
- // 绑定事件
- function bindEvent() {
- // 统一执行命令的方法
- var commandFn;
- function command(e, callback) {
- // 执行命令之前,先存储html内容
- html = $txt.html();
- // 监控内容变化
- var cb = function () {
- if (callback) {
- callback();
- }
- if (html !== $txt.html()) {
- $txt.change();
- }
- };
- // 执行命令
- if (commandFn) {
- editor.customCommand(e, commandFn, cb);
- }
- }
- // 删除
- $delete.click(function (e) {
- commandFn = function () {
- $currentTable.remove();
- };
- command(e, function () {
- setTimeout(hide, 100);
- });
- });
- // 放大
- $zoomBig.click(function (e) {
- commandFn = function () {
- $currentTable.css({
- width: '100%'
- });
- };
- command(e, function () {
- setTimeout(show);
- });
- });
- // 缩小
- $zoomSmall.click(function (e) {
- commandFn = function () {
- $currentTable.css({
- width: 'auto'
- });
- };
- command(e, function () {
- setTimeout(show);
- });
- });
- }
- // 显示 toolbar
- function show() {
- if (editor._disabled) {
- // 编辑器已经被禁用,则不让显示
- return;
- }
- if ($currentTable == null) {
- return;
- }
- $currentTable.addClass('clicked');
- var tablePosition = $currentTable.position();
- var tableTop = tablePosition.top;
- var tableLeft = tablePosition.left;
- var tableHeight = $currentTable.outerHeight();
- var tableWidth = $currentTable.outerWidth();
- // --- 定位 toolbar ---
- // 计算初步结果
- var top = tableTop + tableHeight;
- var left = tableLeft;
- var marginLeft = 0;
- var txtTop = $currentTxt.position().top;
- var txtHeight = $currentTxt.outerHeight();
- if (top > (txtTop + txtHeight)) {
- // top 不得超出编辑范围
- top = txtTop + txtHeight;
- }
- // 显示(方便计算 margin)
- $toolbar.show();
- // 计算 margin
- var width = $toolbar.outerWidth();
- marginLeft = tableWidth / 2 - width / 2;
- // 定位
- $toolbar.css({
- top: top + 5,
- left: left,
- 'margin-left': marginLeft
- });
- // 如果定位太靠左了
- if (marginLeft < 0) {
- // 得到三角形的margin-left
- $toolbar.css('margin-left', '0');
- $triangle.hide();
- } else {
- $triangle.show();
- }
- }
- // 隐藏 toolbar
- function hide() {
- if ($currentTable == null) {
- return;
- }
- $currentTable.removeClass('clicked');
- $currentTable = null;
- $toolbar.hide();
- }
- // click table 事件
- $currentTxt.on('click', 'table', function (e) {
- var $table = $(e.currentTarget);
- // 渲染
- render();
- if ($currentTable && ($currentTable.get(0) === $table.get(0))) {
- setTimeout(hide, 100);
- return;
- }
- // 显示 toolbar
- $currentTable = $table;
- show();
- // 阻止冒泡
- e.preventDefault();
- e.stopPropagation();
- }).on('click keydown scroll', function (e) {
- setTimeout(hide, 100);
- });
- E.$body.on('click keydown scroll', function (e) {
- setTimeout(hide, 100);
- });
- });
- });
- // 编辑器区域 img toolbar
- _e(function (E, $) {
- if (E.userAgent.indexOf('MSIE 8') > 0) {
- return;
- }
- E.plugin(function () {
- var editor = this;
- var lang = editor.config.lang;
- var txt = editor.txt;
- var $txt = txt.$txt;
- var html = '';
- // 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
- var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
- var $editorContainer = editor.$editorContainer;
- var $currentImg;
- var currentLink = '';
- // 用到的dom节点
- var isRendered = false;
- var $dragPoint = $('<div class="img-drag-point"></div>');
- var $toolbar = $('<div class="txt-toolbar"></div>');
- var $triangle = $('<div class="tip-triangle"></div>');
- var $menuContainer = $('<div></div>');
- var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
- var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
- var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
- // var $floatLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
- // var $noFloat = $('<a href="#"><i class="wangeditor-menu-img-align-justify"></i></a>');
- // var $floatRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
- var $alignLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
- var $alignCenter = $('<a href="#"><i class="wangeditor-menu-img-align-center"></i></a>');
- var $alignRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
- var $link = $('<a href="#"><i class="wangeditor-menu-img-link"></i></a>');
- var $unLink = $('<a href="#"><i class="wangeditor-menu-img-unlink"></i></a>');
- var $linkInputContainer = $('<div style="display:none;"></div>');
- var $linkInput = $('<input type="text" style="height:26px; margin-left:10px; width:200px;"/>');
- var $linkBtnSubmit = $('<button class="right">' + lang.submit + '</button>');
- var $linkBtnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
- // 记录是否正在拖拽
- var isOnDrag = false;
- // 获取 / 设置 链接
- function imgLink(e, url) {
- if (!$currentImg) {
- return;
- }
- var commandFn;
- var callback = function () {
- // 及时保存currentLink
- if (url != null) {
- currentLink = url;
- }
- if (html !== $txt.html()) {
- $txt.change();
- }
- };
- var $link;
- var inLink = false;
- var $parent = $currentImg.parent();
- if ($parent.get(0).nodeName.toLowerCase() === 'a') {
- // 父元素就是图片链接
- $link = $parent;
- inLink = true;
- } else {
- // 父元素不是图片链接,则重新创建一个链接
- $link = $('<a target="_blank"></a>');
- }
- if (url == null) {
- // url 无值,是获取链接
- return $link.attr('href') || '';
- } else if (url === '') {
- // url 是空字符串,是取消链接
- if (inLink) {
- commandFn = function () {
- $currentImg.unwrap();
- };
- }
- } else {
- // url 有值,是设置链接
- if (url === currentLink) {
- return;
- }
- commandFn = function () {
- $link.attr('href', url);
- if (!inLink) {
- // 当前图片未包含在链接中,则包含进来
- $currentImg.wrap($link);
- }
- };
- }
- // 执行命令
- if (commandFn) {
- // 记录下执行命令之前的html内容
- html = $txt.html();
- // 执行命令
- editor.customCommand(e, commandFn, callback);
- }
- }
- // 渲染到页面
- function render() {
- if (isRendered) {
- return;
- }
- // 绑定事件
- bindToolbarEvent();
- bindDragEvent();
- // 菜单放入 container
- $menuContainer.append($delete)
- .append($zoomSmall)
- .append($zoomBig)
- // .append($floatLeft)
- // .append($noFloat)
- // .append($floatRight);
- .append($alignLeft)
- .append($alignCenter)
- .append($alignRight)
- .append($link)
- .append($unLink);
- // 链接input放入container
- $linkInputContainer.append($linkInput)
- .append($linkBtnCancel)
- .append($linkBtnSubmit);
- // 拼接 渲染到页面上
- $toolbar.append($triangle)
- .append($menuContainer)
- .append($linkInputContainer);
- editor.$editorContainer.append($toolbar).append($dragPoint);
- isRendered = true;
- }
- // 绑定toolbar事件
- function bindToolbarEvent() {
- // 统一执行命令的方法
- var commandFn;
- function customCommand(e, callback) {
- var cb;
- // 记录下执行命令之前的html内容
- html = $txt.html();
- cb = function () {
- if (callback) {
- callback();
- }
- if (html !== $txt.html()) {
- $txt.change();
- }
- };
- // 执行命令
- if (commandFn) {
- editor.customCommand(e, commandFn, cb);
- }
- }
- // 删除
- $delete.click(function (e) {
- // 删除之前先unlink
- imgLink(e, '');
- // 删除图片
- commandFn = function () {
- $currentImg.remove();
- };
- customCommand(e, function () {
- setTimeout(hide, 100);
- });
- });
- // 放大
- $zoomBig.click(function (e) {
- commandFn = function () {
- var img = $currentImg.get(0);
- var width = img.width;
- var height = img.height;
- width = width * 1.1;
- height = height * 1.1;
- $currentImg.css({
- width: width + 'px',
- height: height + 'px'
- });
- };
- customCommand(e, function () {
- setTimeout(show);
- });
- });
- // 缩小
- $zoomSmall.click(function (e) {
- commandFn = function () {
- var img = $currentImg.get(0);
- var width = img.width;
- var height = img.height;
- width = width * 0.9;
- height = height * 0.9;
- $currentImg.css({
- width: width + 'px',
- height: height + 'px'
- });
- };
- customCommand(e, function () {
- setTimeout(show);
- });
- });
- // // 左浮动
- // $floatLeft.click(function (e) {
- // commandFn = function () {
- // $currentImg.css({
- // float: 'left'
- // });
- // };
- // customCommand(e, function () {
- // setTimeout(hide, 100);
- // });
- // });
- // alignLeft
- $alignLeft.click(function (e) {
- commandFn = function () {
- // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
- $currentImg.parents('p').css({
- 'text-align': 'left'
- }).attr('align', 'left');
- };
- customCommand(e, function () {
- setTimeout(hide, 100);
- });
- });
- // // 右浮动
- // $floatRight.click(function (e) {
- // commandFn = function () {
- // $currentImg.css({
- // float: 'right'
- // });
- // };
- // customCommand(e, function () {
- // setTimeout(hide, 100);
- // });
- // });
- // alignRight
- $alignRight.click(function (e) {
- commandFn = function () {
- // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
- $currentImg.parents('p').css({
- 'text-align': 'right'
- }).attr('align', 'right');
- };
- customCommand(e, function () {
- setTimeout(hide, 100);
- });
- });
- // // 无浮动
- // $noFloat.click(function (e) {
- // commandFn = function () {
- // $currentImg.css({
- // float: 'none'
- // });
- // };
- // customCommand(e, function () {
- // setTimeout(hide, 100);
- // });
- // });
- // alignCenter
- $alignCenter.click(function (e) {
- commandFn = function () {
- // 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
- $currentImg.parents('p').css({
- 'text-align': 'center'
- }).attr('align', 'center');
- };
- customCommand(e, function () {
- setTimeout(hide, 100);
- });
- });
- // link
- // 显示链接input
- $link.click(function (e) {
- e.preventDefault();
- // 获取当前链接,并显示
- currentLink = imgLink(e);
- $linkInput.val(currentLink);
- $menuContainer.hide();
- $linkInputContainer.show();
- });
- // 设置链接
- $linkBtnSubmit.click(function (e) {
- e.preventDefault();
- var url = $.trim($linkInput.val());
- if (url) {
- // 设置链接,同时会自动更新 currentLink 的值
- imgLink(e, url);
- }
- // 隐藏 toolbar
- setTimeout(hide);
- });
- // 取消设置链接
- $linkBtnCancel.click(function (e) {
- e.preventDefault();
- // 重置链接 input
- $linkInput.val(currentLink);
- $menuContainer.show();
- $linkInputContainer.hide();
- });
- // unlink
- $unLink.click(function (e) {
- e.preventDefault();
- // 执行 unlink
- imgLink(e, '');
- // 隐藏 toolbar
- setTimeout(hide);
- });
- }
- // 绑定drag事件
- function bindDragEvent() {
- var _x, _y;
- var dragMarginLeft, dragMarginTop;
- var imgWidth, imgHeight;
- function mousemove (e) {
- var diffX, diffY;
- // 计算差额
- diffX = e.pageX - _x;
- diffY = e.pageY - _y;
- // --------- 计算拖拽点的位置 ---------
- var currentDragMarginLeft = dragMarginLeft + diffX;
- var currentDragMarginTop = dragMarginTop + diffY;
- $dragPoint.css({
- 'margin-left': currentDragMarginLeft,
- 'margin-top': currentDragMarginTop
- });
- // --------- 计算图片的大小 ---------
- var currentImgWidth = imgWidth + diffX;
- var currentImggHeight = imgHeight + diffY;
- $currentImg && $currentImg.css({
- width: currentImgWidth,
- height: currentImggHeight
- });
- }
- $dragPoint.on('mousedown', function(e){
- if (!$currentImg) {
- return;
- }
- // 当前鼠标位置
- _x = e.pageX;
- _y = e.pageY;
- // 当前拖拽点的位置
- dragMarginLeft = parseFloat($dragPoint.css('margin-left'), 10);
- dragMarginTop = parseFloat($dragPoint.css('margin-top'), 10);
- // 当前图片的大小
- imgWidth = $currentImg.width();
- imgHeight = $currentImg.height();
- // 隐藏 $toolbar
- $toolbar.hide();
- // 绑定计算事件
- E.$document.on('mousemove._dragResizeImg', mousemove);
- E.$document.on('mouseup._dragResizeImg', function (e) {
- // 取消绑定
- E.$document.off('mousemove._dragResizeImg');
- E.$document.off('mouseup._dragResizeImg');
- // 隐藏,并还原拖拽点的位置
- hide();
- $dragPoint.css({
- 'margin-left': dragMarginLeft,
- 'margin-top': dragMarginTop
- });
- // 记录
- isOnDrag = false;
- });
- // 记录
- isOnDrag = true;
- });
- }
- // 显示 toolbar
- function show() {
- if (editor._disabled) {
- // 编辑器已经被禁用,则不让显示
- return;
- }
- if ($currentImg == null) {
- return;
- }
- $currentImg.addClass('clicked');
- var imgPosition = $currentImg.position();
- var imgTop = imgPosition.top;
- var imgLeft = imgPosition.left;
- var imgHeight = $currentImg.outerHeight();
- var imgWidth = $currentImg.outerWidth();
- // --- 定位 dragpoint ---
- $dragPoint.css({
- top: imgTop + imgHeight,
- left: imgLeft + imgWidth
- });
- // --- 定位 toolbar ---
- // 计算初步结果
- var top = imgTop + imgHeight;
- var left = imgLeft;
- var marginLeft = 0;
- var txtTop = $currentTxt.position().top;
- var txtHeight = $currentTxt.outerHeight();
- if (top > (txtTop + txtHeight)) {
- // top 不得超出编辑范围
- top = txtTop + txtHeight;
- } else {
- // top 超出编辑范围,dragPoint就不显示了
- $dragPoint.show();
- }
- // 显示(方便计算 margin)
- $toolbar.show();
- // 计算 margin
- var width = $toolbar.outerWidth();
- marginLeft = imgWidth / 2 - width / 2;
- // 定位
- $toolbar.css({
- top: top + 5,
- left: left,
- 'margin-left': marginLeft
- });
- // 如果定位太靠左了
- if (marginLeft < 0) {
- // 得到三角形的margin-left
- $toolbar.css('margin-left', '0');
- $triangle.hide();
- } else {
- $triangle.show();
- }
- // disable 菜单
- editor.disableMenusExcept();
- }
- // 隐藏 toolbar
- function hide() {
- if ($currentImg == null) {
- return;
- }
- $currentImg.removeClass('clicked');
- $currentImg = null;
- $toolbar.hide();
- $dragPoint.hide();
- // enable 菜单
- editor.enableMenusExcept();
- }
- // 判断img是否是一个表情
- function isEmotion(imgSrc) {
- var result = false;
- if (!editor.emotionUrls) {
- return result;
- }
- $.each(editor.emotionUrls, function (index, url) {
- var flag = false;
- if (imgSrc === url) {
- result = true;
- flag = true;
- }
- if (flag) {
- return false; // break 循环
- }
- });
- return result;
- }
- // click img 事件
- $currentTxt.on('mousedown', 'img', function (e) {
- e.preventDefault();
- }).on('click', 'img', function (e) {
- var $img = $(e.currentTarget);
- var src = $img.attr('src');
- if (!src || isEmotion(src)) {
- // 是一个表情图标
- return;
- }
- // ---------- 不是表情图标 ----------
- // 渲染
- render();
- if ($currentImg && ($currentImg.get(0) === $img.get(0))) {
- setTimeout(hide, 100);
- return;
- }
- // 显示 toolbar
- $currentImg = $img;
- show();
- // 默认显示menuContainer,其他默认隐藏
- $menuContainer.show();
- $linkInputContainer.hide();
- // 阻止冒泡
- e.preventDefault();
- e.stopPropagation();
- }).on('click keydown scroll', function (e) {
- if (!isOnDrag) {
- setTimeout(hide, 100);
- }
- });
- });
- });
- // 编辑区域 link toolbar
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var lang = editor.config.lang;
- var $txt = editor.txt.$txt;
- // 当前命中的链接
- var $currentLink;
- var $toolbar = $('<div class="txt-toolbar"></div>');
- var $triangle = $('<div class="tip-triangle"></div>');
- var $triggerLink = $('<a href="#" target="_blank"><i class="wangeditor-menu-img-link"></i> ' + lang.openLink + '</a>');
- var isRendered;
- // 记录当前的显示/隐藏状态
- var isShow = false;
- var showTimeoutId, hideTimeoutId;
- var showTimeoutIdByToolbar, hideTimeoutIdByToolbar;
- // 渲染 dom
- function render() {
- if (isRendered) {
- return;
- }
- $toolbar.append($triangle)
- .append($triggerLink);
- editor.$editorContainer.append($toolbar);
- isRendered = true;
- }
- // 定位
- function setPosition() {
- if (!$currentLink) {
- return;
- }
- var position = $currentLink.position();
- var left = position.left;
- var top = position.top;
- var height = $currentLink.height();
- // 初步计算top值
- var topResult = top + height + 5;
- // 判断 toolbar 是否超过了编辑器区域的下边界
- var menuHeight = editor.menuContainer.height();
- var txtHeight = editor.txt.$txt.outerHeight();
- if (topResult > menuHeight + txtHeight) {
- topResult = menuHeight + txtHeight + 5;
- }
- // 最终设置
- $toolbar.css({
- top: topResult,
- left: left
- });
- }
- // 显示 toolbar
- function show() {
- if (isShow) {
- return;
- }
- if (!$currentLink) {
- return;
- }
- render();
- $toolbar.show();
- // 设置链接
- var href = $currentLink.attr('href');
- $triggerLink.attr('href', href);
- // 定位
- setPosition();
- isShow = true;
- }
- // 隐藏 toolbar
- function hide() {
- if (!isShow) {
- return;
- }
- if (!$currentLink) {
- return;
- }
- $toolbar.hide();
- isShow = false;
- }
- // $txt 绑定事件
- $txt.on('mouseenter', 'a', function (e) {
- // 延时 500ms 显示toolbar
- if (showTimeoutId) {
- clearTimeout(showTimeoutId);
- }
- showTimeoutId = setTimeout(function () {
- var a = e.currentTarget;
- var $a = $(a);
- $currentLink = $a;
- var $img = $a.children('img');
- if ($img.length) {
- // 该链接下包含一个图片
- // 图片点击时,隐藏toolbar
- $img.click(function (e) {
- hide();
- });
- if ($img.hasClass('clicked')) {
- // 图片还处于clicked状态,则不显示toolbar
- return;
- }
- }
- // 显示toolbar
- show();
- }, 500);
- }).on('mouseleave', 'a', function (e) {
- // 延时 500ms 隐藏toolbar
- if (hideTimeoutId) {
- clearTimeout(hideTimeoutId);
- }
- hideTimeoutId = setTimeout(hide, 500);
- }).on('click keydown scroll', function (e) {
- setTimeout(hide, 100);
- });
- // $toolbar 绑定事件
- $toolbar.on('mouseenter', function (e) {
- // 先中断掉 $txt.mouseleave 导致的隐藏
- if (hideTimeoutId) {
- clearTimeout(hideTimeoutId);
- }
- }).on('mouseleave', function (e) {
- // 延时 500ms 显示toolbar
- if (showTimeoutIdByToolbar) {
- clearTimeout(showTimeoutIdByToolbar);
- }
- showTimeoutIdByToolbar = setTimeout(hide, 500);
- });
- });
- });
- // menu吸顶
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var menuFixed = editor.config.menuFixed;
- if (menuFixed === false || typeof menuFixed !== 'number') {
- // 没有配置菜单吸顶
- return;
- }
- var bodyMarginTop = parseFloat(E.$body.css('margin-top'), 10);
- if (isNaN(bodyMarginTop)) {
- bodyMarginTop = 0;
- }
- var $editorContainer = editor.$editorContainer;
- var editorTop = $editorContainer.offset().top;
- var editorHeight = $editorContainer.outerHeight();
- var $menuContainer = editor.menuContainer.$menuContainer;
- var menuCssPosition = $menuContainer.css('position');
- var menuCssTop = $menuContainer.css('top');
- var menuTop = $menuContainer.offset().top;
- var menuHeight = $menuContainer.outerHeight();
- var $txt = editor.txt.$txt;
- E.$window.scroll(function () {
- //全屏模式不支持
- if (editor.isFullScreen) {
- return;
- }
- var sTop = E.$window.scrollTop();
- // 需要重新计算宽度,因为浏览器可能此时出现滚动条
- var menuWidth = $menuContainer.width();
- // 如果 menuTop === 0 说明此前编辑器一直隐藏,后来显示出来了,要重新计算相关数据
- if (menuTop === 0) {
- menuTop = $menuContainer.offset().top;
- editorTop = $editorContainer.offset().top;
- editorHeight = $editorContainer.outerHeight();
- menuHeight = $menuContainer.outerHeight();
- }
- if (sTop >= menuTop && sTop + menuFixed + menuHeight + 30 < editorTop + editorHeight) {
- // 吸顶
- $menuContainer.css({
- position: 'fixed',
- top: menuFixed
- });
- // 固定宽度
- $menuContainer.width(menuWidth);
- // 增加body margin-top
- E.$body.css({
- 'margin-top': bodyMarginTop + menuHeight
- });
- // 记录
- if (!editor._isMenufixed) {
- editor._isMenufixed = true;
- }
- } else {
- // 取消吸顶
- $menuContainer.css({
- position: menuCssPosition,
- top: menuCssTop
- });
- // 取消宽度固定
- $menuContainer.css('width', '100%');
- // 还原 body margin-top
- E.$body.css({
- 'margin-top': bodyMarginTop
- });
- // 撤销记录
- if (editor._isMenufixed) {
- editor._isMenufixed = false;
- }
- }
- });
- });
- });
- // 缩进 菜单插件
- _e(function (E, $) {
- // 用 createMenu 方法创建菜单
- E.createMenu(function (check) {
- // 定义菜单id,不要和其他菜单id重复。编辑器自带的所有菜单id,可通过『参数配置-自定义菜单』一节查看
- var menuId = 'indent';
- // check将检查菜单配置(『参数配置-自定义菜单』一节描述)中是否该菜单id,如果没有,则忽略下面的代码。
- if (!check(menuId)) {
- return;
- }
- // this 指向 editor 对象自身
- var editor = this;
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor, // 编辑器对象
- id: menuId, // 菜单id
- title: '缩进', // 菜单标题
- // 正常状态和选中装下的dom对象,样式需要自定义
- $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-indent-left"></i></a>'),
- $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-indent-left"></i></a>')
- });
- // 菜单正常状态下,点击将触发该事件
- menu.clickEvent = function (e) {
- var elem = editor.getRangeElem();
- var p = editor.getSelfOrParentByName(elem, 'p');
- var $p;
- if (!p) {
- // 未找到 p 元素,则忽略
- return e.preventDefault();
- }
- $p = $(p);
- // 使用自定义命令
- function commandFn() {
- $p.css('text-indent', '2em');
- }
- editor.customCommand(e, commandFn);
- };
- // 菜单选中状态下,点击将触发该事件
- menu.clickEventSelected = function (e) {
- var elem = editor.getRangeElem();
- var p = editor.getSelfOrParentByName(elem, 'p');
- var $p;
- if (!p) {
- // 未找到 p 元素,则忽略
- return e.preventDefault();
- }
- $p = $(p);
- // 使用自定义命令
- function commandFn() {
- $p.css('text-indent', '0');
- }
- editor.customCommand(e, commandFn);
- };
- // 根据当前选区,自定义更新菜单的选中状态或者正常状态
- menu.updateSelectedEvent = function () {
- // 获取当前选区所在的父元素
- var elem = editor.getRangeElem();
- var p = editor.getSelfOrParentByName(elem, 'p');
- var $p;
- var indent;
- if (!p) {
- // 未找到 p 元素,则标记为未处于选中状态
- return false;
- }
- $p = $(p);
- indent = $p.css('text-indent');
- if (!indent || indent === '0px') {
- // 得到的p,text-indent 属性是 0,则标记为未处于选中状态
- return false;
- }
- // 找到 p 元素,并且 text-indent 不是 0,则标记为选中状态
- return true;
- };
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // 行高 菜单插件
- _e(function (E, $) {
- // 用 createMenu 方法创建菜单
- E.createMenu(function (check) {
- // 定义菜单id,不要和其他菜单id重复。编辑器自带的所有菜单id,可通过『参数配置-自定义菜单』一节查看
- var menuId = 'lineheight';
- // check将检查菜单配置(『参数配置-自定义菜单』一节描述)中是否该菜单id,如果没有,则忽略下面的代码。
- if (!check(menuId)) {
- return;
- }
- // this 指向 editor 对象自身
- var editor = this;
- // 由于浏览器自身不支持 lineHeight 命令,因此要做一个hook
- editor.commandHooks.lineHeight = function (value) {
- var rangeElem = editor.getRangeElem();
- var targetElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,pre');
- if (!targetElem) {
- return;
- }
- $(targetElem).css('line-height', value + '');
- };
- // 创建 menu 对象
- var menu = new E.Menu({
- editor: editor, // 编辑器对象
- id: menuId, // 菜单id
- title: '行高', // 菜单标题
- commandName: 'lineHeight', // 命令名称
- // 正常状态和选中装下的dom对象,样式需要自定义
- $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-arrows-v"></i></a>'),
- $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-arrows-v"></i></a>')
- });
- // 数据源
- var data = {
- // 格式: 'value' : 'title'
- '1.0': '1.0倍',
- '1.5': '1.5倍',
- '1.8': '1.8倍',
- '2.0': '2.0倍',
- '2.5': '2.5倍',
- '3.0': '3.0倍'
- };
- // 为menu创建droplist对象
- var tpl = '<span style="line-height:{#commandValue}">{#title}</span>';
- menu.dropList = new E.DropList(editor, menu, {
- data: data, // 传入数据源
- tpl: tpl // 传入模板
- });
- // 增加到editor对象中
- editor.menus[menuId] = menu;
- });
- });
- // 自定义上传
- _e(function (E, $) {
- E.plugin(function () {
- var editor = this;
- var customUpload = editor.config.customUpload;
- if (!customUpload) {
- return;
- } else if (editor.config.uploadImgUrl) {
- alert('自定义上传无效,详看浏览器日志console.log');
- E.error('已经配置了 uploadImgUrl ,就不能再配置 customUpload ,两者冲突。将导致自定义上传无效。');
- return;
- }
- var $uploadContent = editor.$uploadContent;
- if (!$uploadContent) {
- E.error('自定义上传,无法获取 editor.$uploadContent');
- }
- // UI
- var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
- $uploadContent.append($uploadIcon);
- // 设置id,并暴露
- var btnId = 'upload' + E.random();
- var containerId = 'upload' + E.random();
- $uploadIcon.attr('id', btnId);
- $uploadContent.attr('id', containerId);
- editor.customUploadBtnId = btnId;
- editor.customUploadContainerId = containerId;
- });
- });
- // 版权提示
- _e(function (E, $) {
- E.info();
- });
- // 最终返回wangEditor构造函数
- return window.wangEditor;
- });
|