summernote.js 256 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278
  1. /**
  2. * Super simple wysiwyg editor v0.8.9
  3. * https://summernote.org
  4. *
  5. * Copyright 2013- Alan Hong. and other contributors
  6. * summernote may be freely distributed under the MIT license.
  7. *
  8. * Date: 2017-12-25T06:39Z
  9. */
  10. (function (global, factory) {
  11. typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
  12. typeof define === 'function' && define.amd ? define(['jquery'], factory) :
  13. (factory(global.jQuery));
  14. }(this, (function ($$1) { 'use strict';
  15. $$1 = $$1 && $$1.hasOwnProperty('default') ? $$1['default'] : $$1;
  16. var Renderer = /** @class */ (function () {
  17. function Renderer(markup, children, options, callback) {
  18. this.markup = markup;
  19. this.children = children;
  20. this.options = options;
  21. this.callback = callback;
  22. }
  23. Renderer.prototype.render = function ($parent) {
  24. var $node = $$1(this.markup);
  25. if (this.options && this.options.contents) {
  26. $node.html(this.options.contents);
  27. }
  28. if (this.options && this.options.className) {
  29. $node.addClass(this.options.className);
  30. }
  31. if (this.options && this.options.data) {
  32. $$1.each(this.options.data, function (k, v) {
  33. $node.attr('data-' + k, v);
  34. });
  35. }
  36. if (this.options && this.options.click) {
  37. $node.on('click', this.options.click);
  38. }
  39. if (this.children) {
  40. var $container_1 = $node.find('.note-children-container');
  41. this.children.forEach(function (child) {
  42. child.render($container_1.length ? $container_1 : $node);
  43. });
  44. }
  45. if (this.callback) {
  46. this.callback($node, this.options);
  47. }
  48. if (this.options && this.options.callback) {
  49. this.options.callback($node);
  50. }
  51. if ($parent) {
  52. $parent.append($node);
  53. }
  54. return $node;
  55. };
  56. return Renderer;
  57. }());
  58. var renderer = {
  59. create: function (markup, callback) {
  60. return function () {
  61. var options = typeof arguments[1] === 'object' ? arguments[1] : arguments[0];
  62. var children = $$1.isArray(arguments[0]) ? arguments[0] : [];
  63. if (options && options.children) {
  64. children = options.children;
  65. }
  66. return new Renderer(markup, children, options, callback);
  67. };
  68. }
  69. };
  70. var editor = renderer.create('<div class="note-editor note-frame panel"/>');
  71. var toolbar = renderer.create('<div class="note-toolbar-wrapper panel-default"><div class="note-toolbar panel-heading"></div></div>');
  72. var editingArea = renderer.create('<div class="note-editing-area"/>');
  73. var codable = renderer.create('<textarea class="note-codable"/>');
  74. var editable = renderer.create('<div class="note-editable" contentEditable="true"/>');
  75. var statusbar = renderer.create([
  76. '<div class="note-statusbar">',
  77. ' <div class="note-resizebar">',
  78. ' <div class="note-icon-bar"/>',
  79. ' <div class="note-icon-bar"/>',
  80. ' <div class="note-icon-bar"/>',
  81. ' </div>',
  82. '</div>'
  83. ].join(''));
  84. var airEditor = renderer.create('<div class="note-editor"/>');
  85. var airEditable = renderer.create('<div class="note-editable" contentEditable="true"/>');
  86. var buttonGroup = renderer.create('<div class="note-btn-group btn-group">');
  87. var dropdown = renderer.create('<div class="dropdown-menu">', function ($node, options) {
  88. var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
  89. var value = (typeof item === 'string') ? item : (item.value || '');
  90. var content = options.template ? options.template(item) : item;
  91. var option = (typeof item === 'object') ? item.option : undefined;
  92. var dataValue = 'data-value="' + value + '"';
  93. var dataOption = (option !== undefined) ? ' data-option="' + option + '"' : '';
  94. return '<li><a href="#" ' + (dataValue + dataOption) + '>' + content + '</a></li>';
  95. }).join('') : options.items;
  96. $node.html(markup);
  97. });
  98. var dropdownButtonContents = function (contents, options) {
  99. return contents + ' ' + icon(options.icons.caret, 'span');
  100. };
  101. var dropdownCheck = renderer.create('<div class="dropdown-menu note-check">', function ($node, options) {
  102. var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
  103. var value = (typeof item === 'string') ? item : (item.value || '');
  104. var content = options.template ? options.template(item) : item;
  105. return '<li><a href="#" data-value="' + value + '">' + icon(options.checkClassName) + ' ' + content + '</a></li>';
  106. }).join('') : options.items;
  107. $node.html(markup);
  108. });
  109. var palette = renderer.create('<div class="note-color-palette"/>', function ($node, options) {
  110. var contents = [];
  111. for (var row = 0, rowSize = options.colors.length; row < rowSize; row++) {
  112. var eventName = options.eventName;
  113. var colors = options.colors[row];
  114. var buttons = [];
  115. for (var col = 0, colSize = colors.length; col < colSize; col++) {
  116. var color = colors[col];
  117. buttons.push([
  118. '<button type="button" class="note-color-btn"',
  119. 'style="background-color:', color, '" ',
  120. 'data-event="', eventName, '" ',
  121. 'data-value="', color, '" ',
  122. 'title="', color, '" ',
  123. 'data-toggle="button" tabindex="-1"></button>'
  124. ].join(''));
  125. }
  126. contents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
  127. }
  128. $node.html(contents.join(''));
  129. if (options.tooltip) {
  130. $node.find('.note-color-btn').tooltip({
  131. container: options.container,
  132. trigger: 'hover',
  133. placement: 'bottom'
  134. });
  135. }
  136. });
  137. var dialog = renderer.create('<div class="modal" aria-hidden="false" tabindex="-1"/>', function ($node, options) {
  138. if (options.fade) {
  139. $node.addClass('fade');
  140. }
  141. $node.html([
  142. '<div class="modal-dialog">',
  143. ' <div class="modal-content">',
  144. (options.title
  145. ? ' <div class="modal-header">' +
  146. ' <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
  147. ' <h4 class="modal-title">' + options.title + '</h4>' +
  148. ' </div>' : ''),
  149. ' <div class="modal-body">' + options.body + '</div>',
  150. (options.footer
  151. ? ' <div class="modal-footer">' + options.footer + '</div>' : ''),
  152. ' </div>',
  153. '</div>'
  154. ].join(''));
  155. });
  156. var popover = renderer.create([
  157. '<div class="note-popover popover in">',
  158. ' <div class="arrow"/>',
  159. ' <div class="popover-content note-children-container"/>',
  160. '</div>'
  161. ].join(''), function ($node, options) {
  162. var direction = typeof options.direction !== 'undefined' ? options.direction : 'bottom';
  163. $node.addClass(direction);
  164. if (options.hideArrow) {
  165. $node.find('.arrow').hide();
  166. }
  167. });
  168. var checkbox = renderer.create('<div class="checkbox"></div>', function ($node, options) {
  169. $node.html([
  170. ' <label' + (options.id ? ' for="' + options.id + '"' : '') + '>',
  171. ' <input type="checkbox"' + (options.id ? ' id="' + options.id + '"' : ''),
  172. (options.checked ? ' checked' : '') + '/>',
  173. (options.text ? options.text : ''),
  174. '</label>'
  175. ].join(''));
  176. });
  177. var icon = function (iconClassName, tagName) {
  178. tagName = tagName || 'i';
  179. return '<' + tagName + ' class="' + iconClassName + '"/>';
  180. };
  181. var ui = {
  182. editor: editor,
  183. toolbar: toolbar,
  184. editingArea: editingArea,
  185. codable: codable,
  186. editable: editable,
  187. statusbar: statusbar,
  188. airEditor: airEditor,
  189. airEditable: airEditable,
  190. buttonGroup: buttonGroup,
  191. dropdown: dropdown,
  192. dropdownButtonContents: dropdownButtonContents,
  193. dropdownCheck: dropdownCheck,
  194. palette: palette,
  195. dialog: dialog,
  196. popover: popover,
  197. checkbox: checkbox,
  198. icon: icon,
  199. options: {},
  200. button: function ($node, options) {
  201. return renderer.create('<button type="button" class="note-btn btn btn-default btn-sm" tabindex="-1">', function ($node, options) {
  202. if (options && options.tooltip) {
  203. $node.attr({
  204. title: options.tooltip
  205. }).tooltip({
  206. container: options.container,
  207. trigger: 'hover',
  208. placement: 'bottom'
  209. });
  210. }
  211. })($node, options);
  212. },
  213. toggleBtn: function ($btn, isEnable) {
  214. $btn.toggleClass('disabled', !isEnable);
  215. $btn.attr('disabled', !isEnable);
  216. },
  217. toggleBtnActive: function ($btn, isActive) {
  218. $btn.toggleClass('active', isActive);
  219. },
  220. onDialogShown: function ($dialog, handler) {
  221. $dialog.one('shown.bs.modal', handler);
  222. },
  223. onDialogHidden: function ($dialog, handler) {
  224. $dialog.one('hidden.bs.modal', handler);
  225. },
  226. showDialog: function ($dialog) {
  227. $dialog.modal('show');
  228. },
  229. hideDialog: function ($dialog) {
  230. $dialog.modal('hide');
  231. },
  232. createLayout: function ($note, options) {
  233. var $editor = (options.airMode ? ui.airEditor([
  234. ui.editingArea([
  235. ui.airEditable()
  236. ])
  237. ]) : ui.editor([
  238. ui.toolbar(),
  239. ui.editingArea([
  240. ui.codable(),
  241. ui.editable()
  242. ]),
  243. ui.statusbar()
  244. ])).render();
  245. $editor.insertAfter($note);
  246. return {
  247. note: $note,
  248. editor: $editor,
  249. toolbar: $editor.find('.note-toolbar'),
  250. editingArea: $editor.find('.note-editing-area'),
  251. editable: $editor.find('.note-editable'),
  252. codable: $editor.find('.note-codable'),
  253. statusbar: $editor.find('.note-statusbar')
  254. };
  255. },
  256. removeLayout: function ($note, layoutInfo) {
  257. $note.html(layoutInfo.editable.html());
  258. layoutInfo.editor.remove();
  259. $note.show();
  260. }
  261. };
  262. /**
  263. * @class core.func
  264. *
  265. * func utils (for high-order func's arg)
  266. *
  267. * @singleton
  268. * @alternateClassName func
  269. */
  270. function eq(itemA) {
  271. return function (itemB) {
  272. return itemA === itemB;
  273. };
  274. }
  275. function eq2(itemA, itemB) {
  276. return itemA === itemB;
  277. }
  278. function peq2(propName) {
  279. return function (itemA, itemB) {
  280. return itemA[propName] === itemB[propName];
  281. };
  282. }
  283. function ok() {
  284. return true;
  285. }
  286. function fail() {
  287. return false;
  288. }
  289. function not(f) {
  290. return function () {
  291. return !f.apply(f, arguments);
  292. };
  293. }
  294. function and(fA, fB) {
  295. return function (item) {
  296. return fA(item) && fB(item);
  297. };
  298. }
  299. function self(a) {
  300. return a;
  301. }
  302. function invoke(obj, method) {
  303. return function () {
  304. return obj[method].apply(obj, arguments);
  305. };
  306. }
  307. var idCounter = 0;
  308. /**
  309. * generate a globally-unique id
  310. *
  311. * @param {String} [prefix]
  312. */
  313. function uniqueId(prefix) {
  314. var id = ++idCounter + '';
  315. return prefix ? prefix + id : id;
  316. }
  317. /**
  318. * returns bnd (bounds) from rect
  319. *
  320. * - IE Compatibility Issue: http://goo.gl/sRLOAo
  321. * - Scroll Issue: http://goo.gl/sNjUc
  322. *
  323. * @param {Rect} rect
  324. * @return {Object} bounds
  325. * @return {Number} bounds.top
  326. * @return {Number} bounds.left
  327. * @return {Number} bounds.width
  328. * @return {Number} bounds.height
  329. */
  330. function rect2bnd(rect) {
  331. var $document = $(document);
  332. return {
  333. top: rect.top + $document.scrollTop(),
  334. left: rect.left + $document.scrollLeft(),
  335. width: rect.right - rect.left,
  336. height: rect.bottom - rect.top
  337. };
  338. }
  339. /**
  340. * returns a copy of the object where the keys have become the values and the values the keys.
  341. * @param {Object} obj
  342. * @return {Object}
  343. */
  344. function invertObject(obj) {
  345. var inverted = {};
  346. for (var key in obj) {
  347. if (obj.hasOwnProperty(key)) {
  348. inverted[obj[key]] = key;
  349. }
  350. }
  351. return inverted;
  352. }
  353. /**
  354. * @param {String} namespace
  355. * @param {String} [prefix]
  356. * @return {String}
  357. */
  358. function namespaceToCamel(namespace, prefix) {
  359. prefix = prefix || '';
  360. return prefix + namespace.split('.').map(function (name) {
  361. return name.substring(0, 1).toUpperCase() + name.substring(1);
  362. }).join('');
  363. }
  364. /**
  365. * Returns a function, that, as long as it continues to be invoked, will not
  366. * be triggered. The function will be called after it stops being called for
  367. * N milliseconds. If `immediate` is passed, trigger the function on the
  368. * leading edge, instead of the trailing.
  369. * @param {Function} func
  370. * @param {Number} wait
  371. * @param {Boolean} immediate
  372. * @return {Function}
  373. */
  374. function debounce(func, wait, immediate) {
  375. var _this = this;
  376. var timeout;
  377. return function () {
  378. var context = _this;
  379. var args = arguments;
  380. var later = function () {
  381. timeout = null;
  382. if (!immediate) {
  383. func.apply(context, args);
  384. }
  385. };
  386. var callNow = immediate && !timeout;
  387. clearTimeout(timeout);
  388. timeout = setTimeout(later, wait);
  389. if (callNow) {
  390. func.apply(context, args);
  391. }
  392. };
  393. }
  394. var func = {
  395. eq: eq,
  396. eq2: eq2,
  397. peq2: peq2,
  398. ok: ok,
  399. fail: fail,
  400. self: self,
  401. not: not,
  402. and: and,
  403. invoke: invoke,
  404. uniqueId: uniqueId,
  405. rect2bnd: rect2bnd,
  406. invertObject: invertObject,
  407. namespaceToCamel: namespaceToCamel,
  408. debounce: debounce
  409. };
  410. /**
  411. * returns the first item of an array.
  412. *
  413. * @param {Array} array
  414. */
  415. function head(array) {
  416. return array[0];
  417. }
  418. /**
  419. * returns the last item of an array.
  420. *
  421. * @param {Array} array
  422. */
  423. function last(array) {
  424. return array[array.length - 1];
  425. }
  426. /**
  427. * returns everything but the last entry of the array.
  428. *
  429. * @param {Array} array
  430. */
  431. function initial(array) {
  432. return array.slice(0, array.length - 1);
  433. }
  434. /**
  435. * returns the rest of the items in an array.
  436. *
  437. * @param {Array} array
  438. */
  439. function tail(array) {
  440. return array.slice(1);
  441. }
  442. /**
  443. * returns item of array
  444. */
  445. function find(array, pred) {
  446. for (var idx = 0, len = array.length; idx < len; idx++) {
  447. var item = array[idx];
  448. if (pred(item)) {
  449. return item;
  450. }
  451. }
  452. }
  453. /**
  454. * returns true if all of the values in the array pass the predicate truth test.
  455. */
  456. function all(array, pred) {
  457. for (var idx = 0, len = array.length; idx < len; idx++) {
  458. if (!pred(array[idx])) {
  459. return false;
  460. }
  461. }
  462. return true;
  463. }
  464. /**
  465. * returns index of item
  466. */
  467. function indexOf(array, item) {
  468. return $$1.inArray(item, array);
  469. }
  470. /**
  471. * returns true if the value is present in the list.
  472. */
  473. function contains(array, item) {
  474. return indexOf(array, item) !== -1;
  475. }
  476. /**
  477. * get sum from a list
  478. *
  479. * @param {Array} array - array
  480. * @param {Function} fn - iterator
  481. */
  482. function sum(array, fn) {
  483. fn = fn || func.self;
  484. return array.reduce(function (memo, v) {
  485. return memo + fn(v);
  486. }, 0);
  487. }
  488. /**
  489. * returns a copy of the collection with array type.
  490. * @param {Collection} collection - collection eg) node.childNodes, ...
  491. */
  492. function from(collection) {
  493. var result = [];
  494. var length = collection.length;
  495. var idx = -1;
  496. while (++idx < length) {
  497. result[idx] = collection[idx];
  498. }
  499. return result;
  500. }
  501. /**
  502. * returns whether list is empty or not
  503. */
  504. function isEmpty$1(array) {
  505. return !array || !array.length;
  506. }
  507. /**
  508. * cluster elements by predicate function.
  509. *
  510. * @param {Array} array - array
  511. * @param {Function} fn - predicate function for cluster rule
  512. * @param {Array[]}
  513. */
  514. function clusterBy(array, fn) {
  515. if (!array.length) {
  516. return [];
  517. }
  518. var aTail = tail(array);
  519. return aTail.reduce(function (memo, v) {
  520. var aLast = last(memo);
  521. if (fn(last(aLast), v)) {
  522. aLast[aLast.length] = v;
  523. }
  524. else {
  525. memo[memo.length] = [v];
  526. }
  527. return memo;
  528. }, [[head(array)]]);
  529. }
  530. /**
  531. * returns a copy of the array with all false values removed
  532. *
  533. * @param {Array} array - array
  534. * @param {Function} fn - predicate function for cluster rule
  535. */
  536. function compact(array) {
  537. var aResult = [];
  538. for (var idx = 0, len = array.length; idx < len; idx++) {
  539. if (array[idx]) {
  540. aResult.push(array[idx]);
  541. }
  542. }
  543. return aResult;
  544. }
  545. /**
  546. * produces a duplicate-free version of the array
  547. *
  548. * @param {Array} array
  549. */
  550. function unique(array) {
  551. var results = [];
  552. for (var idx = 0, len = array.length; idx < len; idx++) {
  553. if (!contains(results, array[idx])) {
  554. results.push(array[idx]);
  555. }
  556. }
  557. return results;
  558. }
  559. /**
  560. * returns next item.
  561. * @param {Array} array
  562. */
  563. function next(array, item) {
  564. var idx = indexOf(array, item);
  565. if (idx === -1) {
  566. return null;
  567. }
  568. return array[idx + 1];
  569. }
  570. /**
  571. * returns prev item.
  572. * @param {Array} array
  573. */
  574. function prev(array, item) {
  575. var idx = indexOf(array, item);
  576. if (idx === -1) {
  577. return null;
  578. }
  579. return array[idx - 1];
  580. }
  581. /**
  582. * @class core.list
  583. *
  584. * list utils
  585. *
  586. * @singleton
  587. * @alternateClassName list
  588. */
  589. var lists = {
  590. head: head,
  591. last: last,
  592. initial: initial,
  593. tail: tail,
  594. prev: prev,
  595. next: next,
  596. find: find,
  597. contains: contains,
  598. all: all,
  599. sum: sum,
  600. from: from,
  601. isEmpty: isEmpty$1,
  602. clusterBy: clusterBy,
  603. compact: compact,
  604. unique: unique
  605. };
  606. var isSupportAmd = typeof define === 'function' && define.amd; // eslint-disable-line
  607. /**
  608. * returns whether font is installed or not.
  609. *
  610. * @param {String} fontName
  611. * @return {Boolean}
  612. */
  613. function isFontInstalled(fontName) {
  614. var testFontName = fontName === 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS';
  615. var $tester = $$1('<div>').css({
  616. position: 'absolute',
  617. left: '-9999px',
  618. top: '-9999px',
  619. fontSize: '200px'
  620. }).text('mmmmmmmmmwwwwwww').appendTo(document.body);
  621. var originalWidth = $tester.css('fontFamily', testFontName).width();
  622. var width = $tester.css('fontFamily', fontName + ',' + testFontName).width();
  623. $tester.remove();
  624. return originalWidth !== width;
  625. }
  626. var userAgent = navigator.userAgent;
  627. var isMSIE = /MSIE|Trident/i.test(userAgent);
  628. var browserVersion;
  629. if (isMSIE) {
  630. var matches = /MSIE (\d+[.]\d+)/.exec(userAgent);
  631. if (matches) {
  632. browserVersion = parseFloat(matches[1]);
  633. }
  634. matches = /Trident\/.*rv:([0-9]{1,}[.0-9]{0,})/.exec(userAgent);
  635. if (matches) {
  636. browserVersion = parseFloat(matches[1]);
  637. }
  638. }
  639. var isEdge = /Edge\/\d+/.test(userAgent);
  640. var hasCodeMirror = !!window.CodeMirror;
  641. if (!hasCodeMirror && isSupportAmd) {
  642. // Webpack
  643. if (typeof __webpack_require__ === 'function') {
  644. try {
  645. // If CodeMirror can't be resolved, `require.resolve` will throw an
  646. // exception and `hasCodeMirror` won't be set to `true`.
  647. require.resolve('codemirror');
  648. hasCodeMirror = true;
  649. }
  650. catch (e) {
  651. // do nothing
  652. }
  653. }
  654. else if (typeof require !== 'undefined') {
  655. // Browserify
  656. if (typeof require.resolve !== 'undefined') {
  657. try {
  658. // If CodeMirror can't be resolved, `require.resolve` will throw an
  659. // exception and `hasCodeMirror` won't be set to `true`.
  660. require.resolve('codemirror');
  661. hasCodeMirror = true;
  662. }
  663. catch (e) {
  664. // do nothing
  665. }
  666. // Almond/Require
  667. }
  668. else if (typeof require.specified !== 'undefined') {
  669. hasCodeMirror = require.specified('codemirror');
  670. }
  671. }
  672. }
  673. var isSupportTouch = (('ontouchstart' in window) ||
  674. (navigator.MaxTouchPoints > 0) ||
  675. (navigator.msMaxTouchPoints > 0));
  676. // [workaround] IE doesn't have input events for contentEditable
  677. // - see: https://goo.gl/4bfIvA
  678. var inputEventName = (isMSIE || isEdge) ? 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted' : 'input';
  679. /**
  680. * @class core.env
  681. *
  682. * Object which check platform and agent
  683. *
  684. * @singleton
  685. * @alternateClassName env
  686. */
  687. var env = {
  688. isMac: navigator.appVersion.indexOf('Mac') > -1,
  689. isMSIE: isMSIE,
  690. isEdge: isEdge,
  691. isFF: !isEdge && /firefox/i.test(userAgent),
  692. isPhantom: /PhantomJS/i.test(userAgent),
  693. isWebkit: !isEdge && /webkit/i.test(userAgent),
  694. isChrome: !isEdge && /chrome/i.test(userAgent),
  695. isSafari: !isEdge && /safari/i.test(userAgent),
  696. browserVersion: browserVersion,
  697. jqueryVersion: parseFloat($$1.fn.jquery),
  698. isSupportAmd: isSupportAmd,
  699. isSupportTouch: isSupportTouch,
  700. hasCodeMirror: hasCodeMirror,
  701. isFontInstalled: isFontInstalled,
  702. isW3CRangeSupport: !!document.createRange,
  703. inputEventName: inputEventName
  704. };
  705. var NBSP_CHAR = String.fromCharCode(160);
  706. var ZERO_WIDTH_NBSP_CHAR = '\ufeff';
  707. /**
  708. * @method isEditable
  709. *
  710. * returns whether node is `note-editable` or not.
  711. *
  712. * @param {Node} node
  713. * @return {Boolean}
  714. */
  715. function isEditable(node) {
  716. return node && $$1(node).hasClass('note-editable');
  717. }
  718. /**
  719. * @method isControlSizing
  720. *
  721. * returns whether node is `note-control-sizing` or not.
  722. *
  723. * @param {Node} node
  724. * @return {Boolean}
  725. */
  726. function isControlSizing(node) {
  727. return node && $$1(node).hasClass('note-control-sizing');
  728. }
  729. /**
  730. * @method makePredByNodeName
  731. *
  732. * returns predicate which judge whether nodeName is same
  733. *
  734. * @param {String} nodeName
  735. * @return {Function}
  736. */
  737. function makePredByNodeName(nodeName) {
  738. nodeName = nodeName.toUpperCase();
  739. return function (node) {
  740. return node && node.nodeName.toUpperCase() === nodeName;
  741. };
  742. }
  743. /**
  744. * @method isText
  745. *
  746. *
  747. *
  748. * @param {Node} node
  749. * @return {Boolean} true if node's type is text(3)
  750. */
  751. function isText(node) {
  752. return node && node.nodeType === 3;
  753. }
  754. /**
  755. * @method isElement
  756. *
  757. *
  758. *
  759. * @param {Node} node
  760. * @return {Boolean} true if node's type is element(1)
  761. */
  762. function isElement(node) {
  763. return node && node.nodeType === 1;
  764. }
  765. /**
  766. * ex) br, col, embed, hr, img, input, ...
  767. * @see http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
  768. */
  769. function isVoid(node) {
  770. return node && /^BR|^IMG|^HR|^IFRAME|^BUTTON|^INPUT/.test(node.nodeName.toUpperCase());
  771. }
  772. function isPara(node) {
  773. if (isEditable(node)) {
  774. return false;
  775. }
  776. // Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
  777. return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName.toUpperCase());
  778. }
  779. function isHeading(node) {
  780. return node && /^H[1-7]/.test(node.nodeName.toUpperCase());
  781. }
  782. var isPre = makePredByNodeName('PRE');
  783. var isLi = makePredByNodeName('LI');
  784. function isPurePara(node) {
  785. return isPara(node) && !isLi(node);
  786. }
  787. var isTable = makePredByNodeName('TABLE');
  788. var isData = makePredByNodeName('DATA');
  789. function isInline(node) {
  790. return !isBodyContainer(node) &&
  791. !isList(node) &&
  792. !isHr(node) &&
  793. !isPara(node) &&
  794. !isTable(node) &&
  795. !isBlockquote(node) &&
  796. !isData(node);
  797. }
  798. function isList(node) {
  799. return node && /^UL|^OL/.test(node.nodeName.toUpperCase());
  800. }
  801. var isHr = makePredByNodeName('HR');
  802. function isCell(node) {
  803. return node && /^TD|^TH/.test(node.nodeName.toUpperCase());
  804. }
  805. var isBlockquote = makePredByNodeName('BLOCKQUOTE');
  806. function isBodyContainer(node) {
  807. return isCell(node) || isBlockquote(node) || isEditable(node);
  808. }
  809. var isAnchor = makePredByNodeName('A');
  810. function isParaInline(node) {
  811. return isInline(node) && !!ancestor(node, isPara);
  812. }
  813. function isBodyInline(node) {
  814. return isInline(node) && !ancestor(node, isPara);
  815. }
  816. var isBody = makePredByNodeName('BODY');
  817. /**
  818. * returns whether nodeB is closest sibling of nodeA
  819. *
  820. * @param {Node} nodeA
  821. * @param {Node} nodeB
  822. * @return {Boolean}
  823. */
  824. function isClosestSibling(nodeA, nodeB) {
  825. return nodeA.nextSibling === nodeB ||
  826. nodeA.previousSibling === nodeB;
  827. }
  828. /**
  829. * returns array of closest siblings with node
  830. *
  831. * @param {Node} node
  832. * @param {function} [pred] - predicate function
  833. * @return {Node[]}
  834. */
  835. function withClosestSiblings(node, pred) {
  836. pred = pred || func.ok;
  837. var siblings = [];
  838. if (node.previousSibling && pred(node.previousSibling)) {
  839. siblings.push(node.previousSibling);
  840. }
  841. siblings.push(node);
  842. if (node.nextSibling && pred(node.nextSibling)) {
  843. siblings.push(node.nextSibling);
  844. }
  845. return siblings;
  846. }
  847. /**
  848. * blank HTML for cursor position
  849. * - [workaround] old IE only works with &nbsp;
  850. * - [workaround] IE11 and other browser works with bogus br
  851. */
  852. var blankHTML = env.isMSIE && env.browserVersion < 11 ? '&nbsp;' : '<br>';
  853. /**
  854. * @method nodeLength
  855. *
  856. * returns #text's text size or element's childNodes size
  857. *
  858. * @param {Node} node
  859. */
  860. function nodeLength(node) {
  861. if (isText(node)) {
  862. return node.nodeValue.length;
  863. }
  864. if (node) {
  865. return node.childNodes.length;
  866. }
  867. return 0;
  868. }
  869. /**
  870. * returns whether node is empty or not.
  871. *
  872. * @param {Node} node
  873. * @return {Boolean}
  874. */
  875. function isEmpty(node) {
  876. var len = nodeLength(node);
  877. if (len === 0) {
  878. return true;
  879. }
  880. else if (!isText(node) && len === 1 && node.innerHTML === blankHTML) {
  881. // ex) <p><br></p>, <span><br></span>
  882. return true;
  883. }
  884. else if (lists.all(node.childNodes, isText) && node.innerHTML === '') {
  885. // ex) <p></p>, <span></span>
  886. return true;
  887. }
  888. return false;
  889. }
  890. /**
  891. * padding blankHTML if node is empty (for cursor position)
  892. */
  893. function paddingBlankHTML(node) {
  894. if (!isVoid(node) && !nodeLength(node)) {
  895. node.innerHTML = blankHTML;
  896. }
  897. }
  898. /**
  899. * find nearest ancestor predicate hit
  900. *
  901. * @param {Node} node
  902. * @param {Function} pred - predicate function
  903. */
  904. function ancestor(node, pred) {
  905. while (node) {
  906. if (pred(node)) {
  907. return node;
  908. }
  909. if (isEditable(node)) {
  910. break;
  911. }
  912. node = node.parentNode;
  913. }
  914. return null;
  915. }
  916. /**
  917. * find nearest ancestor only single child blood line and predicate hit
  918. *
  919. * @param {Node} node
  920. * @param {Function} pred - predicate function
  921. */
  922. function singleChildAncestor(node, pred) {
  923. node = node.parentNode;
  924. while (node) {
  925. if (nodeLength(node) !== 1) {
  926. break;
  927. }
  928. if (pred(node)) {
  929. return node;
  930. }
  931. if (isEditable(node)) {
  932. break;
  933. }
  934. node = node.parentNode;
  935. }
  936. return null;
  937. }
  938. /**
  939. * returns new array of ancestor nodes (until predicate hit).
  940. *
  941. * @param {Node} node
  942. * @param {Function} [optional] pred - predicate function
  943. */
  944. function listAncestor(node, pred) {
  945. pred = pred || func.fail;
  946. var ancestors = [];
  947. ancestor(node, function (el) {
  948. if (!isEditable(el)) {
  949. ancestors.push(el);
  950. }
  951. return pred(el);
  952. });
  953. return ancestors;
  954. }
  955. /**
  956. * find farthest ancestor predicate hit
  957. */
  958. function lastAncestor(node, pred) {
  959. var ancestors = listAncestor(node);
  960. return lists.last(ancestors.filter(pred));
  961. }
  962. /**
  963. * returns common ancestor node between two nodes.
  964. *
  965. * @param {Node} nodeA
  966. * @param {Node} nodeB
  967. */
  968. function commonAncestor(nodeA, nodeB) {
  969. var ancestors = listAncestor(nodeA);
  970. for (var n = nodeB; n; n = n.parentNode) {
  971. if ($$1.inArray(n, ancestors) > -1) {
  972. return n;
  973. }
  974. }
  975. return null; // difference document area
  976. }
  977. /**
  978. * listing all previous siblings (until predicate hit).
  979. *
  980. * @param {Node} node
  981. * @param {Function} [optional] pred - predicate function
  982. */
  983. function listPrev(node, pred) {
  984. pred = pred || func.fail;
  985. var nodes = [];
  986. while (node) {
  987. if (pred(node)) {
  988. break;
  989. }
  990. nodes.push(node);
  991. node = node.previousSibling;
  992. }
  993. return nodes;
  994. }
  995. /**
  996. * listing next siblings (until predicate hit).
  997. *
  998. * @param {Node} node
  999. * @param {Function} [pred] - predicate function
  1000. */
  1001. function listNext(node, pred) {
  1002. pred = pred || func.fail;
  1003. var nodes = [];
  1004. while (node) {
  1005. if (pred(node)) {
  1006. break;
  1007. }
  1008. nodes.push(node);
  1009. node = node.nextSibling;
  1010. }
  1011. return nodes;
  1012. }
  1013. /**
  1014. * listing descendant nodes
  1015. *
  1016. * @param {Node} node
  1017. * @param {Function} [pred] - predicate function
  1018. */
  1019. function listDescendant(node, pred) {
  1020. var descendants = [];
  1021. pred = pred || func.ok;
  1022. // start DFS(depth first search) with node
  1023. (function fnWalk(current) {
  1024. if (node !== current && pred(current)) {
  1025. descendants.push(current);
  1026. }
  1027. for (var idx = 0, len = current.childNodes.length; idx < len; idx++) {
  1028. fnWalk(current.childNodes[idx]);
  1029. }
  1030. })(node);
  1031. return descendants;
  1032. }
  1033. /**
  1034. * wrap node with new tag.
  1035. *
  1036. * @param {Node} node
  1037. * @param {Node} tagName of wrapper
  1038. * @return {Node} - wrapper
  1039. */
  1040. function wrap(node, wrapperName) {
  1041. var parent = node.parentNode;
  1042. var wrapper = $$1('<' + wrapperName + '>')[0];
  1043. parent.insertBefore(wrapper, node);
  1044. wrapper.appendChild(node);
  1045. return wrapper;
  1046. }
  1047. /**
  1048. * insert node after preceding
  1049. *
  1050. * @param {Node} node
  1051. * @param {Node} preceding - predicate function
  1052. */
  1053. function insertAfter(node, preceding) {
  1054. var next = preceding.nextSibling;
  1055. var parent = preceding.parentNode;
  1056. if (next) {
  1057. parent.insertBefore(node, next);
  1058. }
  1059. else {
  1060. parent.appendChild(node);
  1061. }
  1062. return node;
  1063. }
  1064. /**
  1065. * append elements.
  1066. *
  1067. * @param {Node} node
  1068. * @param {Collection} aChild
  1069. */
  1070. function appendChildNodes(node, aChild) {
  1071. $$1.each(aChild, function (idx, child) {
  1072. node.appendChild(child);
  1073. });
  1074. return node;
  1075. }
  1076. /**
  1077. * returns whether boundaryPoint is left edge or not.
  1078. *
  1079. * @param {BoundaryPoint} point
  1080. * @return {Boolean}
  1081. */
  1082. function isLeftEdgePoint(point) {
  1083. return point.offset === 0;
  1084. }
  1085. /**
  1086. * returns whether boundaryPoint is right edge or not.
  1087. *
  1088. * @param {BoundaryPoint} point
  1089. * @return {Boolean}
  1090. */
  1091. function isRightEdgePoint(point) {
  1092. return point.offset === nodeLength(point.node);
  1093. }
  1094. /**
  1095. * returns whether boundaryPoint is edge or not.
  1096. *
  1097. * @param {BoundaryPoint} point
  1098. * @return {Boolean}
  1099. */
  1100. function isEdgePoint(point) {
  1101. return isLeftEdgePoint(point) || isRightEdgePoint(point);
  1102. }
  1103. /**
  1104. * returns whether node is left edge of ancestor or not.
  1105. *
  1106. * @param {Node} node
  1107. * @param {Node} ancestor
  1108. * @return {Boolean}
  1109. */
  1110. function isLeftEdgeOf(node, ancestor) {
  1111. while (node && node !== ancestor) {
  1112. if (position(node) !== 0) {
  1113. return false;
  1114. }
  1115. node = node.parentNode;
  1116. }
  1117. return true;
  1118. }
  1119. /**
  1120. * returns whether node is right edge of ancestor or not.
  1121. *
  1122. * @param {Node} node
  1123. * @param {Node} ancestor
  1124. * @return {Boolean}
  1125. */
  1126. function isRightEdgeOf(node, ancestor) {
  1127. if (!ancestor) {
  1128. return false;
  1129. }
  1130. while (node && node !== ancestor) {
  1131. if (position(node) !== nodeLength(node.parentNode) - 1) {
  1132. return false;
  1133. }
  1134. node = node.parentNode;
  1135. }
  1136. return true;
  1137. }
  1138. /**
  1139. * returns whether point is left edge of ancestor or not.
  1140. * @param {BoundaryPoint} point
  1141. * @param {Node} ancestor
  1142. * @return {Boolean}
  1143. */
  1144. function isLeftEdgePointOf(point, ancestor) {
  1145. return isLeftEdgePoint(point) && isLeftEdgeOf(point.node, ancestor);
  1146. }
  1147. /**
  1148. * returns whether point is right edge of ancestor or not.
  1149. * @param {BoundaryPoint} point
  1150. * @param {Node} ancestor
  1151. * @return {Boolean}
  1152. */
  1153. function isRightEdgePointOf(point, ancestor) {
  1154. return isRightEdgePoint(point) && isRightEdgeOf(point.node, ancestor);
  1155. }
  1156. /**
  1157. * returns offset from parent.
  1158. *
  1159. * @param {Node} node
  1160. */
  1161. function position(node) {
  1162. var offset = 0;
  1163. while ((node = node.previousSibling)) {
  1164. offset += 1;
  1165. }
  1166. return offset;
  1167. }
  1168. function hasChildren(node) {
  1169. return !!(node && node.childNodes && node.childNodes.length);
  1170. }
  1171. /**
  1172. * returns previous boundaryPoint
  1173. *
  1174. * @param {BoundaryPoint} point
  1175. * @param {Boolean} isSkipInnerOffset
  1176. * @return {BoundaryPoint}
  1177. */
  1178. function prevPoint(point, isSkipInnerOffset) {
  1179. var node;
  1180. var offset;
  1181. if (point.offset === 0) {
  1182. if (isEditable(point.node)) {
  1183. return null;
  1184. }
  1185. node = point.node.parentNode;
  1186. offset = position(point.node);
  1187. }
  1188. else if (hasChildren(point.node)) {
  1189. node = point.node.childNodes[point.offset - 1];
  1190. offset = nodeLength(node);
  1191. }
  1192. else {
  1193. node = point.node;
  1194. offset = isSkipInnerOffset ? 0 : point.offset - 1;
  1195. }
  1196. return {
  1197. node: node,
  1198. offset: offset
  1199. };
  1200. }
  1201. /**
  1202. * returns next boundaryPoint
  1203. *
  1204. * @param {BoundaryPoint} point
  1205. * @param {Boolean} isSkipInnerOffset
  1206. * @return {BoundaryPoint}
  1207. */
  1208. function nextPoint(point, isSkipInnerOffset) {
  1209. var node, offset;
  1210. if (nodeLength(point.node) === point.offset) {
  1211. if (isEditable(point.node)) {
  1212. return null;
  1213. }
  1214. node = point.node.parentNode;
  1215. offset = position(point.node) + 1;
  1216. }
  1217. else if (hasChildren(point.node)) {
  1218. node = point.node.childNodes[point.offset];
  1219. offset = 0;
  1220. }
  1221. else {
  1222. node = point.node;
  1223. offset = isSkipInnerOffset ? nodeLength(point.node) : point.offset + 1;
  1224. }
  1225. return {
  1226. node: node,
  1227. offset: offset
  1228. };
  1229. }
  1230. /**
  1231. * returns whether pointA and pointB is same or not.
  1232. *
  1233. * @param {BoundaryPoint} pointA
  1234. * @param {BoundaryPoint} pointB
  1235. * @return {Boolean}
  1236. */
  1237. function isSamePoint(pointA, pointB) {
  1238. return pointA.node === pointB.node && pointA.offset === pointB.offset;
  1239. }
  1240. /**
  1241. * returns whether point is visible (can set cursor) or not.
  1242. *
  1243. * @param {BoundaryPoint} point
  1244. * @return {Boolean}
  1245. */
  1246. function isVisiblePoint(point) {
  1247. if (isText(point.node) || !hasChildren(point.node) || isEmpty(point.node)) {
  1248. return true;
  1249. }
  1250. var leftNode = point.node.childNodes[point.offset - 1];
  1251. var rightNode = point.node.childNodes[point.offset];
  1252. if ((!leftNode || isVoid(leftNode)) && (!rightNode || isVoid(rightNode))) {
  1253. return true;
  1254. }
  1255. return false;
  1256. }
  1257. /**
  1258. * @method prevPointUtil
  1259. *
  1260. * @param {BoundaryPoint} point
  1261. * @param {Function} pred
  1262. * @return {BoundaryPoint}
  1263. */
  1264. function prevPointUntil(point, pred) {
  1265. while (point) {
  1266. if (pred(point)) {
  1267. return point;
  1268. }
  1269. point = prevPoint(point);
  1270. }
  1271. return null;
  1272. }
  1273. /**
  1274. * @method nextPointUntil
  1275. *
  1276. * @param {BoundaryPoint} point
  1277. * @param {Function} pred
  1278. * @return {BoundaryPoint}
  1279. */
  1280. function nextPointUntil(point, pred) {
  1281. while (point) {
  1282. if (pred(point)) {
  1283. return point;
  1284. }
  1285. point = nextPoint(point);
  1286. }
  1287. return null;
  1288. }
  1289. /**
  1290. * returns whether point has character or not.
  1291. *
  1292. * @param {Point} point
  1293. * @return {Boolean}
  1294. */
  1295. function isCharPoint(point) {
  1296. if (!isText(point.node)) {
  1297. return false;
  1298. }
  1299. var ch = point.node.nodeValue.charAt(point.offset - 1);
  1300. return ch && (ch !== ' ' && ch !== NBSP_CHAR);
  1301. }
  1302. /**
  1303. * @method walkPoint
  1304. *
  1305. * @param {BoundaryPoint} startPoint
  1306. * @param {BoundaryPoint} endPoint
  1307. * @param {Function} handler
  1308. * @param {Boolean} isSkipInnerOffset
  1309. */
  1310. function walkPoint(startPoint, endPoint, handler, isSkipInnerOffset) {
  1311. var point = startPoint;
  1312. while (point) {
  1313. handler(point);
  1314. if (isSamePoint(point, endPoint)) {
  1315. break;
  1316. }
  1317. var isSkipOffset = isSkipInnerOffset &&
  1318. startPoint.node !== point.node &&
  1319. endPoint.node !== point.node;
  1320. point = nextPoint(point, isSkipOffset);
  1321. }
  1322. }
  1323. /**
  1324. * @method makeOffsetPath
  1325. *
  1326. * return offsetPath(array of offset) from ancestor
  1327. *
  1328. * @param {Node} ancestor - ancestor node
  1329. * @param {Node} node
  1330. */
  1331. function makeOffsetPath(ancestor, node) {
  1332. var ancestors = listAncestor(node, func.eq(ancestor));
  1333. return ancestors.map(position).reverse();
  1334. }
  1335. /**
  1336. * @method fromOffsetPath
  1337. *
  1338. * return element from offsetPath(array of offset)
  1339. *
  1340. * @param {Node} ancestor - ancestor node
  1341. * @param {array} offsets - offsetPath
  1342. */
  1343. function fromOffsetPath(ancestor, offsets) {
  1344. var current = ancestor;
  1345. for (var i = 0, len = offsets.length; i < len; i++) {
  1346. if (current.childNodes.length <= offsets[i]) {
  1347. current = current.childNodes[current.childNodes.length - 1];
  1348. }
  1349. else {
  1350. current = current.childNodes[offsets[i]];
  1351. }
  1352. }
  1353. return current;
  1354. }
  1355. /**
  1356. * @method splitNode
  1357. *
  1358. * split element or #text
  1359. *
  1360. * @param {BoundaryPoint} point
  1361. * @param {Object} [options]
  1362. * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
  1363. * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
  1364. * @return {Node} right node of boundaryPoint
  1365. */
  1366. function splitNode(point, options) {
  1367. var isSkipPaddingBlankHTML = options && options.isSkipPaddingBlankHTML;
  1368. var isNotSplitEdgePoint = options && options.isNotSplitEdgePoint;
  1369. // edge case
  1370. if (isEdgePoint(point) && (isText(point.node) || isNotSplitEdgePoint)) {
  1371. if (isLeftEdgePoint(point)) {
  1372. return point.node;
  1373. }
  1374. else if (isRightEdgePoint(point)) {
  1375. return point.node.nextSibling;
  1376. }
  1377. }
  1378. // split #text
  1379. if (isText(point.node)) {
  1380. return point.node.splitText(point.offset);
  1381. }
  1382. else {
  1383. var childNode = point.node.childNodes[point.offset];
  1384. var clone = insertAfter(point.node.cloneNode(false), point.node);
  1385. appendChildNodes(clone, listNext(childNode));
  1386. if (!isSkipPaddingBlankHTML) {
  1387. paddingBlankHTML(point.node);
  1388. paddingBlankHTML(clone);
  1389. }
  1390. return clone;
  1391. }
  1392. }
  1393. /**
  1394. * @method splitTree
  1395. *
  1396. * split tree by point
  1397. *
  1398. * @param {Node} root - split root
  1399. * @param {BoundaryPoint} point
  1400. * @param {Object} [options]
  1401. * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
  1402. * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
  1403. * @return {Node} right node of boundaryPoint
  1404. */
  1405. function splitTree(root, point, options) {
  1406. // ex) [#text, <span>, <p>]
  1407. var ancestors = listAncestor(point.node, func.eq(root));
  1408. if (!ancestors.length) {
  1409. return null;
  1410. }
  1411. else if (ancestors.length === 1) {
  1412. return splitNode(point, options);
  1413. }
  1414. return ancestors.reduce(function (node, parent) {
  1415. if (node === point.node) {
  1416. node = splitNode(point, options);
  1417. }
  1418. return splitNode({
  1419. node: parent,
  1420. offset: node ? position(node) : nodeLength(parent)
  1421. }, options);
  1422. });
  1423. }
  1424. /**
  1425. * split point
  1426. *
  1427. * @param {Point} point
  1428. * @param {Boolean} isInline
  1429. * @return {Object}
  1430. */
  1431. function splitPoint(point, isInline) {
  1432. // find splitRoot, container
  1433. // - inline: splitRoot is a child of paragraph
  1434. // - block: splitRoot is a child of bodyContainer
  1435. var pred = isInline ? isPara : isBodyContainer;
  1436. var ancestors = listAncestor(point.node, pred);
  1437. var topAncestor = lists.last(ancestors) || point.node;
  1438. var splitRoot, container;
  1439. if (pred(topAncestor)) {
  1440. splitRoot = ancestors[ancestors.length - 2];
  1441. container = topAncestor;
  1442. }
  1443. else {
  1444. splitRoot = topAncestor;
  1445. container = splitRoot.parentNode;
  1446. }
  1447. // if splitRoot is exists, split with splitTree
  1448. var pivot = splitRoot && splitTree(splitRoot, point, {
  1449. isSkipPaddingBlankHTML: isInline,
  1450. isNotSplitEdgePoint: isInline
  1451. });
  1452. // if container is point.node, find pivot with point.offset
  1453. if (!pivot && container === point.node) {
  1454. pivot = point.node.childNodes[point.offset];
  1455. }
  1456. return {
  1457. rightNode: pivot,
  1458. container: container
  1459. };
  1460. }
  1461. function create(nodeName) {
  1462. return document.createElement(nodeName);
  1463. }
  1464. function createText(text) {
  1465. return document.createTextNode(text);
  1466. }
  1467. /**
  1468. * @method remove
  1469. *
  1470. * remove node, (isRemoveChild: remove child or not)
  1471. *
  1472. * @param {Node} node
  1473. * @param {Boolean} isRemoveChild
  1474. */
  1475. function remove(node, isRemoveChild) {
  1476. if (!node || !node.parentNode) {
  1477. return;
  1478. }
  1479. if (node.removeNode) {
  1480. return node.removeNode(isRemoveChild);
  1481. }
  1482. var parent = node.parentNode;
  1483. if (!isRemoveChild) {
  1484. var nodes = [];
  1485. for (var i = 0, len = node.childNodes.length; i < len; i++) {
  1486. nodes.push(node.childNodes[i]);
  1487. }
  1488. for (var i = 0, len = nodes.length; i < len; i++) {
  1489. parent.insertBefore(nodes[i], node);
  1490. }
  1491. }
  1492. parent.removeChild(node);
  1493. }
  1494. /**
  1495. * @method removeWhile
  1496. *
  1497. * @param {Node} node
  1498. * @param {Function} pred
  1499. */
  1500. function removeWhile(node, pred) {
  1501. while (node) {
  1502. if (isEditable(node) || !pred(node)) {
  1503. break;
  1504. }
  1505. var parent = node.parentNode;
  1506. remove(node);
  1507. node = parent;
  1508. }
  1509. }
  1510. /**
  1511. * @method replace
  1512. *
  1513. * replace node with provided nodeName
  1514. *
  1515. * @param {Node} node
  1516. * @param {String} nodeName
  1517. * @return {Node} - new node
  1518. */
  1519. function replace(node, nodeName) {
  1520. if (node.nodeName.toUpperCase() === nodeName.toUpperCase()) {
  1521. return node;
  1522. }
  1523. var newNode = create(nodeName);
  1524. if (node.style.cssText) {
  1525. newNode.style.cssText = node.style.cssText;
  1526. }
  1527. appendChildNodes(newNode, lists.from(node.childNodes));
  1528. insertAfter(newNode, node);
  1529. remove(node);
  1530. return newNode;
  1531. }
  1532. var isTextarea = makePredByNodeName('TEXTAREA');
  1533. /**
  1534. * @param {jQuery} $node
  1535. * @param {Boolean} [stripLinebreaks] - default: false
  1536. */
  1537. function value($node, stripLinebreaks) {
  1538. var val = isTextarea($node[0]) ? $node.val() : $node.html();
  1539. if (stripLinebreaks) {
  1540. return val.replace(/[\n\r]/g, '');
  1541. }
  1542. return val;
  1543. }
  1544. /**
  1545. * @method html
  1546. *
  1547. * get the HTML contents of node
  1548. *
  1549. * @param {jQuery} $node
  1550. * @param {Boolean} [isNewlineOnBlock]
  1551. */
  1552. function html($node, isNewlineOnBlock) {
  1553. var markup = value($node);
  1554. if (isNewlineOnBlock) {
  1555. var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
  1556. markup = markup.replace(regexTag, function (match, endSlash, name) {
  1557. name = name.toUpperCase();
  1558. var isEndOfInlineContainer = /^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(name) &&
  1559. !!endSlash;
  1560. var isBlockNode = /^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(name);
  1561. return match + ((isEndOfInlineContainer || isBlockNode) ? '\n' : '');
  1562. });
  1563. markup = $$1.trim(markup);
  1564. }
  1565. return markup;
  1566. }
  1567. function posFromPlaceholder(placeholder) {
  1568. var $placeholder = $$1(placeholder);
  1569. var pos = $placeholder.offset();
  1570. var height = $placeholder.outerHeight(true); // include margin
  1571. return {
  1572. left: pos.left,
  1573. top: pos.top + height
  1574. };
  1575. }
  1576. function attachEvents($node, events) {
  1577. Object.keys(events).forEach(function (key) {
  1578. $node.on(key, events[key]);
  1579. });
  1580. }
  1581. function detachEvents($node, events) {
  1582. Object.keys(events).forEach(function (key) {
  1583. $node.off(key, events[key]);
  1584. });
  1585. }
  1586. /**
  1587. * @method isCustomStyleTag
  1588. *
  1589. * assert if a node contains a "note-styletag" class,
  1590. * which implies that's a custom-made style tag node
  1591. *
  1592. * @param {Node} an HTML DOM node
  1593. */
  1594. function isCustomStyleTag(node) {
  1595. return node && !isText(node) && lists.contains(node.classList, 'note-styletag');
  1596. }
  1597. var dom = {
  1598. /** @property {String} NBSP_CHAR */
  1599. NBSP_CHAR: NBSP_CHAR,
  1600. /** @property {String} ZERO_WIDTH_NBSP_CHAR */
  1601. ZERO_WIDTH_NBSP_CHAR: ZERO_WIDTH_NBSP_CHAR,
  1602. /** @property {String} blank */
  1603. blank: blankHTML,
  1604. /** @property {String} emptyPara */
  1605. emptyPara: "<p>" + blankHTML + "</p>",
  1606. makePredByNodeName: makePredByNodeName,
  1607. isEditable: isEditable,
  1608. isControlSizing: isControlSizing,
  1609. isText: isText,
  1610. isElement: isElement,
  1611. isVoid: isVoid,
  1612. isPara: isPara,
  1613. isPurePara: isPurePara,
  1614. isHeading: isHeading,
  1615. isInline: isInline,
  1616. isBlock: func.not(isInline),
  1617. isBodyInline: isBodyInline,
  1618. isBody: isBody,
  1619. isParaInline: isParaInline,
  1620. isPre: isPre,
  1621. isList: isList,
  1622. isTable: isTable,
  1623. isData: isData,
  1624. isCell: isCell,
  1625. isBlockquote: isBlockquote,
  1626. isBodyContainer: isBodyContainer,
  1627. isAnchor: isAnchor,
  1628. isDiv: makePredByNodeName('DIV'),
  1629. isLi: isLi,
  1630. isBR: makePredByNodeName('BR'),
  1631. isSpan: makePredByNodeName('SPAN'),
  1632. isB: makePredByNodeName('B'),
  1633. isU: makePredByNodeName('U'),
  1634. isS: makePredByNodeName('S'),
  1635. isI: makePredByNodeName('I'),
  1636. isImg: makePredByNodeName('IMG'),
  1637. isTextarea: isTextarea,
  1638. isEmpty: isEmpty,
  1639. isEmptyAnchor: func.and(isAnchor, isEmpty),
  1640. isClosestSibling: isClosestSibling,
  1641. withClosestSiblings: withClosestSiblings,
  1642. nodeLength: nodeLength,
  1643. isLeftEdgePoint: isLeftEdgePoint,
  1644. isRightEdgePoint: isRightEdgePoint,
  1645. isEdgePoint: isEdgePoint,
  1646. isLeftEdgeOf: isLeftEdgeOf,
  1647. isRightEdgeOf: isRightEdgeOf,
  1648. isLeftEdgePointOf: isLeftEdgePointOf,
  1649. isRightEdgePointOf: isRightEdgePointOf,
  1650. prevPoint: prevPoint,
  1651. nextPoint: nextPoint,
  1652. isSamePoint: isSamePoint,
  1653. isVisiblePoint: isVisiblePoint,
  1654. prevPointUntil: prevPointUntil,
  1655. nextPointUntil: nextPointUntil,
  1656. isCharPoint: isCharPoint,
  1657. walkPoint: walkPoint,
  1658. ancestor: ancestor,
  1659. singleChildAncestor: singleChildAncestor,
  1660. listAncestor: listAncestor,
  1661. lastAncestor: lastAncestor,
  1662. listNext: listNext,
  1663. listPrev: listPrev,
  1664. listDescendant: listDescendant,
  1665. commonAncestor: commonAncestor,
  1666. wrap: wrap,
  1667. insertAfter: insertAfter,
  1668. appendChildNodes: appendChildNodes,
  1669. position: position,
  1670. hasChildren: hasChildren,
  1671. makeOffsetPath: makeOffsetPath,
  1672. fromOffsetPath: fromOffsetPath,
  1673. splitTree: splitTree,
  1674. splitPoint: splitPoint,
  1675. create: create,
  1676. createText: createText,
  1677. remove: remove,
  1678. removeWhile: removeWhile,
  1679. replace: replace,
  1680. html: html,
  1681. value: value,
  1682. posFromPlaceholder: posFromPlaceholder,
  1683. attachEvents: attachEvents,
  1684. detachEvents: detachEvents,
  1685. isCustomStyleTag: isCustomStyleTag
  1686. };
  1687. $$1.summernote = $$1.summernote || {
  1688. lang: {}
  1689. };
  1690. $$1.extend($$1.summernote.lang, {
  1691. 'en-US': {
  1692. font: {
  1693. bold: 'Bold',
  1694. italic: 'Italic',
  1695. underline: 'Underline',
  1696. clear: 'Remove Font Style',
  1697. height: 'Line Height',
  1698. name: 'Font Family',
  1699. strikethrough: 'Strikethrough',
  1700. subscript: 'Subscript',
  1701. superscript: 'Superscript',
  1702. size: 'Font Size'
  1703. },
  1704. image: {
  1705. image: 'Picture',
  1706. insert: 'Insert Image',
  1707. resizeFull: 'Resize Full',
  1708. resizeHalf: 'Resize Half',
  1709. resizeQuarter: 'Resize Quarter',
  1710. floatLeft: 'Float Left',
  1711. floatRight: 'Float Right',
  1712. floatNone: 'Float None',
  1713. shapeRounded: 'Shape: Rounded',
  1714. shapeCircle: 'Shape: Circle',
  1715. shapeThumbnail: 'Shape: Thumbnail',
  1716. shapeNone: 'Shape: None',
  1717. dragImageHere: 'Drag image or text here',
  1718. dropImage: 'Drop image or Text',
  1719. selectFromFiles: 'Select from files',
  1720. maximumFileSize: 'Maximum file size',
  1721. maximumFileSizeError: 'Maximum file size exceeded.',
  1722. url: 'Image URL',
  1723. remove: 'Remove Image',
  1724. original: 'Original'
  1725. },
  1726. video: {
  1727. video: 'Video',
  1728. videoLink: 'Video Link',
  1729. insert: 'Insert Video',
  1730. url: 'Video URL',
  1731. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)'
  1732. },
  1733. link: {
  1734. link: 'Link',
  1735. insert: 'Insert Link',
  1736. unlink: 'Unlink',
  1737. edit: 'Edit',
  1738. textToDisplay: 'Text to display',
  1739. url: 'To what URL should this link go?',
  1740. openInNewWindow: 'Open in new window'
  1741. },
  1742. table: {
  1743. table: 'Table',
  1744. addRowAbove: 'Add row above',
  1745. addRowBelow: 'Add row below',
  1746. addColLeft: 'Add column left',
  1747. addColRight: 'Add column right',
  1748. delRow: 'Delete row',
  1749. delCol: 'Delete column',
  1750. delTable: 'Delete table'
  1751. },
  1752. hr: {
  1753. insert: 'Insert Horizontal Rule'
  1754. },
  1755. style: {
  1756. style: 'Style',
  1757. p: 'Normal',
  1758. blockquote: 'Quote',
  1759. pre: 'Code',
  1760. h1: 'Header 1',
  1761. h2: 'Header 2',
  1762. h3: 'Header 3',
  1763. h4: 'Header 4',
  1764. h5: 'Header 5',
  1765. h6: 'Header 6'
  1766. },
  1767. lists: {
  1768. unordered: 'Unordered list',
  1769. ordered: 'Ordered list'
  1770. },
  1771. options: {
  1772. help: 'Help',
  1773. fullscreen: 'Full Screen',
  1774. codeview: 'Code View'
  1775. },
  1776. paragraph: {
  1777. paragraph: 'Paragraph',
  1778. outdent: 'Outdent',
  1779. indent: 'Indent',
  1780. left: 'Align left',
  1781. center: 'Align center',
  1782. right: 'Align right',
  1783. justify: 'Justify full'
  1784. },
  1785. color: {
  1786. recent: 'Recent Color',
  1787. more: 'More Color',
  1788. background: 'Background Color',
  1789. foreground: 'Foreground Color',
  1790. transparent: 'Transparent',
  1791. setTransparent: 'Set transparent',
  1792. reset: 'Reset',
  1793. resetToDefault: 'Reset to default'
  1794. },
  1795. shortcut: {
  1796. shortcuts: 'Keyboard shortcuts',
  1797. close: 'Close',
  1798. textFormatting: 'Text formatting',
  1799. action: 'Action',
  1800. paragraphFormatting: 'Paragraph formatting',
  1801. documentStyle: 'Document Style',
  1802. extraKeys: 'Extra keys'
  1803. },
  1804. help: {
  1805. 'insertParagraph': 'Insert Paragraph',
  1806. 'undo': 'Undoes the last command',
  1807. 'redo': 'Redoes the last command',
  1808. 'tab': 'Tab',
  1809. 'untab': 'Untab',
  1810. 'bold': 'Set a bold style',
  1811. 'italic': 'Set a italic style',
  1812. 'underline': 'Set a underline style',
  1813. 'strikethrough': 'Set a strikethrough style',
  1814. 'removeFormat': 'Clean a style',
  1815. 'justifyLeft': 'Set left align',
  1816. 'justifyCenter': 'Set center align',
  1817. 'justifyRight': 'Set right align',
  1818. 'justifyFull': 'Set full align',
  1819. 'insertUnorderedList': 'Toggle unordered list',
  1820. 'insertOrderedList': 'Toggle ordered list',
  1821. 'outdent': 'Outdent on current paragraph',
  1822. 'indent': 'Indent on current paragraph',
  1823. 'formatPara': 'Change current block\'s format as a paragraph(P tag)',
  1824. 'formatH1': 'Change current block\'s format as H1',
  1825. 'formatH2': 'Change current block\'s format as H2',
  1826. 'formatH3': 'Change current block\'s format as H3',
  1827. 'formatH4': 'Change current block\'s format as H4',
  1828. 'formatH5': 'Change current block\'s format as H5',
  1829. 'formatH6': 'Change current block\'s format as H6',
  1830. 'insertHorizontalRule': 'Insert horizontal rule',
  1831. 'linkDialog.show': 'Show Link Dialog'
  1832. },
  1833. history: {
  1834. undo: 'Undo',
  1835. redo: 'Redo'
  1836. },
  1837. specialChar: {
  1838. specialChar: 'SPECIAL CHARACTERS',
  1839. select: 'Select Special characters'
  1840. }
  1841. }
  1842. });
  1843. var KEY_MAP = {
  1844. 'BACKSPACE': 8,
  1845. 'TAB': 9,
  1846. 'ENTER': 13,
  1847. 'SPACE': 32,
  1848. 'DELETE': 46,
  1849. // Arrow
  1850. 'LEFT': 37,
  1851. 'UP': 38,
  1852. 'RIGHT': 39,
  1853. 'DOWN': 40,
  1854. // Number: 0-9
  1855. 'NUM0': 48,
  1856. 'NUM1': 49,
  1857. 'NUM2': 50,
  1858. 'NUM3': 51,
  1859. 'NUM4': 52,
  1860. 'NUM5': 53,
  1861. 'NUM6': 54,
  1862. 'NUM7': 55,
  1863. 'NUM8': 56,
  1864. // Alphabet: a-z
  1865. 'B': 66,
  1866. 'E': 69,
  1867. 'I': 73,
  1868. 'J': 74,
  1869. 'K': 75,
  1870. 'L': 76,
  1871. 'R': 82,
  1872. 'S': 83,
  1873. 'U': 85,
  1874. 'V': 86,
  1875. 'Y': 89,
  1876. 'Z': 90,
  1877. 'SLASH': 191,
  1878. 'LEFTBRACKET': 219,
  1879. 'BACKSLASH': 220,
  1880. 'RIGHTBRACKET': 221
  1881. };
  1882. /**
  1883. * @class core.key
  1884. *
  1885. * Object for keycodes.
  1886. *
  1887. * @singleton
  1888. * @alternateClassName key
  1889. */
  1890. var key = {
  1891. /**
  1892. * @method isEdit
  1893. *
  1894. * @param {Number} keyCode
  1895. * @return {Boolean}
  1896. */
  1897. isEdit: function (keyCode) {
  1898. return lists.contains([
  1899. KEY_MAP.BACKSPACE,
  1900. KEY_MAP.TAB,
  1901. KEY_MAP.ENTER,
  1902. KEY_MAP.SPACE,
  1903. KEY_MAP.DELETE
  1904. ], keyCode);
  1905. },
  1906. /**
  1907. * @method isMove
  1908. *
  1909. * @param {Number} keyCode
  1910. * @return {Boolean}
  1911. */
  1912. isMove: function (keyCode) {
  1913. return lists.contains([
  1914. KEY_MAP.LEFT,
  1915. KEY_MAP.UP,
  1916. KEY_MAP.RIGHT,
  1917. KEY_MAP.DOWN
  1918. ], keyCode);
  1919. },
  1920. /**
  1921. * @property {Object} nameFromCode
  1922. * @property {String} nameFromCode.8 "BACKSPACE"
  1923. */
  1924. nameFromCode: func.invertObject(KEY_MAP),
  1925. code: KEY_MAP
  1926. };
  1927. /**
  1928. * return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
  1929. *
  1930. * @param {TextRange} textRange
  1931. * @param {Boolean} isStart
  1932. * @return {BoundaryPoint}
  1933. *
  1934. * @see http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
  1935. */
  1936. function textRangeToPoint(textRange, isStart) {
  1937. var container = textRange.parentElement();
  1938. var offset;
  1939. var tester = document.body.createTextRange();
  1940. var prevContainer;
  1941. var childNodes = lists.from(container.childNodes);
  1942. for (offset = 0; offset < childNodes.length; offset++) {
  1943. if (dom.isText(childNodes[offset])) {
  1944. continue;
  1945. }
  1946. tester.moveToElementText(childNodes[offset]);
  1947. if (tester.compareEndPoints('StartToStart', textRange) >= 0) {
  1948. break;
  1949. }
  1950. prevContainer = childNodes[offset];
  1951. }
  1952. if (offset !== 0 && dom.isText(childNodes[offset - 1])) {
  1953. var textRangeStart = document.body.createTextRange();
  1954. var curTextNode = null;
  1955. textRangeStart.moveToElementText(prevContainer || container);
  1956. textRangeStart.collapse(!prevContainer);
  1957. curTextNode = prevContainer ? prevContainer.nextSibling : container.firstChild;
  1958. var pointTester = textRange.duplicate();
  1959. pointTester.setEndPoint('StartToStart', textRangeStart);
  1960. var textCount = pointTester.text.replace(/[\r\n]/g, '').length;
  1961. while (textCount > curTextNode.nodeValue.length && curTextNode.nextSibling) {
  1962. textCount -= curTextNode.nodeValue.length;
  1963. curTextNode = curTextNode.nextSibling;
  1964. }
  1965. // [workaround] enforce IE to re-reference curTextNode, hack
  1966. var dummy = curTextNode.nodeValue; // eslint-disable-line
  1967. if (isStart && curTextNode.nextSibling && dom.isText(curTextNode.nextSibling) &&
  1968. textCount === curTextNode.nodeValue.length) {
  1969. textCount -= curTextNode.nodeValue.length;
  1970. curTextNode = curTextNode.nextSibling;
  1971. }
  1972. container = curTextNode;
  1973. offset = textCount;
  1974. }
  1975. return {
  1976. cont: container,
  1977. offset: offset
  1978. };
  1979. }
  1980. /**
  1981. * return TextRange from boundary point (inspired by google closure-library)
  1982. * @param {BoundaryPoint} point
  1983. * @return {TextRange}
  1984. */
  1985. function pointToTextRange(point) {
  1986. var textRangeInfo = function (container, offset) {
  1987. var node, isCollapseToStart;
  1988. if (dom.isText(container)) {
  1989. var prevTextNodes = dom.listPrev(container, func.not(dom.isText));
  1990. var prevContainer = lists.last(prevTextNodes).previousSibling;
  1991. node = prevContainer || container.parentNode;
  1992. offset += lists.sum(lists.tail(prevTextNodes), dom.nodeLength);
  1993. isCollapseToStart = !prevContainer;
  1994. }
  1995. else {
  1996. node = container.childNodes[offset] || container;
  1997. if (dom.isText(node)) {
  1998. return textRangeInfo(node, 0);
  1999. }
  2000. offset = 0;
  2001. isCollapseToStart = false;
  2002. }
  2003. return {
  2004. node: node,
  2005. collapseToStart: isCollapseToStart,
  2006. offset: offset
  2007. };
  2008. };
  2009. var textRange = document.body.createTextRange();
  2010. var info = textRangeInfo(point.node, point.offset);
  2011. textRange.moveToElementText(info.node);
  2012. textRange.collapse(info.collapseToStart);
  2013. textRange.moveStart('character', info.offset);
  2014. return textRange;
  2015. }
  2016. /**
  2017. * Wrapped Range
  2018. *
  2019. * @constructor
  2020. * @param {Node} sc - start container
  2021. * @param {Number} so - start offset
  2022. * @param {Node} ec - end container
  2023. * @param {Number} eo - end offset
  2024. */
  2025. var WrappedRange = /** @class */ (function () {
  2026. function WrappedRange(sc, so, ec, eo) {
  2027. this.sc = sc;
  2028. this.so = so;
  2029. this.ec = ec;
  2030. this.eo = eo;
  2031. // isOnEditable: judge whether range is on editable or not
  2032. this.isOnEditable = this.makeIsOn(dom.isEditable);
  2033. // isOnList: judge whether range is on list node or not
  2034. this.isOnList = this.makeIsOn(dom.isList);
  2035. // isOnAnchor: judge whether range is on anchor node or not
  2036. this.isOnAnchor = this.makeIsOn(dom.isAnchor);
  2037. // isOnCell: judge whether range is on cell node or not
  2038. this.isOnCell = this.makeIsOn(dom.isCell);
  2039. // isOnData: judge whether range is on data node or not
  2040. this.isOnData = this.makeIsOn(dom.isData);
  2041. }
  2042. // nativeRange: get nativeRange from sc, so, ec, eo
  2043. WrappedRange.prototype.nativeRange = function () {
  2044. if (env.isW3CRangeSupport) {
  2045. var w3cRange = document.createRange();
  2046. w3cRange.setStart(this.sc, this.so);
  2047. w3cRange.setEnd(this.ec, this.eo);
  2048. return w3cRange;
  2049. }
  2050. else {
  2051. var textRange = pointToTextRange({
  2052. node: this.sc,
  2053. offset: this.so
  2054. });
  2055. textRange.setEndPoint('EndToEnd', pointToTextRange({
  2056. node: this.ec,
  2057. offset: this.eo
  2058. }));
  2059. return textRange;
  2060. }
  2061. };
  2062. WrappedRange.prototype.getPoints = function () {
  2063. return {
  2064. sc: this.sc,
  2065. so: this.so,
  2066. ec: this.ec,
  2067. eo: this.eo
  2068. };
  2069. };
  2070. WrappedRange.prototype.getStartPoint = function () {
  2071. return {
  2072. node: this.sc,
  2073. offset: this.so
  2074. };
  2075. };
  2076. WrappedRange.prototype.getEndPoint = function () {
  2077. return {
  2078. node: this.ec,
  2079. offset: this.eo
  2080. };
  2081. };
  2082. /**
  2083. * select update visible range
  2084. */
  2085. WrappedRange.prototype.select = function () {
  2086. var nativeRng = this.nativeRange();
  2087. if (env.isW3CRangeSupport) {
  2088. var selection = document.getSelection();
  2089. if (selection.rangeCount > 0) {
  2090. selection.removeAllRanges();
  2091. }
  2092. selection.addRange(nativeRng);
  2093. }
  2094. else {
  2095. nativeRng.select();
  2096. }
  2097. return this;
  2098. };
  2099. /**
  2100. * Moves the scrollbar to start container(sc) of current range
  2101. *
  2102. * @return {WrappedRange}
  2103. */
  2104. WrappedRange.prototype.scrollIntoView = function (container) {
  2105. var height = $$1(container).height();
  2106. if (container.scrollTop + height < this.sc.offsetTop) {
  2107. container.scrollTop += Math.abs(container.scrollTop + height - this.sc.offsetTop);
  2108. }
  2109. return this;
  2110. };
  2111. /**
  2112. * @return {WrappedRange}
  2113. */
  2114. WrappedRange.prototype.normalize = function () {
  2115. /**
  2116. * @param {BoundaryPoint} point
  2117. * @param {Boolean} isLeftToRight
  2118. * @return {BoundaryPoint}
  2119. */
  2120. var getVisiblePoint = function (point, isLeftToRight) {
  2121. if ((dom.isVisiblePoint(point) && !dom.isEdgePoint(point)) ||
  2122. (dom.isVisiblePoint(point) && dom.isRightEdgePoint(point) && !isLeftToRight) ||
  2123. (dom.isVisiblePoint(point) && dom.isLeftEdgePoint(point) && isLeftToRight) ||
  2124. (dom.isVisiblePoint(point) && dom.isBlock(point.node) && dom.isEmpty(point.node))) {
  2125. return point;
  2126. }
  2127. // point on block's edge
  2128. var block = dom.ancestor(point.node, dom.isBlock);
  2129. if (((dom.isLeftEdgePointOf(point, block) || dom.isVoid(dom.prevPoint(point).node)) && !isLeftToRight) ||
  2130. ((dom.isRightEdgePointOf(point, block) || dom.isVoid(dom.nextPoint(point).node)) && isLeftToRight)) {
  2131. // returns point already on visible point
  2132. if (dom.isVisiblePoint(point)) {
  2133. return point;
  2134. }
  2135. // reverse direction
  2136. isLeftToRight = !isLeftToRight;
  2137. }
  2138. var nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint)
  2139. : dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint);
  2140. return nextPoint || point;
  2141. };
  2142. var endPoint = getVisiblePoint(this.getEndPoint(), false);
  2143. var startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true);
  2144. return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
  2145. };
  2146. /**
  2147. * returns matched nodes on range
  2148. *
  2149. * @param {Function} [pred] - predicate function
  2150. * @param {Object} [options]
  2151. * @param {Boolean} [options.includeAncestor]
  2152. * @param {Boolean} [options.fullyContains]
  2153. * @return {Node[]}
  2154. */
  2155. WrappedRange.prototype.nodes = function (pred, options) {
  2156. pred = pred || func.ok;
  2157. var includeAncestor = options && options.includeAncestor;
  2158. var fullyContains = options && options.fullyContains;
  2159. // TODO compare points and sort
  2160. var startPoint = this.getStartPoint();
  2161. var endPoint = this.getEndPoint();
  2162. var nodes = [];
  2163. var leftEdgeNodes = [];
  2164. dom.walkPoint(startPoint, endPoint, function (point) {
  2165. if (dom.isEditable(point.node)) {
  2166. return;
  2167. }
  2168. var node;
  2169. if (fullyContains) {
  2170. if (dom.isLeftEdgePoint(point)) {
  2171. leftEdgeNodes.push(point.node);
  2172. }
  2173. if (dom.isRightEdgePoint(point) && lists.contains(leftEdgeNodes, point.node)) {
  2174. node = point.node;
  2175. }
  2176. }
  2177. else if (includeAncestor) {
  2178. node = dom.ancestor(point.node, pred);
  2179. }
  2180. else {
  2181. node = point.node;
  2182. }
  2183. if (node && pred(node)) {
  2184. nodes.push(node);
  2185. }
  2186. }, true);
  2187. return lists.unique(nodes);
  2188. };
  2189. /**
  2190. * returns commonAncestor of range
  2191. * @return {Element} - commonAncestor
  2192. */
  2193. WrappedRange.prototype.commonAncestor = function () {
  2194. return dom.commonAncestor(this.sc, this.ec);
  2195. };
  2196. /**
  2197. * returns expanded range by pred
  2198. *
  2199. * @param {Function} pred - predicate function
  2200. * @return {WrappedRange}
  2201. */
  2202. WrappedRange.prototype.expand = function (pred) {
  2203. var startAncestor = dom.ancestor(this.sc, pred);
  2204. var endAncestor = dom.ancestor(this.ec, pred);
  2205. if (!startAncestor && !endAncestor) {
  2206. return new WrappedRange(this.sc, this.so, this.ec, this.eo);
  2207. }
  2208. var boundaryPoints = this.getPoints();
  2209. if (startAncestor) {
  2210. boundaryPoints.sc = startAncestor;
  2211. boundaryPoints.so = 0;
  2212. }
  2213. if (endAncestor) {
  2214. boundaryPoints.ec = endAncestor;
  2215. boundaryPoints.eo = dom.nodeLength(endAncestor);
  2216. }
  2217. return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
  2218. };
  2219. /**
  2220. * @param {Boolean} isCollapseToStart
  2221. * @return {WrappedRange}
  2222. */
  2223. WrappedRange.prototype.collapse = function (isCollapseToStart) {
  2224. if (isCollapseToStart) {
  2225. return new WrappedRange(this.sc, this.so, this.sc, this.so);
  2226. }
  2227. else {
  2228. return new WrappedRange(this.ec, this.eo, this.ec, this.eo);
  2229. }
  2230. };
  2231. /**
  2232. * splitText on range
  2233. */
  2234. WrappedRange.prototype.splitText = function () {
  2235. var isSameContainer = this.sc === this.ec;
  2236. var boundaryPoints = this.getPoints();
  2237. if (dom.isText(this.ec) && !dom.isEdgePoint(this.getEndPoint())) {
  2238. this.ec.splitText(this.eo);
  2239. }
  2240. if (dom.isText(this.sc) && !dom.isEdgePoint(this.getStartPoint())) {
  2241. boundaryPoints.sc = this.sc.splitText(this.so);
  2242. boundaryPoints.so = 0;
  2243. if (isSameContainer) {
  2244. boundaryPoints.ec = boundaryPoints.sc;
  2245. boundaryPoints.eo = this.eo - this.so;
  2246. }
  2247. }
  2248. return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
  2249. };
  2250. /**
  2251. * delete contents on range
  2252. * @return {WrappedRange}
  2253. */
  2254. WrappedRange.prototype.deleteContents = function () {
  2255. if (this.isCollapsed()) {
  2256. return this;
  2257. }
  2258. var rng = this.splitText();
  2259. var nodes = rng.nodes(null, {
  2260. fullyContains: true
  2261. });
  2262. // find new cursor point
  2263. var point = dom.prevPointUntil(rng.getStartPoint(), function (point) {
  2264. return !lists.contains(nodes, point.node);
  2265. });
  2266. var emptyParents = [];
  2267. $$1.each(nodes, function (idx, node) {
  2268. // find empty parents
  2269. var parent = node.parentNode;
  2270. if (point.node !== parent && dom.nodeLength(parent) === 1) {
  2271. emptyParents.push(parent);
  2272. }
  2273. dom.remove(node, false);
  2274. });
  2275. // remove empty parents
  2276. $$1.each(emptyParents, function (idx, node) {
  2277. dom.remove(node, false);
  2278. });
  2279. return new WrappedRange(point.node, point.offset, point.node, point.offset).normalize();
  2280. };
  2281. /**
  2282. * makeIsOn: return isOn(pred) function
  2283. */
  2284. WrappedRange.prototype.makeIsOn = function (pred) {
  2285. return function () {
  2286. var ancestor = dom.ancestor(this.sc, pred);
  2287. return !!ancestor && (ancestor === dom.ancestor(this.ec, pred));
  2288. };
  2289. };
  2290. /**
  2291. * @param {Function} pred
  2292. * @return {Boolean}
  2293. */
  2294. WrappedRange.prototype.isLeftEdgeOf = function (pred) {
  2295. if (!dom.isLeftEdgePoint(this.getStartPoint())) {
  2296. return false;
  2297. }
  2298. var node = dom.ancestor(this.sc, pred);
  2299. return node && dom.isLeftEdgeOf(this.sc, node);
  2300. };
  2301. /**
  2302. * returns whether range was collapsed or not
  2303. */
  2304. WrappedRange.prototype.isCollapsed = function () {
  2305. return this.sc === this.ec && this.so === this.eo;
  2306. };
  2307. /**
  2308. * wrap inline nodes which children of body with paragraph
  2309. *
  2310. * @return {WrappedRange}
  2311. */
  2312. WrappedRange.prototype.wrapBodyInlineWithPara = function () {
  2313. if (dom.isBodyContainer(this.sc) && dom.isEmpty(this.sc)) {
  2314. this.sc.innerHTML = dom.emptyPara;
  2315. return new WrappedRange(this.sc.firstChild, 0, this.sc.firstChild, 0);
  2316. }
  2317. /**
  2318. * [workaround] firefox often create range on not visible point. so normalize here.
  2319. * - firefox: |<p>text</p>|
  2320. * - chrome: <p>|text|</p>
  2321. */
  2322. var rng = this.normalize();
  2323. if (dom.isParaInline(this.sc) || dom.isPara(this.sc)) {
  2324. return rng;
  2325. }
  2326. // find inline top ancestor
  2327. var topAncestor;
  2328. if (dom.isInline(rng.sc)) {
  2329. var ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline));
  2330. topAncestor = lists.last(ancestors);
  2331. if (!dom.isInline(topAncestor)) {
  2332. topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so];
  2333. }
  2334. }
  2335. else {
  2336. topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0];
  2337. }
  2338. // siblings not in paragraph
  2339. var inlineSiblings = dom.listPrev(topAncestor, dom.isParaInline).reverse();
  2340. inlineSiblings = inlineSiblings.concat(dom.listNext(topAncestor.nextSibling, dom.isParaInline));
  2341. // wrap with paragraph
  2342. if (inlineSiblings.length) {
  2343. var para = dom.wrap(lists.head(inlineSiblings), 'p');
  2344. dom.appendChildNodes(para, lists.tail(inlineSiblings));
  2345. }
  2346. return this.normalize();
  2347. };
  2348. /**
  2349. * insert node at current cursor
  2350. *
  2351. * @param {Node} node
  2352. * @return {Node}
  2353. */
  2354. WrappedRange.prototype.insertNode = function (node) {
  2355. var rng = this.wrapBodyInlineWithPara().deleteContents();
  2356. var info = dom.splitPoint(rng.getStartPoint(), dom.isInline(node));
  2357. if (info.rightNode) {
  2358. info.rightNode.parentNode.insertBefore(node, info.rightNode);
  2359. }
  2360. else {
  2361. info.container.appendChild(node);
  2362. }
  2363. return node;
  2364. };
  2365. /**
  2366. * insert html at current cursor
  2367. */
  2368. WrappedRange.prototype.pasteHTML = function (markup) {
  2369. var contentsContainer = $$1('<div></div>').html(markup)[0];
  2370. var childNodes = lists.from(contentsContainer.childNodes);
  2371. var rng = this.wrapBodyInlineWithPara().deleteContents();
  2372. return childNodes.reverse().map(function (childNode) {
  2373. return rng.insertNode(childNode);
  2374. }).reverse();
  2375. };
  2376. /**
  2377. * returns text in range
  2378. *
  2379. * @return {String}
  2380. */
  2381. WrappedRange.prototype.toString = function () {
  2382. var nativeRng = this.nativeRange();
  2383. return env.isW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
  2384. };
  2385. /**
  2386. * returns range for word before cursor
  2387. *
  2388. * @param {Boolean} [findAfter] - find after cursor, default: false
  2389. * @return {WrappedRange}
  2390. */
  2391. WrappedRange.prototype.getWordRange = function (findAfter) {
  2392. var endPoint = this.getEndPoint();
  2393. if (!dom.isCharPoint(endPoint)) {
  2394. return this;
  2395. }
  2396. var startPoint = dom.prevPointUntil(endPoint, function (point) {
  2397. return !dom.isCharPoint(point);
  2398. });
  2399. if (findAfter) {
  2400. endPoint = dom.nextPointUntil(endPoint, function (point) {
  2401. return !dom.isCharPoint(point);
  2402. });
  2403. }
  2404. return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
  2405. };
  2406. /**
  2407. * create offsetPath bookmark
  2408. *
  2409. * @param {Node} editable
  2410. */
  2411. WrappedRange.prototype.bookmark = function (editable) {
  2412. return {
  2413. s: {
  2414. path: dom.makeOffsetPath(editable, this.sc),
  2415. offset: this.so
  2416. },
  2417. e: {
  2418. path: dom.makeOffsetPath(editable, this.ec),
  2419. offset: this.eo
  2420. }
  2421. };
  2422. };
  2423. /**
  2424. * create offsetPath bookmark base on paragraph
  2425. *
  2426. * @param {Node[]} paras
  2427. */
  2428. WrappedRange.prototype.paraBookmark = function (paras) {
  2429. return {
  2430. s: {
  2431. path: lists.tail(dom.makeOffsetPath(lists.head(paras), this.sc)),
  2432. offset: this.so
  2433. },
  2434. e: {
  2435. path: lists.tail(dom.makeOffsetPath(lists.last(paras), this.ec)),
  2436. offset: this.eo
  2437. }
  2438. };
  2439. };
  2440. /**
  2441. * getClientRects
  2442. * @return {Rect[]}
  2443. */
  2444. WrappedRange.prototype.getClientRects = function () {
  2445. var nativeRng = this.nativeRange();
  2446. return nativeRng.getClientRects();
  2447. };
  2448. return WrappedRange;
  2449. }());
  2450. /**
  2451. * Data structure
  2452. * * BoundaryPoint: a point of dom tree
  2453. * * BoundaryPoints: two boundaryPoints corresponding to the start and the end of the Range
  2454. *
  2455. * See to http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Position
  2456. */
  2457. var range = {
  2458. /**
  2459. * create Range Object From arguments or Browser Selection
  2460. *
  2461. * @param {Node} sc - start container
  2462. * @param {Number} so - start offset
  2463. * @param {Node} ec - end container
  2464. * @param {Number} eo - end offset
  2465. * @return {WrappedRange}
  2466. */
  2467. create: function (sc, so, ec, eo) {
  2468. if (arguments.length === 4) {
  2469. return new WrappedRange(sc, so, ec, eo);
  2470. }
  2471. else if (arguments.length === 2) {
  2472. ec = sc;
  2473. eo = so;
  2474. return new WrappedRange(sc, so, ec, eo);
  2475. }
  2476. else {
  2477. var wrappedRange = this.createFromSelection();
  2478. if (!wrappedRange && arguments.length === 1) {
  2479. wrappedRange = this.createFromNode(arguments[0]);
  2480. return wrappedRange.collapse(dom.emptyPara === arguments[0].innerHTML);
  2481. }
  2482. return wrappedRange;
  2483. }
  2484. },
  2485. createFromSelection: function () {
  2486. var sc, so, ec, eo;
  2487. if (env.isW3CRangeSupport) {
  2488. var selection = document.getSelection();
  2489. if (!selection || selection.rangeCount === 0) {
  2490. return null;
  2491. }
  2492. else if (dom.isBody(selection.anchorNode)) {
  2493. // Firefox: returns entire body as range on initialization.
  2494. // We won't never need it.
  2495. return null;
  2496. }
  2497. var nativeRng = selection.getRangeAt(0);
  2498. sc = nativeRng.startContainer;
  2499. so = nativeRng.startOffset;
  2500. ec = nativeRng.endContainer;
  2501. eo = nativeRng.endOffset;
  2502. }
  2503. else {
  2504. var textRange = document.selection.createRange();
  2505. var textRangeEnd = textRange.duplicate();
  2506. textRangeEnd.collapse(false);
  2507. var textRangeStart = textRange;
  2508. textRangeStart.collapse(true);
  2509. var startPoint = textRangeToPoint(textRangeStart, true);
  2510. var endPoint = textRangeToPoint(textRangeEnd, false);
  2511. // same visible point case: range was collapsed.
  2512. if (dom.isText(startPoint.node) && dom.isLeftEdgePoint(startPoint) &&
  2513. dom.isTextNode(endPoint.node) && dom.isRightEdgePoint(endPoint) &&
  2514. endPoint.node.nextSibling === startPoint.node) {
  2515. startPoint = endPoint;
  2516. }
  2517. sc = startPoint.cont;
  2518. so = startPoint.offset;
  2519. ec = endPoint.cont;
  2520. eo = endPoint.offset;
  2521. }
  2522. return new WrappedRange(sc, so, ec, eo);
  2523. },
  2524. /**
  2525. * @method
  2526. *
  2527. * create WrappedRange from node
  2528. *
  2529. * @param {Node} node
  2530. * @return {WrappedRange}
  2531. */
  2532. createFromNode: function (node) {
  2533. var sc = node;
  2534. var so = 0;
  2535. var ec = node;
  2536. var eo = dom.nodeLength(ec);
  2537. // browsers can't target a picture or void node
  2538. if (dom.isVoid(sc)) {
  2539. so = dom.listPrev(sc).length - 1;
  2540. sc = sc.parentNode;
  2541. }
  2542. if (dom.isBR(ec)) {
  2543. eo = dom.listPrev(ec).length - 1;
  2544. ec = ec.parentNode;
  2545. }
  2546. else if (dom.isVoid(ec)) {
  2547. eo = dom.listPrev(ec).length;
  2548. ec = ec.parentNode;
  2549. }
  2550. return this.create(sc, so, ec, eo);
  2551. },
  2552. /**
  2553. * create WrappedRange from node after position
  2554. *
  2555. * @param {Node} node
  2556. * @return {WrappedRange}
  2557. */
  2558. createFromNodeBefore: function (node) {
  2559. return this.createFromNode(node).collapse(true);
  2560. },
  2561. /**
  2562. * create WrappedRange from node after position
  2563. *
  2564. * @param {Node} node
  2565. * @return {WrappedRange}
  2566. */
  2567. createFromNodeAfter: function (node) {
  2568. return this.createFromNode(node).collapse();
  2569. },
  2570. /**
  2571. * @method
  2572. *
  2573. * create WrappedRange from bookmark
  2574. *
  2575. * @param {Node} editable
  2576. * @param {Object} bookmark
  2577. * @return {WrappedRange}
  2578. */
  2579. createFromBookmark: function (editable, bookmark) {
  2580. var sc = dom.fromOffsetPath(editable, bookmark.s.path);
  2581. var so = bookmark.s.offset;
  2582. var ec = dom.fromOffsetPath(editable, bookmark.e.path);
  2583. var eo = bookmark.e.offset;
  2584. return new WrappedRange(sc, so, ec, eo);
  2585. },
  2586. /**
  2587. * @method
  2588. *
  2589. * create WrappedRange from paraBookmark
  2590. *
  2591. * @param {Object} bookmark
  2592. * @param {Node[]} paras
  2593. * @return {WrappedRange}
  2594. */
  2595. createFromParaBookmark: function (bookmark, paras) {
  2596. var so = bookmark.s.offset;
  2597. var eo = bookmark.e.offset;
  2598. var sc = dom.fromOffsetPath(lists.head(paras), bookmark.s.path);
  2599. var ec = dom.fromOffsetPath(lists.last(paras), bookmark.e.path);
  2600. return new WrappedRange(sc, so, ec, eo);
  2601. }
  2602. };
  2603. /**
  2604. * @method readFileAsDataURL
  2605. *
  2606. * read contents of file as representing URL
  2607. *
  2608. * @param {File} file
  2609. * @return {Promise} - then: dataUrl
  2610. */
  2611. function readFileAsDataURL(file) {
  2612. return $$1.Deferred(function (deferred) {
  2613. $$1.extend(new FileReader(), {
  2614. onload: function (e) {
  2615. var dataURL = e.target.result;
  2616. deferred.resolve(dataURL);
  2617. },
  2618. onerror: function (err) {
  2619. deferred.reject(err);
  2620. }
  2621. }).readAsDataURL(file);
  2622. }).promise();
  2623. }
  2624. /**
  2625. * @method createImage
  2626. *
  2627. * create `<image>` from url string
  2628. *
  2629. * @param {String} url
  2630. * @return {Promise} - then: $image
  2631. */
  2632. function createImage(url) {
  2633. return $$1.Deferred(function (deferred) {
  2634. var $img = $$1('<img>');
  2635. $img.one('load', function () {
  2636. $img.off('error abort');
  2637. deferred.resolve($img);
  2638. }).one('error abort', function () {
  2639. $img.off('load').detach();
  2640. deferred.reject($img);
  2641. }).css({
  2642. display: 'none'
  2643. }).appendTo(document.body).attr('src', url);
  2644. }).promise();
  2645. }
  2646. var History = /** @class */ (function () {
  2647. function History($editable) {
  2648. this.stack = [];
  2649. this.stackOffset = -1;
  2650. this.$editable = $editable;
  2651. this.editable = $editable[0];
  2652. }
  2653. History.prototype.makeSnapshot = function () {
  2654. var rng = range.create(this.editable);
  2655. var emptyBookmark = { s: { path: [], offset: 0 }, e: { path: [], offset: 0 } };
  2656. return {
  2657. contents: this.$editable.html(),
  2658. bookmark: (rng ? rng.bookmark(this.editable) : emptyBookmark)
  2659. };
  2660. };
  2661. History.prototype.applySnapshot = function (snapshot) {
  2662. if (snapshot.contents !== null) {
  2663. this.$editable.html(snapshot.contents);
  2664. }
  2665. if (snapshot.bookmark !== null) {
  2666. range.createFromBookmark(this.editable, snapshot.bookmark).select();
  2667. }
  2668. };
  2669. /**
  2670. * @method rewind
  2671. * Rewinds the history stack back to the first snapshot taken.
  2672. * Leaves the stack intact, so that "Redo" can still be used.
  2673. */
  2674. History.prototype.rewind = function () {
  2675. // Create snap shot if not yet recorded
  2676. if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
  2677. this.recordUndo();
  2678. }
  2679. // Return to the first available snapshot.
  2680. this.stackOffset = 0;
  2681. // Apply that snapshot.
  2682. this.applySnapshot(this.stack[this.stackOffset]);
  2683. };
  2684. /**
  2685. * @method reset
  2686. * Resets the history stack completely; reverting to an empty editor.
  2687. */
  2688. History.prototype.reset = function () {
  2689. // Clear the stack.
  2690. this.stack = [];
  2691. // Restore stackOffset to its original value.
  2692. this.stackOffset = -1;
  2693. // Clear the editable area.
  2694. this.$editable.html('');
  2695. // Record our first snapshot (of nothing).
  2696. this.recordUndo();
  2697. };
  2698. /**
  2699. * undo
  2700. */
  2701. History.prototype.undo = function () {
  2702. // Create snap shot if not yet recorded
  2703. if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
  2704. this.recordUndo();
  2705. }
  2706. if (this.stackOffset > 0) {
  2707. this.stackOffset--;
  2708. this.applySnapshot(this.stack[this.stackOffset]);
  2709. }
  2710. };
  2711. /**
  2712. * redo
  2713. */
  2714. History.prototype.redo = function () {
  2715. if (this.stack.length - 1 > this.stackOffset) {
  2716. this.stackOffset++;
  2717. this.applySnapshot(this.stack[this.stackOffset]);
  2718. }
  2719. };
  2720. /**
  2721. * recorded undo
  2722. */
  2723. History.prototype.recordUndo = function () {
  2724. this.stackOffset++;
  2725. // Wash out stack after stackOffset
  2726. if (this.stack.length > this.stackOffset) {
  2727. this.stack = this.stack.slice(0, this.stackOffset);
  2728. }
  2729. // Create new snapshot and push it to the end
  2730. this.stack.push(this.makeSnapshot());
  2731. };
  2732. return History;
  2733. }());
  2734. var Style = /** @class */ (function () {
  2735. function Style() {
  2736. }
  2737. /**
  2738. * @method jQueryCSS
  2739. *
  2740. * [workaround] for old jQuery
  2741. * passing an array of style properties to .css()
  2742. * will result in an object of property-value pairs.
  2743. * (compability with version < 1.9)
  2744. *
  2745. * @private
  2746. * @param {jQuery} $obj
  2747. * @param {Array} propertyNames - An array of one or more CSS properties.
  2748. * @return {Object}
  2749. */
  2750. Style.prototype.jQueryCSS = function ($obj, propertyNames) {
  2751. if (env.jqueryVersion < 1.9) {
  2752. var result_1 = {};
  2753. $$1.each(propertyNames, function (idx, propertyName) {
  2754. result_1[propertyName] = $obj.css(propertyName);
  2755. });
  2756. return result_1;
  2757. }
  2758. return $obj.css(propertyNames);
  2759. };
  2760. /**
  2761. * returns style object from node
  2762. *
  2763. * @param {jQuery} $node
  2764. * @return {Object}
  2765. */
  2766. Style.prototype.fromNode = function ($node) {
  2767. var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
  2768. var styleInfo = this.jQueryCSS($node, properties) || {};
  2769. styleInfo['font-size'] = parseInt(styleInfo['font-size'], 10);
  2770. return styleInfo;
  2771. };
  2772. /**
  2773. * paragraph level style
  2774. *
  2775. * @param {WrappedRange} rng
  2776. * @param {Object} styleInfo
  2777. */
  2778. Style.prototype.stylePara = function (rng, styleInfo) {
  2779. $$1.each(rng.nodes(dom.isPara, {
  2780. includeAncestor: true
  2781. }), function (idx, para) {
  2782. $$1(para).css(styleInfo);
  2783. });
  2784. };
  2785. /**
  2786. * insert and returns styleNodes on range.
  2787. *
  2788. * @param {WrappedRange} rng
  2789. * @param {Object} [options] - options for styleNodes
  2790. * @param {String} [options.nodeName] - default: `SPAN`
  2791. * @param {Boolean} [options.expandClosestSibling] - default: `false`
  2792. * @param {Boolean} [options.onlyPartialContains] - default: `false`
  2793. * @return {Node[]}
  2794. */
  2795. Style.prototype.styleNodes = function (rng, options) {
  2796. rng = rng.splitText();
  2797. var nodeName = (options && options.nodeName) || 'SPAN';
  2798. var expandClosestSibling = !!(options && options.expandClosestSibling);
  2799. var onlyPartialContains = !!(options && options.onlyPartialContains);
  2800. if (rng.isCollapsed()) {
  2801. return [rng.insertNode(dom.create(nodeName))];
  2802. }
  2803. var pred = dom.makePredByNodeName(nodeName);
  2804. var nodes = rng.nodes(dom.isText, {
  2805. fullyContains: true
  2806. }).map(function (text) {
  2807. return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
  2808. });
  2809. if (expandClosestSibling) {
  2810. if (onlyPartialContains) {
  2811. var nodesInRange_1 = rng.nodes();
  2812. // compose with partial contains predication
  2813. pred = func.and(pred, function (node) {
  2814. return lists.contains(nodesInRange_1, node);
  2815. });
  2816. }
  2817. return nodes.map(function (node) {
  2818. var siblings = dom.withClosestSiblings(node, pred);
  2819. var head = lists.head(siblings);
  2820. var tails = lists.tail(siblings);
  2821. $$1.each(tails, function (idx, elem) {
  2822. dom.appendChildNodes(head, elem.childNodes);
  2823. dom.remove(elem);
  2824. });
  2825. return lists.head(siblings);
  2826. });
  2827. }
  2828. else {
  2829. return nodes;
  2830. }
  2831. };
  2832. /**
  2833. * get current style on cursor
  2834. *
  2835. * @param {WrappedRange} rng
  2836. * @return {Object} - object contains style properties.
  2837. */
  2838. Style.prototype.current = function (rng) {
  2839. var $cont = $$1(!dom.isElement(rng.sc) ? rng.sc.parentNode : rng.sc);
  2840. var styleInfo = this.fromNode($cont);
  2841. // document.queryCommandState for toggle state
  2842. // [workaround] prevent Firefox nsresult: "0x80004005 (NS_ERROR_FAILURE)"
  2843. try {
  2844. styleInfo = $$1.extend(styleInfo, {
  2845. 'font-bold': document.queryCommandState('bold') ? 'bold' : 'normal',
  2846. 'font-italic': document.queryCommandState('italic') ? 'italic' : 'normal',
  2847. 'font-underline': document.queryCommandState('underline') ? 'underline' : 'normal',
  2848. 'font-subscript': document.queryCommandState('subscript') ? 'subscript' : 'normal',
  2849. 'font-superscript': document.queryCommandState('superscript') ? 'superscript' : 'normal',
  2850. 'font-strikethrough': document.queryCommandState('strikethrough') ? 'strikethrough' : 'normal',
  2851. 'font-family': document.queryCommandValue('fontname') || styleInfo['font-family']
  2852. });
  2853. }
  2854. catch (e) { }
  2855. // list-style-type to list-style(unordered, ordered)
  2856. if (!rng.isOnList()) {
  2857. styleInfo['list-style'] = 'none';
  2858. }
  2859. else {
  2860. var orderedTypes = ['circle', 'disc', 'disc-leading-zero', 'square'];
  2861. var isUnordered = $$1.inArray(styleInfo['list-style-type'], orderedTypes) > -1;
  2862. styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
  2863. }
  2864. var para = dom.ancestor(rng.sc, dom.isPara);
  2865. if (para && para.style['line-height']) {
  2866. styleInfo['line-height'] = para.style.lineHeight;
  2867. }
  2868. else {
  2869. var lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
  2870. styleInfo['line-height'] = lineHeight.toFixed(1);
  2871. }
  2872. styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
  2873. styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
  2874. styleInfo.range = rng;
  2875. return styleInfo;
  2876. };
  2877. return Style;
  2878. }());
  2879. var Bullet = /** @class */ (function () {
  2880. function Bullet() {
  2881. }
  2882. /**
  2883. * toggle ordered list
  2884. */
  2885. Bullet.prototype.insertOrderedList = function (editable) {
  2886. this.toggleList('OL', editable);
  2887. };
  2888. /**
  2889. * toggle unordered list
  2890. */
  2891. Bullet.prototype.insertUnorderedList = function (editable) {
  2892. this.toggleList('UL', editable);
  2893. };
  2894. /**
  2895. * indent
  2896. */
  2897. Bullet.prototype.indent = function (editable) {
  2898. var _this = this;
  2899. var rng = range.create(editable).wrapBodyInlineWithPara();
  2900. var paras = rng.nodes(dom.isPara, { includeAncestor: true });
  2901. var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
  2902. $$1.each(clustereds, function (idx, paras) {
  2903. var head = lists.head(paras);
  2904. if (dom.isLi(head)) {
  2905. _this.wrapList(paras, head.parentNode.nodeName);
  2906. }
  2907. else {
  2908. $$1.each(paras, function (idx, para) {
  2909. $$1(para).css('marginLeft', function (idx, val) {
  2910. return (parseInt(val, 10) || 0) + 25;
  2911. });
  2912. });
  2913. }
  2914. });
  2915. rng.select();
  2916. };
  2917. /**
  2918. * outdent
  2919. */
  2920. Bullet.prototype.outdent = function (editable) {
  2921. var _this = this;
  2922. var rng = range.create(editable).wrapBodyInlineWithPara();
  2923. var paras = rng.nodes(dom.isPara, { includeAncestor: true });
  2924. var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
  2925. $$1.each(clustereds, function (idx, paras) {
  2926. var head = lists.head(paras);
  2927. if (dom.isLi(head)) {
  2928. _this.releaseList([paras]);
  2929. }
  2930. else {
  2931. $$1.each(paras, function (idx, para) {
  2932. $$1(para).css('marginLeft', function (idx, val) {
  2933. val = (parseInt(val, 10) || 0);
  2934. return val > 25 ? val - 25 : '';
  2935. });
  2936. });
  2937. }
  2938. });
  2939. rng.select();
  2940. };
  2941. /**
  2942. * toggle list
  2943. *
  2944. * @param {String} listName - OL or UL
  2945. */
  2946. Bullet.prototype.toggleList = function (listName, editable) {
  2947. var _this = this;
  2948. var rng = range.create(editable).wrapBodyInlineWithPara();
  2949. var paras = rng.nodes(dom.isPara, { includeAncestor: true });
  2950. var bookmark = rng.paraBookmark(paras);
  2951. var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
  2952. // paragraph to list
  2953. if (lists.find(paras, dom.isPurePara)) {
  2954. var wrappedParas_1 = [];
  2955. $$1.each(clustereds, function (idx, paras) {
  2956. wrappedParas_1 = wrappedParas_1.concat(_this.wrapList(paras, listName));
  2957. });
  2958. paras = wrappedParas_1;
  2959. // list to paragraph or change list style
  2960. }
  2961. else {
  2962. var diffLists = rng.nodes(dom.isList, {
  2963. includeAncestor: true
  2964. }).filter(function (listNode) {
  2965. return !$$1.nodeName(listNode, listName);
  2966. });
  2967. if (diffLists.length) {
  2968. $$1.each(diffLists, function (idx, listNode) {
  2969. dom.replace(listNode, listName);
  2970. });
  2971. }
  2972. else {
  2973. paras = this.releaseList(clustereds, true);
  2974. }
  2975. }
  2976. range.createFromParaBookmark(bookmark, paras).select();
  2977. };
  2978. /**
  2979. * @param {Node[]} paras
  2980. * @param {String} listName
  2981. * @return {Node[]}
  2982. */
  2983. Bullet.prototype.wrapList = function (paras, listName) {
  2984. var head = lists.head(paras);
  2985. var last = lists.last(paras);
  2986. var prevList = dom.isList(head.previousSibling) && head.previousSibling;
  2987. var nextList = dom.isList(last.nextSibling) && last.nextSibling;
  2988. var listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
  2989. // P to LI
  2990. paras = paras.map(function (para) {
  2991. return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
  2992. });
  2993. // append to list(<ul>, <ol>)
  2994. dom.appendChildNodes(listNode, paras);
  2995. if (nextList) {
  2996. dom.appendChildNodes(listNode, lists.from(nextList.childNodes));
  2997. dom.remove(nextList);
  2998. }
  2999. return paras;
  3000. };
  3001. /**
  3002. * @method releaseList
  3003. *
  3004. * @param {Array[]} clustereds
  3005. * @param {Boolean} isEscapseToBody
  3006. * @return {Node[]}
  3007. */
  3008. Bullet.prototype.releaseList = function (clustereds, isEscapseToBody) {
  3009. var releasedParas = [];
  3010. $$1.each(clustereds, function (idx, paras) {
  3011. var head = lists.head(paras);
  3012. var last = lists.last(paras);
  3013. var headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) : head.parentNode;
  3014. var lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
  3015. node: last.parentNode,
  3016. offset: dom.position(last) + 1
  3017. }, {
  3018. isSkipPaddingBlankHTML: true
  3019. }) : null;
  3020. var middleList = dom.splitTree(headList, {
  3021. node: head.parentNode,
  3022. offset: dom.position(head)
  3023. }, {
  3024. isSkipPaddingBlankHTML: true
  3025. });
  3026. paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi)
  3027. : lists.from(middleList.childNodes).filter(dom.isLi);
  3028. // LI to P
  3029. if (isEscapseToBody || !dom.isList(headList.parentNode)) {
  3030. paras = paras.map(function (para) {
  3031. return dom.replace(para, 'P');
  3032. });
  3033. }
  3034. $$1.each(lists.from(paras).reverse(), function (idx, para) {
  3035. dom.insertAfter(para, headList);
  3036. });
  3037. // remove empty lists
  3038. var rootLists = lists.compact([headList, middleList, lastList]);
  3039. $$1.each(rootLists, function (idx, rootList) {
  3040. var listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
  3041. $$1.each(listNodes.reverse(), function (idx, listNode) {
  3042. if (!dom.nodeLength(listNode)) {
  3043. dom.remove(listNode, true);
  3044. }
  3045. });
  3046. });
  3047. releasedParas = releasedParas.concat(paras);
  3048. });
  3049. return releasedParas;
  3050. };
  3051. return Bullet;
  3052. }());
  3053. /**
  3054. * @class editing.Typing
  3055. *
  3056. * Typing
  3057. *
  3058. */
  3059. var Typing = /** @class */ (function () {
  3060. function Typing() {
  3061. // a Bullet instance to toggle lists off
  3062. this.bullet = new Bullet();
  3063. }
  3064. /**
  3065. * insert tab
  3066. *
  3067. * @param {WrappedRange} rng
  3068. * @param {Number} tabsize
  3069. */
  3070. Typing.prototype.insertTab = function (rng, tabsize) {
  3071. var tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
  3072. rng = rng.deleteContents();
  3073. rng.insertNode(tab, true);
  3074. rng = range.create(tab, tabsize);
  3075. rng.select();
  3076. };
  3077. /**
  3078. * insert paragraph
  3079. */
  3080. Typing.prototype.insertParagraph = function (editable) {
  3081. var rng = range.create(editable);
  3082. // deleteContents on range.
  3083. rng = rng.deleteContents();
  3084. // Wrap range if it needs to be wrapped by paragraph
  3085. rng = rng.wrapBodyInlineWithPara();
  3086. // finding paragraph
  3087. var splitRoot = dom.ancestor(rng.sc, dom.isPara);
  3088. var nextPara;
  3089. // on paragraph: split paragraph
  3090. if (splitRoot) {
  3091. // if it is an empty line with li
  3092. if (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
  3093. // toogle UL/OL and escape
  3094. this.bullet.toggleList(splitRoot.parentNode.nodeName);
  3095. return;
  3096. // if it is an empty line with para on blockquote
  3097. }
  3098. else if (dom.isEmpty(splitRoot) && dom.isPara(splitRoot) && dom.isBlockquote(splitRoot.parentNode)) {
  3099. // escape blockquote
  3100. dom.insertAfter(splitRoot, splitRoot.parentNode);
  3101. nextPara = splitRoot;
  3102. // if new line has content (not a line break)
  3103. }
  3104. else {
  3105. nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
  3106. var emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
  3107. emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
  3108. $$1.each(emptyAnchors, function (idx, anchor) {
  3109. dom.remove(anchor);
  3110. });
  3111. // replace empty heading, pre or custom-made styleTag with P tag
  3112. if ((dom.isHeading(nextPara) || dom.isPre(nextPara) || dom.isCustomStyleTag(nextPara)) && dom.isEmpty(nextPara)) {
  3113. nextPara = dom.replace(nextPara, 'p');
  3114. }
  3115. }
  3116. // no paragraph: insert empty paragraph
  3117. }
  3118. else {
  3119. var next = rng.sc.childNodes[rng.so];
  3120. nextPara = $$1(dom.emptyPara)[0];
  3121. if (next) {
  3122. rng.sc.insertBefore(nextPara, next);
  3123. }
  3124. else {
  3125. rng.sc.appendChild(nextPara);
  3126. }
  3127. }
  3128. range.create(nextPara, 0).normalize().select().scrollIntoView(editable);
  3129. };
  3130. return Typing;
  3131. }());
  3132. /**
  3133. * @class Create a virtual table to create what actions to do in change.
  3134. * @param {object} startPoint Cell selected to apply change.
  3135. * @param {enum} where Where change will be applied Row or Col. Use enum: TableResultAction.where
  3136. * @param {enum} action Action to be applied. Use enum: TableResultAction.requestAction
  3137. * @param {object} domTable Dom element of table to make changes.
  3138. */
  3139. var TableResultAction = function (startPoint, where, action, domTable) {
  3140. var _startPoint = { 'colPos': 0, 'rowPos': 0 };
  3141. var _virtualTable = [];
  3142. var _actionCellList = [];
  3143. /// ///////////////////////////////////////////
  3144. // Private functions
  3145. /// ///////////////////////////////////////////
  3146. /**
  3147. * Set the startPoint of action.
  3148. */
  3149. function setStartPoint() {
  3150. if (!startPoint || !startPoint.tagName || (startPoint.tagName.toLowerCase() !== 'td' && startPoint.tagName.toLowerCase() !== 'th')) {
  3151. console.error('Impossible to identify start Cell point.', startPoint);
  3152. return;
  3153. }
  3154. _startPoint.colPos = startPoint.cellIndex;
  3155. if (!startPoint.parentElement || !startPoint.parentElement.tagName || startPoint.parentElement.tagName.toLowerCase() !== 'tr') {
  3156. console.error('Impossible to identify start Row point.', startPoint);
  3157. return;
  3158. }
  3159. _startPoint.rowPos = startPoint.parentElement.rowIndex;
  3160. }
  3161. /**
  3162. * Define virtual table position info object.
  3163. *
  3164. * @param {int} rowIndex Index position in line of virtual table.
  3165. * @param {int} cellIndex Index position in column of virtual table.
  3166. * @param {object} baseRow Row affected by this position.
  3167. * @param {object} baseCell Cell affected by this position.
  3168. * @param {bool} isSpan Inform if it is an span cell/row.
  3169. */
  3170. function setVirtualTablePosition(rowIndex, cellIndex, baseRow, baseCell, isRowSpan, isColSpan, isVirtualCell) {
  3171. var objPosition = {
  3172. 'baseRow': baseRow,
  3173. 'baseCell': baseCell,
  3174. 'isRowSpan': isRowSpan,
  3175. 'isColSpan': isColSpan,
  3176. 'isVirtual': isVirtualCell
  3177. };
  3178. if (!_virtualTable[rowIndex]) {
  3179. _virtualTable[rowIndex] = [];
  3180. }
  3181. _virtualTable[rowIndex][cellIndex] = objPosition;
  3182. }
  3183. /**
  3184. * Create action cell object.
  3185. *
  3186. * @param {object} virtualTableCellObj Object of specific position on virtual table.
  3187. * @param {enum} resultAction Action to be applied in that item.
  3188. */
  3189. function getActionCell(virtualTableCellObj, resultAction, virtualRowPosition, virtualColPosition) {
  3190. return {
  3191. 'baseCell': virtualTableCellObj.baseCell,
  3192. 'action': resultAction,
  3193. 'virtualTable': {
  3194. 'rowIndex': virtualRowPosition,
  3195. 'cellIndex': virtualColPosition
  3196. }
  3197. };
  3198. }
  3199. /**
  3200. * Recover free index of row to append Cell.
  3201. *
  3202. * @param {int} rowIndex Index of row to find free space.
  3203. * @param {int} cellIndex Index of cell to find free space in table.
  3204. */
  3205. function recoverCellIndex(rowIndex, cellIndex) {
  3206. if (!_virtualTable[rowIndex]) {
  3207. return cellIndex;
  3208. }
  3209. if (!_virtualTable[rowIndex][cellIndex]) {
  3210. return cellIndex;
  3211. }
  3212. var newCellIndex = cellIndex;
  3213. while (_virtualTable[rowIndex][newCellIndex]) {
  3214. newCellIndex++;
  3215. if (!_virtualTable[rowIndex][newCellIndex]) {
  3216. return newCellIndex;
  3217. }
  3218. }
  3219. }
  3220. /**
  3221. * Recover info about row and cell and add information to virtual table.
  3222. *
  3223. * @param {object} row Row to recover information.
  3224. * @param {object} cell Cell to recover information.
  3225. */
  3226. function addCellInfoToVirtual(row, cell) {
  3227. var cellIndex = recoverCellIndex(row.rowIndex, cell.cellIndex);
  3228. var cellHasColspan = (cell.colSpan > 1);
  3229. var cellHasRowspan = (cell.rowSpan > 1);
  3230. var isThisSelectedCell = (row.rowIndex === _startPoint.rowPos && cell.cellIndex === _startPoint.colPos);
  3231. setVirtualTablePosition(row.rowIndex, cellIndex, row, cell, cellHasRowspan, cellHasColspan, false);
  3232. // Add span rows to virtual Table.
  3233. var rowspanNumber = cell.attributes.rowSpan ? parseInt(cell.attributes.rowSpan.value, 10) : 0;
  3234. if (rowspanNumber > 1) {
  3235. for (var rp = 1; rp < rowspanNumber; rp++) {
  3236. var rowspanIndex = row.rowIndex + rp;
  3237. adjustStartPoint(rowspanIndex, cellIndex, cell, isThisSelectedCell);
  3238. setVirtualTablePosition(rowspanIndex, cellIndex, row, cell, true, cellHasColspan, true);
  3239. }
  3240. }
  3241. // Add span cols to virtual table.
  3242. var colspanNumber = cell.attributes.colSpan ? parseInt(cell.attributes.colSpan.value, 10) : 0;
  3243. if (colspanNumber > 1) {
  3244. for (var cp = 1; cp < colspanNumber; cp++) {
  3245. var cellspanIndex = recoverCellIndex(row.rowIndex, (cellIndex + cp));
  3246. adjustStartPoint(row.rowIndex, cellspanIndex, cell, isThisSelectedCell);
  3247. setVirtualTablePosition(row.rowIndex, cellspanIndex, row, cell, cellHasRowspan, true, true);
  3248. }
  3249. }
  3250. }
  3251. /**
  3252. * Process validation and adjust of start point if needed
  3253. *
  3254. * @param {int} rowIndex
  3255. * @param {int} cellIndex
  3256. * @param {object} cell
  3257. * @param {bool} isSelectedCell
  3258. */
  3259. function adjustStartPoint(rowIndex, cellIndex, cell, isSelectedCell) {
  3260. if (rowIndex === _startPoint.rowPos && _startPoint.colPos >= cell.cellIndex && cell.cellIndex <= cellIndex && !isSelectedCell) {
  3261. _startPoint.colPos++;
  3262. }
  3263. }
  3264. /**
  3265. * Create virtual table of cells with all cells, including span cells.
  3266. */
  3267. function createVirtualTable() {
  3268. var rows = domTable.rows;
  3269. for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) {
  3270. var cells = rows[rowIndex].cells;
  3271. for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) {
  3272. addCellInfoToVirtual(rows[rowIndex], cells[cellIndex]);
  3273. }
  3274. }
  3275. }
  3276. /**
  3277. * Get action to be applied on the cell.
  3278. *
  3279. * @param {object} cell virtual table cell to apply action
  3280. */
  3281. function getDeleteResultActionToCell(cell) {
  3282. switch (where) {
  3283. case TableResultAction.where.Column:
  3284. if (cell.isColSpan) {
  3285. return TableResultAction.resultAction.SubtractSpanCount;
  3286. }
  3287. break;
  3288. case TableResultAction.where.Row:
  3289. if (!cell.isVirtual && cell.isRowSpan) {
  3290. return TableResultAction.resultAction.AddCell;
  3291. }
  3292. else if (cell.isRowSpan) {
  3293. return TableResultAction.resultAction.SubtractSpanCount;
  3294. }
  3295. break;
  3296. }
  3297. return TableResultAction.resultAction.RemoveCell;
  3298. }
  3299. /**
  3300. * Get action to be applied on the cell.
  3301. *
  3302. * @param {object} cell virtual table cell to apply action
  3303. */
  3304. function getAddResultActionToCell(cell) {
  3305. switch (where) {
  3306. case TableResultAction.where.Column:
  3307. if (cell.isColSpan) {
  3308. return TableResultAction.resultAction.SumSpanCount;
  3309. }
  3310. else if (cell.isRowSpan && cell.isVirtual) {
  3311. return TableResultAction.resultAction.Ignore;
  3312. }
  3313. break;
  3314. case TableResultAction.where.Row:
  3315. if (cell.isRowSpan) {
  3316. return TableResultAction.resultAction.SumSpanCount;
  3317. }
  3318. else if (cell.isColSpan && cell.isVirtual) {
  3319. return TableResultAction.resultAction.Ignore;
  3320. }
  3321. break;
  3322. }
  3323. return TableResultAction.resultAction.AddCell;
  3324. }
  3325. function init() {
  3326. setStartPoint();
  3327. createVirtualTable();
  3328. }
  3329. /// ///////////////////////////////////////////
  3330. // Public functions
  3331. /// ///////////////////////////////////////////
  3332. /**
  3333. * Recover array os what to do in table.
  3334. */
  3335. this.getActionList = function () {
  3336. var fixedRow = (where === TableResultAction.where.Row) ? _startPoint.rowPos : -1;
  3337. var fixedCol = (where === TableResultAction.where.Column) ? _startPoint.colPos : -1;
  3338. var actualPosition = 0;
  3339. var canContinue = true;
  3340. while (canContinue) {
  3341. var rowPosition = (fixedRow >= 0) ? fixedRow : actualPosition;
  3342. var colPosition = (fixedCol >= 0) ? fixedCol : actualPosition;
  3343. var row = _virtualTable[rowPosition];
  3344. if (!row) {
  3345. canContinue = false;
  3346. return _actionCellList;
  3347. }
  3348. var cell = row[colPosition];
  3349. if (!cell) {
  3350. canContinue = false;
  3351. return _actionCellList;
  3352. }
  3353. // Define action to be applied in this cell
  3354. var resultAction = TableResultAction.resultAction.Ignore;
  3355. switch (action) {
  3356. case TableResultAction.requestAction.Add:
  3357. resultAction = getAddResultActionToCell(cell);
  3358. break;
  3359. case TableResultAction.requestAction.Delete:
  3360. resultAction = getDeleteResultActionToCell(cell);
  3361. break;
  3362. }
  3363. _actionCellList.push(getActionCell(cell, resultAction, rowPosition, colPosition));
  3364. actualPosition++;
  3365. }
  3366. return _actionCellList;
  3367. };
  3368. init();
  3369. };
  3370. /**
  3371. *
  3372. * Where action occours enum.
  3373. */
  3374. TableResultAction.where = { 'Row': 0, 'Column': 1 };
  3375. /**
  3376. *
  3377. * Requested action to apply enum.
  3378. */
  3379. TableResultAction.requestAction = { 'Add': 0, 'Delete': 1 };
  3380. /**
  3381. *
  3382. * Result action to be executed enum.
  3383. */
  3384. TableResultAction.resultAction = { 'Ignore': 0, 'SubtractSpanCount': 1, 'RemoveCell': 2, 'AddCell': 3, 'SumSpanCount': 4 };
  3385. /**
  3386. *
  3387. * @class editing.Table
  3388. *
  3389. * Table
  3390. *
  3391. */
  3392. var Table = /** @class */ (function () {
  3393. function Table() {
  3394. }
  3395. /**
  3396. * handle tab key
  3397. *
  3398. * @param {WrappedRange} rng
  3399. * @param {Boolean} isShift
  3400. */
  3401. Table.prototype.tab = function (rng, isShift) {
  3402. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3403. var table = dom.ancestor(cell, dom.isTable);
  3404. var cells = dom.listDescendant(table, dom.isCell);
  3405. var nextCell = lists[isShift ? 'prev' : 'next'](cells, cell);
  3406. if (nextCell) {
  3407. range.create(nextCell, 0).select();
  3408. }
  3409. };
  3410. /**
  3411. * Add a new row
  3412. *
  3413. * @param {WrappedRange} rng
  3414. * @param {String} position (top/bottom)
  3415. * @return {Node}
  3416. */
  3417. Table.prototype.addRow = function (rng, position) {
  3418. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3419. var currentTr = $$1(cell).closest('tr');
  3420. var trAttributes = this.recoverAttributes(currentTr);
  3421. var html = $$1('<tr' + trAttributes + '></tr>');
  3422. var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Add, $$1(currentTr).closest('table')[0]);
  3423. var actions = vTable.getActionList();
  3424. for (var idCell = 0; idCell < actions.length; idCell++) {
  3425. var currentCell = actions[idCell];
  3426. var tdAttributes = this.recoverAttributes(currentCell.baseCell);
  3427. switch (currentCell.action) {
  3428. case TableResultAction.resultAction.AddCell:
  3429. html.append('<td' + tdAttributes + '>' + dom.blank + '</td>');
  3430. break;
  3431. case TableResultAction.resultAction.SumSpanCount:
  3432. if (position === 'top') {
  3433. var baseCellTr = currentCell.baseCell.parent;
  3434. var isTopFromRowSpan = (!baseCellTr ? 0 : currentCell.baseCell.closest('tr').rowIndex) <= currentTr[0].rowIndex;
  3435. if (isTopFromRowSpan) {
  3436. var newTd = $$1('<div></div>').append($$1('<td' + tdAttributes + '>' + dom.blank + '</td>').removeAttr('rowspan')).html();
  3437. html.append(newTd);
  3438. break;
  3439. }
  3440. }
  3441. var rowspanNumber = parseInt(currentCell.baseCell.rowSpan, 10);
  3442. rowspanNumber++;
  3443. currentCell.baseCell.setAttribute('rowSpan', rowspanNumber);
  3444. break;
  3445. }
  3446. }
  3447. if (position === 'top') {
  3448. currentTr.before(html);
  3449. }
  3450. else {
  3451. var cellHasRowspan = (cell.rowSpan > 1);
  3452. if (cellHasRowspan) {
  3453. var lastTrIndex = currentTr[0].rowIndex + (cell.rowSpan - 2);
  3454. $$1($$1(currentTr).parent().find('tr')[lastTrIndex]).after($$1(html));
  3455. return;
  3456. }
  3457. currentTr.after(html);
  3458. }
  3459. };
  3460. /**
  3461. * Add a new col
  3462. *
  3463. * @param {WrappedRange} rng
  3464. * @param {String} position (left/right)
  3465. * @return {Node}
  3466. */
  3467. Table.prototype.addCol = function (rng, position) {
  3468. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3469. var row = $$1(cell).closest('tr');
  3470. var rowsGroup = $$1(row).siblings();
  3471. rowsGroup.push(row);
  3472. var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Add, $$1(row).closest('table')[0]);
  3473. var actions = vTable.getActionList();
  3474. for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
  3475. var currentCell = actions[actionIndex];
  3476. var tdAttributes = this.recoverAttributes(currentCell.baseCell);
  3477. switch (currentCell.action) {
  3478. case TableResultAction.resultAction.AddCell:
  3479. if (position === 'right') {
  3480. $$1(currentCell.baseCell).after('<td' + tdAttributes + '>' + dom.blank + '</td>');
  3481. }
  3482. else {
  3483. $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
  3484. }
  3485. break;
  3486. case TableResultAction.resultAction.SumSpanCount:
  3487. if (position === 'right') {
  3488. var colspanNumber = parseInt(currentCell.baseCell.colSpan, 10);
  3489. colspanNumber++;
  3490. currentCell.baseCell.setAttribute('colSpan', colspanNumber);
  3491. }
  3492. else {
  3493. $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
  3494. }
  3495. break;
  3496. }
  3497. }
  3498. };
  3499. /*
  3500. * Copy attributes from element.
  3501. *
  3502. * @param {object} Element to recover attributes.
  3503. * @return {string} Copied string elements.
  3504. */
  3505. Table.prototype.recoverAttributes = function (el) {
  3506. var resultStr = '';
  3507. if (!el) {
  3508. return resultStr;
  3509. }
  3510. var attrList = el.attributes || [];
  3511. for (var i = 0; i < attrList.length; i++) {
  3512. if (attrList[i].name.toLowerCase() === 'id') {
  3513. continue;
  3514. }
  3515. if (attrList[i].specified) {
  3516. resultStr += ' ' + attrList[i].name + '=\'' + attrList[i].value + '\'';
  3517. }
  3518. }
  3519. return resultStr;
  3520. };
  3521. /**
  3522. * Delete current row
  3523. *
  3524. * @param {WrappedRange} rng
  3525. * @return {Node}
  3526. */
  3527. Table.prototype.deleteRow = function (rng) {
  3528. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3529. var row = $$1(cell).closest('tr');
  3530. var cellPos = row.children('td, th').index($$1(cell));
  3531. var rowPos = row[0].rowIndex;
  3532. var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
  3533. var actions = vTable.getActionList();
  3534. for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
  3535. if (!actions[actionIndex]) {
  3536. continue;
  3537. }
  3538. var baseCell = actions[actionIndex].baseCell;
  3539. var virtualPosition = actions[actionIndex].virtualTable;
  3540. var hasRowspan = (baseCell.rowSpan && baseCell.rowSpan > 1);
  3541. var rowspanNumber = (hasRowspan) ? parseInt(baseCell.rowSpan, 10) : 0;
  3542. switch (actions[actionIndex].action) {
  3543. case TableResultAction.resultAction.Ignore:
  3544. continue;
  3545. case TableResultAction.resultAction.AddCell:
  3546. var nextRow = row.next('tr')[0];
  3547. if (!nextRow) {
  3548. continue;
  3549. }
  3550. var cloneRow = row[0].cells[cellPos];
  3551. if (hasRowspan) {
  3552. if (rowspanNumber > 2) {
  3553. rowspanNumber--;
  3554. nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
  3555. nextRow.cells[cellPos].setAttribute('rowSpan', rowspanNumber);
  3556. nextRow.cells[cellPos].innerHTML = '';
  3557. }
  3558. else if (rowspanNumber === 2) {
  3559. nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
  3560. nextRow.cells[cellPos].removeAttribute('rowSpan');
  3561. nextRow.cells[cellPos].innerHTML = '';
  3562. }
  3563. }
  3564. continue;
  3565. case TableResultAction.resultAction.SubtractSpanCount:
  3566. if (hasRowspan) {
  3567. if (rowspanNumber > 2) {
  3568. rowspanNumber--;
  3569. baseCell.setAttribute('rowSpan', rowspanNumber);
  3570. if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
  3571. baseCell.innerHTML = '';
  3572. }
  3573. }
  3574. else if (rowspanNumber === 2) {
  3575. baseCell.removeAttribute('rowSpan');
  3576. if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
  3577. baseCell.innerHTML = '';
  3578. }
  3579. }
  3580. }
  3581. continue;
  3582. case TableResultAction.resultAction.RemoveCell:
  3583. // Do not need remove cell because row will be deleted.
  3584. continue;
  3585. }
  3586. }
  3587. row.remove();
  3588. };
  3589. /**
  3590. * Delete current col
  3591. *
  3592. * @param {WrappedRange} rng
  3593. * @return {Node}
  3594. */
  3595. Table.prototype.deleteCol = function (rng) {
  3596. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3597. var row = $$1(cell).closest('tr');
  3598. var cellPos = row.children('td, th').index($$1(cell));
  3599. var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
  3600. var actions = vTable.getActionList();
  3601. for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
  3602. if (!actions[actionIndex]) {
  3603. continue;
  3604. }
  3605. switch (actions[actionIndex].action) {
  3606. case TableResultAction.resultAction.Ignore:
  3607. continue;
  3608. case TableResultAction.resultAction.SubtractSpanCount:
  3609. var baseCell = actions[actionIndex].baseCell;
  3610. var hasColspan = (baseCell.colSpan && baseCell.colSpan > 1);
  3611. if (hasColspan) {
  3612. var colspanNumber = (baseCell.colSpan) ? parseInt(baseCell.colSpan, 10) : 0;
  3613. if (colspanNumber > 2) {
  3614. colspanNumber--;
  3615. baseCell.setAttribute('colSpan', colspanNumber);
  3616. if (baseCell.cellIndex === cellPos) {
  3617. baseCell.innerHTML = '';
  3618. }
  3619. }
  3620. else if (colspanNumber === 2) {
  3621. baseCell.removeAttribute('colSpan');
  3622. if (baseCell.cellIndex === cellPos) {
  3623. baseCell.innerHTML = '';
  3624. }
  3625. }
  3626. }
  3627. continue;
  3628. case TableResultAction.resultAction.RemoveCell:
  3629. dom.remove(actions[actionIndex].baseCell, true);
  3630. continue;
  3631. }
  3632. }
  3633. };
  3634. /**
  3635. * create empty table element
  3636. *
  3637. * @param {Number} rowCount
  3638. * @param {Number} colCount
  3639. * @return {Node}
  3640. */
  3641. Table.prototype.createTable = function (colCount, rowCount, options) {
  3642. var tds = [];
  3643. var tdHTML;
  3644. for (var idxCol = 0; idxCol < colCount; idxCol++) {
  3645. tds.push('<td>' + dom.blank + '</td>');
  3646. }
  3647. tdHTML = tds.join('');
  3648. var trs = [];
  3649. var trHTML;
  3650. for (var idxRow = 0; idxRow < rowCount; idxRow++) {
  3651. trs.push('<tr>' + tdHTML + '</tr>');
  3652. }
  3653. trHTML = trs.join('');
  3654. var $table = $$1('<table>' + trHTML + '</table>');
  3655. if (options && options.tableClassName) {
  3656. $table.addClass(options.tableClassName);
  3657. }
  3658. return $table[0];
  3659. };
  3660. /**
  3661. * Delete current table
  3662. *
  3663. * @param {WrappedRange} rng
  3664. * @return {Node}
  3665. */
  3666. Table.prototype.deleteTable = function (rng) {
  3667. var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
  3668. $$1(cell).closest('table').remove();
  3669. };
  3670. return Table;
  3671. }());
  3672. var KEY_BOGUS = 'bogus';
  3673. /**
  3674. * @class Editor
  3675. */
  3676. var Editor = /** @class */ (function () {
  3677. function Editor(context) {
  3678. var _this = this;
  3679. this.context = context;
  3680. this.$note = context.layoutInfo.note;
  3681. this.$editor = context.layoutInfo.editor;
  3682. this.$editable = context.layoutInfo.editable;
  3683. this.options = context.options;
  3684. this.lang = this.options.langInfo;
  3685. this.editable = this.$editable[0];
  3686. this.lastRange = null;
  3687. this.style = new Style();
  3688. this.table = new Table();
  3689. this.typing = new Typing();
  3690. this.bullet = new Bullet();
  3691. this.history = new History(this.$editable);
  3692. this.context.memo('help.undo', this.lang.help.undo);
  3693. this.context.memo('help.redo', this.lang.help.redo);
  3694. this.context.memo('help.tab', this.lang.help.tab);
  3695. this.context.memo('help.untab', this.lang.help.untab);
  3696. this.context.memo('help.insertParagraph', this.lang.help.insertParagraph);
  3697. this.context.memo('help.insertOrderedList', this.lang.help.insertOrderedList);
  3698. this.context.memo('help.insertUnorderedList', this.lang.help.insertUnorderedList);
  3699. this.context.memo('help.indent', this.lang.help.indent);
  3700. this.context.memo('help.outdent', this.lang.help.outdent);
  3701. this.context.memo('help.formatPara', this.lang.help.formatPara);
  3702. this.context.memo('help.insertHorizontalRule', this.lang.help.insertHorizontalRule);
  3703. this.context.memo('help.fontName', this.lang.help.fontName);
  3704. // native commands(with execCommand), generate function for execCommand
  3705. var commands = [
  3706. 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
  3707. 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
  3708. 'formatBlock', 'removeFormat', 'backColor'
  3709. ];
  3710. for (var idx = 0, len = commands.length; idx < len; idx++) {
  3711. this[commands[idx]] = (function (sCmd) {
  3712. return function (value) {
  3713. _this.beforeCommand();
  3714. document.execCommand(sCmd, false, value);
  3715. _this.afterCommand(true);
  3716. };
  3717. })(commands[idx]);
  3718. this.context.memo('help.' + commands[idx], this.lang.help[commands[idx]]);
  3719. }
  3720. this.fontName = this.wrapCommand(function (value) {
  3721. return _this.fontStyling('font-family', "\'" + value + "\'");
  3722. });
  3723. this.fontSize = this.wrapCommand(function (value) {
  3724. return _this.fontStyling('font-size', value + 'px');
  3725. });
  3726. for (var idx = 1; idx <= 6; idx++) {
  3727. this['formatH' + idx] = (function (idx) {
  3728. return function () {
  3729. _this.formatBlock('H' + idx);
  3730. };
  3731. })(idx);
  3732. this.context.memo('help.formatH' + idx, this.lang.help['formatH' + idx]);
  3733. }
  3734. this.insertParagraph = this.wrapCommand(function () {
  3735. _this.typing.insertParagraph(_this.editable);
  3736. });
  3737. this.insertOrderedList = this.wrapCommand(function () {
  3738. _this.bullet.insertOrderedList(_this.editable);
  3739. });
  3740. this.insertUnorderedList = this.wrapCommand(function () {
  3741. _this.bullet.insertUnorderedList(_this.editable);
  3742. });
  3743. this.indent = this.wrapCommand(function () {
  3744. _this.bullet.indent(_this.editable);
  3745. });
  3746. this.outdent = this.wrapCommand(function () {
  3747. _this.bullet.outdent(_this.editable);
  3748. });
  3749. /**
  3750. * insertNode
  3751. * insert node
  3752. * @param {Node} node
  3753. */
  3754. this.insertNode = this.wrapCommand(function (node) {
  3755. if (_this.isLimited($$1(node).text().length)) {
  3756. return;
  3757. }
  3758. var rng = _this.createRange();
  3759. rng.insertNode(node);
  3760. range.createFromNodeAfter(node).select();
  3761. });
  3762. /**
  3763. * insert text
  3764. * @param {String} text
  3765. */
  3766. this.insertText = this.wrapCommand(function (text) {
  3767. if (_this.isLimited(text.length)) {
  3768. return;
  3769. }
  3770. var rng = _this.createRange();
  3771. var textNode = rng.insertNode(dom.createText(text));
  3772. range.create(textNode, dom.nodeLength(textNode)).select();
  3773. });
  3774. /**
  3775. * paste HTML
  3776. * @param {String} markup
  3777. */
  3778. this.pasteHTML = this.wrapCommand(function (markup) {
  3779. if (_this.isLimited(markup.length)) {
  3780. return;
  3781. }
  3782. var contents = _this.createRange().pasteHTML(markup);
  3783. range.createFromNodeAfter(lists.last(contents)).select();
  3784. });
  3785. /**
  3786. * formatBlock
  3787. *
  3788. * @param {String} tagName
  3789. */
  3790. this.formatBlock = this.wrapCommand(function (tagName, $target) {
  3791. var onApplyCustomStyle = _this.options.callbacks.onApplyCustomStyle;
  3792. if (onApplyCustomStyle) {
  3793. onApplyCustomStyle.call(_this, $target, _this.context, _this.onFormatBlock);
  3794. }
  3795. else {
  3796. _this.onFormatBlock(tagName, $target);
  3797. }
  3798. });
  3799. /**
  3800. * insert horizontal rule
  3801. */
  3802. this.insertHorizontalRule = this.wrapCommand(function () {
  3803. var hrNode = _this.createRange().insertNode(dom.create('HR'));
  3804. if (hrNode.nextSibling) {
  3805. range.create(hrNode.nextSibling, 0).normalize().select();
  3806. }
  3807. });
  3808. /**
  3809. * lineHeight
  3810. * @param {String} value
  3811. */
  3812. this.lineHeight = this.wrapCommand(function (value) {
  3813. _this.style.stylePara(_this.createRange(), {
  3814. lineHeight: value
  3815. });
  3816. });
  3817. /**
  3818. * create link (command)
  3819. *
  3820. * @param {Object} linkInfo
  3821. */
  3822. this.createLink = this.wrapCommand(function (linkInfo) {
  3823. var linkUrl = linkInfo.url;
  3824. var linkText = linkInfo.text;
  3825. var isNewWindow = linkInfo.isNewWindow;
  3826. var rng = linkInfo.range || _this.createRange();
  3827. var isTextChanged = rng.toString() !== linkText;
  3828. // handle spaced urls from input
  3829. if (typeof linkUrl === 'string') {
  3830. linkUrl = linkUrl.trim();
  3831. }
  3832. if (_this.options.onCreateLink) {
  3833. linkUrl = _this.options.onCreateLink(linkUrl);
  3834. }
  3835. else {
  3836. // if url doesn't match an URL schema, set http:// as default
  3837. linkUrl = /^[A-Za-z][A-Za-z0-9+-.]*\:[\/\/]?/.test(linkUrl)
  3838. ? linkUrl : 'http://' + linkUrl;
  3839. }
  3840. var anchors = [];
  3841. if (isTextChanged) {
  3842. rng = rng.deleteContents();
  3843. var anchor = rng.insertNode($$1('<A>' + linkText + '</A>')[0]);
  3844. anchors.push(anchor);
  3845. }
  3846. else {
  3847. anchors = _this.style.styleNodes(rng, {
  3848. nodeName: 'A',
  3849. expandClosestSibling: true,
  3850. onlyPartialContains: true
  3851. });
  3852. }
  3853. $$1.each(anchors, function (idx, anchor) {
  3854. $$1(anchor).attr('href', linkUrl);
  3855. if (isNewWindow) {
  3856. $$1(anchor).attr('target', '_blank');
  3857. }
  3858. else {
  3859. $$1(anchor).removeAttr('target');
  3860. }
  3861. });
  3862. var startRange = range.createFromNodeBefore(lists.head(anchors));
  3863. var startPoint = startRange.getStartPoint();
  3864. var endRange = range.createFromNodeAfter(lists.last(anchors));
  3865. var endPoint = endRange.getEndPoint();
  3866. range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset).select();
  3867. });
  3868. /**
  3869. * setting color
  3870. *
  3871. * @param {Object} sObjColor color code
  3872. * @param {String} sObjColor.foreColor foreground color
  3873. * @param {String} sObjColor.backColor background color
  3874. */
  3875. this.color = this.wrapCommand(function (colorInfo) {
  3876. var foreColor = colorInfo.foreColor;
  3877. var backColor = colorInfo.backColor;
  3878. if (foreColor) {
  3879. document.execCommand('foreColor', false, foreColor);
  3880. }
  3881. if (backColor) {
  3882. document.execCommand('backColor', false, backColor);
  3883. }
  3884. });
  3885. /**
  3886. * Set foreground color
  3887. *
  3888. * @param {String} colorCode foreground color code
  3889. */
  3890. this.foreColor = this.wrapCommand(function (colorInfo) {
  3891. document.execCommand('styleWithCSS', false, true);
  3892. document.execCommand('foreColor', false, colorInfo);
  3893. });
  3894. /**
  3895. * insert Table
  3896. *
  3897. * @param {String} dimension of table (ex : "5x5")
  3898. */
  3899. this.insertTable = this.wrapCommand(function (dim) {
  3900. var dimension = dim.split('x');
  3901. var rng = _this.createRange().deleteContents();
  3902. rng.insertNode(_this.table.createTable(dimension[0], dimension[1], _this.options));
  3903. });
  3904. /**
  3905. * remove media object and Figure Elements if media object is img with Figure.
  3906. */
  3907. this.removeMedia = this.wrapCommand(function () {
  3908. var $target = $$1(_this.restoreTarget()).parent();
  3909. if ($target.parent('figure').length) {
  3910. $target.parent('figure').remove();
  3911. }
  3912. else {
  3913. $target = $$1(_this.restoreTarget()).detach();
  3914. }
  3915. _this.context.triggerEvent('media.delete', $target, _this.$editable);
  3916. });
  3917. /**
  3918. * float me
  3919. *
  3920. * @param {String} value
  3921. */
  3922. this.floatMe = this.wrapCommand(function (value) {
  3923. var $target = $$1(_this.restoreTarget());
  3924. $target.toggleClass('note-float-left', value === 'left');
  3925. $target.toggleClass('note-float-right', value === 'right');
  3926. $target.css('float', value);
  3927. });
  3928. /**
  3929. * resize overlay element
  3930. * @param {String} value
  3931. */
  3932. this.resize = this.wrapCommand(function (value) {
  3933. var $target = $$1(_this.restoreTarget());
  3934. $target.css({
  3935. width: value * 100 + '%',
  3936. height: ''
  3937. });
  3938. });
  3939. }
  3940. Editor.prototype.initialize = function () {
  3941. var _this = this;
  3942. // bind custom events
  3943. this.$editable.on('keydown', function (event) {
  3944. if (event.keyCode === key.code.ENTER) {
  3945. _this.context.triggerEvent('enter', event);
  3946. }
  3947. _this.context.triggerEvent('keydown', event);
  3948. if (!event.isDefaultPrevented()) {
  3949. if (_this.options.shortcuts) {
  3950. _this.handleKeyMap(event);
  3951. }
  3952. else {
  3953. _this.preventDefaultEditableShortCuts(event);
  3954. }
  3955. }
  3956. if (_this.isLimited(1, event)) {
  3957. return false;
  3958. }
  3959. }).on('keyup', function (event) {
  3960. _this.context.triggerEvent('keyup', event);
  3961. }).on('focus', function (event) {
  3962. _this.context.triggerEvent('focus', event);
  3963. }).on('blur', function (event) {
  3964. _this.context.triggerEvent('blur', event);
  3965. }).on('mousedown', function (event) {
  3966. _this.context.triggerEvent('mousedown', event);
  3967. }).on('mouseup', function (event) {
  3968. _this.context.triggerEvent('mouseup', event);
  3969. }).on('scroll', function (event) {
  3970. _this.context.triggerEvent('scroll', event);
  3971. }).on('paste', function (event) {
  3972. _this.context.triggerEvent('paste', event);
  3973. });
  3974. // init content before set event
  3975. this.$editable.html(dom.html(this.$note) || dom.emptyPara);
  3976. this.$editable.on(env.inputEventName, func.debounce(function () {
  3977. _this.context.triggerEvent('change', _this.$editable.html());
  3978. }, 100));
  3979. this.$editor.on('focusin', function (event) {
  3980. _this.context.triggerEvent('focusin', event);
  3981. }).on('focusout', function (event) {
  3982. _this.context.triggerEvent('focusout', event);
  3983. });
  3984. if (!this.options.airMode) {
  3985. if (this.options.width) {
  3986. this.$editor.outerWidth(this.options.width);
  3987. }
  3988. if (this.options.height) {
  3989. this.$editable.outerHeight(this.options.height);
  3990. }
  3991. if (this.options.maxHeight) {
  3992. this.$editable.css('max-height', this.options.maxHeight);
  3993. }
  3994. if (this.options.minHeight) {
  3995. this.$editable.css('min-height', this.options.minHeight);
  3996. }
  3997. }
  3998. this.history.recordUndo();
  3999. };
  4000. Editor.prototype.destroy = function () {
  4001. this.$editable.off();
  4002. };
  4003. Editor.prototype.handleKeyMap = function (event) {
  4004. var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
  4005. var keys = [];
  4006. if (event.metaKey) {
  4007. keys.push('CMD');
  4008. }
  4009. if (event.ctrlKey && !event.altKey) {
  4010. keys.push('CTRL');
  4011. }
  4012. if (event.shiftKey) {
  4013. keys.push('SHIFT');
  4014. }
  4015. var keyName = key.nameFromCode[event.keyCode];
  4016. if (keyName) {
  4017. keys.push(keyName);
  4018. }
  4019. var eventName = keyMap[keys.join('+')];
  4020. if (eventName) {
  4021. if (this.context.invoke(eventName) !== false) {
  4022. event.preventDefault();
  4023. }
  4024. }
  4025. else if (key.isEdit(event.keyCode)) {
  4026. this.afterCommand();
  4027. }
  4028. };
  4029. Editor.prototype.preventDefaultEditableShortCuts = function (event) {
  4030. // B(Bold, 66) / I(Italic, 73) / U(Underline, 85)
  4031. if ((event.ctrlKey || event.metaKey) &&
  4032. lists.contains([66, 73, 85], event.keyCode)) {
  4033. event.preventDefault();
  4034. }
  4035. };
  4036. Editor.prototype.isLimited = function (pad, event) {
  4037. pad = pad || 0;
  4038. if (typeof event !== 'undefined') {
  4039. if (key.isMove(event.keyCode) ||
  4040. (event.ctrlKey || event.metaKey) ||
  4041. lists.contains([key.code.BACKSPACE, key.code.DELETE], event.keyCode)) {
  4042. return false;
  4043. }
  4044. }
  4045. if (this.options.maxTextLength > 0) {
  4046. if ((this.$editable.text().length + pad) >= this.options.maxTextLength) {
  4047. return true;
  4048. }
  4049. }
  4050. return false;
  4051. };
  4052. /**
  4053. * create range
  4054. * @return {WrappedRange}
  4055. */
  4056. Editor.prototype.createRange = function () {
  4057. this.focus();
  4058. return range.create(this.editable);
  4059. };
  4060. /**
  4061. * saveRange
  4062. *
  4063. * save current range
  4064. *
  4065. * @param {Boolean} [thenCollapse=false]
  4066. */
  4067. Editor.prototype.saveRange = function (thenCollapse) {
  4068. this.lastRange = this.createRange();
  4069. if (thenCollapse) {
  4070. this.lastRange.collapse().select();
  4071. }
  4072. };
  4073. /**
  4074. * restoreRange
  4075. *
  4076. * restore lately range
  4077. */
  4078. Editor.prototype.restoreRange = function () {
  4079. if (this.lastRange) {
  4080. this.lastRange.select();
  4081. this.focus();
  4082. }
  4083. };
  4084. Editor.prototype.saveTarget = function (node) {
  4085. this.$editable.data('target', node);
  4086. };
  4087. Editor.prototype.clearTarget = function () {
  4088. this.$editable.removeData('target');
  4089. };
  4090. Editor.prototype.restoreTarget = function () {
  4091. return this.$editable.data('target');
  4092. };
  4093. /**
  4094. * currentStyle
  4095. *
  4096. * current style
  4097. * @return {Object|Boolean} unfocus
  4098. */
  4099. Editor.prototype.currentStyle = function () {
  4100. var rng = range.create();
  4101. if (rng) {
  4102. rng = rng.normalize();
  4103. }
  4104. return rng ? this.style.current(rng) : this.style.fromNode(this.$editable);
  4105. };
  4106. /**
  4107. * style from node
  4108. *
  4109. * @param {jQuery} $node
  4110. * @return {Object}
  4111. */
  4112. Editor.prototype.styleFromNode = function ($node) {
  4113. return this.style.fromNode($node);
  4114. };
  4115. /**
  4116. * undo
  4117. */
  4118. Editor.prototype.undo = function () {
  4119. this.context.triggerEvent('before.command', this.$editable.html());
  4120. this.history.undo();
  4121. this.context.triggerEvent('change', this.$editable.html());
  4122. };
  4123. /**
  4124. * redo
  4125. */
  4126. Editor.prototype.redo = function () {
  4127. this.context.triggerEvent('before.command', this.$editable.html());
  4128. this.history.redo();
  4129. this.context.triggerEvent('change', this.$editable.html());
  4130. };
  4131. /**
  4132. * before command
  4133. */
  4134. Editor.prototype.beforeCommand = function () {
  4135. this.context.triggerEvent('before.command', this.$editable.html());
  4136. // keep focus on editable before command execution
  4137. this.focus();
  4138. };
  4139. /**
  4140. * after command
  4141. * @param {Boolean} isPreventTrigger
  4142. */
  4143. Editor.prototype.afterCommand = function (isPreventTrigger) {
  4144. this.history.recordUndo();
  4145. if (!isPreventTrigger) {
  4146. this.context.triggerEvent('change', this.$editable.html());
  4147. }
  4148. };
  4149. /**
  4150. * handle tab key
  4151. */
  4152. Editor.prototype.tab = function () {
  4153. var rng = this.createRange();
  4154. if (rng.isCollapsed() && rng.isOnCell()) {
  4155. this.table.tab(rng);
  4156. }
  4157. else {
  4158. if (this.options.tabSize === 0) {
  4159. return false;
  4160. }
  4161. if (!this.isLimited(this.options.tabSize)) {
  4162. this.beforeCommand();
  4163. this.typing.insertTab(rng, this.options.tabSize);
  4164. this.afterCommand();
  4165. }
  4166. }
  4167. };
  4168. /**
  4169. * handle shift+tab key
  4170. */
  4171. Editor.prototype.untab = function () {
  4172. var rng = this.createRange();
  4173. if (rng.isCollapsed() && rng.isOnCell()) {
  4174. this.table.tab(rng, true);
  4175. }
  4176. else {
  4177. if (this.options.tabSize === 0) {
  4178. return false;
  4179. }
  4180. }
  4181. };
  4182. /**
  4183. * run given function between beforeCommand and afterCommand
  4184. */
  4185. Editor.prototype.wrapCommand = function (fn) {
  4186. var _this = this;
  4187. return function () {
  4188. _this.beforeCommand();
  4189. fn.apply(_this, arguments);
  4190. _this.afterCommand();
  4191. };
  4192. };
  4193. /**
  4194. * insert image
  4195. *
  4196. * @param {String} src
  4197. * @param {String|Function} param
  4198. * @return {Promise}
  4199. */
  4200. Editor.prototype.insertImage = function (src, param) {
  4201. var _this = this;
  4202. return createImage(src, param).then(function ($image) {
  4203. _this.beforeCommand();
  4204. if (typeof param === 'function') {
  4205. param($image);
  4206. }
  4207. else {
  4208. if (typeof param === 'string') {
  4209. $image.attr('data-filename', param);
  4210. }
  4211. $image.css('width', Math.min(_this.$editable.width(), $image.width()));
  4212. }
  4213. $image.show();
  4214. range.create(_this.editable).insertNode($image[0]);
  4215. range.createFromNodeAfter($image[0]).select();
  4216. _this.afterCommand();
  4217. }).fail(function (e) {
  4218. _this.context.triggerEvent('image.upload.error', e);
  4219. });
  4220. };
  4221. /**
  4222. * insertImages
  4223. * @param {File[]} files
  4224. */
  4225. Editor.prototype.insertImages = function (files) {
  4226. var _this = this;
  4227. $$1.each(files, function (idx, file) {
  4228. var filename = file.name;
  4229. if (_this.options.maximumImageFileSize && _this.options.maximumImageFileSize < file.size) {
  4230. _this.context.triggerEvent('image.upload.error', _this.lang.image.maximumFileSizeError);
  4231. }
  4232. else {
  4233. readFileAsDataURL(file).then(function (dataURL) {
  4234. return _this.insertImage(dataURL, filename);
  4235. }).fail(function () {
  4236. _this.context.triggerEvent('image.upload.error');
  4237. });
  4238. }
  4239. });
  4240. };
  4241. /**
  4242. * insertImagesOrCallback
  4243. * @param {File[]} files
  4244. */
  4245. Editor.prototype.insertImagesOrCallback = function (files) {
  4246. var callbacks = this.options.callbacks;
  4247. // If onImageUpload this.options setted
  4248. if (callbacks.onImageUpload) {
  4249. this.context.triggerEvent('image.upload', files);
  4250. // else insert Image as dataURL
  4251. }
  4252. else {
  4253. this.insertImages(files);
  4254. }
  4255. };
  4256. /**
  4257. * return selected plain text
  4258. * @return {String} text
  4259. */
  4260. Editor.prototype.getSelectedText = function () {
  4261. var rng = this.createRange();
  4262. // if range on anchor, expand range with anchor
  4263. if (rng.isOnAnchor()) {
  4264. rng = range.createFromNode(dom.ancestor(rng.sc, dom.isAnchor));
  4265. }
  4266. return rng.toString();
  4267. };
  4268. Editor.prototype.onFormatBlock = function (tagName, $target) {
  4269. // [workaround] for MSIE, IE need `<`
  4270. tagName = env.isMSIE ? '<' + tagName + '>' : tagName;
  4271. document.execCommand('FormatBlock', false, tagName);
  4272. // support custom class
  4273. if ($target && $target.length) {
  4274. var className = $target[0].className || '';
  4275. if (className) {
  4276. var currentRange = this.createRange();
  4277. var $parent = $$1([currentRange.sc, currentRange.ec]).closest(tagName);
  4278. $parent.addClass(className);
  4279. }
  4280. }
  4281. };
  4282. Editor.prototype.formatPara = function () {
  4283. this.formatBlock('P');
  4284. };
  4285. Editor.prototype.fontStyling = function (target, value) {
  4286. var rng = this.createRange();
  4287. if (rng) {
  4288. var spans = this.style.styleNodes(rng);
  4289. $$1(spans).css(target, value);
  4290. // [workaround] added styled bogus span for style
  4291. // - also bogus character needed for cursor position
  4292. if (rng.isCollapsed()) {
  4293. var firstSpan = lists.head(spans);
  4294. if (firstSpan && !dom.nodeLength(firstSpan)) {
  4295. firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
  4296. range.createFromNodeAfter(firstSpan.firstChild).select();
  4297. this.$editable.data(KEY_BOGUS, firstSpan);
  4298. }
  4299. }
  4300. }
  4301. };
  4302. /**
  4303. * unlink
  4304. *
  4305. * @type command
  4306. */
  4307. Editor.prototype.unlink = function () {
  4308. var rng = this.createRange();
  4309. if (rng.isOnAnchor()) {
  4310. var anchor = dom.ancestor(rng.sc, dom.isAnchor);
  4311. rng = range.createFromNode(anchor);
  4312. rng.select();
  4313. this.beforeCommand();
  4314. document.execCommand('unlink');
  4315. this.afterCommand();
  4316. }
  4317. };
  4318. /**
  4319. * returns link info
  4320. *
  4321. * @return {Object}
  4322. * @return {WrappedRange} return.range
  4323. * @return {String} return.text
  4324. * @return {Boolean} [return.isNewWindow=true]
  4325. * @return {String} [return.url=""]
  4326. */
  4327. Editor.prototype.getLinkInfo = function () {
  4328. var rng = this.createRange().expand(dom.isAnchor);
  4329. // Get the first anchor on range(for edit).
  4330. var $anchor = $$1(lists.head(rng.nodes(dom.isAnchor)));
  4331. var linkInfo = {
  4332. range: rng,
  4333. text: rng.toString(),
  4334. url: $anchor.length ? $anchor.attr('href') : ''
  4335. };
  4336. // Define isNewWindow when anchor exists.
  4337. if ($anchor.length) {
  4338. linkInfo.isNewWindow = $anchor.attr('target') === '_blank';
  4339. }
  4340. return linkInfo;
  4341. };
  4342. Editor.prototype.addRow = function (position) {
  4343. var rng = this.createRange(this.$editable);
  4344. if (rng.isCollapsed() && rng.isOnCell()) {
  4345. this.beforeCommand();
  4346. this.table.addRow(rng, position);
  4347. this.afterCommand();
  4348. }
  4349. };
  4350. Editor.prototype.addCol = function (position) {
  4351. var rng = this.createRange(this.$editable);
  4352. if (rng.isCollapsed() && rng.isOnCell()) {
  4353. this.beforeCommand();
  4354. this.table.addCol(rng, position);
  4355. this.afterCommand();
  4356. }
  4357. };
  4358. Editor.prototype.deleteRow = function () {
  4359. var rng = this.createRange(this.$editable);
  4360. if (rng.isCollapsed() && rng.isOnCell()) {
  4361. this.beforeCommand();
  4362. this.table.deleteRow(rng);
  4363. this.afterCommand();
  4364. }
  4365. };
  4366. Editor.prototype.deleteCol = function () {
  4367. var rng = this.createRange(this.$editable);
  4368. if (rng.isCollapsed() && rng.isOnCell()) {
  4369. this.beforeCommand();
  4370. this.table.deleteCol(rng);
  4371. this.afterCommand();
  4372. }
  4373. };
  4374. Editor.prototype.deleteTable = function () {
  4375. var rng = this.createRange(this.$editable);
  4376. if (rng.isCollapsed() && rng.isOnCell()) {
  4377. this.beforeCommand();
  4378. this.table.deleteTable(rng);
  4379. this.afterCommand();
  4380. }
  4381. };
  4382. /**
  4383. * @param {Position} pos
  4384. * @param {jQuery} $target - target element
  4385. * @param {Boolean} [bKeepRatio] - keep ratio
  4386. */
  4387. Editor.prototype.resizeTo = function (pos, $target, bKeepRatio) {
  4388. var imageSize;
  4389. if (bKeepRatio) {
  4390. var newRatio = pos.y / pos.x;
  4391. var ratio = $target.data('ratio');
  4392. imageSize = {
  4393. width: ratio > newRatio ? pos.x : pos.y / ratio,
  4394. height: ratio > newRatio ? pos.x * ratio : pos.y
  4395. };
  4396. }
  4397. else {
  4398. imageSize = {
  4399. width: pos.x,
  4400. height: pos.y
  4401. };
  4402. }
  4403. $target.css(imageSize);
  4404. };
  4405. /**
  4406. * returns whether editable area has focus or not.
  4407. */
  4408. Editor.prototype.hasFocus = function () {
  4409. return this.$editable.is(':focus');
  4410. };
  4411. /**
  4412. * set focus
  4413. */
  4414. Editor.prototype.focus = function () {
  4415. // [workaround] Screen will move when page is scolled in IE.
  4416. // - do focus when not focused
  4417. if (!this.hasFocus()) {
  4418. this.$editable.focus();
  4419. }
  4420. };
  4421. /**
  4422. * returns whether contents is empty or not.
  4423. * @return {Boolean}
  4424. */
  4425. Editor.prototype.isEmpty = function () {
  4426. return dom.isEmpty(this.$editable[0]) || dom.emptyPara === this.$editable.html();
  4427. };
  4428. /**
  4429. * Removes all contents and restores the editable instance to an _emptyPara_.
  4430. */
  4431. Editor.prototype.empty = function () {
  4432. this.context.invoke('code', dom.emptyPara);
  4433. };
  4434. return Editor;
  4435. }());
  4436. var Clipboard = /** @class */ (function () {
  4437. function Clipboard(context) {
  4438. this.context = context;
  4439. this.$editable = context.layoutInfo.editable;
  4440. }
  4441. Clipboard.prototype.initialize = function () {
  4442. this.$editable.on('paste', this.pasteByEvent.bind(this));
  4443. };
  4444. /**
  4445. * paste by clipboard event
  4446. *
  4447. * @param {Event} event
  4448. */
  4449. Clipboard.prototype.pasteByEvent = function (event) {
  4450. var clipboardData = event.originalEvent.clipboardData;
  4451. if (clipboardData && clipboardData.items && clipboardData.items.length) {
  4452. var item = lists.head(clipboardData.items);
  4453. if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
  4454. this.context.invoke('editor.insertImagesOrCallback', [item.getAsFile()]);
  4455. }
  4456. this.context.invoke('editor.afterCommand');
  4457. }
  4458. };
  4459. return Clipboard;
  4460. }());
  4461. var Dropzone = /** @class */ (function () {
  4462. function Dropzone(context) {
  4463. this.context = context;
  4464. this.$eventListener = $$1(document);
  4465. this.$editor = context.layoutInfo.editor;
  4466. this.$editable = context.layoutInfo.editable;
  4467. this.options = context.options;
  4468. this.lang = this.options.langInfo;
  4469. this.documentEventHandlers = {};
  4470. this.$dropzone = $$1([
  4471. '<div class="note-dropzone">',
  4472. ' <div class="note-dropzone-message"/>',
  4473. '</div>'
  4474. ].join('')).prependTo(this.$editor);
  4475. }
  4476. /**
  4477. * attach Drag and Drop Events
  4478. */
  4479. Dropzone.prototype.initialize = function () {
  4480. if (this.options.disableDragAndDrop) {
  4481. // prevent default drop event
  4482. this.documentEventHandlers.onDrop = function (e) {
  4483. e.preventDefault();
  4484. };
  4485. // do not consider outside of dropzone
  4486. this.$eventListener = this.$dropzone;
  4487. this.$eventListener.on('drop', this.documentEventHandlers.onDrop);
  4488. }
  4489. else {
  4490. this.attachDragAndDropEvent();
  4491. }
  4492. };
  4493. /**
  4494. * attach Drag and Drop Events
  4495. */
  4496. Dropzone.prototype.attachDragAndDropEvent = function () {
  4497. var _this = this;
  4498. var collection = $$1();
  4499. var $dropzoneMessage = this.$dropzone.find('.note-dropzone-message');
  4500. this.documentEventHandlers.onDragenter = function (e) {
  4501. var isCodeview = _this.context.invoke('codeview.isActivated');
  4502. var hasEditorSize = _this.$editor.width() > 0 && _this.$editor.height() > 0;
  4503. if (!isCodeview && !collection.length && hasEditorSize) {
  4504. _this.$editor.addClass('dragover');
  4505. _this.$dropzone.width(_this.$editor.width());
  4506. _this.$dropzone.height(_this.$editor.height());
  4507. $dropzoneMessage.text(_this.lang.image.dragImageHere);
  4508. }
  4509. collection = collection.add(e.target);
  4510. };
  4511. this.documentEventHandlers.onDragleave = function (e) {
  4512. collection = collection.not(e.target);
  4513. if (!collection.length) {
  4514. _this.$editor.removeClass('dragover');
  4515. }
  4516. };
  4517. this.documentEventHandlers.onDrop = function () {
  4518. collection = $$1();
  4519. _this.$editor.removeClass('dragover');
  4520. };
  4521. // show dropzone on dragenter when dragging a object to document
  4522. // -but only if the editor is visible, i.e. has a positive width and height
  4523. this.$eventListener.on('dragenter', this.documentEventHandlers.onDragenter)
  4524. .on('dragleave', this.documentEventHandlers.onDragleave)
  4525. .on('drop', this.documentEventHandlers.onDrop);
  4526. // change dropzone's message on hover.
  4527. this.$dropzone.on('dragenter', function () {
  4528. _this.$dropzone.addClass('hover');
  4529. $dropzoneMessage.text(_this.lang.image.dropImage);
  4530. }).on('dragleave', function () {
  4531. _this.$dropzone.removeClass('hover');
  4532. $dropzoneMessage.text(_this.lang.image.dragImageHere);
  4533. });
  4534. // attach dropImage
  4535. this.$dropzone.on('drop', function (event) {
  4536. var dataTransfer = event.originalEvent.dataTransfer;
  4537. // stop the browser from opening the dropped content
  4538. event.preventDefault();
  4539. if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
  4540. _this.$editable.focus();
  4541. _this.context.invoke('editor.insertImagesOrCallback', dataTransfer.files);
  4542. }
  4543. else {
  4544. $$1.each(dataTransfer.types, function (idx, type) {
  4545. var content = dataTransfer.getData(type);
  4546. if (type.toLowerCase().indexOf('text') > -1) {
  4547. _this.context.invoke('editor.pasteHTML', content);
  4548. }
  4549. else {
  4550. $$1(content).each(function (idx, item) {
  4551. _this.context.invoke('editor.insertNode', item);
  4552. });
  4553. }
  4554. });
  4555. }
  4556. }).on('dragover', false); // prevent default dragover event
  4557. };
  4558. Dropzone.prototype.destroy = function () {
  4559. var _this = this;
  4560. Object.keys(this.documentEventHandlers).forEach(function (key) {
  4561. _this.$eventListener.off(key.substr(2).toLowerCase(), _this.documentEventHandlers[key]);
  4562. });
  4563. this.documentEventHandlers = {};
  4564. };
  4565. return Dropzone;
  4566. }());
  4567. var CodeMirror;
  4568. if (env.hasCodeMirror) {
  4569. if (env.isSupportAmd) {
  4570. require(['codemirror'], function (cm) {
  4571. CodeMirror = cm;
  4572. });
  4573. }
  4574. else {
  4575. CodeMirror = window.CodeMirror;
  4576. }
  4577. }
  4578. /**
  4579. * @class Codeview
  4580. */
  4581. var CodeView = /** @class */ (function () {
  4582. function CodeView(context) {
  4583. this.context = context;
  4584. this.$editor = context.layoutInfo.editor;
  4585. this.$editable = context.layoutInfo.editable;
  4586. this.$codable = context.layoutInfo.codable;
  4587. this.options = context.options;
  4588. }
  4589. CodeView.prototype.sync = function () {
  4590. var isCodeview = this.isActivated();
  4591. if (isCodeview && env.hasCodeMirror) {
  4592. this.$codable.data('cmEditor').save();
  4593. }
  4594. };
  4595. /**
  4596. * @return {Boolean}
  4597. */
  4598. CodeView.prototype.isActivated = function () {
  4599. return this.$editor.hasClass('codeview');
  4600. };
  4601. /**
  4602. * toggle codeview
  4603. */
  4604. CodeView.prototype.toggle = function () {
  4605. if (this.isActivated()) {
  4606. this.deactivate();
  4607. }
  4608. else {
  4609. this.activate();
  4610. }
  4611. this.context.triggerEvent('codeview.toggled');
  4612. };
  4613. /**
  4614. * activate code view
  4615. */
  4616. CodeView.prototype.activate = function () {
  4617. var _this = this;
  4618. this.$codable.val(dom.html(this.$editable, this.options.prettifyHtml));
  4619. this.$codable.height(this.$editable.height());
  4620. this.context.invoke('toolbar.updateCodeview', true);
  4621. this.$editor.addClass('codeview');
  4622. this.$codable.focus();
  4623. // activate CodeMirror as codable
  4624. if (env.hasCodeMirror) {
  4625. var cmEditor_1 = CodeMirror.fromTextArea(this.$codable[0], this.options.codemirror);
  4626. // CodeMirror TernServer
  4627. if (this.options.codemirror.tern) {
  4628. var server_1 = new CodeMirror.TernServer(this.options.codemirror.tern);
  4629. cmEditor_1.ternServer = server_1;
  4630. cmEditor_1.on('cursorActivity', function (cm) {
  4631. server_1.updateArgHints(cm);
  4632. });
  4633. }
  4634. cmEditor_1.on('blur', function (event) {
  4635. _this.context.triggerEvent('blur.codeview', cmEditor_1.getValue(), event);
  4636. });
  4637. // CodeMirror hasn't Padding.
  4638. cmEditor_1.setSize(null, this.$editable.outerHeight());
  4639. this.$codable.data('cmEditor', cmEditor_1);
  4640. }
  4641. else {
  4642. this.$codable.on('blur', function (event) {
  4643. _this.context.triggerEvent('blur.codeview', _this.$codable.val(), event);
  4644. });
  4645. }
  4646. };
  4647. /**
  4648. * deactivate code view
  4649. */
  4650. CodeView.prototype.deactivate = function () {
  4651. // deactivate CodeMirror as codable
  4652. if (env.hasCodeMirror) {
  4653. var cmEditor = this.$codable.data('cmEditor');
  4654. this.$codable.val(cmEditor.getValue());
  4655. cmEditor.toTextArea();
  4656. }
  4657. var value = dom.value(this.$codable, this.options.prettifyHtml) || dom.emptyPara;
  4658. var isChange = this.$editable.html() !== value;
  4659. this.$editable.html(value);
  4660. this.$editable.height(this.options.height ? this.$codable.height() : 'auto');
  4661. this.$editor.removeClass('codeview');
  4662. if (isChange) {
  4663. this.context.triggerEvent('change', this.$editable.html(), this.$editable);
  4664. }
  4665. this.$editable.focus();
  4666. this.context.invoke('toolbar.updateCodeview', false);
  4667. };
  4668. CodeView.prototype.destroy = function () {
  4669. if (this.isActivated()) {
  4670. this.deactivate();
  4671. }
  4672. };
  4673. return CodeView;
  4674. }());
  4675. var EDITABLE_PADDING = 24;
  4676. var Statusbar = /** @class */ (function () {
  4677. function Statusbar(context) {
  4678. this.$document = $$1(document);
  4679. this.$statusbar = context.layoutInfo.statusbar;
  4680. this.$editable = context.layoutInfo.editable;
  4681. this.options = context.options;
  4682. }
  4683. Statusbar.prototype.initialize = function () {
  4684. var _this = this;
  4685. if (this.options.airMode || this.options.disableResizeEditor) {
  4686. this.destroy();
  4687. return;
  4688. }
  4689. this.$statusbar.on('mousedown', function (event) {
  4690. event.preventDefault();
  4691. event.stopPropagation();
  4692. var editableTop = _this.$editable.offset().top - _this.$document.scrollTop();
  4693. var onMouseMove = function (event) {
  4694. var height = event.clientY - (editableTop + EDITABLE_PADDING);
  4695. height = (_this.options.minheight > 0) ? Math.max(height, _this.options.minheight) : height;
  4696. height = (_this.options.maxHeight > 0) ? Math.min(height, _this.options.maxHeight) : height;
  4697. _this.$editable.height(height);
  4698. };
  4699. _this.$document.on('mousemove', onMouseMove).one('mouseup', function () {
  4700. _this.$document.off('mousemove', onMouseMove);
  4701. });
  4702. });
  4703. };
  4704. Statusbar.prototype.destroy = function () {
  4705. this.$statusbar.off();
  4706. };
  4707. return Statusbar;
  4708. }());
  4709. var Fullscreen = /** @class */ (function () {
  4710. function Fullscreen(context) {
  4711. var _this = this;
  4712. this.context = context;
  4713. this.$editor = context.layoutInfo.editor;
  4714. this.$toolbar = context.layoutInfo.toolbar;
  4715. this.$editable = context.layoutInfo.editable;
  4716. this.$codable = context.layoutInfo.codable;
  4717. this.$window = $$1(window);
  4718. this.$scrollbar = $$1('html, body');
  4719. this.onResize = function () {
  4720. _this.resizeTo({
  4721. h: _this.$window.height() - _this.$toolbar.outerHeight()
  4722. });
  4723. };
  4724. }
  4725. Fullscreen.prototype.resizeTo = function (size) {
  4726. this.$editable.css('height', size.h);
  4727. this.$codable.css('height', size.h);
  4728. if (this.$codable.data('cmeditor')) {
  4729. this.$codable.data('cmeditor').setsize(null, size.h);
  4730. }
  4731. };
  4732. /**
  4733. * toggle fullscreen
  4734. */
  4735. Fullscreen.prototype.toggle = function () {
  4736. this.$editor.toggleClass('fullscreen');
  4737. if (this.isFullscreen()) {
  4738. this.$editable.data('orgHeight', this.$editable.css('height'));
  4739. this.$window.on('resize', this.onResize).trigger('resize');
  4740. this.$scrollbar.css('overflow', 'hidden');
  4741. }
  4742. else {
  4743. this.$window.off('resize', this.onResize);
  4744. this.resizeTo({ h: this.$editable.data('orgHeight') });
  4745. this.$scrollbar.css('overflow', 'visible');
  4746. }
  4747. this.context.invoke('toolbar.updateFullscreen', this.isFullscreen());
  4748. };
  4749. Fullscreen.prototype.isFullscreen = function () {
  4750. return this.$editor.hasClass('fullscreen');
  4751. };
  4752. return Fullscreen;
  4753. }());
  4754. var Handle = /** @class */ (function () {
  4755. function Handle(context) {
  4756. var _this = this;
  4757. this.context = context;
  4758. this.$document = $$1(document);
  4759. this.$editingArea = context.layoutInfo.editingArea;
  4760. this.options = context.options;
  4761. this.lang = this.options.langInfo;
  4762. this.events = {
  4763. 'summernote.mousedown': function (we, e) {
  4764. if (_this.update(e.target)) {
  4765. e.preventDefault();
  4766. }
  4767. },
  4768. 'summernote.keyup summernote.scroll summernote.change summernote.dialog.shown': function () {
  4769. _this.update();
  4770. },
  4771. 'summernote.disable': function () {
  4772. _this.hide();
  4773. },
  4774. 'summernote.codeview.toggled': function () {
  4775. _this.update();
  4776. }
  4777. };
  4778. }
  4779. Handle.prototype.initialize = function () {
  4780. var _this = this;
  4781. this.$handle = $$1([
  4782. '<div class="note-handle">',
  4783. '<div class="note-control-selection">',
  4784. '<div class="note-control-selection-bg"></div>',
  4785. '<div class="note-control-holder note-control-nw"></div>',
  4786. '<div class="note-control-holder note-control-ne"></div>',
  4787. '<div class="note-control-holder note-control-sw"></div>',
  4788. '<div class="',
  4789. (this.options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing'),
  4790. ' note-control-se"></div>',
  4791. (this.options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>'),
  4792. '</div>',
  4793. '</div>'
  4794. ].join('')).prependTo(this.$editingArea);
  4795. this.$handle.on('mousedown', function (event) {
  4796. if (dom.isControlSizing(event.target)) {
  4797. event.preventDefault();
  4798. event.stopPropagation();
  4799. var $target_1 = _this.$handle.find('.note-control-selection').data('target');
  4800. var posStart_1 = $target_1.offset();
  4801. var scrollTop_1 = _this.$document.scrollTop();
  4802. var onMouseMove_1 = function (event) {
  4803. _this.context.invoke('editor.resizeTo', {
  4804. x: event.clientX - posStart_1.left,
  4805. y: event.clientY - (posStart_1.top - scrollTop_1)
  4806. }, $target_1, !event.shiftKey);
  4807. _this.update($target_1[0]);
  4808. };
  4809. _this.$document
  4810. .on('mousemove', onMouseMove_1)
  4811. .one('mouseup', function (e) {
  4812. e.preventDefault();
  4813. _this.$document.off('mousemove', onMouseMove_1);
  4814. _this.context.invoke('editor.afterCommand');
  4815. });
  4816. if (!$target_1.data('ratio')) {
  4817. $target_1.data('ratio', $target_1.height() / $target_1.width());
  4818. }
  4819. }
  4820. });
  4821. // Listen for scrolling on the handle overlay.
  4822. this.$handle.on('wheel', function (e) {
  4823. e.preventDefault();
  4824. _this.update();
  4825. });
  4826. };
  4827. Handle.prototype.destroy = function () {
  4828. this.$handle.remove();
  4829. };
  4830. Handle.prototype.update = function (target) {
  4831. if (this.context.isDisabled()) {
  4832. return false;
  4833. }
  4834. var isImage = dom.isImg(target);
  4835. var $selection = this.$handle.find('.note-control-selection');
  4836. this.context.invoke('imagePopover.update', target);
  4837. if (isImage) {
  4838. var $image = $$1(target);
  4839. var position = $image.position();
  4840. var pos = {
  4841. left: position.left + parseInt($image.css('marginLeft'), 10),
  4842. top: position.top + parseInt($image.css('marginTop'), 10)
  4843. };
  4844. // exclude margin
  4845. var imageSize = {
  4846. w: $image.outerWidth(false),
  4847. h: $image.outerHeight(false)
  4848. };
  4849. $selection.css({
  4850. display: 'block',
  4851. left: pos.left,
  4852. top: pos.top,
  4853. width: imageSize.w,
  4854. height: imageSize.h
  4855. }).data('target', $image); // save current image element.
  4856. var origImageObj = new Image();
  4857. origImageObj.src = $image.attr('src');
  4858. var sizingText = imageSize.w + 'x' + imageSize.h + ' (' + this.lang.image.original + ': ' + origImageObj.width + 'x' + origImageObj.height + ')';
  4859. $selection.find('.note-control-selection-info').text(sizingText);
  4860. this.context.invoke('editor.saveTarget', target);
  4861. }
  4862. else {
  4863. this.hide();
  4864. }
  4865. return isImage;
  4866. };
  4867. /**
  4868. * hide
  4869. *
  4870. * @param {jQuery} $handle
  4871. */
  4872. Handle.prototype.hide = function () {
  4873. this.context.invoke('editor.clearTarget');
  4874. this.$handle.children().hide();
  4875. };
  4876. return Handle;
  4877. }());
  4878. var defaultScheme = 'http://';
  4879. var linkPattern = /^([A-Za-z][A-Za-z0-9+-.]*\:[\/\/]?|mailto:[A-Z0-9._%+-]+@)?(www\.)?(.+)$/i;
  4880. var AutoLink = /** @class */ (function () {
  4881. function AutoLink(context) {
  4882. var _this = this;
  4883. this.context = context;
  4884. this.events = {
  4885. 'summernote.keyup': function (we, e) {
  4886. if (!e.isDefaultPrevented()) {
  4887. _this.handleKeyup(e);
  4888. }
  4889. },
  4890. 'summernote.keydown': function (we, e) {
  4891. _this.handleKeydown(e);
  4892. }
  4893. };
  4894. }
  4895. AutoLink.prototype.initialize = function () {
  4896. this.lastWordRange = null;
  4897. };
  4898. AutoLink.prototype.destroy = function () {
  4899. this.lastWordRange = null;
  4900. };
  4901. AutoLink.prototype.replace = function () {
  4902. if (!this.lastWordRange) {
  4903. return;
  4904. }
  4905. var keyword = this.lastWordRange.toString();
  4906. var match = keyword.match(linkPattern);
  4907. if (match && (match[1] || match[2])) {
  4908. var link = match[1] ? keyword : defaultScheme + keyword;
  4909. var node = $$1('<a />').html(keyword).attr('href', link)[0];
  4910. this.lastWordRange.insertNode(node);
  4911. this.lastWordRange = null;
  4912. this.context.invoke('editor.focus');
  4913. }
  4914. };
  4915. AutoLink.prototype.handleKeydown = function (e) {
  4916. if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
  4917. var wordRange = this.context.invoke('editor.createRange').getWordRange();
  4918. this.lastWordRange = wordRange;
  4919. }
  4920. };
  4921. AutoLink.prototype.handleKeyup = function (e) {
  4922. if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
  4923. this.replace();
  4924. }
  4925. };
  4926. return AutoLink;
  4927. }());
  4928. /**
  4929. * textarea auto sync.
  4930. */
  4931. var AutoSync = /** @class */ (function () {
  4932. function AutoSync(context) {
  4933. var _this = this;
  4934. this.$note = context.layoutInfo.note;
  4935. this.events = {
  4936. 'summernote.change': function () {
  4937. _this.$note.val(context.invoke('code'));
  4938. }
  4939. };
  4940. }
  4941. AutoSync.prototype.shouldInitialize = function () {
  4942. return dom.isTextarea(this.$note[0]);
  4943. };
  4944. return AutoSync;
  4945. }());
  4946. var Placeholder = /** @class */ (function () {
  4947. function Placeholder(context) {
  4948. var _this = this;
  4949. this.context = context;
  4950. this.$editingArea = context.layoutInfo.editingArea;
  4951. this.options = context.options;
  4952. this.events = {
  4953. 'summernote.init summernote.change': function () {
  4954. _this.update();
  4955. },
  4956. 'summernote.codeview.toggled': function () {
  4957. _this.update();
  4958. }
  4959. };
  4960. }
  4961. Placeholder.prototype.shouldInitialize = function () {
  4962. return !!this.options.placeholder;
  4963. };
  4964. Placeholder.prototype.initialize = function () {
  4965. var _this = this;
  4966. this.$placeholder = $$1('<div class="note-placeholder">');
  4967. this.$placeholder.on('click', function () {
  4968. _this.context.invoke('focus');
  4969. }).text(this.options.placeholder).prependTo(this.$editingArea);
  4970. this.update();
  4971. };
  4972. Placeholder.prototype.destroy = function () {
  4973. this.$placeholder.remove();
  4974. };
  4975. Placeholder.prototype.update = function () {
  4976. var isShow = !this.context.invoke('codeview.isActivated') && this.context.invoke('editor.isEmpty');
  4977. this.$placeholder.toggle(isShow);
  4978. };
  4979. return Placeholder;
  4980. }());
  4981. var Buttons = /** @class */ (function () {
  4982. function Buttons(context) {
  4983. this.ui = $$1.summernote.ui;
  4984. this.context = context;
  4985. this.$toolbar = context.layoutInfo.toolbar;
  4986. this.options = context.options;
  4987. this.lang = this.options.langInfo;
  4988. this.invertedKeyMap = func.invertObject(this.options.keyMap[env.isMac ? 'mac' : 'pc']);
  4989. }
  4990. Buttons.prototype.representShortcut = function (editorMethod) {
  4991. var shortcut = this.invertedKeyMap[editorMethod];
  4992. if (!this.options.shortcuts || !shortcut) {
  4993. return '';
  4994. }
  4995. if (env.isMac) {
  4996. shortcut = shortcut.replace('CMD', '⌘').replace('SHIFT', '⇧');
  4997. }
  4998. shortcut = shortcut.replace('BACKSLASH', '\\')
  4999. .replace('SLASH', '/')
  5000. .replace('LEFTBRACKET', '[')
  5001. .replace('RIGHTBRACKET', ']');
  5002. return ' (' + shortcut + ')';
  5003. };
  5004. Buttons.prototype.button = function (o) {
  5005. if (!this.options.tooltip && o.tooltip) {
  5006. delete o.tooltip;
  5007. }
  5008. o.container = this.options.container;
  5009. return this.ui.button(o);
  5010. };
  5011. Buttons.prototype.initialize = function () {
  5012. this.addToolbarButtons();
  5013. this.addImagePopoverButtons();
  5014. this.addLinkPopoverButtons();
  5015. this.addTablePopoverButtons();
  5016. this.fontInstalledMap = {};
  5017. };
  5018. Buttons.prototype.destroy = function () {
  5019. delete this.fontInstalledMap;
  5020. };
  5021. Buttons.prototype.isFontInstalled = function (name) {
  5022. if (!this.fontInstalledMap.hasOwnProperty(name)) {
  5023. this.fontInstalledMap[name] = env.isFontInstalled(name) ||
  5024. lists.contains(this.options.fontNamesIgnoreCheck, name);
  5025. }
  5026. return this.fontInstalledMap[name];
  5027. };
  5028. Buttons.prototype.isFontDeservedToAdd = function (name) {
  5029. var genericFamilies = ['sans-serif', 'serif', 'monospace', 'cursive', 'fantasy'];
  5030. name = name.toLowerCase();
  5031. return ((name !== '') && this.isFontInstalled(name) && ($$1.inArray(name, genericFamilies) === -1));
  5032. };
  5033. Buttons.prototype.addToolbarButtons = function () {
  5034. var _this = this;
  5035. this.context.memo('button.style', function () {
  5036. return _this.ui.buttonGroup([
  5037. _this.button({
  5038. className: 'dropdown-toggle',
  5039. contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.magic), _this.options),
  5040. tooltip: _this.lang.style.style,
  5041. data: {
  5042. toggle: 'dropdown'
  5043. }
  5044. }),
  5045. _this.ui.dropdown({
  5046. className: 'dropdown-style',
  5047. items: _this.options.styleTags,
  5048. template: function (item) {
  5049. if (typeof item === 'string') {
  5050. item = { tag: item, title: (_this.lang.style.hasOwnProperty(item) ? _this.lang.style[item] : item) };
  5051. }
  5052. var tag = item.tag;
  5053. var title = item.title;
  5054. var style = item.style ? ' style="' + item.style + '" ' : '';
  5055. var className = item.className ? ' class="' + item.className + '"' : '';
  5056. return '<' + tag + style + className + '>' + title + '</' + tag + '>';
  5057. },
  5058. click: _this.context.createInvokeHandler('editor.formatBlock')
  5059. })
  5060. ]).render();
  5061. });
  5062. var _loop_1 = function (styleIdx, styleLen) {
  5063. var item = this_1.options.styleTags[styleIdx];
  5064. this_1.context.memo('button.style.' + item, function () {
  5065. return _this.button({
  5066. className: 'note-btn-style-' + item,
  5067. contents: '<div data-value="' + item + '">' + item.toUpperCase() + '</div>',
  5068. tooltip: item.toUpperCase(),
  5069. click: _this.context.createInvokeHandler('editor.formatBlock')
  5070. }).render();
  5071. });
  5072. };
  5073. var this_1 = this;
  5074. for (var styleIdx = 0, styleLen = this.options.styleTags.length; styleIdx < styleLen; styleIdx++) {
  5075. _loop_1(styleIdx, styleLen);
  5076. }
  5077. this.context.memo('button.bold', function () {
  5078. return _this.button({
  5079. className: 'note-btn-bold',
  5080. contents: _this.ui.icon(_this.options.icons.bold),
  5081. tooltip: _this.lang.font.bold + _this.representShortcut('bold'),
  5082. click: _this.context.createInvokeHandlerAndUpdateState('editor.bold')
  5083. }).render();
  5084. });
  5085. this.context.memo('button.italic', function () {
  5086. return _this.button({
  5087. className: 'note-btn-italic',
  5088. contents: _this.ui.icon(_this.options.icons.italic),
  5089. tooltip: _this.lang.font.italic + _this.representShortcut('italic'),
  5090. click: _this.context.createInvokeHandlerAndUpdateState('editor.italic')
  5091. }).render();
  5092. });
  5093. this.context.memo('button.underline', function () {
  5094. return _this.button({
  5095. className: 'note-btn-underline',
  5096. contents: _this.ui.icon(_this.options.icons.underline),
  5097. tooltip: _this.lang.font.underline + _this.representShortcut('underline'),
  5098. click: _this.context.createInvokeHandlerAndUpdateState('editor.underline')
  5099. }).render();
  5100. });
  5101. this.context.memo('button.clear', function () {
  5102. return _this.button({
  5103. contents: _this.ui.icon(_this.options.icons.eraser),
  5104. tooltip: _this.lang.font.clear + _this.representShortcut('removeFormat'),
  5105. click: _this.context.createInvokeHandler('editor.removeFormat')
  5106. }).render();
  5107. });
  5108. this.context.memo('button.strikethrough', function () {
  5109. return _this.button({
  5110. className: 'note-btn-strikethrough',
  5111. contents: _this.ui.icon(_this.options.icons.strikethrough),
  5112. tooltip: _this.lang.font.strikethrough + _this.representShortcut('strikethrough'),
  5113. click: _this.context.createInvokeHandlerAndUpdateState('editor.strikethrough')
  5114. }).render();
  5115. });
  5116. this.context.memo('button.superscript', function () {
  5117. return _this.button({
  5118. className: 'note-btn-superscript',
  5119. contents: _this.ui.icon(_this.options.icons.superscript),
  5120. tooltip: _this.lang.font.superscript,
  5121. click: _this.context.createInvokeHandlerAndUpdateState('editor.superscript')
  5122. }).render();
  5123. });
  5124. this.context.memo('button.subscript', function () {
  5125. return _this.button({
  5126. className: 'note-btn-subscript',
  5127. contents: _this.ui.icon(_this.options.icons.subscript),
  5128. tooltip: _this.lang.font.subscript,
  5129. click: _this.context.createInvokeHandlerAndUpdateState('editor.subscript')
  5130. }).render();
  5131. });
  5132. this.context.memo('button.fontname', function () {
  5133. var styleInfo = _this.context.invoke('editor.currentStyle');
  5134. // Add 'default' fonts into the fontnames array if not exist
  5135. $$1.each(styleInfo['font-family'].split(','), function (idx, fontname) {
  5136. fontname = fontname.trim().replace(/['"]+/g, '');
  5137. if (_this.isFontDeservedToAdd(fontname)) {
  5138. if ($$1.inArray(fontname, _this.options.fontNames) === -1) {
  5139. _this.options.fontNames.push(fontname);
  5140. }
  5141. }
  5142. });
  5143. return _this.ui.buttonGroup([
  5144. _this.button({
  5145. className: 'dropdown-toggle',
  5146. contents: _this.ui.dropdownButtonContents('<span class="note-current-fontname"/>', _this.options),
  5147. tooltip: _this.lang.font.name,
  5148. data: {
  5149. toggle: 'dropdown'
  5150. }
  5151. }),
  5152. _this.ui.dropdownCheck({
  5153. className: 'dropdown-fontname',
  5154. checkClassName: _this.options.icons.menuCheck,
  5155. items: _this.options.fontNames.filter(_this.isFontInstalled.bind(_this)),
  5156. template: function (item) {
  5157. return '<span style="font-family: \'' + item + '\'">' + item + '</span>';
  5158. },
  5159. click: _this.context.createInvokeHandlerAndUpdateState('editor.fontName')
  5160. })
  5161. ]).render();
  5162. });
  5163. this.context.memo('button.fontsize', function () {
  5164. return _this.ui.buttonGroup([
  5165. _this.button({
  5166. className: 'dropdown-toggle',
  5167. contents: _this.ui.dropdownButtonContents('<span class="note-current-fontsize"/>', _this.options),
  5168. tooltip: _this.lang.font.size,
  5169. data: {
  5170. toggle: 'dropdown'
  5171. }
  5172. }),
  5173. _this.ui.dropdownCheck({
  5174. className: 'dropdown-fontsize',
  5175. checkClassName: _this.options.icons.menuCheck,
  5176. items: _this.options.fontSizes,
  5177. click: _this.context.createInvokeHandlerAndUpdateState('editor.fontSize')
  5178. })
  5179. ]).render();
  5180. });
  5181. this.context.memo('button.color', function () {
  5182. return _this.ui.buttonGroup({
  5183. className: 'note-color',
  5184. children: [
  5185. _this.button({
  5186. className: 'note-current-color-button',
  5187. contents: _this.ui.icon(_this.options.icons.font + ' note-recent-color'),
  5188. tooltip: _this.lang.color.recent,
  5189. click: function (e) {
  5190. var $button = $$1(e.currentTarget);
  5191. _this.context.invoke('editor.color', {
  5192. backColor: $button.attr('data-backColor'),
  5193. foreColor: $button.attr('data-foreColor')
  5194. });
  5195. },
  5196. callback: function ($button) {
  5197. var $recentColor = $button.find('.note-recent-color');
  5198. $recentColor.css('background-color', '#FFFF00');
  5199. $button.attr('data-backColor', '#FFFF00');
  5200. }
  5201. }),
  5202. _this.button({
  5203. className: 'dropdown-toggle',
  5204. contents: _this.ui.dropdownButtonContents('', _this.options),
  5205. tooltip: _this.lang.color.more,
  5206. data: {
  5207. toggle: 'dropdown'
  5208. }
  5209. }),
  5210. _this.ui.dropdown({
  5211. items: [
  5212. '<div class="note-palette">',
  5213. ' <div class="note-palette-title">' + _this.lang.color.background + '</div>',
  5214. ' <div>',
  5215. ' <button type="button" class="note-color-reset btn btn-light" data-event="backColor" data-value="inherit">',
  5216. _this.lang.color.transparent,
  5217. ' </button>',
  5218. ' </div>',
  5219. ' <div class="note-holder" data-event="backColor"/>',
  5220. '</div>',
  5221. '<div class="note-palette">',
  5222. ' <div class="note-palette-title">' + _this.lang.color.foreground + '</div>',
  5223. ' <div>',
  5224. ' <button type="button" class="note-color-reset btn btn-light" data-event="removeFormat" data-value="foreColor">',
  5225. _this.lang.color.resetToDefault,
  5226. ' </button>',
  5227. ' </div>',
  5228. ' <div class="note-holder" data-event="foreColor"/>',
  5229. '</div>'
  5230. ].join(''),
  5231. callback: function ($dropdown) {
  5232. $dropdown.find('.note-holder').each(function (idx, item) {
  5233. var $holder = $$1(item);
  5234. $holder.append(_this.ui.palette({
  5235. colors: _this.options.colors,
  5236. eventName: $holder.data('event'),
  5237. container: _this.options.container,
  5238. tooltip: _this.options.tooltip
  5239. }).render());
  5240. });
  5241. },
  5242. click: function (event) {
  5243. var $button = $$1(event.target);
  5244. var eventName = $button.data('event');
  5245. var value = $button.data('value');
  5246. if (eventName && value) {
  5247. var key = eventName === 'backColor' ? 'background-color' : 'color';
  5248. var $color = $button.closest('.note-color').find('.note-recent-color');
  5249. var $currentButton = $button.closest('.note-color').find('.note-current-color-button');
  5250. $color.css(key, value);
  5251. $currentButton.attr('data-' + eventName, value);
  5252. _this.context.invoke('editor.' + eventName, value);
  5253. }
  5254. }
  5255. })
  5256. ]
  5257. }).render();
  5258. });
  5259. this.context.memo('button.ul', function () {
  5260. return _this.button({
  5261. contents: _this.ui.icon(_this.options.icons.unorderedlist),
  5262. tooltip: _this.lang.lists.unordered + _this.representShortcut('insertUnorderedList'),
  5263. click: _this.context.createInvokeHandler('editor.insertUnorderedList')
  5264. }).render();
  5265. });
  5266. this.context.memo('button.ol', function () {
  5267. return _this.button({
  5268. contents: _this.ui.icon(_this.options.icons.orderedlist),
  5269. tooltip: _this.lang.lists.ordered + _this.representShortcut('insertOrderedList'),
  5270. click: _this.context.createInvokeHandler('editor.insertOrderedList')
  5271. }).render();
  5272. });
  5273. var justifyLeft = this.button({
  5274. contents: this.ui.icon(this.options.icons.alignLeft),
  5275. tooltip: this.lang.paragraph.left + this.representShortcut('justifyLeft'),
  5276. click: this.context.createInvokeHandler('editor.justifyLeft')
  5277. });
  5278. var justifyCenter = this.button({
  5279. contents: this.ui.icon(this.options.icons.alignCenter),
  5280. tooltip: this.lang.paragraph.center + this.representShortcut('justifyCenter'),
  5281. click: this.context.createInvokeHandler('editor.justifyCenter')
  5282. });
  5283. var justifyRight = this.button({
  5284. contents: this.ui.icon(this.options.icons.alignRight),
  5285. tooltip: this.lang.paragraph.right + this.representShortcut('justifyRight'),
  5286. click: this.context.createInvokeHandler('editor.justifyRight')
  5287. });
  5288. var justifyFull = this.button({
  5289. contents: this.ui.icon(this.options.icons.alignJustify),
  5290. tooltip: this.lang.paragraph.justify + this.representShortcut('justifyFull'),
  5291. click: this.context.createInvokeHandler('editor.justifyFull')
  5292. });
  5293. var outdent = this.button({
  5294. contents: this.ui.icon(this.options.icons.outdent),
  5295. tooltip: this.lang.paragraph.outdent + this.representShortcut('outdent'),
  5296. click: this.context.createInvokeHandler('editor.outdent')
  5297. });
  5298. var indent = this.button({
  5299. contents: this.ui.icon(this.options.icons.indent),
  5300. tooltip: this.lang.paragraph.indent + this.representShortcut('indent'),
  5301. click: this.context.createInvokeHandler('editor.indent')
  5302. });
  5303. this.context.memo('button.justifyLeft', func.invoke(justifyLeft, 'render'));
  5304. this.context.memo('button.justifyCenter', func.invoke(justifyCenter, 'render'));
  5305. this.context.memo('button.justifyRight', func.invoke(justifyRight, 'render'));
  5306. this.context.memo('button.justifyFull', func.invoke(justifyFull, 'render'));
  5307. this.context.memo('button.outdent', func.invoke(outdent, 'render'));
  5308. this.context.memo('button.indent', func.invoke(indent, 'render'));
  5309. this.context.memo('button.paragraph', function () {
  5310. return _this.ui.buttonGroup([
  5311. _this.button({
  5312. className: 'dropdown-toggle',
  5313. contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.alignLeft), _this.options),
  5314. tooltip: _this.lang.paragraph.paragraph,
  5315. data: {
  5316. toggle: 'dropdown'
  5317. }
  5318. }),
  5319. _this.ui.dropdown([
  5320. _this.ui.buttonGroup({
  5321. className: 'note-align',
  5322. children: [justifyLeft, justifyCenter, justifyRight, justifyFull]
  5323. }),
  5324. _this.ui.buttonGroup({
  5325. className: 'note-list',
  5326. children: [outdent, indent]
  5327. })
  5328. ])
  5329. ]).render();
  5330. });
  5331. this.context.memo('button.height', function () {
  5332. return _this.ui.buttonGroup([
  5333. _this.button({
  5334. className: 'dropdown-toggle',
  5335. contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.textHeight), _this.options),
  5336. tooltip: _this.lang.font.height,
  5337. data: {
  5338. toggle: 'dropdown'
  5339. }
  5340. }),
  5341. _this.ui.dropdownCheck({
  5342. items: _this.options.lineHeights,
  5343. checkClassName: _this.options.icons.menuCheck,
  5344. className: 'dropdown-line-height',
  5345. click: _this.context.createInvokeHandler('editor.lineHeight')
  5346. })
  5347. ]).render();
  5348. });
  5349. this.context.memo('button.table', function () {
  5350. return _this.ui.buttonGroup([
  5351. _this.button({
  5352. className: 'dropdown-toggle',
  5353. contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.table), _this.options),
  5354. tooltip: _this.lang.table.table,
  5355. data: {
  5356. toggle: 'dropdown'
  5357. }
  5358. }),
  5359. _this.ui.dropdown({
  5360. className: 'note-table',
  5361. items: [
  5362. '<div class="note-dimension-picker">',
  5363. ' <div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"/>',
  5364. ' <div class="note-dimension-picker-highlighted"/>',
  5365. ' <div class="note-dimension-picker-unhighlighted"/>',
  5366. '</div>',
  5367. '<div class="note-dimension-display">1 x 1</div>'
  5368. ].join('')
  5369. })
  5370. ], {
  5371. callback: function ($node) {
  5372. var $catcher = $node.find('.note-dimension-picker-mousecatcher');
  5373. $catcher.css({
  5374. width: _this.options.insertTableMaxSize.col + 'em',
  5375. height: _this.options.insertTableMaxSize.row + 'em'
  5376. }).mousedown(_this.context.createInvokeHandler('editor.insertTable'))
  5377. .on('mousemove', _this.tableMoveHandler.bind(_this));
  5378. }
  5379. }).render();
  5380. });
  5381. this.context.memo('button.link', function () {
  5382. return _this.button({
  5383. contents: _this.ui.icon(_this.options.icons.link),
  5384. tooltip: _this.lang.link.link + _this.representShortcut('linkDialog.show'),
  5385. click: _this.context.createInvokeHandler('linkDialog.show')
  5386. }).render();
  5387. });
  5388. this.context.memo('button.picture', function () {
  5389. return _this.button({
  5390. contents: _this.ui.icon(_this.options.icons.picture),
  5391. tooltip: _this.lang.image.image,
  5392. click: _this.context.createInvokeHandler('imageDialog.show')
  5393. }).render();
  5394. });
  5395. this.context.memo('button.video', function () {
  5396. return _this.button({
  5397. contents: _this.ui.icon(_this.options.icons.video),
  5398. tooltip: _this.lang.video.video,
  5399. click: _this.context.createInvokeHandler('videoDialog.show')
  5400. }).render();
  5401. });
  5402. this.context.memo('button.hr', function () {
  5403. return _this.button({
  5404. contents: _this.ui.icon(_this.options.icons.minus),
  5405. tooltip: _this.lang.hr.insert + _this.representShortcut('insertHorizontalRule'),
  5406. click: _this.context.createInvokeHandler('editor.insertHorizontalRule')
  5407. }).render();
  5408. });
  5409. this.context.memo('button.fullscreen', function () {
  5410. return _this.button({
  5411. className: 'btn-fullscreen',
  5412. contents: _this.ui.icon(_this.options.icons.arrowsAlt),
  5413. tooltip: _this.options.fullscreen,
  5414. click: _this.context.createInvokeHandler('fullscreen.toggle')
  5415. }).render();
  5416. });
  5417. this.context.memo('button.codeview', function () {
  5418. return _this.button({
  5419. className: 'btn-codeview',
  5420. contents: _this.ui.icon(_this.options.icons.code),
  5421. tooltip: _this.options.codeview,
  5422. click: _this.context.createInvokeHandler('codeview.toggle')
  5423. }).render();
  5424. });
  5425. this.context.memo('button.redo', function () {
  5426. return _this.button({
  5427. contents: _this.ui.icon(_this.options.icons.redo),
  5428. tooltip: _this.lang.history.redo + _this.representShortcut('redo'),
  5429. click: _this.context.createInvokeHandler('editor.redo')
  5430. }).render();
  5431. });
  5432. this.context.memo('button.undo', function () {
  5433. return _this.button({
  5434. contents: _this.ui.icon(_this.options.icons.undo),
  5435. tooltip: _this.lang.history.undo + _this.representShortcut('undo'),
  5436. click: _this.context.createInvokeHandler('editor.undo')
  5437. }).render();
  5438. });
  5439. this.context.memo('button.help', function () {
  5440. return _this.button({
  5441. contents: _this.ui.icon(_this.options.icons.question),
  5442. tooltip: _this.options.help,
  5443. click: _this.context.createInvokeHandler('helpDialog.show')
  5444. }).render();
  5445. });
  5446. };
  5447. /**
  5448. * image : [
  5449. * ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
  5450. * ['float', ['floatLeft', 'floatRight', 'floatNone' ]],
  5451. * ['remove', ['removeMedia']]
  5452. * ],
  5453. */
  5454. Buttons.prototype.addImagePopoverButtons = function () {
  5455. var _this = this;
  5456. // Image Size Buttons
  5457. this.context.memo('button.imageSize100', function () {
  5458. return _this.button({
  5459. contents: '<span class="note-fontsize-10">100%</span>',
  5460. tooltip: _this.lang.image.resizeFull,
  5461. click: _this.context.createInvokeHandler('editor.resize', '1')
  5462. }).render();
  5463. });
  5464. this.context.memo('button.imageSize50', function () {
  5465. return _this.button({
  5466. contents: '<span class="note-fontsize-10">50%</span>',
  5467. tooltip: _this.lang.image.resizeHalf,
  5468. click: _this.context.createInvokeHandler('editor.resize', '0.5')
  5469. }).render();
  5470. });
  5471. this.context.memo('button.imageSize25', function () {
  5472. return _this.button({
  5473. contents: '<span class="note-fontsize-10">25%</span>',
  5474. tooltip: _this.lang.image.resizeQuarter,
  5475. click: _this.context.createInvokeHandler('editor.resize', '0.25')
  5476. }).render();
  5477. });
  5478. // Float Buttons
  5479. this.context.memo('button.floatLeft', function () {
  5480. return _this.button({
  5481. contents: _this.ui.icon(_this.options.icons.alignLeft),
  5482. tooltip: _this.lang.image.floatLeft,
  5483. click: _this.context.createInvokeHandler('editor.floatMe', 'left')
  5484. }).render();
  5485. });
  5486. this.context.memo('button.floatRight', function () {
  5487. return _this.button({
  5488. contents: _this.ui.icon(_this.options.icons.alignRight),
  5489. tooltip: _this.lang.image.floatRight,
  5490. click: _this.context.createInvokeHandler('editor.floatMe', 'right')
  5491. }).render();
  5492. });
  5493. this.context.memo('button.floatNone', function () {
  5494. return _this.button({
  5495. contents: _this.ui.icon(_this.options.icons.alignJustify),
  5496. tooltip: _this.lang.image.floatNone,
  5497. click: _this.context.createInvokeHandler('editor.floatMe', 'none')
  5498. }).render();
  5499. });
  5500. // Remove Buttons
  5501. this.context.memo('button.removeMedia', function () {
  5502. return _this.button({
  5503. contents: _this.ui.icon(_this.options.icons.trash),
  5504. tooltip: _this.lang.image.remove,
  5505. click: _this.context.createInvokeHandler('editor.removeMedia')
  5506. }).render();
  5507. });
  5508. };
  5509. Buttons.prototype.addLinkPopoverButtons = function () {
  5510. var _this = this;
  5511. this.context.memo('button.linkDialogShow', function () {
  5512. return _this.button({
  5513. contents: _this.ui.icon(_this.options.icons.link),
  5514. tooltip: _this.lang.link.edit,
  5515. click: _this.context.createInvokeHandler('linkDialog.show')
  5516. }).render();
  5517. });
  5518. this.context.memo('button.unlink', function () {
  5519. return _this.button({
  5520. contents: _this.ui.icon(_this.options.icons.unlink),
  5521. tooltip: _this.lang.link.unlink,
  5522. click: _this.context.createInvokeHandler('editor.unlink')
  5523. }).render();
  5524. });
  5525. };
  5526. /**
  5527. * table : [
  5528. * ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
  5529. * ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
  5530. * ],
  5531. */
  5532. Buttons.prototype.addTablePopoverButtons = function () {
  5533. var _this = this;
  5534. this.context.memo('button.addRowUp', function () {
  5535. return _this.button({
  5536. className: 'btn-md',
  5537. contents: _this.ui.icon(_this.options.icons.rowAbove),
  5538. tooltip: _this.lang.table.addRowAbove,
  5539. click: _this.context.createInvokeHandler('editor.addRow', 'top')
  5540. }).render();
  5541. });
  5542. this.context.memo('button.addRowDown', function () {
  5543. return _this.button({
  5544. className: 'btn-md',
  5545. contents: _this.ui.icon(_this.options.icons.rowBelow),
  5546. tooltip: _this.lang.table.addRowBelow,
  5547. click: _this.context.createInvokeHandler('editor.addRow', 'bottom')
  5548. }).render();
  5549. });
  5550. this.context.memo('button.addColLeft', function () {
  5551. return _this.button({
  5552. className: 'btn-md',
  5553. contents: _this.ui.icon(_this.options.icons.colBefore),
  5554. tooltip: _this.lang.table.addColLeft,
  5555. click: _this.context.createInvokeHandler('editor.addCol', 'left')
  5556. }).render();
  5557. });
  5558. this.context.memo('button.addColRight', function () {
  5559. return _this.button({
  5560. className: 'btn-md',
  5561. contents: _this.ui.icon(_this.options.icons.colAfter),
  5562. tooltip: _this.lang.table.addColRight,
  5563. click: _this.context.createInvokeHandler('editor.addCol', 'right')
  5564. }).render();
  5565. });
  5566. this.context.memo('button.deleteRow', function () {
  5567. return _this.button({
  5568. className: 'btn-md',
  5569. contents: _this.ui.icon(_this.options.icons.rowRemove),
  5570. tooltip: _this.lang.table.delRow,
  5571. click: _this.context.createInvokeHandler('editor.deleteRow')
  5572. }).render();
  5573. });
  5574. this.context.memo('button.deleteCol', function () {
  5575. return _this.button({
  5576. className: 'btn-md',
  5577. contents: _this.ui.icon(_this.options.icons.colRemove),
  5578. tooltip: _this.lang.table.delCol,
  5579. click: _this.context.createInvokeHandler('editor.deleteCol')
  5580. }).render();
  5581. });
  5582. this.context.memo('button.deleteTable', function () {
  5583. return _this.button({
  5584. className: 'btn-md',
  5585. contents: _this.ui.icon(_this.options.icons.trash),
  5586. tooltip: _this.lang.table.delTable,
  5587. click: _this.context.createInvokeHandler('editor.deleteTable')
  5588. }).render();
  5589. });
  5590. };
  5591. Buttons.prototype.build = function ($container, groups) {
  5592. for (var groupIdx = 0, groupLen = groups.length; groupIdx < groupLen; groupIdx++) {
  5593. var group = groups[groupIdx];
  5594. var groupName = $$1.isArray(group) ? group[0] : group;
  5595. var buttons = $$1.isArray(group) ? ((group.length === 1) ? [group[0]] : group[1]) : [group];
  5596. var $group = this.ui.buttonGroup({
  5597. className: 'note-' + groupName
  5598. }).render();
  5599. for (var idx = 0, len = buttons.length; idx < len; idx++) {
  5600. var btn = this.context.memo('button.' + buttons[idx]);
  5601. if (btn) {
  5602. $group.append(typeof btn === 'function' ? btn(this.context) : btn);
  5603. }
  5604. }
  5605. $group.appendTo($container);
  5606. }
  5607. };
  5608. /**
  5609. * @param {jQuery} [$container]
  5610. */
  5611. Buttons.prototype.updateCurrentStyle = function ($container) {
  5612. var _this = this;
  5613. var $cont = $container || this.$toolbar;
  5614. var styleInfo = this.context.invoke('editor.currentStyle');
  5615. this.updateBtnStates($cont, {
  5616. '.note-btn-bold': function () {
  5617. return styleInfo['font-bold'] === 'bold';
  5618. },
  5619. '.note-btn-italic': function () {
  5620. return styleInfo['font-italic'] === 'italic';
  5621. },
  5622. '.note-btn-underline': function () {
  5623. return styleInfo['font-underline'] === 'underline';
  5624. },
  5625. '.note-btn-subscript': function () {
  5626. return styleInfo['font-subscript'] === 'subscript';
  5627. },
  5628. '.note-btn-superscript': function () {
  5629. return styleInfo['font-superscript'] === 'superscript';
  5630. },
  5631. '.note-btn-strikethrough': function () {
  5632. return styleInfo['font-strikethrough'] === 'strikethrough';
  5633. }
  5634. });
  5635. if (styleInfo['font-family']) {
  5636. var fontNames = styleInfo['font-family'].split(',').map(function (name) {
  5637. return name.replace(/[\'\"]/g, '')
  5638. .replace(/\s+$/, '')
  5639. .replace(/^\s+/, '');
  5640. });
  5641. var fontName_1 = lists.find(fontNames, this.isFontInstalled.bind(this));
  5642. $cont.find('.dropdown-fontname a').each(function (idx, item) {
  5643. var $item = $$1(item);
  5644. // always compare string to avoid creating another func.
  5645. var isChecked = ($item.data('value') + '') === (fontName_1 + '');
  5646. $item.toggleClass('checked', isChecked);
  5647. });
  5648. $cont.find('.note-current-fontname').text(fontName_1).css('font-family', fontName_1);
  5649. }
  5650. if (styleInfo['font-size']) {
  5651. var fontSize_1 = styleInfo['font-size'];
  5652. $cont.find('.dropdown-fontsize a').each(function (idx, item) {
  5653. var $item = $$1(item);
  5654. // always compare with string to avoid creating another func.
  5655. var isChecked = ($item.data('value') + '') === (fontSize_1 + '');
  5656. $item.toggleClass('checked', isChecked);
  5657. });
  5658. $cont.find('.note-current-fontsize').text(fontSize_1);
  5659. }
  5660. if (styleInfo['line-height']) {
  5661. var lineHeight_1 = styleInfo['line-height'];
  5662. $cont.find('.dropdown-line-height li a').each(function (idx, item) {
  5663. // always compare with string to avoid creating another func.
  5664. var isChecked = ($$1(item).data('value') + '') === (lineHeight_1 + '');
  5665. _this.className = isChecked ? 'checked' : '';
  5666. });
  5667. }
  5668. };
  5669. Buttons.prototype.updateBtnStates = function ($container, infos) {
  5670. var _this = this;
  5671. $$1.each(infos, function (selector, pred) {
  5672. _this.ui.toggleBtnActive($container.find(selector), pred());
  5673. });
  5674. };
  5675. Buttons.prototype.tableMoveHandler = function (event) {
  5676. var PX_PER_EM = 18;
  5677. var $picker = $$1(event.target.parentNode); // target is mousecatcher
  5678. var $dimensionDisplay = $picker.next();
  5679. var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
  5680. var $highlighted = $picker.find('.note-dimension-picker-highlighted');
  5681. var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
  5682. var posOffset;
  5683. // HTML5 with jQuery - e.offsetX is undefined in Firefox
  5684. if (event.offsetX === undefined) {
  5685. var posCatcher = $$1(event.target).offset();
  5686. posOffset = {
  5687. x: event.pageX - posCatcher.left,
  5688. y: event.pageY - posCatcher.top
  5689. };
  5690. }
  5691. else {
  5692. posOffset = {
  5693. x: event.offsetX,
  5694. y: event.offsetY
  5695. };
  5696. }
  5697. var dim = {
  5698. c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
  5699. r: Math.ceil(posOffset.y / PX_PER_EM) || 1
  5700. };
  5701. $highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
  5702. $catcher.data('value', dim.c + 'x' + dim.r);
  5703. if (dim.c > 3 && dim.c < this.options.insertTableMaxSize.col) {
  5704. $unhighlighted.css({ width: dim.c + 1 + 'em' });
  5705. }
  5706. if (dim.r > 3 && dim.r < this.options.insertTableMaxSize.row) {
  5707. $unhighlighted.css({ height: dim.r + 1 + 'em' });
  5708. }
  5709. $dimensionDisplay.html(dim.c + ' x ' + dim.r);
  5710. };
  5711. return Buttons;
  5712. }());
  5713. var Toolbar = /** @class */ (function () {
  5714. function Toolbar(context) {
  5715. this.context = context;
  5716. this.$window = $$1(window);
  5717. this.$document = $$1(document);
  5718. this.ui = $$1.summernote.ui;
  5719. this.$note = context.layoutInfo.note;
  5720. this.$editor = context.layoutInfo.editor;
  5721. this.$toolbar = context.layoutInfo.toolbar;
  5722. this.options = context.options;
  5723. this.followScroll = this.followScroll.bind(this);
  5724. }
  5725. Toolbar.prototype.shouldInitialize = function () {
  5726. return !this.options.airMode;
  5727. };
  5728. Toolbar.prototype.initialize = function () {
  5729. var _this = this;
  5730. this.options.toolbar = this.options.toolbar || [];
  5731. if (!this.options.toolbar.length) {
  5732. this.$toolbar.hide();
  5733. }
  5734. else {
  5735. this.context.invoke('buttons.build', this.$toolbar, this.options.toolbar);
  5736. }
  5737. if (this.options.toolbarContainer) {
  5738. this.$toolbar.appendTo(this.options.toolbarContainer);
  5739. }
  5740. this.changeContainer(false);
  5741. this.$note.on('summernote.keyup summernote.mouseup summernote.change', function () {
  5742. _this.context.invoke('buttons.updateCurrentStyle');
  5743. });
  5744. this.context.invoke('buttons.updateCurrentStyle');
  5745. if (this.options.followingToolbar) {
  5746. this.$window.on('scroll resize', this.followScroll);
  5747. }
  5748. };
  5749. Toolbar.prototype.destroy = function () {
  5750. this.$toolbar.children().remove();
  5751. if (this.options.followingToolbar) {
  5752. this.$window.off('scroll resize', this.followScroll);
  5753. }
  5754. };
  5755. Toolbar.prototype.followScroll = function () {
  5756. if (this.$editor.hasClass('fullscreen')) {
  5757. return false;
  5758. }
  5759. var $toolbarWrapper = this.$toolbar.parent('.note-toolbar-wrapper');
  5760. var editorHeight = this.$editor.outerHeight();
  5761. var editorWidth = this.$editor.width();
  5762. var toolbarHeight = this.$toolbar.height();
  5763. $toolbarWrapper.css({
  5764. height: toolbarHeight
  5765. });
  5766. // check if the web app is currently using another static bar
  5767. var otherBarHeight = 0;
  5768. if (this.options.otherStaticBar) {
  5769. otherBarHeight = $$1(this.options.otherStaticBar).outerHeight();
  5770. }
  5771. var currentOffset = this.$document.scrollTop();
  5772. var editorOffsetTop = this.$editor.offset().top;
  5773. var editorOffsetBottom = editorOffsetTop + editorHeight;
  5774. var activateOffset = editorOffsetTop - otherBarHeight;
  5775. var deactivateOffsetBottom = editorOffsetBottom - otherBarHeight - toolbarHeight;
  5776. if ((currentOffset > activateOffset) && (currentOffset < deactivateOffsetBottom)) {
  5777. this.$toolbar.css({
  5778. position: 'fixed',
  5779. top: otherBarHeight,
  5780. width: editorWidth
  5781. });
  5782. }
  5783. else {
  5784. this.$toolbar.css({
  5785. position: 'relative',
  5786. top: 0,
  5787. width: '100%'
  5788. });
  5789. }
  5790. };
  5791. Toolbar.prototype.changeContainer = function (isFullscreen) {
  5792. if (isFullscreen) {
  5793. this.$toolbar.prependTo(this.$editor);
  5794. }
  5795. else {
  5796. if (this.options.toolbarContainer) {
  5797. this.$toolbar.appendTo(this.options.toolbarContainer);
  5798. }
  5799. }
  5800. };
  5801. Toolbar.prototype.updateFullscreen = function (isFullscreen) {
  5802. this.ui.toggleBtnActive(this.$toolbar.find('.btn-fullscreen'), isFullscreen);
  5803. this.changeContainer(isFullscreen);
  5804. };
  5805. Toolbar.prototype.updateCodeview = function (isCodeview) {
  5806. this.ui.toggleBtnActive(this.$toolbar.find('.btn-codeview'), isCodeview);
  5807. if (isCodeview) {
  5808. this.deactivate();
  5809. }
  5810. else {
  5811. this.activate();
  5812. }
  5813. };
  5814. Toolbar.prototype.activate = function (isIncludeCodeview) {
  5815. var $btn = this.$toolbar.find('button');
  5816. if (!isIncludeCodeview) {
  5817. $btn = $btn.not('.btn-codeview');
  5818. }
  5819. this.ui.toggleBtn($btn, true);
  5820. };
  5821. Toolbar.prototype.deactivate = function (isIncludeCodeview) {
  5822. var $btn = this.$toolbar.find('button');
  5823. if (!isIncludeCodeview) {
  5824. $btn = $btn.not('.btn-codeview');
  5825. }
  5826. this.ui.toggleBtn($btn, false);
  5827. };
  5828. return Toolbar;
  5829. }());
  5830. var LinkDialog = /** @class */ (function () {
  5831. function LinkDialog(context) {
  5832. this.context = context;
  5833. this.ui = $$1.summernote.ui;
  5834. this.$body = $$1(document.body);
  5835. this.$editor = context.layoutInfo.editor;
  5836. this.options = context.options;
  5837. this.lang = this.options.langInfo;
  5838. context.memo('help.linkDialog.show', this.options.langInfo.help['linkDialog.show']);
  5839. }
  5840. LinkDialog.prototype.initialize = function () {
  5841. var $container = this.options.dialogsInBody ? this.$body : this.$editor;
  5842. var body = [
  5843. '<div class="form-group note-form-group">',
  5844. "<label class=\"note-form-label\">" + this.lang.link.textToDisplay + "</label>",
  5845. '<input class="note-link-text form-control note-form-control note-input" type="text" />',
  5846. '</div>',
  5847. '<div class="form-group note-form-group">',
  5848. "<label class=\"note-form-label\">" + this.lang.link.url + "</label>",
  5849. '<input class="note-link-url form-control note-form-control note-input" type="text" value="http://" />',
  5850. '</div>',
  5851. !this.options.disableLinkTarget
  5852. ? $$1('<div/>').append(this.ui.checkbox({
  5853. id: 'sn-checkbox-open-in-new-window',
  5854. text: this.lang.link.openInNewWindow,
  5855. checked: true
  5856. }).render()).html()
  5857. : ''
  5858. ].join('');
  5859. var buttonClass = 'btn btn-primary note-btn note-btn-primary note-link-btn';
  5860. var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.link.insert + "</button>";
  5861. this.$dialog = this.ui.dialog({
  5862. className: 'link-dialog',
  5863. title: this.lang.link.insert,
  5864. fade: this.options.dialogsFade,
  5865. body: body,
  5866. footer: footer
  5867. }).render().appendTo($container);
  5868. };
  5869. LinkDialog.prototype.destroy = function () {
  5870. this.ui.hideDialog(this.$dialog);
  5871. this.$dialog.remove();
  5872. };
  5873. LinkDialog.prototype.bindEnterKey = function ($input, $btn) {
  5874. $input.on('keypress', function (event) {
  5875. if (event.keyCode === key.code.ENTER) {
  5876. event.preventDefault();
  5877. $btn.trigger('click');
  5878. }
  5879. });
  5880. };
  5881. /**
  5882. * toggle update button
  5883. */
  5884. LinkDialog.prototype.toggleLinkBtn = function ($linkBtn, $linkText, $linkUrl) {
  5885. this.ui.toggleBtn($linkBtn, $linkText.val() && $linkUrl.val());
  5886. };
  5887. /**
  5888. * Show link dialog and set event handlers on dialog controls.
  5889. *
  5890. * @param {Object} linkInfo
  5891. * @return {Promise}
  5892. */
  5893. LinkDialog.prototype.showLinkDialog = function (linkInfo) {
  5894. var _this = this;
  5895. return $$1.Deferred(function (deferred) {
  5896. var $linkText = _this.$dialog.find('.note-link-text');
  5897. var $linkUrl = _this.$dialog.find('.note-link-url');
  5898. var $linkBtn = _this.$dialog.find('.note-link-btn');
  5899. var $openInNewWindow = _this.$dialog.find('input[type=checkbox]');
  5900. _this.ui.onDialogShown(_this.$dialog, function () {
  5901. _this.context.triggerEvent('dialog.shown');
  5902. // if no url was given, copy text to url
  5903. if (!linkInfo.url) {
  5904. linkInfo.url = linkInfo.text;
  5905. }
  5906. $linkText.val(linkInfo.text);
  5907. var handleLinkTextUpdate = function () {
  5908. _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
  5909. // if linktext was modified by keyup,
  5910. // stop cloning text from linkUrl
  5911. linkInfo.text = $linkText.val();
  5912. };
  5913. $linkText.on('input', handleLinkTextUpdate).on('paste', function () {
  5914. setTimeout(handleLinkTextUpdate, 0);
  5915. });
  5916. var handleLinkUrlUpdate = function () {
  5917. _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
  5918. // display same link on `Text to display` input
  5919. // when create a new link
  5920. if (!linkInfo.text) {
  5921. $linkText.val($linkUrl.val());
  5922. }
  5923. };
  5924. $linkUrl.on('input', handleLinkUrlUpdate).on('paste', function () {
  5925. setTimeout(handleLinkUrlUpdate, 0);
  5926. }).val(linkInfo.url);
  5927. if (!env.isSupportTouch) {
  5928. $linkUrl.trigger('focus');
  5929. }
  5930. _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
  5931. _this.bindEnterKey($linkUrl, $linkBtn);
  5932. _this.bindEnterKey($linkText, $linkBtn);
  5933. var isChecked = linkInfo.isNewWindow !== undefined
  5934. ? linkInfo.isNewWindow : _this.context.options.linkTargetBlank;
  5935. $openInNewWindow.prop('checked', isChecked);
  5936. $linkBtn.one('click', function (event) {
  5937. event.preventDefault();
  5938. deferred.resolve({
  5939. range: linkInfo.range,
  5940. url: $linkUrl.val(),
  5941. text: $linkText.val(),
  5942. isNewWindow: $openInNewWindow.is(':checked')
  5943. });
  5944. _this.ui.hideDialog(_this.$dialog);
  5945. });
  5946. });
  5947. _this.ui.onDialogHidden(_this.$dialog, function () {
  5948. // detach events
  5949. $linkText.off('input paste keypress');
  5950. $linkUrl.off('input paste keypress');
  5951. $linkBtn.off('click');
  5952. if (deferred.state() === 'pending') {
  5953. deferred.reject();
  5954. }
  5955. });
  5956. _this.ui.showDialog(_this.$dialog);
  5957. }).promise();
  5958. };
  5959. /**
  5960. * @param {Object} layoutInfo
  5961. */
  5962. LinkDialog.prototype.show = function () {
  5963. var _this = this;
  5964. var linkInfo = this.context.invoke('editor.getLinkInfo');
  5965. this.context.invoke('editor.saveRange');
  5966. this.showLinkDialog(linkInfo).then(function (linkInfo) {
  5967. _this.context.invoke('editor.restoreRange');
  5968. _this.context.invoke('editor.createLink', linkInfo);
  5969. }).fail(function () {
  5970. _this.context.invoke('editor.restoreRange');
  5971. });
  5972. };
  5973. return LinkDialog;
  5974. }());
  5975. var LinkPopover = /** @class */ (function () {
  5976. function LinkPopover(context) {
  5977. var _this = this;
  5978. this.context = context;
  5979. this.ui = $$1.summernote.ui;
  5980. this.options = context.options;
  5981. this.events = {
  5982. 'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function () {
  5983. _this.update();
  5984. },
  5985. 'summernote.disable summernote.dialog.shown': function () {
  5986. _this.hide();
  5987. }
  5988. };
  5989. }
  5990. LinkPopover.prototype.shouldInitialize = function () {
  5991. return !lists.isEmpty(this.options.popover.link);
  5992. };
  5993. LinkPopover.prototype.initialize = function () {
  5994. this.$popover = this.ui.popover({
  5995. className: 'note-link-popover',
  5996. callback: function ($node) {
  5997. var $content = $node.find('.popover-content,.note-popover-content');
  5998. $content.prepend('<span><a target="_blank"></a>&nbsp;</span>');
  5999. }
  6000. }).render().appendTo(this.options.container);
  6001. var $content = this.$popover.find('.popover-content,.note-popover-content');
  6002. this.context.invoke('buttons.build', $content, this.options.popover.link);
  6003. };
  6004. LinkPopover.prototype.destroy = function () {
  6005. this.$popover.remove();
  6006. };
  6007. LinkPopover.prototype.update = function () {
  6008. // Prevent focusing on editable when invoke('code') is executed
  6009. if (!this.context.invoke('editor.hasFocus')) {
  6010. this.hide();
  6011. return;
  6012. }
  6013. var rng = this.context.invoke('editor.createRange');
  6014. if (rng.isCollapsed() && rng.isOnAnchor()) {
  6015. var anchor = dom.ancestor(rng.sc, dom.isAnchor);
  6016. var href = $$1(anchor).attr('href');
  6017. this.$popover.find('a').attr('href', href).html(href);
  6018. var pos = dom.posFromPlaceholder(anchor);
  6019. this.$popover.css({
  6020. display: 'block',
  6021. left: pos.left,
  6022. top: pos.top
  6023. });
  6024. }
  6025. else {
  6026. this.hide();
  6027. }
  6028. };
  6029. LinkPopover.prototype.hide = function () {
  6030. this.$popover.hide();
  6031. };
  6032. return LinkPopover;
  6033. }());
  6034. var ImageDialog = /** @class */ (function () {
  6035. function ImageDialog(context) {
  6036. this.context = context;
  6037. this.ui = $$1.summernote.ui;
  6038. this.$body = $$1(document.body);
  6039. this.$editor = context.layoutInfo.editor;
  6040. this.options = context.options;
  6041. this.lang = this.options.langInfo;
  6042. }
  6043. ImageDialog.prototype.initialize = function () {
  6044. var $container = this.options.dialogsInBody ? this.$body : this.$editor;
  6045. var imageLimitation = '';
  6046. if (this.options.maximumImageFileSize) {
  6047. var unit = Math.floor(Math.log(this.options.maximumImageFileSize) / Math.log(1024));
  6048. var readableSize = (this.options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
  6049. ' ' + ' KMGTP'[unit] + 'B';
  6050. imageLimitation = "<small>" + (this.lang.image.maximumFileSize + ' : ' + readableSize) + "</small>";
  6051. }
  6052. var body = [
  6053. '<div class="form-group note-form-group note-group-select-from-files">',
  6054. '<label class="note-form-label">' + this.lang.image.selectFromFiles + '</label>',
  6055. '<input class="note-image-input note-form-control note-input" ',
  6056. ' type="file" name="files" accept="image/*" multiple="multiple" />',
  6057. imageLimitation,
  6058. '</div>',
  6059. '<div class="form-group note-group-image-url" style="overflow:auto;">',
  6060. '<label class="note-form-label">' + this.lang.image.url + '</label>',
  6061. '<input class="note-image-url form-control note-form-control note-input ',
  6062. ' col-md-12" type="text" />',
  6063. '</div>'
  6064. ].join('');
  6065. var buttonClass = 'btn btn-primary note-btn note-btn-primary note-image-btn';
  6066. var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.image.insert + "</button>";
  6067. this.$dialog = this.ui.dialog({
  6068. title: this.lang.image.insert,
  6069. fade: this.options.dialogsFade,
  6070. body: body,
  6071. footer: footer
  6072. }).render().appendTo($container);
  6073. };
  6074. ImageDialog.prototype.destroy = function () {
  6075. this.ui.hideDialog(this.$dialog);
  6076. this.$dialog.remove();
  6077. };
  6078. ImageDialog.prototype.bindEnterKey = function ($input, $btn) {
  6079. $input.on('keypress', function (event) {
  6080. if (event.keyCode === key.code.ENTER) {
  6081. event.preventDefault();
  6082. $btn.trigger('click');
  6083. }
  6084. });
  6085. };
  6086. ImageDialog.prototype.show = function () {
  6087. var _this = this;
  6088. this.context.invoke('editor.saveRange');
  6089. this.showImageDialog().then(function (data) {
  6090. // [workaround] hide dialog before restore range for IE range focus
  6091. _this.ui.hideDialog(_this.$dialog);
  6092. _this.context.invoke('editor.restoreRange');
  6093. if (typeof data === 'string') {
  6094. _this.context.invoke('editor.insertImage', data);
  6095. }
  6096. else {
  6097. _this.context.invoke('editor.insertImagesOrCallback', data);
  6098. }
  6099. }).fail(function () {
  6100. _this.context.invoke('editor.restoreRange');
  6101. });
  6102. };
  6103. /**
  6104. * show image dialog
  6105. *
  6106. * @param {jQuery} $dialog
  6107. * @return {Promise}
  6108. */
  6109. ImageDialog.prototype.showImageDialog = function () {
  6110. var _this = this;
  6111. return $$1.Deferred(function (deferred) {
  6112. var $imageInput = _this.$dialog.find('.note-image-input');
  6113. var $imageUrl = _this.$dialog.find('.note-image-url');
  6114. var $imageBtn = _this.$dialog.find('.note-image-btn');
  6115. _this.ui.onDialogShown(_this.$dialog, function () {
  6116. _this.context.triggerEvent('dialog.shown');
  6117. // Cloning imageInput to clear element.
  6118. $imageInput.replaceWith($imageInput.clone().on('change', function (event) {
  6119. deferred.resolve(event.target.files || event.target.value);
  6120. }).val(''));
  6121. $imageBtn.click(function (event) {
  6122. event.preventDefault();
  6123. deferred.resolve($imageUrl.val());
  6124. });
  6125. $imageUrl.on('keyup paste', function () {
  6126. var url = $imageUrl.val();
  6127. _this.ui.toggleBtn($imageBtn, url);
  6128. }).val('');
  6129. if (!env.isSupportTouch) {
  6130. $imageUrl.trigger('focus');
  6131. }
  6132. _this.bindEnterKey($imageUrl, $imageBtn);
  6133. });
  6134. _this.ui.onDialogHidden(_this.$dialog, function () {
  6135. $imageInput.off('change');
  6136. $imageUrl.off('keyup paste keypress');
  6137. $imageBtn.off('click');
  6138. if (deferred.state() === 'pending') {
  6139. deferred.reject();
  6140. }
  6141. });
  6142. _this.ui.showDialog(_this.$dialog);
  6143. });
  6144. };
  6145. return ImageDialog;
  6146. }());
  6147. /**
  6148. * Image popover module
  6149. * mouse events that show/hide popover will be handled by Handle.js.
  6150. * Handle.js will receive the events and invoke 'imagePopover.update'.
  6151. */
  6152. var ImagePopover = /** @class */ (function () {
  6153. function ImagePopover(context) {
  6154. var _this = this;
  6155. this.context = context;
  6156. this.ui = $$1.summernote.ui;
  6157. this.editable = context.layoutInfo.editable[0];
  6158. this.options = context.options;
  6159. this.events = {
  6160. 'summernote.disable': function () {
  6161. _this.hide();
  6162. }
  6163. };
  6164. }
  6165. ImagePopover.prototype.shouldInitialize = function () {
  6166. return !lists.isEmpty(this.options.popover.image);
  6167. };
  6168. ImagePopover.prototype.initialize = function () {
  6169. this.$popover = this.ui.popover({
  6170. className: 'note-image-popover'
  6171. }).render().appendTo(this.options.container);
  6172. var $content = this.$popover.find('.popover-content,.note-popover-content');
  6173. this.context.invoke('buttons.build', $content, this.options.popover.image);
  6174. };
  6175. ImagePopover.prototype.destroy = function () {
  6176. this.$popover.remove();
  6177. };
  6178. ImagePopover.prototype.update = function (target) {
  6179. if (dom.isImg(target)) {
  6180. var pos = dom.posFromPlaceholder(target);
  6181. var posEditor = dom.posFromPlaceholder(this.editable);
  6182. this.$popover.css({
  6183. display: 'block',
  6184. left: this.options.popatmouse ? event.pageX - 20 : pos.left,
  6185. top: this.options.popatmouse ? event.pageY : Math.min(pos.top, posEditor.top)
  6186. });
  6187. }
  6188. else {
  6189. this.hide();
  6190. }
  6191. };
  6192. ImagePopover.prototype.hide = function () {
  6193. this.$popover.hide();
  6194. };
  6195. return ImagePopover;
  6196. }());
  6197. var TablePopover = /** @class */ (function () {
  6198. function TablePopover(context) {
  6199. var _this = this;
  6200. this.context = context;
  6201. this.ui = $$1.summernote.ui;
  6202. this.options = context.options;
  6203. this.events = {
  6204. 'summernote.mousedown': function (we, e) {
  6205. _this.update(e.target);
  6206. },
  6207. 'summernote.keyup summernote.scroll summernote.change': function () {
  6208. _this.update();
  6209. },
  6210. 'summernote.disable': function () {
  6211. _this.hide();
  6212. }
  6213. };
  6214. }
  6215. TablePopover.prototype.shouldInitialize = function () {
  6216. return !lists.isEmpty(this.options.popover.table);
  6217. };
  6218. TablePopover.prototype.initialize = function () {
  6219. this.$popover = this.ui.popover({
  6220. className: 'note-table-popover'
  6221. }).render().appendTo(this.options.container);
  6222. var $content = this.$popover.find('.popover-content,.note-popover-content');
  6223. this.context.invoke('buttons.build', $content, this.options.popover.table);
  6224. // [workaround] Disable Firefox's default table editor
  6225. if (env.isFF) {
  6226. document.execCommand('enableInlineTableEditing', false, false);
  6227. }
  6228. };
  6229. TablePopover.prototype.destroy = function () {
  6230. this.$popover.remove();
  6231. };
  6232. TablePopover.prototype.update = function (target) {
  6233. if (this.context.isDisabled()) {
  6234. return false;
  6235. }
  6236. var isCell = dom.isCell(target);
  6237. if (isCell) {
  6238. var pos = dom.posFromPlaceholder(target);
  6239. this.$popover.css({
  6240. display: 'block',
  6241. left: pos.left,
  6242. top: pos.top
  6243. });
  6244. }
  6245. else {
  6246. this.hide();
  6247. }
  6248. return isCell;
  6249. };
  6250. TablePopover.prototype.hide = function () {
  6251. this.$popover.hide();
  6252. };
  6253. return TablePopover;
  6254. }());
  6255. var VideoDialog = /** @class */ (function () {
  6256. function VideoDialog(context) {
  6257. this.context = context;
  6258. this.ui = $$1.summernote.ui;
  6259. this.$body = $$1(document.body);
  6260. this.$editor = context.layoutInfo.editor;
  6261. this.options = context.options;
  6262. this.lang = this.options.langInfo;
  6263. }
  6264. VideoDialog.prototype.initialize = function () {
  6265. var $container = this.options.dialogsInBody ? this.$body : this.$editor;
  6266. var body = [
  6267. '<div class="form-group note-form-group row-fluid">',
  6268. "<label class=\"note-form-label\">" + this.lang.video.url + " <small class=\"text-muted\">" + this.lang.video.providers + "</small></label>",
  6269. '<input class="note-video-url form-control note-form-control note-input" type="text" />',
  6270. '</div>'
  6271. ].join('');
  6272. var buttonClass = 'btn btn-primary note-btn note-btn-primary note-video-btn';
  6273. var footer = "<button type=\"submit\" href=\"#\" class=\"" + buttonClass + "\" disabled>" + this.lang.video.insert + "</button>";
  6274. this.$dialog = this.ui.dialog({
  6275. title: this.lang.video.insert,
  6276. fade: this.options.dialogsFade,
  6277. body: body,
  6278. footer: footer
  6279. }).render().appendTo($container);
  6280. };
  6281. VideoDialog.prototype.destroy = function () {
  6282. this.ui.hideDialog(this.$dialog);
  6283. this.$dialog.remove();
  6284. };
  6285. VideoDialog.prototype.bindEnterKey = function ($input, $btn) {
  6286. $input.on('keypress', function (event) {
  6287. if (event.keyCode === key.code.ENTER) {
  6288. event.preventDefault();
  6289. $btn.trigger('click');
  6290. }
  6291. });
  6292. };
  6293. VideoDialog.prototype.createVideoNode = function (url) {
  6294. // video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm)
  6295. var ytRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
  6296. var ytMatch = url.match(ytRegExp);
  6297. var igRegExp = /(?:www\.|\/\/)instagram\.com\/p\/(.[a-zA-Z0-9_-]*)/;
  6298. var igMatch = url.match(igRegExp);
  6299. var vRegExp = /\/\/vine\.co\/v\/([a-zA-Z0-9]+)/;
  6300. var vMatch = url.match(vRegExp);
  6301. var vimRegExp = /\/\/(player\.)?vimeo\.com\/([a-z]*\/)*(\d+)[?]?.*/;
  6302. var vimMatch = url.match(vimRegExp);
  6303. var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
  6304. var dmMatch = url.match(dmRegExp);
  6305. var youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/;
  6306. var youkuMatch = url.match(youkuRegExp);
  6307. var qqRegExp = /\/\/v\.qq\.com.*?vid=(.+)/;
  6308. var qqMatch = url.match(qqRegExp);
  6309. var qqRegExp2 = /\/\/v\.qq\.com\/x?\/?(page|cover).*?\/([^\/]+)\.html\??.*/;
  6310. var qqMatch2 = url.match(qqRegExp2);
  6311. var mp4RegExp = /^.+.(mp4|m4v)$/;
  6312. var mp4Match = url.match(mp4RegExp);
  6313. var oggRegExp = /^.+.(ogg|ogv)$/;
  6314. var oggMatch = url.match(oggRegExp);
  6315. var webmRegExp = /^.+.(webm)$/;
  6316. var webmMatch = url.match(webmRegExp);
  6317. var $video;
  6318. if (ytMatch && ytMatch[1].length === 11) {
  6319. var youtubeId = ytMatch[1];
  6320. $video = $$1('<iframe>')
  6321. .attr('frameborder', 0)
  6322. .attr('src', '//www.youtube.com/embed/' + youtubeId)
  6323. .attr('width', '640').attr('height', '360');
  6324. }
  6325. else if (igMatch && igMatch[0].length) {
  6326. $video = $$1('<iframe>')
  6327. .attr('frameborder', 0)
  6328. .attr('src', 'https://instagram.com/p/' + igMatch[1] + '/embed/')
  6329. .attr('width', '612').attr('height', '710')
  6330. .attr('scrolling', 'no')
  6331. .attr('allowtransparency', 'true');
  6332. }
  6333. else if (vMatch && vMatch[0].length) {
  6334. $video = $$1('<iframe>')
  6335. .attr('frameborder', 0)
  6336. .attr('src', vMatch[0] + '/embed/simple')
  6337. .attr('width', '600').attr('height', '600')
  6338. .attr('class', 'vine-embed');
  6339. }
  6340. else if (vimMatch && vimMatch[3].length) {
  6341. $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
  6342. .attr('frameborder', 0)
  6343. .attr('src', '//player.vimeo.com/video/' + vimMatch[3])
  6344. .attr('width', '640').attr('height', '360');
  6345. }
  6346. else if (dmMatch && dmMatch[2].length) {
  6347. $video = $$1('<iframe>')
  6348. .attr('frameborder', 0)
  6349. .attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
  6350. .attr('width', '640').attr('height', '360');
  6351. }
  6352. else if (youkuMatch && youkuMatch[1].length) {
  6353. $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
  6354. .attr('frameborder', 0)
  6355. .attr('height', '498')
  6356. .attr('width', '510')
  6357. .attr('src', '//player.youku.com/embed/' + youkuMatch[1]);
  6358. }
  6359. else if ((qqMatch && qqMatch[1].length) || (qqMatch2 && qqMatch2[2].length)) {
  6360. var vid = ((qqMatch && qqMatch[1].length) ? qqMatch[1] : qqMatch2[2]);
  6361. $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
  6362. .attr('frameborder', 0)
  6363. .attr('height', '310')
  6364. .attr('width', '500')
  6365. .attr('src', 'http://v.qq.com/iframe/player.html?vid=' + vid + '&amp;auto=0');
  6366. }
  6367. else if (mp4Match || oggMatch || webmMatch) {
  6368. $video = $$1('<video controls>')
  6369. .attr('src', url)
  6370. .attr('width', '640').attr('height', '360');
  6371. }
  6372. else {
  6373. // this is not a known video link. Now what, Cat? Now what?
  6374. return false;
  6375. }
  6376. $video.addClass('note-video-clip');
  6377. return $video[0];
  6378. };
  6379. VideoDialog.prototype.show = function () {
  6380. var _this = this;
  6381. var text = this.context.invoke('editor.getSelectedText');
  6382. this.context.invoke('editor.saveRange');
  6383. this.showVideoDialog(text).then(function (url) {
  6384. // [workaround] hide dialog before restore range for IE range focus
  6385. _this.ui.hideDialog(_this.$dialog);
  6386. _this.context.invoke('editor.restoreRange');
  6387. // build node
  6388. var $node = _this.createVideoNode(url);
  6389. if ($node) {
  6390. // insert video node
  6391. _this.context.invoke('editor.insertNode', $node);
  6392. }
  6393. }).fail(function () {
  6394. _this.context.invoke('editor.restoreRange');
  6395. });
  6396. };
  6397. /**
  6398. * show image dialog
  6399. *
  6400. * @param {jQuery} $dialog
  6401. * @return {Promise}
  6402. */
  6403. VideoDialog.prototype.showVideoDialog = function (text) {
  6404. var _this = this;
  6405. return $$1.Deferred(function (deferred) {
  6406. var $videoUrl = _this.$dialog.find('.note-video-url');
  6407. var $videoBtn = _this.$dialog.find('.note-video-btn');
  6408. _this.ui.onDialogShown(_this.$dialog, function () {
  6409. _this.context.triggerEvent('dialog.shown');
  6410. $videoUrl.val(text).on('input', function () {
  6411. _this.ui.toggleBtn($videoBtn, $videoUrl.val());
  6412. });
  6413. if (!env.isSupportTouch) {
  6414. $videoUrl.trigger('focus');
  6415. }
  6416. $videoBtn.click(function (event) {
  6417. event.preventDefault();
  6418. deferred.resolve($videoUrl.val());
  6419. });
  6420. _this.bindEnterKey($videoUrl, $videoBtn);
  6421. });
  6422. _this.ui.onDialogHidden(_this.$dialog, function () {
  6423. $videoUrl.off('input');
  6424. $videoBtn.off('click');
  6425. if (deferred.state() === 'pending') {
  6426. deferred.reject();
  6427. }
  6428. });
  6429. _this.ui.showDialog(_this.$dialog);
  6430. });
  6431. };
  6432. return VideoDialog;
  6433. }());
  6434. var HelpDialog = /** @class */ (function () {
  6435. function HelpDialog(context) {
  6436. this.context = context;
  6437. this.ui = $$1.summernote.ui;
  6438. this.$body = $$1(document.body);
  6439. this.$editor = context.layoutInfo.editor;
  6440. this.options = context.options;
  6441. this.lang = this.options.langInfo;
  6442. }
  6443. HelpDialog.prototype.initialize = function () {
  6444. var $container = this.options.dialogsInBody ? this.$body : this.$editor;
  6445. var body = [
  6446. '<p class="text-center">',
  6447. '<a href="http://summernote.org/" target="_blank">Summernote 0.8.9</a> · ',
  6448. '<a href="https://github.com/summernote/summernote" target="_blank">Project</a> · ',
  6449. '<a href="https://github.com/summernote/summernote/issues" target="_blank">Issues</a>',
  6450. '</p>'
  6451. ].join('');
  6452. this.$dialog = this.ui.dialog({
  6453. title: this.lang.options.help,
  6454. fade: this.options.dialogsFade,
  6455. body: this.createShortcutList(),
  6456. footer: body,
  6457. callback: function ($node) {
  6458. $node.find('.modal-body,.note-modal-body').css({
  6459. 'max-height': 300,
  6460. 'overflow': 'scroll'
  6461. });
  6462. }
  6463. }).render().appendTo($container);
  6464. };
  6465. HelpDialog.prototype.destroy = function () {
  6466. this.ui.hideDialog(this.$dialog);
  6467. this.$dialog.remove();
  6468. };
  6469. HelpDialog.prototype.createShortcutList = function () {
  6470. var _this = this;
  6471. var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
  6472. return Object.keys(keyMap).map(function (key) {
  6473. var command = keyMap[key];
  6474. var $row = $$1('<div><div class="help-list-item"/></div>');
  6475. $row.append($$1('<label><kbd>' + key + '</kdb></label>').css({
  6476. 'width': 180,
  6477. 'margin-right': 10
  6478. })).append($$1('<span/>').html(_this.context.memo('help.' + command) || command));
  6479. return $row.html();
  6480. }).join('');
  6481. };
  6482. /**
  6483. * show help dialog
  6484. *
  6485. * @return {Promise}
  6486. */
  6487. HelpDialog.prototype.showHelpDialog = function () {
  6488. var _this = this;
  6489. return $$1.Deferred(function (deferred) {
  6490. _this.ui.onDialogShown(_this.$dialog, function () {
  6491. _this.context.triggerEvent('dialog.shown');
  6492. deferred.resolve();
  6493. });
  6494. _this.ui.showDialog(_this.$dialog);
  6495. }).promise();
  6496. };
  6497. HelpDialog.prototype.show = function () {
  6498. var _this = this;
  6499. this.context.invoke('editor.saveRange');
  6500. this.showHelpDialog().then(function () {
  6501. _this.context.invoke('editor.restoreRange');
  6502. });
  6503. };
  6504. return HelpDialog;
  6505. }());
  6506. var AIR_MODE_POPOVER_X_OFFSET = 20;
  6507. var AirPopover = /** @class */ (function () {
  6508. function AirPopover(context) {
  6509. var _this = this;
  6510. this.context = context;
  6511. this.ui = $$1.summernote.ui;
  6512. this.options = context.options;
  6513. this.events = {
  6514. 'summernote.keyup summernote.mouseup summernote.scroll': function () {
  6515. _this.update();
  6516. },
  6517. 'summernote.disable summernote.change summernote.dialog.shown': function () {
  6518. _this.hide();
  6519. },
  6520. 'summernote.focusout': function (we, e) {
  6521. // [workaround] Firefox doesn't support relatedTarget on focusout
  6522. // - Ignore hide action on focus out in FF.
  6523. if (env.isFF) {
  6524. return;
  6525. }
  6526. if (!e.relatedTarget || !dom.ancestor(e.relatedTarget, func.eq(_this.$popover[0]))) {
  6527. _this.hide();
  6528. }
  6529. }
  6530. };
  6531. }
  6532. AirPopover.prototype.shouldInitialize = function () {
  6533. return this.options.airMode && !lists.isEmpty(this.options.popover.air);
  6534. };
  6535. AirPopover.prototype.initialize = function () {
  6536. this.$popover = this.ui.popover({
  6537. className: 'note-air-popover'
  6538. }).render().appendTo(this.options.container);
  6539. var $content = this.$popover.find('.popover-content');
  6540. this.context.invoke('buttons.build', $content, this.options.popover.air);
  6541. };
  6542. AirPopover.prototype.destroy = function () {
  6543. this.$popover.remove();
  6544. };
  6545. AirPopover.prototype.update = function () {
  6546. var styleInfo = this.context.invoke('editor.currentStyle');
  6547. if (styleInfo.range && !styleInfo.range.isCollapsed()) {
  6548. var rect = lists.last(styleInfo.range.getClientRects());
  6549. if (rect) {
  6550. var bnd = func.rect2bnd(rect);
  6551. this.$popover.css({
  6552. display: 'block',
  6553. left: Math.max(bnd.left + bnd.width / 2, 0) - AIR_MODE_POPOVER_X_OFFSET,
  6554. top: bnd.top + bnd.height
  6555. });
  6556. this.context.invoke('buttons.updateCurrentStyle', this.$popover);
  6557. }
  6558. }
  6559. else {
  6560. this.hide();
  6561. }
  6562. };
  6563. AirPopover.prototype.hide = function () {
  6564. this.$popover.hide();
  6565. };
  6566. return AirPopover;
  6567. }());
  6568. var POPOVER_DIST = 5;
  6569. var HintPopover = /** @class */ (function () {
  6570. function HintPopover(context) {
  6571. var _this = this;
  6572. this.context = context;
  6573. this.ui = $$1.summernote.ui;
  6574. this.$editable = context.layoutInfo.editable;
  6575. this.options = context.options;
  6576. this.hint = this.options.hint || [];
  6577. this.direction = this.options.hintDirection || 'bottom';
  6578. this.hints = $$1.isArray(this.hint) ? this.hint : [this.hint];
  6579. this.events = {
  6580. 'summernote.keyup': function (we, e) {
  6581. if (!e.isDefaultPrevented()) {
  6582. _this.handleKeyup(e);
  6583. }
  6584. },
  6585. 'summernote.keydown': function (we, e) {
  6586. _this.handleKeydown(e);
  6587. },
  6588. 'summernote.disable summernote.dialog.shown': function () {
  6589. _this.hide();
  6590. }
  6591. };
  6592. }
  6593. HintPopover.prototype.shouldInitialize = function () {
  6594. return this.hints.length > 0;
  6595. };
  6596. HintPopover.prototype.initialize = function () {
  6597. var _this = this;
  6598. this.lastWordRange = null;
  6599. this.$popover = this.ui.popover({
  6600. className: 'note-hint-popover',
  6601. hideArrow: true,
  6602. direction: ''
  6603. }).render().appendTo(this.options.container);
  6604. this.$popover.hide();
  6605. this.$content = this.$popover.find('.popover-content,.note-popover-content');
  6606. this.$content.on('click', '.note-hint-item', function () {
  6607. _this.$content.find('.active').removeClass('active');
  6608. $$1(_this).addClass('active');
  6609. _this.replace();
  6610. });
  6611. };
  6612. HintPopover.prototype.destroy = function () {
  6613. this.$popover.remove();
  6614. };
  6615. HintPopover.prototype.selectItem = function ($item) {
  6616. this.$content.find('.active').removeClass('active');
  6617. $item.addClass('active');
  6618. this.$content[0].scrollTop = $item[0].offsetTop - (this.$content.innerHeight() / 2);
  6619. };
  6620. HintPopover.prototype.moveDown = function () {
  6621. var $current = this.$content.find('.note-hint-item.active');
  6622. var $next = $current.next();
  6623. if ($next.length) {
  6624. this.selectItem($next);
  6625. }
  6626. else {
  6627. var $nextGroup = $current.parent().next();
  6628. if (!$nextGroup.length) {
  6629. $nextGroup = this.$content.find('.note-hint-group').first();
  6630. }
  6631. this.selectItem($nextGroup.find('.note-hint-item').first());
  6632. }
  6633. };
  6634. HintPopover.prototype.moveUp = function () {
  6635. var $current = this.$content.find('.note-hint-item.active');
  6636. var $prev = $current.prev();
  6637. if ($prev.length) {
  6638. this.selectItem($prev);
  6639. }
  6640. else {
  6641. var $prevGroup = $current.parent().prev();
  6642. if (!$prevGroup.length) {
  6643. $prevGroup = this.$content.find('.note-hint-group').last();
  6644. }
  6645. this.selectItem($prevGroup.find('.note-hint-item').last());
  6646. }
  6647. };
  6648. HintPopover.prototype.replace = function () {
  6649. var $item = this.$content.find('.note-hint-item.active');
  6650. if ($item.length) {
  6651. var node = this.nodeFromItem($item);
  6652. // XXX: consider to move codes to editor for recording redo/undo.
  6653. this.lastWordRange.insertNode(node);
  6654. range.createFromNode(node).collapse().select();
  6655. this.lastWordRange = null;
  6656. this.hide();
  6657. this.context.triggerEvent('change', this.$editable.html(), this.$editable[0]);
  6658. this.context.invoke('editor.focus');
  6659. }
  6660. };
  6661. HintPopover.prototype.nodeFromItem = function ($item) {
  6662. var hint = this.hints[$item.data('index')];
  6663. var item = $item.data('item');
  6664. var node = hint.content ? hint.content(item) : item;
  6665. if (typeof node === 'string') {
  6666. node = dom.createText(node);
  6667. }
  6668. return node;
  6669. };
  6670. HintPopover.prototype.createItemTemplates = function (hintIdx, items) {
  6671. var hint = this.hints[hintIdx];
  6672. return items.map(function (item, idx) {
  6673. var $item = $$1('<div class="note-hint-item"/>');
  6674. $item.append(hint.template ? hint.template(item) : item + '');
  6675. $item.data({
  6676. 'index': hintIdx,
  6677. 'item': item
  6678. });
  6679. return $item;
  6680. });
  6681. };
  6682. HintPopover.prototype.handleKeydown = function (e) {
  6683. if (!this.$popover.is(':visible')) {
  6684. return;
  6685. }
  6686. if (e.keyCode === key.code.ENTER) {
  6687. e.preventDefault();
  6688. this.replace();
  6689. }
  6690. else if (e.keyCode === key.code.UP) {
  6691. e.preventDefault();
  6692. this.moveUp();
  6693. }
  6694. else if (e.keyCode === key.code.DOWN) {
  6695. e.preventDefault();
  6696. this.moveDown();
  6697. }
  6698. };
  6699. HintPopover.prototype.searchKeyword = function (index, keyword, callback) {
  6700. var hint = this.hints[index];
  6701. if (hint && hint.match.test(keyword) && hint.search) {
  6702. var matches = hint.match.exec(keyword);
  6703. hint.search(matches[1], callback);
  6704. }
  6705. else {
  6706. callback();
  6707. }
  6708. };
  6709. HintPopover.prototype.createGroup = function (idx, keyword) {
  6710. var _this = this;
  6711. var $group = $$1('<div class="note-hint-group note-hint-group-' + idx + '"/>');
  6712. this.searchKeyword(idx, keyword, function (items) {
  6713. items = items || [];
  6714. if (items.length) {
  6715. $group.html(_this.createItemTemplates(idx, items));
  6716. _this.show();
  6717. }
  6718. });
  6719. return $group;
  6720. };
  6721. HintPopover.prototype.handleKeyup = function (e) {
  6722. var _this = this;
  6723. if (!lists.contains([key.code.ENTER, key.code.UP, key.code.DOWN], e.keyCode)) {
  6724. var wordRange = this.context.invoke('editor.createRange').getWordRange();
  6725. var keyword_1 = wordRange.toString();
  6726. if (this.hints.length && keyword_1) {
  6727. this.$content.empty();
  6728. var bnd = func.rect2bnd(lists.last(wordRange.getClientRects()));
  6729. if (bnd) {
  6730. this.$popover.hide();
  6731. this.lastWordRange = wordRange;
  6732. this.hints.forEach(function (hint, idx) {
  6733. if (hint.match.test(keyword_1)) {
  6734. _this.createGroup(idx, keyword_1).appendTo(_this.$content);
  6735. }
  6736. });
  6737. // select first .note-hint-item
  6738. this.$content.find('.note-hint-item:first').addClass('active');
  6739. // set position for popover after group is created
  6740. if (this.direction === 'top') {
  6741. this.$popover.css({
  6742. left: bnd.left,
  6743. top: bnd.top - this.$popover.outerHeight() - POPOVER_DIST
  6744. });
  6745. }
  6746. else {
  6747. this.$popover.css({
  6748. left: bnd.left,
  6749. top: bnd.top + bnd.height + POPOVER_DIST
  6750. });
  6751. }
  6752. }
  6753. }
  6754. else {
  6755. this.hide();
  6756. }
  6757. }
  6758. };
  6759. HintPopover.prototype.show = function () {
  6760. this.$popover.show();
  6761. };
  6762. HintPopover.prototype.hide = function () {
  6763. this.$popover.hide();
  6764. };
  6765. return HintPopover;
  6766. }());
  6767. var Context = /** @class */ (function () {
  6768. /**
  6769. * @param {jQuery} $note
  6770. * @param {Object} options
  6771. */
  6772. function Context($note, options) {
  6773. this.ui = $$1.summernote.ui;
  6774. this.$note = $note;
  6775. this.memos = {};
  6776. this.modules = {};
  6777. this.layoutInfo = {};
  6778. this.options = options;
  6779. this.initialize();
  6780. }
  6781. /**
  6782. * create layout and initialize modules and other resources
  6783. */
  6784. Context.prototype.initialize = function () {
  6785. this.layoutInfo = this.ui.createLayout(this.$note, this.options);
  6786. this._initialize();
  6787. this.$note.hide();
  6788. return this;
  6789. };
  6790. /**
  6791. * destroy modules and other resources and remove layout
  6792. */
  6793. Context.prototype.destroy = function () {
  6794. this._destroy();
  6795. this.$note.removeData('summernote');
  6796. this.ui.removeLayout(this.$note, this.layoutInfo);
  6797. };
  6798. /**
  6799. * destory modules and other resources and initialize it again
  6800. */
  6801. Context.prototype.reset = function () {
  6802. var disabled = this.isDisabled();
  6803. this.code(dom.emptyPara);
  6804. this._destroy();
  6805. this._initialize();
  6806. if (disabled) {
  6807. this.disable();
  6808. }
  6809. };
  6810. Context.prototype._initialize = function () {
  6811. var _this = this;
  6812. // add optional buttons
  6813. var buttons = $$1.extend({}, this.options.buttons);
  6814. Object.keys(buttons).forEach(function (key) {
  6815. _this.memo('button.' + key, buttons[key]);
  6816. });
  6817. var modules = $$1.extend({}, this.options.modules, $$1.summernote.plugins || {});
  6818. // add and initialize modules
  6819. Object.keys(modules).forEach(function (key) {
  6820. _this.module(key, modules[key], true);
  6821. });
  6822. Object.keys(this.modules).forEach(function (key) {
  6823. _this.initializeModule(key);
  6824. });
  6825. };
  6826. Context.prototype._destroy = function () {
  6827. var _this = this;
  6828. // destroy modules with reversed order
  6829. Object.keys(this.modules).reverse().forEach(function (key) {
  6830. _this.removeModule(key);
  6831. });
  6832. Object.keys(this.memos).forEach(function (key) {
  6833. _this.removeMemo(key);
  6834. });
  6835. // trigger custom onDestroy callback
  6836. this.triggerEvent('destroy', this);
  6837. };
  6838. Context.prototype.code = function (html) {
  6839. var isActivated = this.invoke('codeview.isActivated');
  6840. if (html === undefined) {
  6841. this.invoke('codeview.sync');
  6842. return isActivated ? this.layoutInfo.codable.val() : this.layoutInfo.editable.html();
  6843. }
  6844. else {
  6845. if (isActivated) {
  6846. this.layoutInfo.codable.val(html);
  6847. }
  6848. else {
  6849. this.layoutInfo.editable.html(html);
  6850. }
  6851. this.$note.val(html);
  6852. this.triggerEvent('change', html);
  6853. }
  6854. };
  6855. Context.prototype.isDisabled = function () {
  6856. return this.layoutInfo.editable.attr('contenteditable') === 'false';
  6857. };
  6858. Context.prototype.enable = function () {
  6859. this.layoutInfo.editable.attr('contenteditable', true);
  6860. this.invoke('toolbar.activate', true);
  6861. this.triggerEvent('disable', false);
  6862. };
  6863. Context.prototype.disable = function () {
  6864. // close codeview if codeview is opend
  6865. if (this.invoke('codeview.isActivated')) {
  6866. this.invoke('codeview.deactivate');
  6867. }
  6868. this.layoutInfo.editable.attr('contenteditable', false);
  6869. this.invoke('toolbar.deactivate', true);
  6870. this.triggerEvent('disable', true);
  6871. };
  6872. Context.prototype.triggerEvent = function () {
  6873. var namespace = lists.head(arguments);
  6874. var args = lists.tail(lists.from(arguments));
  6875. var callback = this.options.callbacks[func.namespaceToCamel(namespace, 'on')];
  6876. if (callback) {
  6877. callback.apply(this.$note[0], args);
  6878. }
  6879. this.$note.trigger('summernote.' + namespace, args);
  6880. };
  6881. Context.prototype.initializeModule = function (key) {
  6882. var module = this.modules[key];
  6883. module.shouldInitialize = module.shouldInitialize || func.ok;
  6884. if (!module.shouldInitialize()) {
  6885. return;
  6886. }
  6887. // initialize module
  6888. if (module.initialize) {
  6889. module.initialize();
  6890. }
  6891. // attach events
  6892. if (module.events) {
  6893. dom.attachEvents(this.$note, module.events);
  6894. }
  6895. };
  6896. Context.prototype.module = function (key, ModuleClass, withoutIntialize) {
  6897. if (arguments.length === 1) {
  6898. return this.modules[key];
  6899. }
  6900. this.modules[key] = new ModuleClass(this);
  6901. if (!withoutIntialize) {
  6902. this.initializeModule(key);
  6903. }
  6904. };
  6905. Context.prototype.removeModule = function (key) {
  6906. var module = this.modules[key];
  6907. if (module.shouldInitialize()) {
  6908. if (module.events) {
  6909. dom.detachEvents(this.$note, module.events);
  6910. }
  6911. if (module.destroy) {
  6912. module.destroy();
  6913. }
  6914. }
  6915. delete this.modules[key];
  6916. };
  6917. Context.prototype.memo = function (key, obj) {
  6918. if (arguments.length === 1) {
  6919. return this.memos[key];
  6920. }
  6921. this.memos[key] = obj;
  6922. };
  6923. Context.prototype.removeMemo = function (key) {
  6924. if (this.memos[key] && this.memos[key].destroy) {
  6925. this.memos[key].destroy();
  6926. }
  6927. delete this.memos[key];
  6928. };
  6929. /**
  6930. * Some buttons need to change their visual style immediately once they get pressed
  6931. */
  6932. Context.prototype.createInvokeHandlerAndUpdateState = function (namespace, value) {
  6933. var _this = this;
  6934. return function (event) {
  6935. _this.createInvokeHandler(namespace, value)(event);
  6936. _this.invoke('buttons.updateCurrentStyle');
  6937. };
  6938. };
  6939. Context.prototype.createInvokeHandler = function (namespace, value) {
  6940. var _this = this;
  6941. return function (event) {
  6942. event.preventDefault();
  6943. var $target = $$1(event.target);
  6944. _this.invoke(namespace, value || $target.closest('[data-value]').data('value'), $target);
  6945. };
  6946. };
  6947. Context.prototype.invoke = function () {
  6948. var namespace = lists.head(arguments);
  6949. var args = lists.tail(lists.from(arguments));
  6950. var splits = namespace.split('.');
  6951. var hasSeparator = splits.length > 1;
  6952. var moduleName = hasSeparator && lists.head(splits);
  6953. var methodName = hasSeparator ? lists.last(splits) : lists.head(splits);
  6954. var module = this.modules[moduleName || 'editor'];
  6955. if (!moduleName && this[methodName]) {
  6956. return this[methodName].apply(this, args);
  6957. }
  6958. else if (module && module[methodName] && module.shouldInitialize()) {
  6959. return module[methodName].apply(module, args);
  6960. }
  6961. };
  6962. return Context;
  6963. }());
  6964. $$1.fn.extend({
  6965. /**
  6966. * Summernote API
  6967. *
  6968. * @param {Object|String}
  6969. * @return {this}
  6970. */
  6971. summernote: function () {
  6972. var type = $$1.type(lists.head(arguments));
  6973. var isExternalAPICalled = type === 'string';
  6974. var hasInitOptions = type === 'object';
  6975. var options = $$1.extend({}, $$1.summernote.options, hasInitOptions ? lists.head(arguments) : {});
  6976. // Update options
  6977. options.langInfo = $$1.extend(true, {}, $$1.summernote.lang['en-US'], $$1.summernote.lang[options.lang]);
  6978. options.icons = $$1.extend(true, {}, $$1.summernote.options.icons, options.icons);
  6979. options.tooltip = options.tooltip === 'auto' ? !env.isSupportTouch : options.tooltip;
  6980. this.each(function (idx, note) {
  6981. var $note = $$1(note);
  6982. if (!$note.data('summernote')) {
  6983. var context = new Context($note, options);
  6984. $note.data('summernote', context);
  6985. $note.data('summernote').triggerEvent('init', context.layoutInfo);
  6986. }
  6987. });
  6988. var $note = this.first();
  6989. if ($note.length) {
  6990. var context = $note.data('summernote');
  6991. if (isExternalAPICalled) {
  6992. return context.invoke.apply(context, lists.from(arguments));
  6993. }
  6994. else if (options.focus) {
  6995. context.invoke('editor.focus');
  6996. }
  6997. }
  6998. return this;
  6999. }
  7000. });
  7001. $$1.summernote = $$1.extend($$1.summernote, {
  7002. version: '0.8.9',
  7003. ui: ui,
  7004. dom: dom,
  7005. plugins: {},
  7006. options: {
  7007. modules: {
  7008. 'editor': Editor,
  7009. 'clipboard': Clipboard,
  7010. 'dropzone': Dropzone,
  7011. 'codeview': CodeView,
  7012. 'statusbar': Statusbar,
  7013. 'fullscreen': Fullscreen,
  7014. 'handle': Handle,
  7015. // FIXME: HintPopover must be front of autolink
  7016. // - Script error about range when Enter key is pressed on hint popover
  7017. 'hintPopover': HintPopover,
  7018. 'autoLink': AutoLink,
  7019. 'autoSync': AutoSync,
  7020. 'placeholder': Placeholder,
  7021. 'buttons': Buttons,
  7022. 'toolbar': Toolbar,
  7023. 'linkDialog': LinkDialog,
  7024. 'linkPopover': LinkPopover,
  7025. 'imageDialog': ImageDialog,
  7026. 'imagePopover': ImagePopover,
  7027. 'tablePopover': TablePopover,
  7028. 'videoDialog': VideoDialog,
  7029. 'helpDialog': HelpDialog,
  7030. 'airPopover': AirPopover
  7031. },
  7032. buttons: {},
  7033. lang: 'en-US',
  7034. followingToolbar: true,
  7035. otherStaticBar: '',
  7036. // toolbar
  7037. toolbar: [
  7038. ['style', ['style']],
  7039. ['font', ['bold', 'underline', 'clear']],
  7040. ['fontname', ['fontname']],
  7041. ['color', ['color']],
  7042. ['para', ['ul', 'ol', 'paragraph']],
  7043. ['table', ['table']],
  7044. ['insert', ['link', 'picture', 'video']],
  7045. ['view', ['fullscreen', 'codeview', 'help']]
  7046. ],
  7047. // popover
  7048. popatmouse: true,
  7049. popover: {
  7050. image: [
  7051. ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
  7052. ['float', ['floatLeft', 'floatRight', 'floatNone']],
  7053. ['remove', ['removeMedia']]
  7054. ],
  7055. link: [
  7056. ['link', ['linkDialogShow', 'unlink']]
  7057. ],
  7058. table: [
  7059. ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
  7060. ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
  7061. ],
  7062. air: [
  7063. ['color', ['color']],
  7064. ['font', ['bold', 'underline', 'clear']],
  7065. ['para', ['ul', 'paragraph']],
  7066. ['table', ['table']],
  7067. ['insert', ['link', 'picture']]
  7068. ]
  7069. },
  7070. // air mode: inline editor
  7071. airMode: false,
  7072. width: null,
  7073. height: null,
  7074. linkTargetBlank: true,
  7075. focus: false,
  7076. tabSize: 4,
  7077. styleWithSpan: true,
  7078. shortcuts: true,
  7079. textareaAutoSync: true,
  7080. hintDirection: 'bottom',
  7081. tooltip: 'auto',
  7082. container: 'body',
  7083. maxTextLength: 0,
  7084. styleTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
  7085. fontNames: [
  7086. 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
  7087. 'Helvetica Neue', 'Helvetica', 'Impact', 'Lucida Grande',
  7088. 'Tahoma', 'Times New Roman', 'Verdana'
  7089. ],
  7090. fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
  7091. // pallete colors(n x n)
  7092. colors: [
  7093. ['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
  7094. ['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
  7095. ['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
  7096. ['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
  7097. ['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
  7098. ['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
  7099. ['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
  7100. ['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
  7101. ],
  7102. lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
  7103. tableClassName: 'table table-bordered',
  7104. insertTableMaxSize: {
  7105. col: 10,
  7106. row: 10
  7107. },
  7108. dialogsInBody: false,
  7109. dialogsFade: false,
  7110. maximumImageFileSize: null,
  7111. callbacks: {
  7112. onInit: null,
  7113. onFocus: null,
  7114. onBlur: null,
  7115. onBlurCodeview: null,
  7116. onEnter: null,
  7117. onKeyup: null,
  7118. onKeydown: null,
  7119. onImageUpload: null,
  7120. onImageUploadError: null
  7121. },
  7122. codemirror: {
  7123. mode: 'text/html',
  7124. htmlMode: true,
  7125. lineNumbers: true
  7126. },
  7127. keyMap: {
  7128. pc: {
  7129. 'ENTER': 'insertParagraph',
  7130. 'CTRL+Z': 'undo',
  7131. 'CTRL+Y': 'redo',
  7132. 'TAB': 'tab',
  7133. 'SHIFT+TAB': 'untab',
  7134. 'CTRL+B': 'bold',
  7135. 'CTRL+I': 'italic',
  7136. 'CTRL+U': 'underline',
  7137. 'CTRL+SHIFT+S': 'strikethrough',
  7138. 'CTRL+BACKSLASH': 'removeFormat',
  7139. 'CTRL+SHIFT+L': 'justifyLeft',
  7140. 'CTRL+SHIFT+E': 'justifyCenter',
  7141. 'CTRL+SHIFT+R': 'justifyRight',
  7142. 'CTRL+SHIFT+J': 'justifyFull',
  7143. 'CTRL+SHIFT+NUM7': 'insertUnorderedList',
  7144. 'CTRL+SHIFT+NUM8': 'insertOrderedList',
  7145. 'CTRL+LEFTBRACKET': 'outdent',
  7146. 'CTRL+RIGHTBRACKET': 'indent',
  7147. 'CTRL+NUM0': 'formatPara',
  7148. 'CTRL+NUM1': 'formatH1',
  7149. 'CTRL+NUM2': 'formatH2',
  7150. 'CTRL+NUM3': 'formatH3',
  7151. 'CTRL+NUM4': 'formatH4',
  7152. 'CTRL+NUM5': 'formatH5',
  7153. 'CTRL+NUM6': 'formatH6',
  7154. 'CTRL+ENTER': 'insertHorizontalRule',
  7155. 'CTRL+K': 'linkDialog.show'
  7156. },
  7157. mac: {
  7158. 'ENTER': 'insertParagraph',
  7159. 'CMD+Z': 'undo',
  7160. 'CMD+SHIFT+Z': 'redo',
  7161. 'TAB': 'tab',
  7162. 'SHIFT+TAB': 'untab',
  7163. 'CMD+B': 'bold',
  7164. 'CMD+I': 'italic',
  7165. 'CMD+U': 'underline',
  7166. 'CMD+SHIFT+S': 'strikethrough',
  7167. 'CMD+BACKSLASH': 'removeFormat',
  7168. 'CMD+SHIFT+L': 'justifyLeft',
  7169. 'CMD+SHIFT+E': 'justifyCenter',
  7170. 'CMD+SHIFT+R': 'justifyRight',
  7171. 'CMD+SHIFT+J': 'justifyFull',
  7172. 'CMD+SHIFT+NUM7': 'insertUnorderedList',
  7173. 'CMD+SHIFT+NUM8': 'insertOrderedList',
  7174. 'CMD+LEFTBRACKET': 'outdent',
  7175. 'CMD+RIGHTBRACKET': 'indent',
  7176. 'CMD+NUM0': 'formatPara',
  7177. 'CMD+NUM1': 'formatH1',
  7178. 'CMD+NUM2': 'formatH2',
  7179. 'CMD+NUM3': 'formatH3',
  7180. 'CMD+NUM4': 'formatH4',
  7181. 'CMD+NUM5': 'formatH5',
  7182. 'CMD+NUM6': 'formatH6',
  7183. 'CMD+ENTER': 'insertHorizontalRule',
  7184. 'CMD+K': 'linkDialog.show'
  7185. }
  7186. },
  7187. icons: {
  7188. 'align': 'note-icon-align',
  7189. 'alignCenter': 'note-icon-align-center',
  7190. 'alignJustify': 'note-icon-align-justify',
  7191. 'alignLeft': 'note-icon-align-left',
  7192. 'alignRight': 'note-icon-align-right',
  7193. 'rowBelow': 'note-icon-row-below',
  7194. 'colBefore': 'note-icon-col-before',
  7195. 'colAfter': 'note-icon-col-after',
  7196. 'rowAbove': 'note-icon-row-above',
  7197. 'rowRemove': 'note-icon-row-remove',
  7198. 'colRemove': 'note-icon-col-remove',
  7199. 'indent': 'note-icon-align-indent',
  7200. 'outdent': 'note-icon-align-outdent',
  7201. 'arrowsAlt': 'note-icon-arrows-alt',
  7202. 'bold': 'note-icon-bold',
  7203. 'caret': 'note-icon-caret',
  7204. 'circle': 'note-icon-circle',
  7205. 'close': 'note-icon-close',
  7206. 'code': 'note-icon-code',
  7207. 'eraser': 'note-icon-eraser',
  7208. 'font': 'note-icon-font',
  7209. 'frame': 'note-icon-frame',
  7210. 'italic': 'note-icon-italic',
  7211. 'link': 'note-icon-link',
  7212. 'unlink': 'note-icon-chain-broken',
  7213. 'magic': 'note-icon-magic',
  7214. 'menuCheck': 'note-icon-menu-check',
  7215. 'minus': 'note-icon-minus',
  7216. 'orderedlist': 'note-icon-orderedlist',
  7217. 'pencil': 'note-icon-pencil',
  7218. 'picture': 'note-icon-picture',
  7219. 'question': 'note-icon-question',
  7220. 'redo': 'note-icon-redo',
  7221. 'square': 'note-icon-square',
  7222. 'strikethrough': 'note-icon-strikethrough',
  7223. 'subscript': 'note-icon-subscript',
  7224. 'superscript': 'note-icon-superscript',
  7225. 'table': 'note-icon-table',
  7226. 'textHeight': 'note-icon-text-height',
  7227. 'trash': 'note-icon-trash',
  7228. 'underline': 'note-icon-underline',
  7229. 'undo': 'note-icon-undo',
  7230. 'unorderedlist': 'note-icon-unorderedlist',
  7231. 'video': 'note-icon-video'
  7232. }
  7233. }
  7234. });
  7235. })));
  7236. //# sourceMappingURL=summernote.js.map