simditor.js 183 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833
  1. /*!
  2. * Simditor v2.3.27
  3. * http://simditor.tower.im/
  4. * 2019-08-15
  5. */
  6. (function (root, factory) {
  7. if (typeof define === 'function' && define.amd) {
  8. // AMD. Register as an anonymous module unless amdModuleId is set
  9. define('simditor', ["jquery","simple-module","simple-hotkeys","simple-uploader","dompurify"], function ($, SimpleModule, simpleHotkeys, simpleUploader, DOMPurify) {
  10. return (root['Simditor'] = factory($, SimpleModule, simpleHotkeys, simpleUploader, DOMPurify));
  11. });
  12. } else if (typeof exports === 'object') {
  13. // Node. Does not work with strict CommonJS, but
  14. // only CommonJS-like environments that support module.exports,
  15. // like Node.
  16. module.exports = factory(require("jquery"),require("simple-module"),require("simple-hotkeys"),require("simple-uploader"),require("dompurify"));
  17. } else {
  18. root['Simditor'] = factory(jQuery,SimpleModule,simple.hotkeys,simple.uploader,window.DOMPurify);
  19. }
  20. }(this, function ($, SimpleModule, simpleHotkeys, simpleUploader, DOMPurify) {
  21. var AlignmentButton, BlockquoteButton, BoldButton, Button, Clipboard, CodeButton, CodePopover, ColorButton, FontScaleButton, Formatter, HrButton, ImageButton, ImagePopover, IndentButton, Indentation, InputManager, ItalicButton, Keystroke, LinkButton, LinkPopover, ListButton, OrderListButton, OutdentButton, Popover, Selection, Simditor, StrikethroughButton, TableButton, TitleButton, Toolbar, UnderlineButton, UndoManager, UnorderListButton, Util,
  22. extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  23. hasProp = {}.hasOwnProperty,
  24. indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
  25. slice = [].slice;
  26. Selection = (function(superClass) {
  27. extend(Selection, superClass);
  28. function Selection() {
  29. return Selection.__super__.constructor.apply(this, arguments);
  30. }
  31. Selection.pluginName = 'Selection';
  32. Selection.prototype._range = null;
  33. Selection.prototype._startNodes = null;
  34. Selection.prototype._endNodes = null;
  35. Selection.prototype._containerNode = null;
  36. Selection.prototype._nodes = null;
  37. Selection.prototype._blockNodes = null;
  38. Selection.prototype._rootNodes = null;
  39. Selection.prototype._init = function() {
  40. this.editor = this._module;
  41. this._selection = document.getSelection();
  42. this.editor.on('selectionchanged', (function(_this) {
  43. return function(e) {
  44. _this.reset();
  45. return _this._range = _this._selection.getRangeAt(0);
  46. };
  47. })(this));
  48. this.editor.on('blur', (function(_this) {
  49. return function(e) {
  50. return _this.reset();
  51. };
  52. })(this));
  53. return this.editor.on('focus', (function(_this) {
  54. return function(e) {
  55. _this.reset();
  56. return _this._range = _this._selection.getRangeAt(0);
  57. };
  58. })(this));
  59. };
  60. Selection.prototype.reset = function() {
  61. this._range = null;
  62. this._startNodes = null;
  63. this._endNodes = null;
  64. this._containerNode = null;
  65. this._nodes = null;
  66. this._blockNodes = null;
  67. return this._rootNodes = null;
  68. };
  69. Selection.prototype.clear = function() {
  70. var e;
  71. try {
  72. this._selection.removeAllRanges();
  73. } catch (_error) {
  74. e = _error;
  75. }
  76. return this.reset();
  77. };
  78. Selection.prototype.range = function(range) {
  79. var ffOrIE;
  80. if (range) {
  81. this.clear();
  82. this._selection.addRange(range);
  83. this._range = range;
  84. ffOrIE = this.editor.util.browser.firefox || this.editor.util.browser.msie;
  85. if (!this.editor.inputManager.focused && ffOrIE) {
  86. this.editor.body.focus();
  87. }
  88. } else if (!this._range && this.editor.inputManager.focused && this._selection.rangeCount) {
  89. this._range = this._selection.getRangeAt(0);
  90. }
  91. return this._range;
  92. };
  93. Selection.prototype.startNodes = function() {
  94. if (this._range) {
  95. this._startNodes || (this._startNodes = (function(_this) {
  96. return function() {
  97. var startNodes;
  98. startNodes = $(_this._range.startContainer).parentsUntil(_this.editor.body).get();
  99. startNodes.unshift(_this._range.startContainer);
  100. return $(startNodes);
  101. };
  102. })(this)());
  103. }
  104. return this._startNodes;
  105. };
  106. Selection.prototype.endNodes = function() {
  107. var endNodes;
  108. if (this._range) {
  109. this._endNodes || (this._endNodes = this._range.collapsed ? this.startNodes() : (endNodes = $(this._range.endContainer).parentsUntil(this.editor.body).get(), endNodes.unshift(this._range.endContainer), $(endNodes)));
  110. }
  111. return this._endNodes;
  112. };
  113. Selection.prototype.containerNode = function() {
  114. if (this._range) {
  115. this._containerNode || (this._containerNode = $(this._range.commonAncestorContainer));
  116. }
  117. return this._containerNode;
  118. };
  119. Selection.prototype.nodes = function() {
  120. if (this._range) {
  121. this._nodes || (this._nodes = (function(_this) {
  122. return function() {
  123. var nodes;
  124. nodes = [];
  125. if (_this.startNodes().first().is(_this.endNodes().first())) {
  126. nodes = _this.startNodes().get();
  127. } else {
  128. _this.startNodes().each(function(i, node) {
  129. var $endNode, $node, $nodes, endIndex, index, sharedIndex, startIndex;
  130. $node = $(node);
  131. if (_this.endNodes().index($node) > -1) {
  132. return nodes.push(node);
  133. } else if ($node.parent().is(_this.editor.body) || (sharedIndex = _this.endNodes().index($node.parent())) > -1) {
  134. if (sharedIndex && sharedIndex > -1) {
  135. $endNode = _this.endNodes().eq(sharedIndex - 1);
  136. } else {
  137. $endNode = _this.endNodes().last();
  138. }
  139. $nodes = $node.parent().contents();
  140. startIndex = $nodes.index($node);
  141. endIndex = $nodes.index($endNode);
  142. return $.merge(nodes, $nodes.slice(startIndex, endIndex).get());
  143. } else {
  144. $nodes = $node.parent().contents();
  145. index = $nodes.index($node);
  146. return $.merge(nodes, $nodes.slice(index).get());
  147. }
  148. });
  149. _this.endNodes().each(function(i, node) {
  150. var $node, $nodes, index;
  151. $node = $(node);
  152. if ($node.parent().is(_this.editor.body) || _this.startNodes().index($node.parent()) > -1) {
  153. nodes.push(node);
  154. return false;
  155. } else {
  156. $nodes = $node.parent().contents();
  157. index = $nodes.index($node);
  158. return $.merge(nodes, $nodes.slice(0, index + 1));
  159. }
  160. });
  161. }
  162. return $($.unique(nodes));
  163. };
  164. })(this)());
  165. }
  166. return this._nodes;
  167. };
  168. Selection.prototype.blockNodes = function() {
  169. if (!this._range) {
  170. return;
  171. }
  172. this._blockNodes || (this._blockNodes = (function(_this) {
  173. return function() {
  174. return _this.nodes().filter(function(i, node) {
  175. return _this.editor.util.isBlockNode(node);
  176. });
  177. };
  178. })(this)());
  179. return this._blockNodes;
  180. };
  181. Selection.prototype.rootNodes = function() {
  182. if (!this._range) {
  183. return;
  184. }
  185. this._rootNodes || (this._rootNodes = (function(_this) {
  186. return function() {
  187. return _this.nodes().filter(function(i, node) {
  188. var $parent;
  189. $parent = $(node).parent();
  190. return $parent.is(_this.editor.body) || $parent.is('blockquote');
  191. });
  192. };
  193. })(this)());
  194. return this._rootNodes;
  195. };
  196. Selection.prototype.rangeAtEndOf = function(node, range) {
  197. var afterLastNode, beforeLastNode, endNode, endNodeLength, lastNodeIsBr, result;
  198. if (range == null) {
  199. range = this.range();
  200. }
  201. if (!(range && range.collapsed)) {
  202. return;
  203. }
  204. node = $(node)[0];
  205. endNode = range.endContainer;
  206. endNodeLength = this.editor.util.getNodeLength(endNode);
  207. beforeLastNode = range.endOffset === endNodeLength - 1;
  208. lastNodeIsBr = $(endNode).contents().last().is('br');
  209. afterLastNode = range.endOffset === endNodeLength;
  210. if (!((beforeLastNode && lastNodeIsBr) || afterLastNode)) {
  211. return false;
  212. }
  213. if (node === endNode) {
  214. return true;
  215. } else if (!$.contains(node, endNode)) {
  216. return false;
  217. }
  218. result = true;
  219. $(endNode).parentsUntil(node).addBack().each(function(i, n) {
  220. var $lastChild, beforeLastbr, isLastNode, nodes;
  221. nodes = $(n).parent().contents().filter(function() {
  222. return !(this !== n && this.nodeType === 3 && !this.nodeValue);
  223. });
  224. $lastChild = nodes.last();
  225. isLastNode = $lastChild.get(0) === n;
  226. beforeLastbr = $lastChild.is('br') && $lastChild.prev().get(0) === n;
  227. if (!(isLastNode || beforeLastbr)) {
  228. result = false;
  229. return false;
  230. }
  231. });
  232. return result;
  233. };
  234. Selection.prototype.rangeAtStartOf = function(node, range) {
  235. var result, startNode;
  236. if (range == null) {
  237. range = this.range();
  238. }
  239. if (!(range && range.collapsed)) {
  240. return;
  241. }
  242. node = $(node)[0];
  243. startNode = range.startContainer;
  244. if (range.startOffset !== 0) {
  245. return false;
  246. }
  247. if (node === startNode) {
  248. return true;
  249. } else if (!$.contains(node, startNode)) {
  250. return false;
  251. }
  252. result = true;
  253. $(startNode).parentsUntil(node).addBack().each(function(i, n) {
  254. var nodes;
  255. nodes = $(n).parent().contents().filter(function() {
  256. return !(this !== n && this.nodeType === 3 && !this.nodeValue);
  257. });
  258. if (nodes.first().get(0) !== n) {
  259. return result = false;
  260. }
  261. });
  262. return result;
  263. };
  264. Selection.prototype.insertNode = function(node, range) {
  265. if (range == null) {
  266. range = this.range();
  267. }
  268. if (!range) {
  269. return;
  270. }
  271. node = $(node)[0];
  272. range.insertNode(node);
  273. return this.setRangeAfter(node, range);
  274. };
  275. Selection.prototype.setRangeAfter = function(node, range) {
  276. if (range == null) {
  277. range = this.range();
  278. }
  279. if (range == null) {
  280. return;
  281. }
  282. node = $(node)[0];
  283. range.setEndAfter(node);
  284. range.collapse(false);
  285. return this.range(range);
  286. };
  287. Selection.prototype.setRangeBefore = function(node, range) {
  288. if (range == null) {
  289. range = this.range();
  290. }
  291. if (range == null) {
  292. return;
  293. }
  294. node = $(node)[0];
  295. range.setEndBefore(node);
  296. range.collapse(false);
  297. return this.range(range);
  298. };
  299. Selection.prototype.setRangeAtStartOf = function(node, range) {
  300. if (range == null) {
  301. range = this.range();
  302. }
  303. node = $(node).get(0);
  304. range.setEnd(node, 0);
  305. range.collapse(false);
  306. return this.range(range);
  307. };
  308. Selection.prototype.setRangeAtEndOf = function(node, range) {
  309. var $lastNode, $node, contents, lastChild, lastChildLength, lastText, nodeLength;
  310. if (range == null) {
  311. range = this.range();
  312. }
  313. $node = $(node);
  314. node = $node[0];
  315. if (!node) {
  316. return;
  317. }
  318. if ($node.is('pre')) {
  319. contents = $node.contents();
  320. if (contents.length > 0) {
  321. lastChild = contents.last();
  322. lastText = lastChild.text();
  323. lastChildLength = this.editor.util.getNodeLength(lastChild[0]);
  324. if (lastText.charAt(lastText.length - 1) === '\n') {
  325. range.setEnd(lastChild[0], lastChildLength - 1);
  326. } else {
  327. range.setEnd(lastChild[0], lastChildLength);
  328. }
  329. } else {
  330. range.setEnd(node, 0);
  331. }
  332. } else {
  333. nodeLength = this.editor.util.getNodeLength(node);
  334. if (node.nodeType !== 3 && nodeLength > 0) {
  335. $lastNode = $(node).contents().last();
  336. if ($lastNode.is('br')) {
  337. nodeLength -= 1;
  338. } else if ($lastNode[0].nodeType !== 3 && this.editor.util.isEmptyNode($lastNode)) {
  339. $lastNode.append(this.editor.util.phBr);
  340. node = $lastNode[0];
  341. nodeLength = 0;
  342. }
  343. }
  344. range.setEnd(node, nodeLength);
  345. }
  346. range.collapse(false);
  347. return this.range(range);
  348. };
  349. Selection.prototype.deleteRangeContents = function(range) {
  350. var atEndOfBody, atStartOfBody, endRange, startRange;
  351. if (range == null) {
  352. range = this.range();
  353. }
  354. startRange = range.cloneRange();
  355. endRange = range.cloneRange();
  356. startRange.collapse(true);
  357. endRange.collapse(false);
  358. atStartOfBody = this.rangeAtStartOf(this.editor.body, startRange);
  359. atEndOfBody = this.rangeAtEndOf(this.editor.body, endRange);
  360. if (!range.collapsed && atStartOfBody && atEndOfBody) {
  361. this.editor.body.empty();
  362. range.setStart(this.editor.body[0], 0);
  363. range.collapse(true);
  364. this.range(range);
  365. } else {
  366. range.deleteContents();
  367. }
  368. return range;
  369. };
  370. Selection.prototype.breakBlockEl = function(el, range) {
  371. var $el;
  372. if (range == null) {
  373. range = this.range();
  374. }
  375. $el = $(el);
  376. if (!range.collapsed) {
  377. return $el;
  378. }
  379. range.setStartBefore($el.get(0));
  380. if (range.collapsed) {
  381. return $el;
  382. }
  383. return $el.before(range.extractContents());
  384. };
  385. Selection.prototype.save = function(range) {
  386. var endCaret, endRange, startCaret;
  387. if (range == null) {
  388. range = this.range();
  389. }
  390. if (this._selectionSaved) {
  391. return;
  392. }
  393. endRange = range.cloneRange();
  394. endRange.collapse(false);
  395. startCaret = $('<span/>').addClass('simditor-caret-start');
  396. endCaret = $('<span/>').addClass('simditor-caret-end');
  397. endRange.insertNode(endCaret[0]);
  398. range.insertNode(startCaret[0]);
  399. this.clear();
  400. return this._selectionSaved = true;
  401. };
  402. Selection.prototype.restore = function() {
  403. var endCaret, endContainer, endOffset, range, startCaret, startContainer, startOffset;
  404. if (!this._selectionSaved) {
  405. return false;
  406. }
  407. startCaret = this.editor.body.find('.simditor-caret-start');
  408. endCaret = this.editor.body.find('.simditor-caret-end');
  409. if (startCaret.length && endCaret.length) {
  410. startContainer = startCaret.parent();
  411. startOffset = startContainer.contents().index(startCaret);
  412. endContainer = endCaret.parent();
  413. endOffset = endContainer.contents().index(endCaret);
  414. if (startContainer[0] === endContainer[0]) {
  415. endOffset -= 1;
  416. }
  417. range = document.createRange();
  418. range.setStart(startContainer.get(0), startOffset);
  419. range.setEnd(endContainer.get(0), endOffset);
  420. startCaret.remove();
  421. endCaret.remove();
  422. this.range(range);
  423. } else {
  424. startCaret.remove();
  425. endCaret.remove();
  426. }
  427. this._selectionSaved = false;
  428. return range;
  429. };
  430. return Selection;
  431. })(SimpleModule);
  432. Formatter = (function(superClass) {
  433. extend(Formatter, superClass);
  434. function Formatter() {
  435. return Formatter.__super__.constructor.apply(this, arguments);
  436. }
  437. Formatter.pluginName = 'Formatter';
  438. Formatter.prototype.opts = {
  439. allowedTags: [],
  440. allowedAttributes: {},
  441. allowedStyles: {}
  442. };
  443. Formatter.prototype._init = function() {
  444. this.editor = this._module;
  445. this._allowedTags = $.merge(['br', 'span', 'a', 'img', 'b', 'strong', 'i', 'strike', 'u', 'font', 'p', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'hr'], this.opts.allowedTags);
  446. this._allowedAttributes = $.extend({
  447. img: ['src', 'alt', 'width', 'height', 'data-non-image'],
  448. a: ['href', 'target'],
  449. font: ['color'],
  450. code: ['class']
  451. }, this.opts.allowedAttributes);
  452. this._allowedStyles = $.extend({
  453. span: ['color', 'font-size'],
  454. b: ['color', 'font-size'],
  455. i: ['color', 'font-size'],
  456. strong: ['color', 'font-size'],
  457. strike: ['color', 'font-size'],
  458. u: ['color', 'font-size'],
  459. p: ['margin-left', 'text-align'],
  460. h1: ['margin-left', 'text-align'],
  461. h2: ['margin-left', 'text-align'],
  462. h3: ['margin-left', 'text-align'],
  463. h4: ['margin-left', 'text-align']
  464. }, this.opts.allowedStyles);
  465. return this.editor.body.on('click', 'a', function(e) {
  466. return false;
  467. });
  468. };
  469. Formatter.prototype.decorate = function($el) {
  470. if ($el == null) {
  471. $el = this.editor.body;
  472. }
  473. this.editor.trigger('decorate', [$el]);
  474. return $el;
  475. };
  476. Formatter.prototype.undecorate = function($el) {
  477. if ($el == null) {
  478. $el = this.editor.body.clone();
  479. }
  480. this.editor.trigger('undecorate', [$el]);
  481. return $el;
  482. };
  483. Formatter.prototype.autolink = function($el) {
  484. var $link, $node, findLinkNode, k, lastIndex, len, linkNodes, match, re, replaceEls, subStr, text, uri;
  485. if ($el == null) {
  486. $el = this.editor.body;
  487. }
  488. linkNodes = [];
  489. findLinkNode = function($parentNode) {
  490. return $parentNode.contents().each(function(i, node) {
  491. var $node, text;
  492. $node = $(node);
  493. if ($node.is('a') || $node.closest('a, pre', $el).length) {
  494. return;
  495. }
  496. if (!$node.is('iframe') && $node.contents().length) {
  497. return findLinkNode($node);
  498. } else if ((text = $node.text()) && /https?:\/\/|www\./ig.test(text)) {
  499. return linkNodes.push($node);
  500. }
  501. });
  502. };
  503. findLinkNode($el);
  504. re = /(https?:\/\/|www\.)[\w\-\.\?&=\/#%:,@\!\+]+/ig;
  505. for (k = 0, len = linkNodes.length; k < len; k++) {
  506. $node = linkNodes[k];
  507. text = $node.text();
  508. replaceEls = [];
  509. match = null;
  510. lastIndex = 0;
  511. while ((match = re.exec(text)) !== null) {
  512. subStr = text.substring(lastIndex, match.index);
  513. replaceEls.push(document.createTextNode(subStr));
  514. lastIndex = re.lastIndex;
  515. uri = /^(http(s)?:\/\/|\/)/.test(match[0]) ? match[0] : 'http://' + match[0];
  516. $link = $("<a href=\"" + uri + "\" rel=\"nofollow\"></a>").text(match[0]);
  517. replaceEls.push($link[0]);
  518. }
  519. replaceEls.push(document.createTextNode(text.substring(lastIndex)));
  520. $node.replaceWith($(replaceEls));
  521. }
  522. return $el;
  523. };
  524. Formatter.prototype.format = function($el) {
  525. var $node, blockNode, k, l, len, len1, n, node, ref, ref1;
  526. if ($el == null) {
  527. $el = this.editor.body;
  528. }
  529. if ($el.is(':empty')) {
  530. $el.append('<p>' + this.editor.util.phBr + '</p>');
  531. return $el;
  532. }
  533. ref = $el.contents();
  534. for (k = 0, len = ref.length; k < len; k++) {
  535. n = ref[k];
  536. this.cleanNode(n, true);
  537. }
  538. ref1 = $el.contents();
  539. for (l = 0, len1 = ref1.length; l < len1; l++) {
  540. node = ref1[l];
  541. $node = $(node);
  542. if ($node.is('br')) {
  543. if (typeof blockNode !== "undefined" && blockNode !== null) {
  544. blockNode = null;
  545. }
  546. $node.remove();
  547. } else if (this.editor.util.isBlockNode(node)) {
  548. if ($node.is('li')) {
  549. if (blockNode && blockNode.is('ul, ol')) {
  550. blockNode.append(node);
  551. } else {
  552. blockNode = $('<ul/>').insertBefore(node);
  553. blockNode.append(node);
  554. }
  555. } else {
  556. blockNode = null;
  557. }
  558. } else {
  559. if (!blockNode || blockNode.is('ul, ol')) {
  560. blockNode = $('<p/>').insertBefore(node);
  561. }
  562. blockNode.append(node);
  563. if (this.editor.util.isEmptyNode(blockNode)) {
  564. blockNode.append(this.editor.util.phBr);
  565. }
  566. }
  567. }
  568. return $el;
  569. };
  570. Formatter.prototype.cleanNode = function(node, recursive) {
  571. var $blockEls, $childImg, $node, $p, $td, allowedAttributes, attr, contents, isDecoration, k, l, len, len1, n, ref, ref1, text, textNode;
  572. $node = $(node);
  573. if (!($node.length > 0)) {
  574. return;
  575. }
  576. if ($node[0].nodeType === 3) {
  577. text = $node.text().replace(/(\r\n|\n|\r)/gm, '');
  578. if (text) {
  579. textNode = document.createTextNode(text);
  580. $node.replaceWith(textNode);
  581. } else {
  582. $node.remove();
  583. }
  584. return;
  585. }
  586. contents = $node.is('iframe') ? null : $node.contents();
  587. isDecoration = this.editor.util.isDecoratedNode($node);
  588. if ($node.is(this._allowedTags.join(',')) || isDecoration) {
  589. if ($node.is('a') && ($childImg = $node.find('img')).length > 0) {
  590. $node.replaceWith($childImg);
  591. $node = $childImg;
  592. contents = null;
  593. }
  594. if ($node.is('td') && ($blockEls = $node.find(this.editor.util.blockNodes.join(','))).length > 0) {
  595. $blockEls.each((function(_this) {
  596. return function(i, blockEl) {
  597. return $(blockEl).contents().unwrap();
  598. };
  599. })(this));
  600. contents = $node.contents();
  601. }
  602. if ($node.is('img') && $node.hasClass('uploading')) {
  603. $node.remove();
  604. }
  605. if (!isDecoration) {
  606. allowedAttributes = this._allowedAttributes[$node[0].tagName.toLowerCase()];
  607. ref = $.makeArray($node[0].attributes);
  608. for (k = 0, len = ref.length; k < len; k++) {
  609. attr = ref[k];
  610. if (attr.name === 'style') {
  611. continue;
  612. }
  613. if (!((allowedAttributes != null) && (ref1 = attr.name, indexOf.call(allowedAttributes, ref1) >= 0))) {
  614. $node.removeAttr(attr.name);
  615. }
  616. }
  617. this._cleanNodeStyles($node);
  618. if ($node.is('span')) {
  619. if ($node[0].attributes.length === 0) {
  620. $node.contents().first().unwrap();
  621. }
  622. if ($node[0].style.length === 2 && $node[0].style.color === 'rgb(51, 51, 51)' && $node[0].style.fontSize === '16px') {
  623. $node.contents().unwrap();
  624. }
  625. }
  626. }
  627. } else if ($node[0].nodeType === 1 && !$node.is(':empty')) {
  628. if ($node.is('div, article, dl, header, footer, tr')) {
  629. $node.append('<br/>');
  630. contents.first().unwrap();
  631. } else if ($node.is('table')) {
  632. $p = $('<p/>');
  633. $node.find('tr').each(function(i, tr) {
  634. return $p.append($(tr).text() + '<br/>');
  635. });
  636. $node.replaceWith($p);
  637. contents = null;
  638. } else if ($node.is('thead, tfoot')) {
  639. $node.remove();
  640. contents = null;
  641. } else if ($node.is('th')) {
  642. $td = $('<td/>').append($node.contents());
  643. $node.replaceWith($td);
  644. } else {
  645. contents.first().unwrap();
  646. }
  647. } else {
  648. $node.remove();
  649. contents = null;
  650. }
  651. if (recursive && (contents != null) && !$node.is('pre')) {
  652. for (l = 0, len1 = contents.length; l < len1; l++) {
  653. n = contents[l];
  654. this.cleanNode(n, true);
  655. }
  656. }
  657. return null;
  658. };
  659. Formatter.prototype._cleanNodeStyles = function($node) {
  660. var allowedStyles, k, len, pair, ref, ref1, style, styleStr, styles;
  661. styleStr = $node.attr('style');
  662. if (!styleStr) {
  663. return;
  664. }
  665. $node.removeAttr('style');
  666. allowedStyles = this._allowedStyles[$node[0].tagName.toLowerCase()];
  667. if (!(allowedStyles && allowedStyles.length > 0)) {
  668. return $node;
  669. }
  670. styles = {};
  671. ref = styleStr.split(';');
  672. for (k = 0, len = ref.length; k < len; k++) {
  673. style = ref[k];
  674. style = $.trim(style);
  675. pair = style.split(':');
  676. if (pair.length !== 2) {
  677. continue;
  678. }
  679. if (pair[0] === 'font-size' && pair[1].indexOf('px') > 0) {
  680. if (parseInt(pair[1], 10) < 12) {
  681. continue;
  682. }
  683. }
  684. if (ref1 = pair[0], indexOf.call(allowedStyles, ref1) >= 0) {
  685. styles[$.trim(pair[0])] = $.trim(pair[1]);
  686. }
  687. }
  688. if (Object.keys(styles).length > 0) {
  689. $node.css(styles);
  690. }
  691. return $node;
  692. };
  693. Formatter.prototype.clearHtml = function(html, lineBreak) {
  694. var container, contents, result;
  695. if (lineBreak == null) {
  696. lineBreak = true;
  697. }
  698. container = $('<div/>').append(html);
  699. contents = container.contents();
  700. result = '';
  701. contents.each((function(_this) {
  702. return function(i, node) {
  703. var $node, children;
  704. if (node.nodeType === 3) {
  705. return result += node.nodeValue;
  706. } else if (node.nodeType === 1) {
  707. $node = $(node);
  708. children = $node.is('iframe') ? null : $node.contents();
  709. if (children && children.length > 0) {
  710. result += _this.clearHtml(children);
  711. }
  712. if (lineBreak && i < contents.length - 1 && $node.is('br, p, div, li,tr, pre, address, artticle, aside, dl, figcaption, footer, h1, h2,h3, h4, header')) {
  713. return result += '\n';
  714. }
  715. }
  716. };
  717. })(this));
  718. return result;
  719. };
  720. Formatter.prototype.beautify = function($contents) {
  721. var uselessP;
  722. uselessP = function($el) {
  723. return !!($el.is('p') && !$el.text() && $el.children(':not(br)').length < 1);
  724. };
  725. return $contents.each(function(i, el) {
  726. var $el, invalid;
  727. $el = $(el);
  728. invalid = $el.is(':not(img, br, col, td, hr, [class^="simditor-"]):empty');
  729. if (invalid || uselessP($el)) {
  730. $el.remove();
  731. }
  732. return $el.find(':not(img, br, col, td, hr, [class^="simditor-"]):empty').remove();
  733. });
  734. };
  735. return Formatter;
  736. })(SimpleModule);
  737. InputManager = (function(superClass) {
  738. extend(InputManager, superClass);
  739. function InputManager() {
  740. return InputManager.__super__.constructor.apply(this, arguments);
  741. }
  742. InputManager.pluginName = 'InputManager';
  743. InputManager.prototype._modifierKeys = [16, 17, 18, 91, 93, 224];
  744. InputManager.prototype._arrowKeys = [37, 38, 39, 40];
  745. InputManager.prototype._init = function() {
  746. var selectAllKey, submitKey;
  747. this.editor = this._module;
  748. this.throttledValueChanged = this.editor.util.throttle((function(_this) {
  749. return function(params) {
  750. return setTimeout(function() {
  751. return _this.editor.trigger('valuechanged', params);
  752. }, 10);
  753. };
  754. })(this), 300);
  755. this.throttledSelectionChanged = this.editor.util.throttle((function(_this) {
  756. return function() {
  757. return _this.editor.trigger('selectionchanged');
  758. };
  759. })(this), 50);
  760. $(document).on('selectionchange.simditor' + this.editor.id, (function(_this) {
  761. return function(e) {
  762. var triggerEvent;
  763. if (!(_this.focused && !_this.editor.clipboard.pasting)) {
  764. return;
  765. }
  766. triggerEvent = function() {
  767. if (_this._selectionTimer) {
  768. clearTimeout(_this._selectionTimer);
  769. _this._selectionTimer = null;
  770. }
  771. if (_this.editor.selection._selection.rangeCount > 0) {
  772. return _this.throttledSelectionChanged();
  773. } else {
  774. return _this._selectionTimer = setTimeout(function() {
  775. _this._selectionTimer = null;
  776. if (_this.focused) {
  777. return triggerEvent();
  778. }
  779. }, 10);
  780. }
  781. };
  782. return triggerEvent();
  783. };
  784. })(this));
  785. this.editor.on('valuechanged', (function(_this) {
  786. return function() {
  787. var $rootBlocks;
  788. _this.lastCaretPosition = null;
  789. $rootBlocks = _this.editor.body.children().filter(function(i, node) {
  790. return _this.editor.util.isBlockNode(node);
  791. });
  792. if (_this.focused && $rootBlocks.length === 0) {
  793. _this.editor.selection.save();
  794. _this.editor.formatter.format();
  795. _this.editor.selection.restore();
  796. }
  797. _this.editor.body.find('hr, pre, .simditor-table').each(function(i, el) {
  798. var $el, formatted;
  799. $el = $(el);
  800. if ($el.parent().is('blockquote') || $el.parent()[0] === _this.editor.body[0]) {
  801. formatted = false;
  802. if ($el.next().length === 0) {
  803. $('<p/>').append(_this.editor.util.phBr).insertAfter($el);
  804. formatted = true;
  805. }
  806. if ($el.prev().length === 0) {
  807. $('<p/>').append(_this.editor.util.phBr).insertBefore($el);
  808. formatted = true;
  809. }
  810. if (formatted) {
  811. return _this.throttledValueChanged();
  812. }
  813. }
  814. });
  815. _this.editor.body.find('pre:empty').append(_this.editor.util.phBr);
  816. if (!_this.editor.util.support.onselectionchange && _this.focused) {
  817. return _this.throttledSelectionChanged();
  818. }
  819. };
  820. })(this));
  821. this.editor.body.on('keydown', $.proxy(this._onKeyDown, this)).on('keypress', $.proxy(this._onKeyPress, this)).on('keyup', $.proxy(this._onKeyUp, this)).on('mouseup', $.proxy(this._onMouseUp, this)).on('focus', $.proxy(this._onFocus, this)).on('blur', $.proxy(this._onBlur, this)).on('drop', $.proxy(this._onDrop, this)).on('input', $.proxy(this._onInput, this));
  822. if (this.editor.util.browser.firefox) {
  823. this.editor.hotkeys.add('cmd+left', (function(_this) {
  824. return function(e) {
  825. e.preventDefault();
  826. _this.editor.selection._selection.modify('move', 'backward', 'lineboundary');
  827. return false;
  828. };
  829. })(this));
  830. this.editor.hotkeys.add('cmd+right', (function(_this) {
  831. return function(e) {
  832. e.preventDefault();
  833. _this.editor.selection._selection.modify('move', 'forward', 'lineboundary');
  834. return false;
  835. };
  836. })(this));
  837. selectAllKey = this.editor.util.os.mac ? 'cmd+a' : 'ctrl+a';
  838. this.editor.hotkeys.add(selectAllKey, (function(_this) {
  839. return function(e) {
  840. var $children, firstBlock, lastBlock, range;
  841. $children = _this.editor.body.children();
  842. if (!($children.length > 0)) {
  843. return;
  844. }
  845. firstBlock = $children.first().get(0);
  846. lastBlock = $children.last().get(0);
  847. range = document.createRange();
  848. range.setStart(firstBlock, 0);
  849. range.setEnd(lastBlock, _this.editor.util.getNodeLength(lastBlock));
  850. _this.editor.selection.range(range);
  851. return false;
  852. };
  853. })(this));
  854. }
  855. submitKey = this.editor.util.os.mac ? 'cmd+enter' : 'ctrl+enter';
  856. return this.editor.hotkeys.add(submitKey, (function(_this) {
  857. return function(e) {
  858. _this.editor.sync();
  859. _this.editor.el.closest('form').find('button:submit').click();
  860. return false;
  861. };
  862. })(this));
  863. };
  864. InputManager.prototype._onFocus = function(e) {
  865. if (this.editor.clipboard.pasting) {
  866. return;
  867. }
  868. this.editor.el.addClass('focus').removeClass('error');
  869. this.focused = true;
  870. return setTimeout((function(_this) {
  871. return function() {
  872. var $blockEl, range;
  873. range = _this.editor.selection._selection.getRangeAt(0);
  874. if (range.startContainer === _this.editor.body[0]) {
  875. if (_this.lastCaretPosition) {
  876. _this.editor.undoManager.caretPosition(_this.lastCaretPosition);
  877. } else {
  878. $blockEl = _this.editor.body.children().first();
  879. range = document.createRange();
  880. _this.editor.selection.setRangeAtStartOf($blockEl, range);
  881. }
  882. }
  883. _this.lastCaretPosition = null;
  884. _this.editor.triggerHandler('focus');
  885. if (!_this.editor.util.support.onselectionchange) {
  886. return _this.throttledSelectionChanged();
  887. }
  888. };
  889. })(this), 0);
  890. };
  891. InputManager.prototype._onBlur = function(e) {
  892. var ref;
  893. if (this.editor.clipboard.pasting) {
  894. return;
  895. }
  896. this.editor.el.removeClass('focus');
  897. this.editor.sync();
  898. this.focused = false;
  899. this.lastCaretPosition = (ref = this.editor.undoManager.currentState()) != null ? ref.caret : void 0;
  900. return this.editor.triggerHandler('blur');
  901. };
  902. InputManager.prototype._onMouseUp = function(e) {
  903. if (!this.editor.util.support.onselectionchange) {
  904. return this.throttledSelectionChanged();
  905. }
  906. };
  907. InputManager.prototype._onKeyDown = function(e) {
  908. var ref, ref1;
  909. if (this.editor.triggerHandler(e) === false) {
  910. return false;
  911. }
  912. if (this.editor.hotkeys.respondTo(e)) {
  913. return;
  914. }
  915. if (this.editor.keystroke.respondTo(e)) {
  916. this.throttledValueChanged();
  917. return false;
  918. }
  919. if ((ref = e.which, indexOf.call(this._modifierKeys, ref) >= 0) || (ref1 = e.which, indexOf.call(this._arrowKeys, ref1) >= 0)) {
  920. return;
  921. }
  922. if (this.editor.util.metaKey(e) && e.which === 86) {
  923. return;
  924. }
  925. if (!this.editor.util.support.oninput) {
  926. this.throttledValueChanged(['typing']);
  927. }
  928. return null;
  929. };
  930. InputManager.prototype._onKeyPress = function(e) {
  931. if (this.editor.triggerHandler(e) === false) {
  932. return false;
  933. }
  934. };
  935. InputManager.prototype._onKeyUp = function(e) {
  936. var p, ref;
  937. if (this.editor.triggerHandler(e) === false) {
  938. return false;
  939. }
  940. if (!this.editor.util.support.onselectionchange && (ref = e.which, indexOf.call(this._arrowKeys, ref) >= 0)) {
  941. this.throttledValueChanged();
  942. return;
  943. }
  944. if ((e.which === 8 || e.which === 46) && this.editor.util.isEmptyNode(this.editor.body)) {
  945. this.editor.body.empty();
  946. p = $('<p/>').append(this.editor.util.phBr).appendTo(this.editor.body);
  947. this.editor.selection.setRangeAtStartOf(p);
  948. }
  949. };
  950. InputManager.prototype._onDrop = function(e) {
  951. if (this.editor.triggerHandler(e) === false) {
  952. return false;
  953. }
  954. return this.throttledValueChanged();
  955. };
  956. InputManager.prototype._onInput = function(e) {
  957. return this.throttledValueChanged(['oninput']);
  958. };
  959. return InputManager;
  960. })(SimpleModule);
  961. Keystroke = (function(superClass) {
  962. extend(Keystroke, superClass);
  963. function Keystroke() {
  964. return Keystroke.__super__.constructor.apply(this, arguments);
  965. }
  966. Keystroke.pluginName = 'Keystroke';
  967. Keystroke.prototype._init = function() {
  968. this.editor = this._module;
  969. this._keystrokeHandlers = {};
  970. return this._initKeystrokeHandlers();
  971. };
  972. Keystroke.prototype.add = function(key, node, handler) {
  973. key = key.toLowerCase();
  974. key = this.editor.hotkeys.constructor.aliases[key] || key;
  975. if (!this._keystrokeHandlers[key]) {
  976. this._keystrokeHandlers[key] = {};
  977. }
  978. return this._keystrokeHandlers[key][node] = handler;
  979. };
  980. Keystroke.prototype.respondTo = function(e) {
  981. var base, key, ref, result;
  982. key = (ref = this.editor.hotkeys.constructor.keyNameMap[e.which]) != null ? ref.toLowerCase() : void 0;
  983. if (!key) {
  984. return;
  985. }
  986. if (key in this._keystrokeHandlers) {
  987. result = typeof (base = this._keystrokeHandlers[key])['*'] === "function" ? base['*'](e) : void 0;
  988. if (!result) {
  989. this.editor.selection.startNodes().each((function(_this) {
  990. return function(i, node) {
  991. var handler, ref1;
  992. if (node.nodeType !== Node.ELEMENT_NODE) {
  993. return;
  994. }
  995. handler = (ref1 = _this._keystrokeHandlers[key]) != null ? ref1[node.tagName.toLowerCase()] : void 0;
  996. result = typeof handler === "function" ? handler(e, $(node)) : void 0;
  997. if (result === true || result === false) {
  998. return false;
  999. }
  1000. };
  1001. })(this));
  1002. }
  1003. if (result) {
  1004. return true;
  1005. }
  1006. }
  1007. };
  1008. Keystroke.prototype._initKeystrokeHandlers = function() {
  1009. var titleEnterHandler;
  1010. if (this.editor.util.browser.safari) {
  1011. this.add('enter', '*', (function(_this) {
  1012. return function(e) {
  1013. var $blockEl, $br;
  1014. if (!e.shiftKey) {
  1015. return;
  1016. }
  1017. $blockEl = _this.editor.selection.blockNodes().last();
  1018. if ($blockEl.is('pre')) {
  1019. return;
  1020. }
  1021. $br = $('<br/>');
  1022. if (_this.editor.selection.rangeAtEndOf($blockEl)) {
  1023. _this.editor.selection.insertNode($br);
  1024. _this.editor.selection.insertNode($('<br/>'));
  1025. _this.editor.selection.setRangeBefore($br);
  1026. } else {
  1027. _this.editor.selection.insertNode($br);
  1028. }
  1029. return true;
  1030. };
  1031. })(this));
  1032. }
  1033. if (this.editor.util.browser.webkit || this.editor.util.browser.msie) {
  1034. titleEnterHandler = (function(_this) {
  1035. return function(e, $node) {
  1036. var $p;
  1037. if (!_this.editor.selection.rangeAtEndOf($node)) {
  1038. return;
  1039. }
  1040. $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node);
  1041. _this.editor.selection.setRangeAtStartOf($p);
  1042. return true;
  1043. };
  1044. })(this);
  1045. this.add('enter', 'h1', titleEnterHandler);
  1046. this.add('enter', 'h2', titleEnterHandler);
  1047. this.add('enter', 'h3', titleEnterHandler);
  1048. this.add('enter', 'h4', titleEnterHandler);
  1049. this.add('enter', 'h5', titleEnterHandler);
  1050. this.add('enter', 'h6', titleEnterHandler);
  1051. }
  1052. this.add('backspace', '*', (function(_this) {
  1053. return function(e) {
  1054. var $blockEl, $prevBlockEl, $rootBlock, isWebkit;
  1055. $rootBlock = _this.editor.selection.rootNodes().first();
  1056. $prevBlockEl = $rootBlock.prev();
  1057. if ($prevBlockEl.is('hr') && _this.editor.selection.rangeAtStartOf($rootBlock)) {
  1058. _this.editor.selection.save();
  1059. $prevBlockEl.remove();
  1060. _this.editor.selection.restore();
  1061. return true;
  1062. }
  1063. $blockEl = _this.editor.selection.blockNodes().last();
  1064. if ($blockEl.is('.simditor-resize-handle') && $rootBlock.is('.simditor-table')) {
  1065. e.preventDefault();
  1066. $rootBlock.remove();
  1067. _this.editor.selection.setRangeAtEndOf($prevBlockEl);
  1068. }
  1069. if ($prevBlockEl.is('.simditor-table') && !$blockEl.is('table') && _this.editor.util.isEmptyNode($blockEl)) {
  1070. e.preventDefault();
  1071. $blockEl.remove();
  1072. _this.editor.selection.setRangeAtEndOf($prevBlockEl);
  1073. }
  1074. isWebkit = _this.editor.util.browser.webkit;
  1075. if (isWebkit && _this.editor.selection.rangeAtStartOf($blockEl)) {
  1076. _this.editor.selection.save();
  1077. _this.editor.formatter.cleanNode($blockEl, true);
  1078. _this.editor.selection.restore();
  1079. return null;
  1080. }
  1081. };
  1082. })(this));
  1083. this.add('enter', 'div', (function(_this) {
  1084. return function(e, $node) {
  1085. var $blockEl, $p;
  1086. if ($node.is('.simditor-table')) {
  1087. $blockEl = _this.editor.selection.blockNodes().last();
  1088. if ($blockEl.is('.simditor-resize-handle')) {
  1089. e.preventDefault();
  1090. $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node);
  1091. return _this.editor.selection.setRangeAtStartOf($p);
  1092. }
  1093. }
  1094. };
  1095. })(this));
  1096. this.add('enter', 'li', (function(_this) {
  1097. return function(e, $node) {
  1098. var $cloneNode, listEl, newBlockEl, newListEl;
  1099. $cloneNode = $node.clone();
  1100. $cloneNode.find('ul, ol').remove();
  1101. if (!(_this.editor.util.isEmptyNode($cloneNode) && $node.is(_this.editor.selection.blockNodes().last()))) {
  1102. return;
  1103. }
  1104. listEl = $node.parent();
  1105. if ($node.next('li').length > 0) {
  1106. if (!_this.editor.util.isEmptyNode($node)) {
  1107. return;
  1108. }
  1109. if (listEl.parent('li').length > 0) {
  1110. newBlockEl = $('<li/>').append(_this.editor.util.phBr).insertAfter(listEl.parent('li'));
  1111. newListEl = $('<' + listEl[0].tagName + '/>').append($node.nextAll('li'));
  1112. newBlockEl.append(newListEl);
  1113. } else {
  1114. newBlockEl = $('<p/>').append(_this.editor.util.phBr).insertAfter(listEl);
  1115. newListEl = $('<' + listEl[0].tagName + '/>').append($node.nextAll('li'));
  1116. newBlockEl.after(newListEl);
  1117. }
  1118. } else {
  1119. if (listEl.parent('li').length > 0) {
  1120. newBlockEl = $('<li/>').insertAfter(listEl.parent('li'));
  1121. if ($node.contents().length > 0) {
  1122. newBlockEl.append($node.contents());
  1123. } else {
  1124. newBlockEl.append(_this.editor.util.phBr);
  1125. }
  1126. } else {
  1127. newBlockEl = $('<p/>').append(_this.editor.util.phBr).insertAfter(listEl);
  1128. if ($node.children('ul, ol').length > 0) {
  1129. newBlockEl.after($node.children('ul, ol'));
  1130. }
  1131. }
  1132. }
  1133. if ($node.prev('li').length) {
  1134. $node.remove();
  1135. } else {
  1136. if ($node.prev('ul').length || $node.prev('ol').length) {
  1137. $node.remove();
  1138. } else {
  1139. listEl.remove();
  1140. }
  1141. }
  1142. _this.editor.selection.setRangeAtStartOf(newBlockEl);
  1143. return true;
  1144. };
  1145. })(this));
  1146. this.add('enter', 'pre', (function(_this) {
  1147. return function(e, $node) {
  1148. var $p, breakNode, range;
  1149. e.preventDefault();
  1150. if (e.shiftKey) {
  1151. $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node);
  1152. _this.editor.selection.setRangeAtStartOf($p);
  1153. return true;
  1154. }
  1155. range = _this.editor.selection.range();
  1156. breakNode = null;
  1157. range.deleteContents();
  1158. if (!_this.editor.util.browser.msie && _this.editor.selection.rangeAtEndOf($node)) {
  1159. breakNode = document.createTextNode('\n\n');
  1160. } else {
  1161. breakNode = document.createTextNode('\n');
  1162. }
  1163. range.insertNode(breakNode);
  1164. range.setEnd(breakNode, 1);
  1165. range.collapse(false);
  1166. _this.editor.selection.range(range);
  1167. return true;
  1168. };
  1169. })(this));
  1170. this.add('enter', 'blockquote', (function(_this) {
  1171. return function(e, $node) {
  1172. var $closestBlock, range;
  1173. $closestBlock = _this.editor.selection.blockNodes().last();
  1174. if (!($closestBlock.is('p') && !$closestBlock.next().length && _this.editor.util.isEmptyNode($closestBlock))) {
  1175. return;
  1176. }
  1177. $node.after($closestBlock);
  1178. range = document.createRange();
  1179. _this.editor.selection.setRangeAtStartOf($closestBlock, range);
  1180. return true;
  1181. };
  1182. })(this));
  1183. this.add('backspace', 'li', (function(_this) {
  1184. return function(e, $node) {
  1185. var $br, $childList, $newLi, $prevChildList, $prevNode, $textNode, isFF, range, text;
  1186. $childList = $node.children('ul, ol');
  1187. $prevNode = $node.prev('li');
  1188. if (!($childList.length > 0 && $prevNode.length > 0)) {
  1189. return false;
  1190. }
  1191. text = '';
  1192. $textNode = null;
  1193. $node.contents().each(function(i, n) {
  1194. if (n.nodeType === 1 && /UL|OL/.test(n.nodeName)) {
  1195. return false;
  1196. }
  1197. if (n.nodeType === 1 && /BR/.test(n.nodeName)) {
  1198. return;
  1199. }
  1200. if (n.nodeType === 3 && n.nodeValue) {
  1201. text += n.nodeValue;
  1202. } else if (n.nodeType === 1) {
  1203. text += $(n).text();
  1204. }
  1205. return $textNode = $(n);
  1206. });
  1207. isFF = _this.editor.util.browser.firefox && !$textNode.next('br').length;
  1208. if ($textNode && text.length === 1 && isFF) {
  1209. $br = $(_this.editor.util.phBr).insertAfter($textNode);
  1210. $textNode.remove();
  1211. _this.editor.selection.setRangeBefore($br);
  1212. return true;
  1213. } else if (text.length > 0) {
  1214. return false;
  1215. }
  1216. range = document.createRange();
  1217. $prevChildList = $prevNode.children('ul, ol');
  1218. if ($prevChildList.length > 0) {
  1219. $newLi = $('<li/>').append(_this.editor.util.phBr).appendTo($prevChildList);
  1220. $prevChildList.append($childList.children('li'));
  1221. $node.remove();
  1222. _this.editor.selection.setRangeAtEndOf($newLi, range);
  1223. } else {
  1224. _this.editor.selection.setRangeAtEndOf($prevNode, range);
  1225. $prevNode.append($childList);
  1226. $node.remove();
  1227. _this.editor.selection.range(range);
  1228. }
  1229. return true;
  1230. };
  1231. })(this));
  1232. this.add('backspace', 'pre', (function(_this) {
  1233. return function(e, $node) {
  1234. var $newNode, codeStr, range;
  1235. if (!_this.editor.selection.rangeAtStartOf($node)) {
  1236. return;
  1237. }
  1238. codeStr = $node.html().replace('\n', '<br/>') || _this.editor.util.phBr;
  1239. $newNode = $('<p/>').append(codeStr).insertAfter($node);
  1240. $node.remove();
  1241. range = document.createRange();
  1242. _this.editor.selection.setRangeAtStartOf($newNode, range);
  1243. return true;
  1244. };
  1245. })(this));
  1246. return this.add('backspace', 'blockquote', (function(_this) {
  1247. return function(e, $node) {
  1248. var $firstChild, range;
  1249. if (!_this.editor.selection.rangeAtStartOf($node)) {
  1250. return;
  1251. }
  1252. $firstChild = $node.children().first().unwrap();
  1253. range = document.createRange();
  1254. _this.editor.selection.setRangeAtStartOf($firstChild, range);
  1255. return true;
  1256. };
  1257. })(this));
  1258. };
  1259. return Keystroke;
  1260. })(SimpleModule);
  1261. UndoManager = (function(superClass) {
  1262. extend(UndoManager, superClass);
  1263. function UndoManager() {
  1264. return UndoManager.__super__.constructor.apply(this, arguments);
  1265. }
  1266. UndoManager.pluginName = 'UndoManager';
  1267. UndoManager.prototype._index = -1;
  1268. UndoManager.prototype._capacity = 20;
  1269. UndoManager.prototype._startPosition = null;
  1270. UndoManager.prototype._endPosition = null;
  1271. UndoManager.prototype._init = function() {
  1272. var redoShortcut, undoShortcut;
  1273. this.editor = this._module;
  1274. this._stack = [];
  1275. if (this.editor.util.os.mac) {
  1276. undoShortcut = 'cmd+z';
  1277. redoShortcut = 'shift+cmd+z';
  1278. } else if (this.editor.util.os.win) {
  1279. undoShortcut = 'ctrl+z';
  1280. redoShortcut = 'ctrl+y';
  1281. } else {
  1282. undoShortcut = 'ctrl+z';
  1283. redoShortcut = 'shift+ctrl+z';
  1284. }
  1285. this.editor.hotkeys.add(undoShortcut, (function(_this) {
  1286. return function(e) {
  1287. e.preventDefault();
  1288. _this.undo();
  1289. return false;
  1290. };
  1291. })(this));
  1292. this.editor.hotkeys.add(redoShortcut, (function(_this) {
  1293. return function(e) {
  1294. e.preventDefault();
  1295. _this.redo();
  1296. return false;
  1297. };
  1298. })(this));
  1299. this.throttledPushState = this.editor.util.throttle((function(_this) {
  1300. return function() {
  1301. return _this._pushUndoState();
  1302. };
  1303. })(this), 2000);
  1304. this.editor.on('valuechanged', (function(_this) {
  1305. return function(e, src) {
  1306. if (src === 'undo' || src === 'redo') {
  1307. return;
  1308. }
  1309. return _this.throttledPushState();
  1310. };
  1311. })(this));
  1312. this.editor.on('selectionchanged', (function(_this) {
  1313. return function(e) {
  1314. _this.resetCaretPosition();
  1315. return _this.update();
  1316. };
  1317. })(this));
  1318. this.editor.on('focus', (function(_this) {
  1319. return function(e) {
  1320. if (_this._stack.length === 0) {
  1321. return _this._pushUndoState();
  1322. }
  1323. };
  1324. })(this));
  1325. return this.editor.on('blur', (function(_this) {
  1326. return function(e) {
  1327. return _this.resetCaretPosition();
  1328. };
  1329. })(this));
  1330. };
  1331. UndoManager.prototype.resetCaretPosition = function() {
  1332. this._startPosition = null;
  1333. return this._endPosition = null;
  1334. };
  1335. UndoManager.prototype.startPosition = function() {
  1336. if (this.editor.selection._range) {
  1337. this._startPosition || (this._startPosition = this._getPosition('start'));
  1338. }
  1339. return this._startPosition;
  1340. };
  1341. UndoManager.prototype.endPosition = function() {
  1342. if (this.editor.selection._range) {
  1343. this._endPosition || (this._endPosition = (function(_this) {
  1344. return function() {
  1345. var range;
  1346. range = _this.editor.selection.range();
  1347. if (range.collapsed) {
  1348. return _this._startPosition;
  1349. }
  1350. return _this._getPosition('end');
  1351. };
  1352. })(this)());
  1353. }
  1354. return this._endPosition;
  1355. };
  1356. UndoManager.prototype._pushUndoState = function() {
  1357. var caret;
  1358. if (this.editor.triggerHandler('pushundostate') === false) {
  1359. return;
  1360. }
  1361. caret = this.caretPosition();
  1362. if (!caret.start) {
  1363. return;
  1364. }
  1365. this._index += 1;
  1366. this._stack.length = this._index;
  1367. this._stack.push({
  1368. html: this.editor.body.html(),
  1369. caret: this.caretPosition()
  1370. });
  1371. if (this._stack.length > this._capacity) {
  1372. this._stack.shift();
  1373. return this._index -= 1;
  1374. }
  1375. };
  1376. UndoManager.prototype.currentState = function() {
  1377. if (this._stack.length && this._index > -1) {
  1378. return this._stack[this._index];
  1379. } else {
  1380. return null;
  1381. }
  1382. };
  1383. UndoManager.prototype.undo = function() {
  1384. var state;
  1385. if (this._index < 1 || this._stack.length < 2) {
  1386. return;
  1387. }
  1388. this.editor.hidePopover();
  1389. this._index -= 1;
  1390. state = this._stack[this._index];
  1391. this.editor.body.get(0).innerHTML = state.html;
  1392. this.caretPosition(state.caret);
  1393. this.editor.body.find('.selected').removeClass('selected');
  1394. this.editor.sync();
  1395. return this.editor.trigger('valuechanged', ['undo']);
  1396. };
  1397. UndoManager.prototype.redo = function() {
  1398. var state;
  1399. if (this._index < 0 || this._stack.length < this._index + 2) {
  1400. return;
  1401. }
  1402. this.editor.hidePopover();
  1403. this._index += 1;
  1404. state = this._stack[this._index];
  1405. this.editor.body.get(0).innerHTML = state.html;
  1406. this.caretPosition(state.caret);
  1407. this.editor.body.find('.selected').removeClass('selected');
  1408. this.editor.sync();
  1409. return this.editor.trigger('valuechanged', ['redo']);
  1410. };
  1411. UndoManager.prototype.update = function() {
  1412. var currentState;
  1413. currentState = this.currentState();
  1414. if (!currentState) {
  1415. return;
  1416. }
  1417. currentState.html = this.editor.body.html();
  1418. return currentState.caret = this.caretPosition();
  1419. };
  1420. UndoManager.prototype._getNodeOffset = function(node, index) {
  1421. var $parent, merging, offset;
  1422. if ($.isNumeric(index)) {
  1423. $parent = $(node);
  1424. } else {
  1425. $parent = $(node).parent();
  1426. }
  1427. offset = 0;
  1428. merging = false;
  1429. $parent.contents().each(function(i, child) {
  1430. if (node === child || (index === i && i === 0)) {
  1431. return false;
  1432. }
  1433. if (child.nodeType === Node.TEXT_NODE) {
  1434. if (!merging && child.nodeValue.length > 0) {
  1435. offset += 1;
  1436. merging = true;
  1437. }
  1438. } else {
  1439. offset += 1;
  1440. merging = false;
  1441. }
  1442. if (index - 1 === i) {
  1443. return false;
  1444. }
  1445. return null;
  1446. });
  1447. return offset;
  1448. };
  1449. UndoManager.prototype._getPosition = function(type) {
  1450. var $nodes, node, nodes, offset, position, prevNode, range;
  1451. if (type == null) {
  1452. type = 'start';
  1453. }
  1454. range = this.editor.selection.range();
  1455. offset = range[type + "Offset"];
  1456. $nodes = this.editor.selection[type + "Nodes"]();
  1457. node = $nodes.first()[0];
  1458. if (node.nodeType === Node.TEXT_NODE) {
  1459. prevNode = node.previousSibling;
  1460. while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {
  1461. node = prevNode;
  1462. offset += this.editor.util.getNodeLength(prevNode);
  1463. prevNode = prevNode.previousSibling;
  1464. }
  1465. nodes = $nodes.get();
  1466. nodes[0] = node;
  1467. $nodes = $(nodes);
  1468. } else {
  1469. offset = this._getNodeOffset(node, offset);
  1470. }
  1471. position = [offset];
  1472. $nodes.each((function(_this) {
  1473. return function(i, node) {
  1474. return position.unshift(_this._getNodeOffset(node));
  1475. };
  1476. })(this));
  1477. return position;
  1478. };
  1479. UndoManager.prototype._getNodeByPosition = function(position) {
  1480. var child, childNodes, i, k, len, node, offset, ref;
  1481. node = this.editor.body[0];
  1482. ref = position.slice(0, position.length - 1);
  1483. for (i = k = 0, len = ref.length; k < len; i = ++k) {
  1484. offset = ref[i];
  1485. childNodes = node.childNodes;
  1486. if (offset > childNodes.length - 1) {
  1487. if (i === position.length - 2 && $(node).is(':empty')) {
  1488. child = document.createTextNode('');
  1489. node.appendChild(child);
  1490. childNodes = node.childNodes;
  1491. } else {
  1492. node = null;
  1493. break;
  1494. }
  1495. }
  1496. node = childNodes[offset];
  1497. }
  1498. return node;
  1499. };
  1500. UndoManager.prototype.caretPosition = function(caret) {
  1501. var endContainer, endOffset, range, startContainer, startOffset;
  1502. if (!caret) {
  1503. range = this.editor.selection.range();
  1504. caret = this.editor.inputManager.focused && (range != null) ? {
  1505. start: this.startPosition(),
  1506. end: this.endPosition(),
  1507. collapsed: range.collapsed
  1508. } : {};
  1509. return caret;
  1510. } else {
  1511. if (!caret.start) {
  1512. return;
  1513. }
  1514. startContainer = this._getNodeByPosition(caret.start);
  1515. startOffset = caret.start[caret.start.length - 1];
  1516. if (caret.collapsed) {
  1517. endContainer = startContainer;
  1518. endOffset = startOffset;
  1519. } else {
  1520. endContainer = this._getNodeByPosition(caret.end);
  1521. endOffset = caret.start[caret.start.length - 1];
  1522. }
  1523. if (!startContainer || !endContainer) {
  1524. if (typeof console !== "undefined" && console !== null) {
  1525. if (typeof console.info === "function") {
  1526. console.info('simditor: invalid caret state');
  1527. }
  1528. }
  1529. return;
  1530. }
  1531. range = document.createRange();
  1532. range.setStart(startContainer, startOffset);
  1533. range.setEnd(endContainer, endOffset);
  1534. return this.editor.selection.range(range);
  1535. }
  1536. };
  1537. return UndoManager;
  1538. })(SimpleModule);
  1539. Util = (function(superClass) {
  1540. extend(Util, superClass);
  1541. function Util() {
  1542. return Util.__super__.constructor.apply(this, arguments);
  1543. }
  1544. Util.pluginName = 'Util';
  1545. Util.prototype._init = function() {
  1546. this.editor = this._module;
  1547. if (this.browser.msie && this.browser.version < 11) {
  1548. return this.phBr = '';
  1549. }
  1550. };
  1551. Util.prototype.phBr = '<br/>';
  1552. Util.prototype.os = (function() {
  1553. var os;
  1554. os = {};
  1555. if (/Mac/.test(navigator.appVersion)) {
  1556. os.mac = true;
  1557. } else if (/Linux/.test(navigator.appVersion)) {
  1558. os.linux = true;
  1559. } else if (/Win/.test(navigator.appVersion)) {
  1560. os.win = true;
  1561. } else if (/X11/.test(navigator.appVersion)) {
  1562. os.unix = true;
  1563. }
  1564. if (/Mobi/.test(navigator.appVersion)) {
  1565. os.mobile = true;
  1566. }
  1567. return os;
  1568. })();
  1569. Util.prototype.browser = (function() {
  1570. var chrome, edge, firefox, ie, ref, ref1, ref2, ref3, ref4, safari, ua;
  1571. ua = navigator.userAgent;
  1572. ie = /(msie|trident)/i.test(ua);
  1573. chrome = /chrome|crios/i.test(ua);
  1574. safari = /safari/i.test(ua) && !chrome;
  1575. firefox = /firefox/i.test(ua);
  1576. edge = /edge/i.test(ua);
  1577. if (ie) {
  1578. return {
  1579. msie: true,
  1580. version: ((ref = ua.match(/(msie |rv:)(\d+(\.\d+)?)/i)) != null ? ref[2] : void 0) * 1
  1581. };
  1582. } else if (edge) {
  1583. return {
  1584. edge: true,
  1585. webkit: true,
  1586. version: ((ref1 = ua.match(/edge\/(\d+(\.\d+)?)/i)) != null ? ref1[1] : void 0) * 1
  1587. };
  1588. } else if (chrome) {
  1589. return {
  1590. webkit: true,
  1591. chrome: true,
  1592. version: ((ref2 = ua.match(/(?:chrome|crios)\/(\d+(\.\d+)?)/i)) != null ? ref2[1] : void 0) * 1
  1593. };
  1594. } else if (safari) {
  1595. return {
  1596. webkit: true,
  1597. safari: true,
  1598. version: ((ref3 = ua.match(/version\/(\d+(\.\d+)?)/i)) != null ? ref3[1] : void 0) * 1
  1599. };
  1600. } else if (firefox) {
  1601. return {
  1602. mozilla: true,
  1603. firefox: true,
  1604. version: ((ref4 = ua.match(/firefox\/(\d+(\.\d+)?)/i)) != null ? ref4[1] : void 0) * 1
  1605. };
  1606. } else {
  1607. return {};
  1608. }
  1609. })();
  1610. Util.prototype.support = (function() {
  1611. return {
  1612. onselectionchange: (function() {
  1613. var e, onselectionchange;
  1614. onselectionchange = document.onselectionchange;
  1615. if (onselectionchange !== void 0) {
  1616. try {
  1617. document.onselectionchange = 0;
  1618. return document.onselectionchange === null;
  1619. } catch (_error) {
  1620. e = _error;
  1621. } finally {
  1622. document.onselectionchange = onselectionchange;
  1623. }
  1624. }
  1625. return false;
  1626. })(),
  1627. oninput: (function() {
  1628. return !/(msie|trident)/i.test(navigator.userAgent);
  1629. })()
  1630. };
  1631. })();
  1632. Util.prototype.reflow = function(el) {
  1633. if (el == null) {
  1634. el = document;
  1635. }
  1636. return $(el)[0].offsetHeight;
  1637. };
  1638. Util.prototype.metaKey = function(e) {
  1639. var isMac;
  1640. isMac = /Mac/.test(navigator.userAgent);
  1641. if (isMac) {
  1642. return e.metaKey;
  1643. } else {
  1644. return e.ctrlKey;
  1645. }
  1646. };
  1647. Util.prototype.isEmptyNode = function(node) {
  1648. var $node;
  1649. $node = $(node);
  1650. return $node.is(':empty') || (!$node.text() && !$node.find(':not(br, span, div, b, a, strong, i, strike, font, u)').length);
  1651. };
  1652. Util.prototype.isDecoratedNode = function(node) {
  1653. return $(node).is('[class^="simditor-"]');
  1654. };
  1655. Util.prototype.blockNodes = ["div", "p", "ul", "ol", "li", "blockquote", "hr", "pre", "h1", "h2", "h3", "h4", "h5", "table"];
  1656. Util.prototype.isBlockNode = function(node) {
  1657. node = $(node)[0];
  1658. if (!node || node.nodeType === 3) {
  1659. return false;
  1660. }
  1661. return new RegExp("^(" + (this.blockNodes.join('|')) + ")$").test(node.nodeName.toLowerCase());
  1662. };
  1663. Util.prototype.getNodeLength = function(node) {
  1664. node = $(node)[0];
  1665. switch (node.nodeType) {
  1666. case 7:
  1667. case 10:
  1668. return 0;
  1669. case 3:
  1670. case 8:
  1671. return node.length;
  1672. default:
  1673. return node.childNodes.length;
  1674. }
  1675. };
  1676. Util.prototype.dataURLtoBlob = function(dataURL) {
  1677. var BlobBuilder, arrayBuffer, bb, blobArray, byteString, hasArrayBufferViewSupport, hasBlobConstructor, i, intArray, k, mimeString, ref, supportBlob;
  1678. hasBlobConstructor = window.Blob && (function() {
  1679. var e;
  1680. try {
  1681. return Boolean(new Blob());
  1682. } catch (_error) {
  1683. e = _error;
  1684. return false;
  1685. }
  1686. })();
  1687. hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && (function() {
  1688. var e;
  1689. try {
  1690. return new Blob([new Uint8Array(100)]).size === 100;
  1691. } catch (_error) {
  1692. e = _error;
  1693. return false;
  1694. }
  1695. })();
  1696. BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
  1697. supportBlob = hasBlobConstructor || BlobBuilder;
  1698. if (!(supportBlob && window.atob && window.ArrayBuffer && window.Uint8Array)) {
  1699. return false;
  1700. }
  1701. if (dataURL.split(',')[0].indexOf('base64') >= 0) {
  1702. byteString = atob(dataURL.split(',')[1]);
  1703. } else {
  1704. byteString = decodeURIComponent(dataURL.split(',')[1]);
  1705. }
  1706. arrayBuffer = new ArrayBuffer(byteString.length);
  1707. intArray = new Uint8Array(arrayBuffer);
  1708. for (i = k = 0, ref = byteString.length; 0 <= ref ? k <= ref : k >= ref; i = 0 <= ref ? ++k : --k) {
  1709. intArray[i] = byteString.charCodeAt(i);
  1710. }
  1711. mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
  1712. if (hasBlobConstructor) {
  1713. blobArray = hasArrayBufferViewSupport ? intArray : arrayBuffer;
  1714. return new Blob([blobArray], {
  1715. type: mimeString
  1716. });
  1717. }
  1718. bb = new BlobBuilder();
  1719. bb.append(arrayBuffer);
  1720. return bb.getBlob(mimeString);
  1721. };
  1722. Util.prototype.throttle = function(func, wait) {
  1723. var args, call, ctx, last, rtn, throttled, timeoutID;
  1724. last = 0;
  1725. timeoutID = 0;
  1726. ctx = args = rtn = null;
  1727. call = function() {
  1728. timeoutID = 0;
  1729. last = +new Date();
  1730. rtn = func.apply(ctx, args);
  1731. ctx = null;
  1732. return args = null;
  1733. };
  1734. throttled = function() {
  1735. var delta;
  1736. ctx = this;
  1737. args = arguments;
  1738. delta = new Date() - last;
  1739. if (!timeoutID) {
  1740. if (delta >= wait) {
  1741. call();
  1742. } else {
  1743. timeoutID = setTimeout(call, wait - delta);
  1744. }
  1745. }
  1746. return rtn;
  1747. };
  1748. throttled.clear = function() {
  1749. if (!timeoutID) {
  1750. return;
  1751. }
  1752. clearTimeout(timeoutID);
  1753. return call();
  1754. };
  1755. return throttled;
  1756. };
  1757. Util.prototype.formatHTML = function(html) {
  1758. var cursor, indentString, lastMatch, level, match, re, repeatString, result, str;
  1759. re = /<(\/?)(.+?)(\/?)>/g;
  1760. result = '';
  1761. level = 0;
  1762. lastMatch = null;
  1763. indentString = ' ';
  1764. repeatString = function(str, n) {
  1765. return new Array(n + 1).join(str);
  1766. };
  1767. while ((match = re.exec(html)) !== null) {
  1768. match.isBlockNode = $.inArray(match[2], this.blockNodes) > -1;
  1769. match.isStartTag = match[1] !== '/' && match[3] !== '/';
  1770. match.isEndTag = match[1] === '/' || match[3] === '/';
  1771. cursor = lastMatch ? lastMatch.index + lastMatch[0].length : 0;
  1772. if ((str = html.substring(cursor, match.index)).length > 0 && $.trim(str)) {
  1773. result += str;
  1774. }
  1775. if (match.isBlockNode && match.isEndTag && !match.isStartTag) {
  1776. level -= 1;
  1777. }
  1778. if (match.isBlockNode && match.isStartTag) {
  1779. if (!(lastMatch && lastMatch.isBlockNode && lastMatch.isEndTag)) {
  1780. result += '\n';
  1781. }
  1782. result += repeatString(indentString, level);
  1783. }
  1784. result += match[0];
  1785. if (match.isBlockNode && match.isEndTag) {
  1786. result += '\n';
  1787. }
  1788. if (match.isBlockNode && match.isStartTag) {
  1789. level += 1;
  1790. }
  1791. lastMatch = match;
  1792. }
  1793. return $.trim(result);
  1794. };
  1795. return Util;
  1796. })(SimpleModule);
  1797. Toolbar = (function(superClass) {
  1798. extend(Toolbar, superClass);
  1799. function Toolbar() {
  1800. return Toolbar.__super__.constructor.apply(this, arguments);
  1801. }
  1802. Toolbar.pluginName = 'Toolbar';
  1803. Toolbar.prototype.opts = {
  1804. toolbar: true,
  1805. toolbarFloat: true,
  1806. toolbarHidden: false,
  1807. toolbarFloatOffset: 0,
  1808. toolbarScrollContainer: window
  1809. };
  1810. Toolbar.prototype._tpl = {
  1811. wrapper: '<div class="simditor-toolbar"><ul></ul></div>',
  1812. separator: '<li><span class="separator"></span></li>'
  1813. };
  1814. Toolbar.prototype._init = function() {
  1815. var floatInitialized, initToolbarFloat, scrollContainerOffset, toolbarHeight;
  1816. this.editor = this._module;
  1817. if (!this.opts.toolbar) {
  1818. return;
  1819. }
  1820. if (!$.isArray(this.opts.toolbar)) {
  1821. this.opts.toolbar = ['bold', 'italic', 'underline', 'strikethrough', '|', 'ol', 'ul', 'blockquote', 'code', '|', 'link', 'image', '|', 'indent', 'outdent'];
  1822. }
  1823. this._render();
  1824. this.list.on('click', function(e) {
  1825. return false;
  1826. });
  1827. this.wrapper.on('mousedown', (function(_this) {
  1828. return function(e) {
  1829. return _this.list.find('.menu-on').removeClass('.menu-on');
  1830. };
  1831. })(this));
  1832. $(document).on('mousedown.simditor' + this.editor.id, (function(_this) {
  1833. return function(e) {
  1834. return _this.list.find('.menu-on').removeClass('.menu-on');
  1835. };
  1836. })(this));
  1837. if (!this.opts.toolbarHidden && this.opts.toolbarFloat) {
  1838. scrollContainerOffset = this.opts.toolbarScrollContainer === window ? {
  1839. top: 0,
  1840. left: 0
  1841. } : $(this.opts.toolbarScrollContainer).offset();
  1842. this.wrapper.css('top', scrollContainerOffset.top + this.opts.toolbarFloatOffset);
  1843. toolbarHeight = 0;
  1844. initToolbarFloat = (function(_this) {
  1845. return function() {
  1846. _this.wrapper.css('position', 'static');
  1847. _this.wrapper.width('auto');
  1848. _this.editor.util.reflow(_this.wrapper);
  1849. _this.wrapper.width(_this.wrapper.outerWidth());
  1850. _this.wrapper.css('left', _this.editor.util.os.mobile ? _this.wrapper.position().left : _this.wrapper.offset().left - scrollContainerOffset.left);
  1851. _this.wrapper.css('position', '');
  1852. toolbarHeight = _this.wrapper.outerHeight();
  1853. _this.editor.placeholderEl.css('top', scrollContainerOffset.top);
  1854. return true;
  1855. };
  1856. })(this);
  1857. floatInitialized = null;
  1858. $(window).on('resize.simditor-' + this.editor.id, function(e) {
  1859. return floatInitialized = initToolbarFloat();
  1860. });
  1861. $(this.opts.toolbarScrollContainer).on('scroll.simditor-' + this.editor.id, (function(_this) {
  1862. return function(e) {
  1863. var bottomEdge, scrollTop, topEdge;
  1864. if (!_this.wrapper.is(':visible')) {
  1865. return;
  1866. }
  1867. topEdge = _this.opts.toolbarScrollContainer === window ? _this.editor.wrapper.get(0).getBoundingClientRect().top : _this.editor.wrapper.offset().top - scrollContainerOffset.top;
  1868. bottomEdge = topEdge + _this.editor.wrapper.outerHeight() - 80;
  1869. scrollTop = $(_this.opts.toolbarScrollContainer).scrollTop() + _this.opts.toolbarFloatOffset;
  1870. if (topEdge > 0 || bottomEdge < 0) {
  1871. _this.editor.wrapper.removeClass('toolbar-floating').css('padding-top', '');
  1872. if (_this.editor.util.os.mobile) {
  1873. return _this.wrapper.css('top', _this.opts.toolbarFloatOffset);
  1874. }
  1875. } else {
  1876. floatInitialized || (floatInitialized = initToolbarFloat());
  1877. _this.editor.wrapper.addClass('toolbar-floating').css('padding-top', toolbarHeight);
  1878. if (_this.editor.util.os.mobile) {
  1879. return _this.wrapper.css('top', scrollTop - topEdge + _this.opts.toolbarFloatOffset);
  1880. }
  1881. }
  1882. };
  1883. })(this));
  1884. }
  1885. this.editor.on('destroy', (function(_this) {
  1886. return function() {
  1887. return _this.buttons.length = 0;
  1888. };
  1889. })(this));
  1890. return $(document).on("mousedown.simditor-" + this.editor.id, (function(_this) {
  1891. return function(e) {
  1892. return _this.list.find('li.menu-on').removeClass('menu-on');
  1893. };
  1894. })(this));
  1895. };
  1896. Toolbar.prototype._render = function() {
  1897. var k, len, name, ref;
  1898. this.buttons = [];
  1899. this.wrapper = $(this._tpl.wrapper).prependTo(this.editor.wrapper);
  1900. this.list = this.wrapper.find('ul');
  1901. ref = this.opts.toolbar;
  1902. for (k = 0, len = ref.length; k < len; k++) {
  1903. name = ref[k];
  1904. if (name === '|') {
  1905. $(this._tpl.separator).appendTo(this.list);
  1906. continue;
  1907. }
  1908. if (!this.constructor.buttons[name]) {
  1909. throw new Error("simditor: invalid toolbar button " + name);
  1910. continue;
  1911. }
  1912. this.buttons.push(new this.constructor.buttons[name]({
  1913. editor: this.editor
  1914. }));
  1915. }
  1916. if (this.opts.toolbarHidden) {
  1917. return this.wrapper.hide();
  1918. }
  1919. };
  1920. Toolbar.prototype.findButton = function(name) {
  1921. var button;
  1922. button = this.list.find('.toolbar-item-' + name).data('button');
  1923. return button != null ? button : null;
  1924. };
  1925. Toolbar.addButton = function(btn) {
  1926. return this.buttons[btn.prototype.name] = btn;
  1927. };
  1928. Toolbar.buttons = {};
  1929. return Toolbar;
  1930. })(SimpleModule);
  1931. Indentation = (function(superClass) {
  1932. extend(Indentation, superClass);
  1933. function Indentation() {
  1934. return Indentation.__super__.constructor.apply(this, arguments);
  1935. }
  1936. Indentation.pluginName = 'Indentation';
  1937. Indentation.prototype.opts = {
  1938. tabIndent: true
  1939. };
  1940. Indentation.prototype._init = function() {
  1941. this.editor = this._module;
  1942. return this.editor.keystroke.add('tab', '*', (function(_this) {
  1943. return function(e) {
  1944. var codeButton;
  1945. codeButton = _this.editor.toolbar.findButton('code');
  1946. if (!(_this.opts.tabIndent || (codeButton && codeButton.active))) {
  1947. return;
  1948. }
  1949. return _this.indent(e.shiftKey);
  1950. };
  1951. })(this));
  1952. };
  1953. Indentation.prototype.indent = function(isBackward) {
  1954. var $blockNodes, $endNodes, $startNodes, nodes, result;
  1955. $startNodes = this.editor.selection.startNodes();
  1956. $endNodes = this.editor.selection.endNodes();
  1957. $blockNodes = this.editor.selection.blockNodes();
  1958. nodes = [];
  1959. $blockNodes = $blockNodes.each(function(i, node) {
  1960. var include, j, k, len, n;
  1961. include = true;
  1962. for (j = k = 0, len = nodes.length; k < len; j = ++k) {
  1963. n = nodes[j];
  1964. if ($.contains(node, n)) {
  1965. include = false;
  1966. break;
  1967. } else if ($.contains(n, node)) {
  1968. nodes.splice(j, 1, node);
  1969. include = false;
  1970. break;
  1971. }
  1972. }
  1973. if (include) {
  1974. return nodes.push(node);
  1975. }
  1976. });
  1977. $blockNodes = $(nodes);
  1978. result = false;
  1979. $blockNodes.each((function(_this) {
  1980. return function(i, blockEl) {
  1981. var r;
  1982. r = isBackward ? _this.outdentBlock(blockEl) : _this.indentBlock(blockEl);
  1983. if (r) {
  1984. return result = r;
  1985. }
  1986. };
  1987. })(this));
  1988. return result;
  1989. };
  1990. Indentation.prototype.indentBlock = function(blockEl) {
  1991. var $blockEl, $childList, $nextTd, $nextTr, $parentLi, $pre, $td, $tr, marginLeft, tagName;
  1992. $blockEl = $(blockEl);
  1993. if (!$blockEl.length) {
  1994. return;
  1995. }
  1996. if ($blockEl.is('pre')) {
  1997. $pre = this.editor.selection.containerNode();
  1998. if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) {
  1999. return;
  2000. }
  2001. this.indentText(this.editor.selection.range());
  2002. } else if ($blockEl.is('li')) {
  2003. $parentLi = $blockEl.prev('li');
  2004. if ($parentLi.length < 1) {
  2005. return;
  2006. }
  2007. this.editor.selection.save();
  2008. tagName = $blockEl.parent()[0].tagName;
  2009. $childList = $parentLi.children('ul, ol');
  2010. if ($childList.length > 0) {
  2011. $childList.append($blockEl);
  2012. } else {
  2013. $('<' + tagName + '/>').append($blockEl).appendTo($parentLi);
  2014. }
  2015. this.editor.selection.restore();
  2016. } else if ($blockEl.is('p, h1, h2, h3, h4')) {
  2017. marginLeft = parseInt($blockEl.css('margin-left')) || 0;
  2018. marginLeft = (Math.round(marginLeft / this.opts.indentWidth) + 1) * this.opts.indentWidth;
  2019. $blockEl.css('margin-left', marginLeft);
  2020. } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) {
  2021. $td = this.editor.selection.containerNode().closest('td, th');
  2022. $nextTd = $td.next('td, th');
  2023. if (!($nextTd.length > 0)) {
  2024. $tr = $td.parent('tr');
  2025. $nextTr = $tr.next('tr');
  2026. if ($nextTr.length < 1 && $tr.parent().is('thead')) {
  2027. $nextTr = $tr.parent('thead').next('tbody').find('tr:first');
  2028. }
  2029. $nextTd = $nextTr.find('td:first, th:first');
  2030. }
  2031. if (!($td.length > 0 && $nextTd.length > 0)) {
  2032. return;
  2033. }
  2034. this.editor.selection.setRangeAtEndOf($nextTd);
  2035. } else {
  2036. return false;
  2037. }
  2038. return true;
  2039. };
  2040. Indentation.prototype.indentText = function(range) {
  2041. var text, textNode;
  2042. text = range.toString().replace(/^(?=.+)/mg, '\u00A0\u00A0');
  2043. textNode = document.createTextNode(text || '\u00A0\u00A0');
  2044. range.deleteContents();
  2045. range.insertNode(textNode);
  2046. if (text) {
  2047. range.selectNode(textNode);
  2048. return this.editor.selection.range(range);
  2049. } else {
  2050. return this.editor.selection.setRangeAfter(textNode);
  2051. }
  2052. };
  2053. Indentation.prototype.outdentBlock = function(blockEl) {
  2054. var $blockEl, $parent, $parentLi, $pre, $prevTd, $prevTr, $td, $tr, marginLeft, range;
  2055. $blockEl = $(blockEl);
  2056. if (!($blockEl && $blockEl.length > 0)) {
  2057. return;
  2058. }
  2059. if ($blockEl.is('pre')) {
  2060. $pre = this.editor.selection.containerNode();
  2061. if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) {
  2062. return;
  2063. }
  2064. this.outdentText(range);
  2065. } else if ($blockEl.is('li')) {
  2066. $parent = $blockEl.parent();
  2067. $parentLi = $parent.parent('li');
  2068. this.editor.selection.save();
  2069. if ($parentLi.length < 1) {
  2070. range = document.createRange();
  2071. range.setStartBefore($parent[0]);
  2072. range.setEndBefore($blockEl[0]);
  2073. $parent.before(range.extractContents());
  2074. $('<p/>').insertBefore($parent).after($blockEl.children('ul, ol')).append($blockEl.contents());
  2075. $blockEl.remove();
  2076. } else {
  2077. if ($blockEl.next('li').length > 0) {
  2078. $('<' + $parent[0].tagName + '/>').append($blockEl.nextAll('li')).appendTo($blockEl);
  2079. }
  2080. $blockEl.insertAfter($parentLi);
  2081. if ($parent.children('li').length < 1) {
  2082. $parent.remove();
  2083. }
  2084. }
  2085. this.editor.selection.restore();
  2086. } else if ($blockEl.is('p, h1, h2, h3, h4')) {
  2087. marginLeft = parseInt($blockEl.css('margin-left')) || 0;
  2088. marginLeft = Math.max(Math.round(marginLeft / this.opts.indentWidth) - 1, 0) * this.opts.indentWidth;
  2089. $blockEl.css('margin-left', marginLeft === 0 ? '' : marginLeft);
  2090. } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) {
  2091. $td = this.editor.selection.containerNode().closest('td, th');
  2092. $prevTd = $td.prev('td, th');
  2093. if (!($prevTd.length > 0)) {
  2094. $tr = $td.parent('tr');
  2095. $prevTr = $tr.prev('tr');
  2096. if ($prevTr.length < 1 && $tr.parent().is('tbody')) {
  2097. $prevTr = $tr.parent('tbody').prev('thead').find('tr:first');
  2098. }
  2099. $prevTd = $prevTr.find('td:last, th:last');
  2100. }
  2101. if (!($td.length > 0 && $prevTd.length > 0)) {
  2102. return;
  2103. }
  2104. this.editor.selection.setRangeAtEndOf($prevTd);
  2105. } else {
  2106. return false;
  2107. }
  2108. return true;
  2109. };
  2110. Indentation.prototype.outdentText = function(range) {};
  2111. return Indentation;
  2112. })(SimpleModule);
  2113. Clipboard = (function(superClass) {
  2114. extend(Clipboard, superClass);
  2115. function Clipboard() {
  2116. return Clipboard.__super__.constructor.apply(this, arguments);
  2117. }
  2118. Clipboard.pluginName = 'Clipboard';
  2119. Clipboard.prototype.opts = {
  2120. pasteImage: false,
  2121. cleanPaste: false
  2122. };
  2123. Clipboard.prototype._init = function() {
  2124. this.editor = this._module;
  2125. if (this.opts.pasteImage && typeof this.opts.pasteImage !== 'string') {
  2126. this.opts.pasteImage = 'inline';
  2127. }
  2128. return this.editor.body.on('paste', (function(_this) {
  2129. return function(e) {
  2130. var pasteBinAnchor, range;
  2131. if (_this.pasting || _this._pasteBin) {
  2132. return;
  2133. }
  2134. if (_this.editor.triggerHandler(e) === false) {
  2135. return false;
  2136. }
  2137. range = _this.editor.selection.deleteRangeContents();
  2138. if (_this.editor.body.html()) {
  2139. if (!range.collapsed) {
  2140. range.collapse(true);
  2141. }
  2142. } else {
  2143. _this.editor.formatter.format();
  2144. _this.editor.selection.setRangeAtStartOf(_this.editor.body.find('p:first'));
  2145. range = _this.editor.selection._range;
  2146. }
  2147. if (_this._processPasteByClipboardApi(e)) {
  2148. return false;
  2149. }
  2150. pasteBinAnchor = $('<span>');
  2151. range.insertNode(pasteBinAnchor[0]);
  2152. _this._createPasteBin(pasteBinAnchor);
  2153. pasteBinAnchor.remove();
  2154. range.collapse(true);
  2155. _this.editor.selection.range(range);
  2156. _this.editor.inputManager.throttledValueChanged.clear();
  2157. _this.editor.inputManager.throttledSelectionChanged.clear();
  2158. _this.editor.undoManager.throttledPushState.clear();
  2159. _this.editor.selection.reset();
  2160. _this.editor.undoManager.resetCaretPosition();
  2161. _this.pasting = true;
  2162. return _this._getPasteContent(function(pasteContent) {
  2163. _this._processPasteContent(pasteContent);
  2164. _this._pasteInBlockEl = null;
  2165. _this._pastePlainText = null;
  2166. return _this.pasting = false;
  2167. });
  2168. };
  2169. })(this));
  2170. };
  2171. Clipboard.prototype._processPasteByClipboardApi = function(e) {
  2172. var imageFile, pasteItem, ref, uploadOpt;
  2173. if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.items && e.originalEvent.clipboardData.items.length > 0) {
  2174. pasteItem = e.originalEvent.clipboardData.items[0];
  2175. if (/^image\//.test(pasteItem.type)) {
  2176. imageFile = pasteItem.getAsFile();
  2177. if (!((imageFile != null) && this.opts.pasteImage)) {
  2178. return;
  2179. }
  2180. if (!imageFile.name) {
  2181. imageFile.name = "Clipboard Image.png";
  2182. }
  2183. if (this.editor.triggerHandler('pasting', [imageFile]) === false) {
  2184. return;
  2185. }
  2186. uploadOpt = {};
  2187. uploadOpt[this.opts.pasteImage] = true;
  2188. if ((ref = this.editor.uploader) != null) {
  2189. ref.upload(imageFile, uploadOpt);
  2190. }
  2191. return true;
  2192. }
  2193. }
  2194. };
  2195. Clipboard.prototype._createPasteBin = function(anchorNode) {
  2196. var anchorOffset, editorOffset;
  2197. anchorOffset = anchorNode.offset();
  2198. editorOffset = this.editor.el.offset();
  2199. return this._pasteBin = $('<div contenteditable="true" />').addClass('simditor-paste-bin').attr('tabIndex', '-1').css({
  2200. top: anchorOffset.top - editorOffset.top,
  2201. left: anchorOffset.left - editorOffset.left
  2202. }).appendTo(this.editor.el);
  2203. };
  2204. Clipboard.prototype._getPasteContent = function(callback) {
  2205. var state;
  2206. state = {
  2207. html: this.editor.body.html(),
  2208. caret: this.editor.undoManager.caretPosition()
  2209. };
  2210. this._pasteBin.focus();
  2211. return setTimeout((function(_this) {
  2212. return function() {
  2213. var pasteContent;
  2214. _this.editor.hidePopover();
  2215. _this.editor.body.get(0).innerHTML = DOMPurify && _this.opts.dompurify.enabled ? DOMPurify.sanitize(state.html, _this.opts.dompurify.options) : state.html;
  2216. _this.editor.undoManager.caretPosition(state.caret);
  2217. _this.editor.body.focus();
  2218. _this.editor.selection.reset();
  2219. _this.editor.selection.range();
  2220. _this._pasteInBlockEl = _this.editor.selection.blockNodes().last();
  2221. _this._pastePlainText = _this.opts.cleanPaste || _this._pasteInBlockEl.is('pre, table');
  2222. if (_this._pastePlainText) {
  2223. pasteContent = _this.editor.formatter.clearHtml(_this._pasteBin.html(), true);
  2224. } else {
  2225. pasteContent = $('<div/>').append(_this._pasteBin.contents());
  2226. pasteContent.find('style').remove();
  2227. pasteContent.find('table colgroup').remove();
  2228. _this._cleanPasteFontSize(pasteContent);
  2229. _this.editor.formatter.format(pasteContent);
  2230. _this.editor.formatter.decorate(pasteContent);
  2231. _this.editor.formatter.beautify(pasteContent.children());
  2232. pasteContent = pasteContent.contents();
  2233. }
  2234. _this._pasteBin.remove();
  2235. _this._pasteBin = null;
  2236. return callback(pasteContent);
  2237. };
  2238. })(this), 0);
  2239. };
  2240. Clipboard.prototype._processPasteContent = function(pasteContent) {
  2241. var $blockEl, $img, blob, children, dataURLtoBlob, img, insertPosition, k, l, lastLine, len, len1, len2, len3, len4, line, lines, m, node, o, q, ref, ref1, ref2, uploadOpt, uploader;
  2242. if (this.editor.triggerHandler('pasting', [pasteContent]) === false) {
  2243. return;
  2244. }
  2245. $blockEl = this._pasteInBlockEl;
  2246. if (!pasteContent) {
  2247. return;
  2248. }
  2249. if (this._pastePlainText) {
  2250. if ($blockEl.is('table')) {
  2251. lines = pasteContent.split('\n');
  2252. lastLine = lines.pop();
  2253. for (k = 0, len = lines.length; k < len; k++) {
  2254. line = lines[k];
  2255. this.editor.selection.insertNode(document.createTextNode(line));
  2256. this.editor.selection.insertNode($('<br/>'));
  2257. }
  2258. this.editor.selection.insertNode(document.createTextNode(lastLine));
  2259. } else {
  2260. pasteContent = $('<div/>').text(pasteContent);
  2261. ref = pasteContent.contents();
  2262. for (l = 0, len1 = ref.length; l < len1; l++) {
  2263. node = ref[l];
  2264. this.editor.selection.insertNode($(node)[0]);
  2265. }
  2266. }
  2267. } else if ($blockEl.is(this.editor.body)) {
  2268. for (m = 0, len2 = pasteContent.length; m < len2; m++) {
  2269. node = pasteContent[m];
  2270. this.editor.selection.insertNode(node);
  2271. }
  2272. } else if (pasteContent.length < 1) {
  2273. return;
  2274. } else if (pasteContent.length === 1) {
  2275. if (pasteContent.is('p')) {
  2276. children = pasteContent.contents();
  2277. if ($blockEl.is('h1, h2, h3, h4, h5')) {
  2278. if (children.length) {
  2279. children.css('font-size', '');
  2280. }
  2281. }
  2282. if (children.length === 1 && children.is('img')) {
  2283. $img = children;
  2284. if (/^data:image/.test($img.attr('src'))) {
  2285. if (!this.opts.pasteImage) {
  2286. return;
  2287. }
  2288. blob = this.editor.util.dataURLtoBlob($img.attr("src"));
  2289. blob.name = "Clipboard Image.png";
  2290. uploadOpt = {};
  2291. uploadOpt[this.opts.pasteImage] = true;
  2292. if ((ref1 = this.editor.uploader) != null) {
  2293. ref1.upload(blob, uploadOpt);
  2294. }
  2295. return;
  2296. } else if (new RegExp('^blob:' + location.origin + '/').test($img.attr('src'))) {
  2297. if (!this.opts.pasteImage) {
  2298. return;
  2299. }
  2300. uploadOpt = {};
  2301. uploadOpt[this.opts.pasteImage] = true;
  2302. dataURLtoBlob = this.editor.util.dataURLtoBlob;
  2303. uploader = this.editor.uploader;
  2304. img = new Image;
  2305. img.onload = function() {
  2306. var canvas;
  2307. canvas = document.createElement('canvas');
  2308. canvas.width = img.naturalWidth;
  2309. canvas.height = img.naturalHeight;
  2310. canvas.getContext('2d').drawImage(img, 0, 0);
  2311. blob = dataURLtoBlob(canvas.toDataURL('image/png'));
  2312. blob.name = 'Clipboard Image.png';
  2313. if (uploader !== null) {
  2314. uploader.upload(blob, uploadOpt);
  2315. }
  2316. };
  2317. img.src = $img.attr('src');
  2318. return;
  2319. } else if ($img.is('img[src^="webkit-fake-url://"]')) {
  2320. return;
  2321. }
  2322. }
  2323. for (o = 0, len3 = children.length; o < len3; o++) {
  2324. node = children[o];
  2325. this.editor.selection.insertNode(node);
  2326. }
  2327. } else if ($blockEl.is('p') && this.editor.util.isEmptyNode($blockEl)) {
  2328. $blockEl.replaceWith(pasteContent);
  2329. this.editor.selection.setRangeAtEndOf(pasteContent);
  2330. } else if (pasteContent.is('ul, ol')) {
  2331. if (pasteContent.find('li').length === 1) {
  2332. pasteContent = $('<div/>').text(pasteContent.text());
  2333. ref2 = pasteContent.contents();
  2334. for (q = 0, len4 = ref2.length; q < len4; q++) {
  2335. node = ref2[q];
  2336. this.editor.selection.insertNode($(node)[0]);
  2337. }
  2338. } else if ($blockEl.is('li')) {
  2339. $blockEl.parent().after(pasteContent);
  2340. this.editor.selection.setRangeAtEndOf(pasteContent);
  2341. } else {
  2342. $blockEl.after(pasteContent);
  2343. this.editor.selection.setRangeAtEndOf(pasteContent);
  2344. }
  2345. } else {
  2346. $blockEl.after(pasteContent);
  2347. this.editor.selection.setRangeAtEndOf(pasteContent);
  2348. }
  2349. } else {
  2350. if ($blockEl.is('li')) {
  2351. $blockEl = $blockEl.parent();
  2352. }
  2353. if (this.editor.selection.rangeAtStartOf($blockEl)) {
  2354. insertPosition = 'before';
  2355. } else if (this.editor.selection.rangeAtEndOf($blockEl)) {
  2356. insertPosition = 'after';
  2357. } else {
  2358. this.editor.selection.breakBlockEl($blockEl);
  2359. insertPosition = 'before';
  2360. }
  2361. $blockEl[insertPosition](pasteContent);
  2362. this.editor.selection.setRangeAtEndOf(pasteContent.last());
  2363. }
  2364. return this.editor.inputManager.throttledValueChanged();
  2365. };
  2366. Clipboard.prototype._cleanPasteFontSize = function(node) {
  2367. var $node, sizeMap;
  2368. $node = $(node);
  2369. if (!($node.length > 0)) {
  2370. return;
  2371. }
  2372. sizeMap = ['1.5em', '1.25em', '0.75em', '0.5em'];
  2373. return $node.find('[style*="font-size"]').map(function(i, el) {
  2374. var $el;
  2375. $el = $(el);
  2376. if ($.inArray($el.css('font-size'), sizeMap) < 0) {
  2377. return $el.css('font-size', '');
  2378. }
  2379. });
  2380. };
  2381. return Clipboard;
  2382. })(SimpleModule);
  2383. Simditor = (function(superClass) {
  2384. extend(Simditor, superClass);
  2385. function Simditor() {
  2386. return Simditor.__super__.constructor.apply(this, arguments);
  2387. }
  2388. Simditor.connect(Util);
  2389. Simditor.connect(InputManager);
  2390. Simditor.connect(Selection);
  2391. Simditor.connect(UndoManager);
  2392. Simditor.connect(Keystroke);
  2393. Simditor.connect(Formatter);
  2394. Simditor.connect(Toolbar);
  2395. Simditor.connect(Indentation);
  2396. Simditor.connect(Clipboard);
  2397. Simditor.count = 0;
  2398. Simditor.prototype.opts = {
  2399. textarea: null,
  2400. placeholder: '',
  2401. defaultImage: 'images/image.png',
  2402. params: {},
  2403. upload: false,
  2404. indentWidth: 40
  2405. };
  2406. Simditor.prototype._init = function() {
  2407. var e, editor, uploadOpts;
  2408. this.textarea = $(this.opts.textarea);
  2409. this.opts.placeholder = this.opts.placeholder || this.textarea.attr('placeholder');
  2410. if (!this.textarea.length) {
  2411. throw new Error('simditor: param textarea is required.');
  2412. return;
  2413. }
  2414. editor = this.textarea.data('simditor');
  2415. if (editor != null) {
  2416. editor.destroy();
  2417. }
  2418. this.id = ++Simditor.count;
  2419. this._render();
  2420. if (simpleHotkeys) {
  2421. this.hotkeys = simpleHotkeys({
  2422. el: this.body
  2423. });
  2424. } else {
  2425. throw new Error('simditor: simple-hotkeys is required.');
  2426. return;
  2427. }
  2428. if (this.opts.upload && simpleUploader) {
  2429. uploadOpts = typeof this.opts.upload === 'object' ? this.opts.upload : {};
  2430. this.uploader = simpleUploader(uploadOpts);
  2431. }
  2432. this.on('initialized', (function(_this) {
  2433. return function() {
  2434. if (_this.opts.placeholder) {
  2435. _this.on('valuechanged', function() {
  2436. return _this._placeholder();
  2437. });
  2438. }
  2439. _this.setValue(_this.textarea.val().trim() || '');
  2440. if (_this.textarea.attr('autofocus')) {
  2441. return _this.focus();
  2442. }
  2443. };
  2444. })(this));
  2445. if (this.util.browser.mozilla) {
  2446. this.util.reflow();
  2447. try {
  2448. document.execCommand('enableObjectResizing', false, false);
  2449. return document.execCommand('enableInlineTableEditing', false, false);
  2450. } catch (_error) {
  2451. e = _error;
  2452. }
  2453. }
  2454. };
  2455. Simditor.prototype._tpl = "<div class=\"simditor\">\n <div class=\"simditor-wrapper\">\n <div class=\"simditor-placeholder\"></div>\n <div class=\"simditor-body\" contenteditable=\"true\">\n </div>\n </div>\n</div>";
  2456. Simditor.prototype._render = function() {
  2457. var key, ref, results, val;
  2458. this.el = $(this._tpl).insertBefore(this.textarea);
  2459. this.wrapper = this.el.find('.simditor-wrapper');
  2460. this.body = this.wrapper.find('.simditor-body');
  2461. this.placeholderEl = this.wrapper.find('.simditor-placeholder').append(this.opts.placeholder);
  2462. this.el.data('simditor', this);
  2463. this.wrapper.append(this.textarea);
  2464. this.textarea.data('simditor', this).blur();
  2465. this.body.attr('tabindex', this.textarea.attr('tabindex'));
  2466. if (this.util.os.mac) {
  2467. this.el.addClass('simditor-mac');
  2468. } else if (this.util.os.linux) {
  2469. this.el.addClass('simditor-linux');
  2470. }
  2471. if (this.util.os.mobile) {
  2472. this.el.addClass('simditor-mobile');
  2473. }
  2474. if (this.opts.params) {
  2475. ref = this.opts.params;
  2476. results = [];
  2477. for (key in ref) {
  2478. val = ref[key];
  2479. results.push($('<input/>', {
  2480. type: 'hidden',
  2481. name: key,
  2482. value: val
  2483. }).insertAfter(this.textarea));
  2484. }
  2485. return results;
  2486. }
  2487. };
  2488. Simditor.prototype._placeholder = function() {
  2489. var children;
  2490. children = this.body.children();
  2491. if (children.length === 0 || (children.length === 1 && this.util.isEmptyNode(children) && parseInt(children.css('margin-left') || 0) < this.opts.indentWidth)) {
  2492. return this.placeholderEl.show();
  2493. } else {
  2494. return this.placeholderEl.hide();
  2495. }
  2496. };
  2497. Simditor.prototype.setValue = function(val) {
  2498. this.hidePopover();
  2499. this.textarea.val(val);
  2500. this.body.get(0).innerHTML = DOMPurify && this.opts.dompurify.enabled ? DOMPurify.sanitize(val, this.opts.dompurify.options) : val;
  2501. this.formatter.format();
  2502. this.formatter.decorate();
  2503. this.util.reflow(this.body);
  2504. this.inputManager.lastCaretPosition = null;
  2505. return this.trigger('valuechanged');
  2506. };
  2507. Simditor.prototype.getValue = function() {
  2508. return this.sync();
  2509. };
  2510. Simditor.prototype.insertHTML = function(html, options) {
  2511. var $nodes, range, selection;
  2512. if (options == null) {
  2513. options = {};
  2514. }
  2515. // 确保编辑器获得焦点
  2516. this.focus();
  2517. // 获取当前选区
  2518. selection = this.selection;
  2519. range = selection.range();
  2520. if (!range) {
  2521. return false;
  2522. }
  2523. // 删除当前选中的内容
  2524. if (!range.collapsed) {
  2525. range.deleteContents();
  2526. }
  2527. html = DOMPurify && _this.opts.dompurify.enabled ? DOMPurify.sanitize(html, _this.opts.dompurify.options) : html;
  2528. // 创建临时容器来解析 HTML
  2529. var $temp = $('<div/>').html(html);
  2530. // 清理和规范化内容
  2531. if (!options.skipClean) {
  2532. this.formatter.format($temp);
  2533. this.formatter.decorate($temp);
  2534. }
  2535. // 获取所有节点
  2536. $nodes = $temp.contents();
  2537. if ($nodes.length === 0) {
  2538. return false;
  2539. }
  2540. // 插入节点
  2541. var lastNode = null;
  2542. var firstNode = null;
  2543. var docFrag = document.createDocumentFragment();
  2544. $nodes.each(function(i, node) {
  2545. var clonedNode = node.cloneNode(true);
  2546. docFrag.appendChild(clonedNode);
  2547. if (i === 0) {
  2548. firstNode = clonedNode;
  2549. }
  2550. lastNode = clonedNode;
  2551. });
  2552. // 插入文档片段
  2553. range.insertNode(docFrag);
  2554. // 设置新的光标位置
  2555. if (lastNode) {
  2556. range = range.cloneRange();
  2557. range.setStartAfter(lastNode);
  2558. range.collapse(true);
  2559. selection.range(range);
  2560. }
  2561. // 触发更新
  2562. this.trigger('valuechanged');
  2563. this.trigger('selectionchanged');
  2564. // 返回插入的节点
  2565. return $(firstNode).parent().find(firstNode).nextUntil($(lastNode).next());
  2566. };
  2567. Simditor.prototype.sync = function() {
  2568. var children, cloneBody, emptyP, firstP, lastP, val;
  2569. cloneBody = this.body.clone();
  2570. this.formatter.undecorate(cloneBody);
  2571. this.formatter.format(cloneBody);
  2572. this.formatter.autolink(cloneBody);
  2573. children = cloneBody.children();
  2574. lastP = children.last('p');
  2575. firstP = children.first('p');
  2576. while (lastP.is('p') && this.util.isEmptyNode(lastP)) {
  2577. emptyP = lastP;
  2578. lastP = lastP.prev('p');
  2579. emptyP.remove();
  2580. }
  2581. while (firstP.is('p') && this.util.isEmptyNode(firstP)) {
  2582. emptyP = firstP;
  2583. firstP = lastP.next('p');
  2584. emptyP.remove();
  2585. }
  2586. cloneBody.find('img.uploading').remove();
  2587. val = $.trim(cloneBody.html());
  2588. this.textarea.val(val);
  2589. return val;
  2590. };
  2591. Simditor.prototype.focus = function() {
  2592. var $blockEl, range;
  2593. if (!(this.body.is(':visible') && this.body.is('[contenteditable]'))) {
  2594. this.el.find('textarea:visible').focus();
  2595. return;
  2596. }
  2597. if (this.inputManager.lastCaretPosition) {
  2598. this.undoManager.caretPosition(this.inputManager.lastCaretPosition);
  2599. return this.inputManager.lastCaretPosition = null;
  2600. } else {
  2601. $blockEl = this.body.children().last();
  2602. if (!$blockEl.is('p')) {
  2603. $blockEl = $('<p/>').append(this.util.phBr).appendTo(this.body);
  2604. }
  2605. range = document.createRange();
  2606. return this.selection.setRangeAtEndOf($blockEl, range);
  2607. }
  2608. };
  2609. Simditor.prototype.blur = function() {
  2610. if (this.body.is(':visible') && this.body.is('[contenteditable]')) {
  2611. return this.body.blur();
  2612. } else {
  2613. return this.body.find('textarea:visible').blur();
  2614. }
  2615. };
  2616. Simditor.prototype.hidePopover = function() {
  2617. return this.el.find('.simditor-popover').each(function(i, popover) {
  2618. popover = $(popover).data('popover');
  2619. if (popover.active) {
  2620. return popover.hide();
  2621. }
  2622. });
  2623. };
  2624. Simditor.prototype.destroy = function() {
  2625. this.triggerHandler('destroy');
  2626. this.textarea.closest('form').off('.simditor .simditor-' + this.id);
  2627. this.selection.clear();
  2628. this.inputManager.focused = false;
  2629. this.textarea.insertBefore(this.el).hide().val('').removeData('simditor');
  2630. this.el.remove();
  2631. $(document).off('.simditor-' + this.id);
  2632. $(window).off('.simditor-' + this.id);
  2633. return this.off();
  2634. };
  2635. return Simditor;
  2636. })(SimpleModule);
  2637. Simditor.i18n = {
  2638. 'zh-CN': {
  2639. 'blockquote': '引用',
  2640. 'bold': '加粗文字',
  2641. 'code': '插入代码',
  2642. 'color': '文字颜色',
  2643. 'coloredText': '彩色文字',
  2644. 'hr': '分隔线',
  2645. 'image': '插入图片',
  2646. 'externalImage': '外链图片',
  2647. 'uploadImage': '上传图片',
  2648. 'selectImage': '选择图片',
  2649. 'uploadFailed': '上传失败了',
  2650. 'uploadError': '上传出错了',
  2651. 'imageUrl': '图片地址',
  2652. 'imageSize': '图片尺寸',
  2653. 'imageAlt': '图片描述',
  2654. 'restoreImageSize': '还原图片尺寸',
  2655. 'uploading': '正在上传',
  2656. 'indent': '向右缩进',
  2657. 'outdent': '向左缩进',
  2658. 'italic': '斜体文字',
  2659. 'link': '插入链接',
  2660. 'linkText': '链接文字',
  2661. 'linkUrl': '链接地址',
  2662. 'linkTarget': '打开方式',
  2663. 'openLinkInCurrentWindow': '在当前窗口中打开',
  2664. 'openLinkInNewWindow': '在新窗口中打开',
  2665. 'removeLink': '移除链接',
  2666. 'ol': '有序列表',
  2667. 'ul': '无序列表',
  2668. 'strikethrough': '删除线文字',
  2669. 'table': '表格',
  2670. 'deleteRow': '删除行',
  2671. 'insertRowAbove': '在上面插入行',
  2672. 'insertRowBelow': '在下面插入行',
  2673. 'deleteColumn': '删除列',
  2674. 'insertColumnLeft': '在左边插入列',
  2675. 'insertColumnRight': '在右边插入列',
  2676. 'deleteTable': '删除表格',
  2677. 'title': '标题',
  2678. 'normalText': '普通文本',
  2679. 'underline': '下划线文字',
  2680. 'alignment': '水平对齐',
  2681. 'alignCenter': '居中',
  2682. 'alignLeft': '居左',
  2683. 'alignRight': '居右',
  2684. 'selectLanguage': '选择程序语言',
  2685. 'fontScale': '字体大小',
  2686. 'fontScaleXLarge': '超大字体',
  2687. 'fontScaleLarge': '大号字体',
  2688. 'fontScaleNormal': '正常大小',
  2689. 'fontScaleSmall': '小号字体',
  2690. 'fontScaleXSmall': '超小字体'
  2691. },
  2692. 'en-US': {
  2693. 'blockquote': 'Block Quote',
  2694. 'bold': 'Bold',
  2695. 'code': 'Code',
  2696. 'color': 'Text Color',
  2697. 'coloredText': 'Colored Text',
  2698. 'hr': 'Horizontal Line',
  2699. 'image': 'Insert Image',
  2700. 'externalImage': 'External Image',
  2701. 'uploadImage': 'Upload Image',
  2702. 'selectImage': 'Select Image',
  2703. 'uploadFailed': 'Upload failed',
  2704. 'uploadError': 'Error occurs during upload',
  2705. 'imageUrl': 'Url',
  2706. 'imageSize': 'Size',
  2707. 'imageAlt': 'Alt',
  2708. 'restoreImageSize': 'Restore Origin Size',
  2709. 'uploading': 'Uploading',
  2710. 'indent': 'Indent',
  2711. 'outdent': 'Outdent',
  2712. 'italic': 'Italic',
  2713. 'link': 'Insert Link',
  2714. 'linkText': 'Text',
  2715. 'linkUrl': 'Url',
  2716. 'linkTarget': 'Target',
  2717. 'openLinkInCurrentWindow': 'Open link in current window',
  2718. 'openLinkInNewWindow': 'Open link in new window',
  2719. 'removeLink': 'Remove Link',
  2720. 'ol': 'Ordered List',
  2721. 'ul': 'Unordered List',
  2722. 'strikethrough': 'Strikethrough',
  2723. 'table': 'Table',
  2724. 'deleteRow': 'Delete Row',
  2725. 'insertRowAbove': 'Insert Row Above',
  2726. 'insertRowBelow': 'Insert Row Below',
  2727. 'deleteColumn': 'Delete Column',
  2728. 'insertColumnLeft': 'Insert Column Left',
  2729. 'insertColumnRight': 'Insert Column Right',
  2730. 'deleteTable': 'Delete Table',
  2731. 'title': 'Title',
  2732. 'normalText': 'Text',
  2733. 'underline': 'Underline',
  2734. 'alignment': 'Alignment',
  2735. 'alignCenter': 'Align Center',
  2736. 'alignLeft': 'Align Left',
  2737. 'alignRight': 'Align Right',
  2738. 'selectLanguage': 'Select Language',
  2739. 'fontScale': 'Font Size',
  2740. 'fontScaleXLarge': 'X Large Size',
  2741. 'fontScaleLarge': 'Large Size',
  2742. 'fontScaleNormal': 'Normal Size',
  2743. 'fontScaleSmall': 'Small Size',
  2744. 'fontScaleXSmall': 'X Small Size'
  2745. }
  2746. };
  2747. Button = (function(superClass) {
  2748. extend(Button, superClass);
  2749. Button.prototype._tpl = {
  2750. item: '<li><a tabindex="-1" unselectable="on" class="toolbar-item" href="javascript:;"><span></span></a></li>',
  2751. menuWrapper: '<div class="toolbar-menu"></div>',
  2752. menuItem: '<li><a tabindex="-1" unselectable="on" class="menu-item" href="javascript:;"><span></span></a></li>',
  2753. separator: '<li><span class="separator"></span></li>'
  2754. };
  2755. Button.prototype.name = '';
  2756. Button.prototype.icon = '';
  2757. Button.prototype.title = '';
  2758. Button.prototype.text = '';
  2759. Button.prototype.htmlTag = '';
  2760. Button.prototype.disableTag = '';
  2761. Button.prototype.menu = false;
  2762. Button.prototype.active = false;
  2763. Button.prototype.disabled = false;
  2764. Button.prototype.needFocus = true;
  2765. Button.prototype.shortcut = null;
  2766. function Button(opts) {
  2767. this.editor = opts.editor;
  2768. this.title = this._t(this.name);
  2769. Button.__super__.constructor.call(this, opts);
  2770. }
  2771. Button.prototype._init = function() {
  2772. var k, len, ref, tag;
  2773. this.render();
  2774. this.el.on('mousedown', (function(_this) {
  2775. return function(e) {
  2776. var exceed, noFocus, param;
  2777. e.preventDefault();
  2778. noFocus = _this.needFocus && !_this.editor.inputManager.focused;
  2779. if (_this.el.hasClass('disabled')) {
  2780. return false;
  2781. }
  2782. if (noFocus) {
  2783. _this.editor.focus();
  2784. }
  2785. if (_this.menu) {
  2786. _this.wrapper.toggleClass('menu-on').siblings('li').removeClass('menu-on');
  2787. if (_this.wrapper.is('.menu-on')) {
  2788. exceed = _this.menuWrapper.offset().left + _this.menuWrapper.outerWidth() + 5 - _this.editor.wrapper.offset().left - _this.editor.wrapper.outerWidth();
  2789. if (exceed > 0) {
  2790. _this.menuWrapper.css({
  2791. 'left': 'auto',
  2792. 'right': 0
  2793. });
  2794. }
  2795. _this.trigger('menuexpand');
  2796. }
  2797. return false;
  2798. }
  2799. param = _this.el.data('param');
  2800. _this.command(param);
  2801. return false;
  2802. };
  2803. })(this));
  2804. this.wrapper.on('click', 'a.menu-item', (function(_this) {
  2805. return function(e) {
  2806. var btn, noFocus, param;
  2807. e.preventDefault();
  2808. btn = $(e.currentTarget);
  2809. _this.wrapper.removeClass('menu-on');
  2810. noFocus = _this.needFocus && !_this.editor.inputManager.focused;
  2811. if (btn.hasClass('disabled') || noFocus) {
  2812. return false;
  2813. }
  2814. _this.editor.toolbar.wrapper.removeClass('menu-on');
  2815. param = btn.data('param');
  2816. _this.command(param);
  2817. return false;
  2818. };
  2819. })(this));
  2820. this.wrapper.on('mousedown', 'a.menu-item', function(e) {
  2821. return false;
  2822. });
  2823. this.editor.on('blur', (function(_this) {
  2824. return function() {
  2825. var editorActive;
  2826. editorActive = _this.editor.body.is(':visible') && _this.editor.body.is('[contenteditable]');
  2827. if (!(editorActive && !_this.editor.clipboard.pasting)) {
  2828. return;
  2829. }
  2830. _this.setActive(false);
  2831. return _this.setDisabled(false);
  2832. };
  2833. })(this));
  2834. if (this.shortcut != null) {
  2835. this.editor.hotkeys.add(this.shortcut, (function(_this) {
  2836. return function(e) {
  2837. _this.el.mousedown();
  2838. return false;
  2839. };
  2840. })(this));
  2841. }
  2842. ref = this.htmlTag.split(',');
  2843. for (k = 0, len = ref.length; k < len; k++) {
  2844. tag = ref[k];
  2845. tag = $.trim(tag);
  2846. if (tag && $.inArray(tag, this.editor.formatter._allowedTags) < 0) {
  2847. this.editor.formatter._allowedTags.push(tag);
  2848. }
  2849. }
  2850. return this.editor.on('selectionchanged', (function(_this) {
  2851. return function(e) {
  2852. if (_this.editor.inputManager.focused) {
  2853. return _this._status();
  2854. }
  2855. };
  2856. })(this));
  2857. };
  2858. Button.prototype.iconClassOf = function(icon) {
  2859. if (icon) {
  2860. return "simditor-icon simditor-icon-" + icon;
  2861. } else {
  2862. return '';
  2863. }
  2864. };
  2865. Button.prototype.setIcon = function(icon) {
  2866. return this.el.find('span').removeClass().addClass(this.iconClassOf(icon)).text(this.text);
  2867. };
  2868. Button.prototype.render = function() {
  2869. this.wrapper = $(this._tpl.item).appendTo(this.editor.toolbar.list);
  2870. this.el = this.wrapper.find('a.toolbar-item');
  2871. this.el.attr('title', this.title).addClass("toolbar-item-" + this.name).data('button', this);
  2872. this.setIcon(this.icon);
  2873. if (!this.menu) {
  2874. return;
  2875. }
  2876. this.menuWrapper = $(this._tpl.menuWrapper).appendTo(this.wrapper);
  2877. this.menuWrapper.addClass("toolbar-menu-" + this.name);
  2878. return this.renderMenu();
  2879. };
  2880. Button.prototype.renderMenu = function() {
  2881. var $menuBtnEl, $menuItemEl, k, len, menuItem, ref, ref1, results;
  2882. if (!$.isArray(this.menu)) {
  2883. return;
  2884. }
  2885. this.menuEl = $('<ul/>').appendTo(this.menuWrapper);
  2886. ref = this.menu;
  2887. results = [];
  2888. for (k = 0, len = ref.length; k < len; k++) {
  2889. menuItem = ref[k];
  2890. if (menuItem === '|') {
  2891. $(this._tpl.separator).appendTo(this.menuEl);
  2892. continue;
  2893. }
  2894. $menuItemEl = $(this._tpl.menuItem).appendTo(this.menuEl);
  2895. $menuBtnEl = $menuItemEl.find('a.menu-item').attr({
  2896. 'title': (ref1 = menuItem.title) != null ? ref1 : menuItem.text,
  2897. 'data-param': menuItem.param
  2898. }).addClass('menu-item-' + menuItem.name);
  2899. if (menuItem.icon) {
  2900. results.push($menuBtnEl.find('span').addClass(this.iconClassOf(menuItem.icon)));
  2901. } else {
  2902. results.push($menuBtnEl.find('span').text(menuItem.text));
  2903. }
  2904. }
  2905. return results;
  2906. };
  2907. Button.prototype.setActive = function(active) {
  2908. if (active === this.active) {
  2909. return;
  2910. }
  2911. this.active = active;
  2912. return this.el.toggleClass('active', this.active);
  2913. };
  2914. Button.prototype.setDisabled = function(disabled) {
  2915. if (disabled === this.disabled) {
  2916. return;
  2917. }
  2918. this.disabled = disabled;
  2919. return this.el.toggleClass('disabled', this.disabled);
  2920. };
  2921. Button.prototype._disableStatus = function() {
  2922. var disabled, endNodes, startNodes;
  2923. startNodes = this.editor.selection.startNodes();
  2924. endNodes = this.editor.selection.endNodes();
  2925. disabled = startNodes.filter(this.disableTag).length > 0 || endNodes.filter(this.disableTag).length > 0;
  2926. this.setDisabled(disabled);
  2927. if (this.disabled) {
  2928. this.setActive(false);
  2929. }
  2930. return this.disabled;
  2931. };
  2932. Button.prototype._activeStatus = function() {
  2933. var active, endNode, endNodes, startNode, startNodes;
  2934. startNodes = this.editor.selection.startNodes();
  2935. endNodes = this.editor.selection.endNodes();
  2936. startNode = startNodes.filter(this.htmlTag);
  2937. endNode = endNodes.filter(this.htmlTag);
  2938. active = startNode.length > 0 && endNode.length > 0 && startNode.is(endNode);
  2939. this.node = active ? startNode : null;
  2940. this.setActive(active);
  2941. return this.active;
  2942. };
  2943. Button.prototype._status = function() {
  2944. this._disableStatus();
  2945. if (this.disabled) {
  2946. return;
  2947. }
  2948. return this._activeStatus();
  2949. };
  2950. Button.prototype.command = function(param) {};
  2951. Button.prototype._t = function() {
  2952. var args, ref, result;
  2953. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  2954. result = Button.__super__._t.apply(this, args);
  2955. if (!result) {
  2956. result = (ref = this.editor)._t.apply(ref, args);
  2957. }
  2958. return result;
  2959. };
  2960. return Button;
  2961. })(SimpleModule);
  2962. Simditor.Button = Button;
  2963. Popover = (function(superClass) {
  2964. extend(Popover, superClass);
  2965. Popover.prototype.offset = {
  2966. top: 4,
  2967. left: 0
  2968. };
  2969. Popover.prototype.target = null;
  2970. Popover.prototype.active = false;
  2971. function Popover(opts) {
  2972. this.button = opts.button;
  2973. this.editor = opts.button.editor;
  2974. Popover.__super__.constructor.call(this, opts);
  2975. }
  2976. Popover.prototype._init = function() {
  2977. this.el = $('<div class="simditor-popover"></div>').appendTo(this.editor.el).data('popover', this);
  2978. this.render();
  2979. this.el.on('mouseenter', (function(_this) {
  2980. return function(e) {
  2981. return _this.el.addClass('hover');
  2982. };
  2983. })(this));
  2984. return this.el.on('mouseleave', (function(_this) {
  2985. return function(e) {
  2986. return _this.el.removeClass('hover');
  2987. };
  2988. })(this));
  2989. };
  2990. Popover.prototype.render = function() {};
  2991. Popover.prototype._initLabelWidth = function() {
  2992. var $fields;
  2993. $fields = this.el.find('.settings-field');
  2994. if (!($fields.length > 0)) {
  2995. return;
  2996. }
  2997. this._labelWidth = 0;
  2998. $fields.each((function(_this) {
  2999. return function(i, field) {
  3000. var $field, $label;
  3001. $field = $(field);
  3002. $label = $field.find('label');
  3003. if (!($label.length > 0)) {
  3004. return;
  3005. }
  3006. return _this._labelWidth = Math.max(_this._labelWidth, $label.width());
  3007. };
  3008. })(this));
  3009. return $fields.find('label').width(this._labelWidth);
  3010. };
  3011. Popover.prototype.show = function($target, position) {
  3012. if (position == null) {
  3013. position = 'bottom';
  3014. }
  3015. if ($target == null) {
  3016. return;
  3017. }
  3018. this.el.siblings('.simditor-popover').each(function(i, popover) {
  3019. popover = $(popover).data('popover');
  3020. if (popover && popover.active) {
  3021. return popover.hide();
  3022. }
  3023. });
  3024. if (this.active && this.target) {
  3025. this.target.removeClass('selected');
  3026. }
  3027. this.target = $target.addClass('selected');
  3028. if (this.active) {
  3029. this.refresh(position);
  3030. return this.trigger('popovershow');
  3031. } else {
  3032. this.active = true;
  3033. this.el.css({
  3034. left: -9999
  3035. }).show();
  3036. if (!this._labelWidth) {
  3037. this._initLabelWidth();
  3038. }
  3039. this.editor.util.reflow();
  3040. this.refresh(position);
  3041. return this.trigger('popovershow');
  3042. }
  3043. };
  3044. Popover.prototype.hide = function() {
  3045. if (!this.active) {
  3046. return;
  3047. }
  3048. if (this.target) {
  3049. this.target.removeClass('selected');
  3050. }
  3051. this.target = null;
  3052. this.active = false;
  3053. this.el.hide();
  3054. return this.trigger('popoverhide');
  3055. };
  3056. Popover.prototype.refresh = function(position) {
  3057. var editorOffset, left, maxLeft, targetH, targetOffset, top;
  3058. if (position == null) {
  3059. position = 'bottom';
  3060. }
  3061. if (!this.active) {
  3062. return;
  3063. }
  3064. editorOffset = this.editor.el.offset();
  3065. targetOffset = this.target.offset();
  3066. targetH = this.target.outerHeight();
  3067. if (position === 'bottom') {
  3068. top = targetOffset.top - editorOffset.top + targetH;
  3069. } else if (position === 'top') {
  3070. top = targetOffset.top - editorOffset.top - this.el.height();
  3071. }
  3072. maxLeft = this.editor.wrapper.width() - this.el.outerWidth() - 10;
  3073. left = Math.min(targetOffset.left - editorOffset.left, maxLeft);
  3074. return this.el.css({
  3075. top: top + this.offset.top,
  3076. left: left + this.offset.left
  3077. });
  3078. };
  3079. Popover.prototype.destroy = function() {
  3080. this.target = null;
  3081. this.active = false;
  3082. this.editor.off('.linkpopover');
  3083. return this.el.remove();
  3084. };
  3085. Popover.prototype._t = function() {
  3086. var args, ref, result;
  3087. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3088. result = Popover.__super__._t.apply(this, args);
  3089. if (!result) {
  3090. result = (ref = this.button)._t.apply(ref, args);
  3091. }
  3092. return result;
  3093. };
  3094. return Popover;
  3095. })(SimpleModule);
  3096. Simditor.Popover = Popover;
  3097. TitleButton = (function(superClass) {
  3098. extend(TitleButton, superClass);
  3099. function TitleButton() {
  3100. return TitleButton.__super__.constructor.apply(this, arguments);
  3101. }
  3102. TitleButton.prototype.name = 'title';
  3103. TitleButton.prototype.htmlTag = 'h1, h2, h3, h4, h5';
  3104. TitleButton.prototype.disableTag = 'pre, table';
  3105. TitleButton.prototype._init = function() {
  3106. this.menu = [
  3107. {
  3108. name: 'normal',
  3109. text: this._t('normalText'),
  3110. param: 'p'
  3111. }, '|', {
  3112. name: 'h1',
  3113. text: this._t('title') + ' 1',
  3114. param: 'h1'
  3115. }, {
  3116. name: 'h2',
  3117. text: this._t('title') + ' 2',
  3118. param: 'h2'
  3119. }, {
  3120. name: 'h3',
  3121. text: this._t('title') + ' 3',
  3122. param: 'h3'
  3123. }, {
  3124. name: 'h4',
  3125. text: this._t('title') + ' 4',
  3126. param: 'h4'
  3127. }, {
  3128. name: 'h5',
  3129. text: this._t('title') + ' 5',
  3130. param: 'h5'
  3131. }
  3132. ];
  3133. return TitleButton.__super__._init.call(this);
  3134. };
  3135. TitleButton.prototype.setActive = function(active, param) {
  3136. TitleButton.__super__.setActive.call(this, active);
  3137. if (active) {
  3138. param || (param = this.node[0].tagName.toLowerCase());
  3139. }
  3140. this.el.removeClass('active-p active-h1 active-h2 active-h3 active-h4 active-h5');
  3141. if (active) {
  3142. return this.el.addClass('active active-' + param);
  3143. }
  3144. };
  3145. TitleButton.prototype.command = function(param) {
  3146. var $rootNodes;
  3147. $rootNodes = this.editor.selection.rootNodes();
  3148. this.editor.selection.save();
  3149. $rootNodes.each((function(_this) {
  3150. return function(i, node) {
  3151. var $node;
  3152. $node = $(node);
  3153. if ($node.is('blockquote') || $node.is(param) || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) {
  3154. return;
  3155. }
  3156. return $('<' + param + '/>').append($node.contents()).replaceAll($node);
  3157. };
  3158. })(this));
  3159. this.editor.selection.restore();
  3160. return this.editor.trigger('valuechanged');
  3161. };
  3162. return TitleButton;
  3163. })(Button);
  3164. Simditor.Toolbar.addButton(TitleButton);
  3165. FontScaleButton = (function(superClass) {
  3166. extend(FontScaleButton, superClass);
  3167. function FontScaleButton() {
  3168. return FontScaleButton.__super__.constructor.apply(this, arguments);
  3169. }
  3170. FontScaleButton.prototype.name = 'fontScale';
  3171. FontScaleButton.prototype.icon = 'font';
  3172. FontScaleButton.prototype.htmlTag = 'span';
  3173. FontScaleButton.prototype.disableTag = 'pre, h1, h2, h3, h4, h5';
  3174. FontScaleButton.prototype.sizeMap = {
  3175. 'x-large': '1.5em',
  3176. 'large': '1.25em',
  3177. 'small': '.75em',
  3178. 'x-small': '.5em'
  3179. };
  3180. FontScaleButton.prototype._init = function() {
  3181. this.menu = [
  3182. {
  3183. name: '150%',
  3184. text: this._t('fontScaleXLarge'),
  3185. param: '5'
  3186. }, {
  3187. name: '125%',
  3188. text: this._t('fontScaleLarge'),
  3189. param: '4'
  3190. }, {
  3191. name: '100%',
  3192. text: this._t('fontScaleNormal'),
  3193. param: '3'
  3194. }, {
  3195. name: '75%',
  3196. text: this._t('fontScaleSmall'),
  3197. param: '2'
  3198. }, {
  3199. name: '50%',
  3200. text: this._t('fontScaleXSmall'),
  3201. param: '1'
  3202. }
  3203. ];
  3204. return FontScaleButton.__super__._init.call(this);
  3205. };
  3206. FontScaleButton.prototype._activeStatus = function() {
  3207. var active, endNode, endNodes, range, startNode, startNodes;
  3208. range = this.editor.selection.range();
  3209. startNodes = this.editor.selection.startNodes();
  3210. endNodes = this.editor.selection.endNodes();
  3211. startNode = startNodes.filter('span[style*="font-size"]');
  3212. endNode = endNodes.filter('span[style*="font-size"]');
  3213. active = startNodes.length > 0 && endNodes.length > 0 && startNode.is(endNode);
  3214. this.setActive(active);
  3215. return this.active;
  3216. };
  3217. FontScaleButton.prototype.command = function(param) {
  3218. var $scales, containerNode, range;
  3219. range = this.editor.selection.range();
  3220. if (range.collapsed) {
  3221. return;
  3222. }
  3223. this.editor.selection.range(range);
  3224. document.execCommand('styleWithCSS', false, true);
  3225. document.execCommand('fontSize', false, param);
  3226. document.execCommand('styleWithCSS', false, false);
  3227. this.editor.selection.reset();
  3228. this.editor.selection.range();
  3229. containerNode = this.editor.selection.containerNode();
  3230. if (containerNode[0].nodeType === Node.TEXT_NODE) {
  3231. $scales = containerNode.closest('span[style*="font-size"]');
  3232. } else {
  3233. $scales = containerNode.find('span[style*="font-size"]');
  3234. }
  3235. $scales.each((function(_this) {
  3236. return function(i, n) {
  3237. var $span, size;
  3238. $span = $(n);
  3239. size = n.style.fontSize;
  3240. if (/large|x-large|small|x-small/.test(size)) {
  3241. return $span.css('fontSize', _this.sizeMap[size]);
  3242. } else if (size === 'medium') {
  3243. if ($span[0].style.length > 1) {
  3244. return $span.css('fontSize', '');
  3245. } else {
  3246. return $span.replaceWith($span.contents());
  3247. }
  3248. }
  3249. };
  3250. })(this));
  3251. return this.editor.trigger('valuechanged');
  3252. };
  3253. return FontScaleButton;
  3254. })(Button);
  3255. Simditor.Toolbar.addButton(FontScaleButton);
  3256. BoldButton = (function(superClass) {
  3257. extend(BoldButton, superClass);
  3258. function BoldButton() {
  3259. return BoldButton.__super__.constructor.apply(this, arguments);
  3260. }
  3261. BoldButton.prototype.name = 'bold';
  3262. BoldButton.prototype.icon = 'bold';
  3263. BoldButton.prototype.htmlTag = 'b, strong';
  3264. BoldButton.prototype.disableTag = 'pre';
  3265. BoldButton.prototype.shortcut = 'cmd+b';
  3266. BoldButton.prototype._init = function() {
  3267. if (this.editor.util.os.mac) {
  3268. this.title = this.title + ' ( Cmd + b )';
  3269. } else {
  3270. this.title = this.title + ' ( Ctrl + b )';
  3271. this.shortcut = 'ctrl+b';
  3272. }
  3273. return BoldButton.__super__._init.call(this);
  3274. };
  3275. BoldButton.prototype._activeStatus = function() {
  3276. var active;
  3277. active = document.queryCommandState('bold') === true;
  3278. this.setActive(active);
  3279. return this.active;
  3280. };
  3281. BoldButton.prototype.command = function() {
  3282. document.execCommand('bold');
  3283. if (!this.editor.util.support.oninput) {
  3284. this.editor.trigger('valuechanged');
  3285. }
  3286. return $(document).trigger('selectionchange');
  3287. };
  3288. return BoldButton;
  3289. })(Button);
  3290. Simditor.Toolbar.addButton(BoldButton);
  3291. ItalicButton = (function(superClass) {
  3292. extend(ItalicButton, superClass);
  3293. function ItalicButton() {
  3294. return ItalicButton.__super__.constructor.apply(this, arguments);
  3295. }
  3296. ItalicButton.prototype.name = 'italic';
  3297. ItalicButton.prototype.icon = 'italic';
  3298. ItalicButton.prototype.htmlTag = 'i';
  3299. ItalicButton.prototype.disableTag = 'pre';
  3300. ItalicButton.prototype.shortcut = 'cmd+i';
  3301. ItalicButton.prototype._init = function() {
  3302. if (this.editor.util.os.mac) {
  3303. this.title = this.title + " ( Cmd + i )";
  3304. } else {
  3305. this.title = this.title + " ( Ctrl + i )";
  3306. this.shortcut = 'ctrl+i';
  3307. }
  3308. return ItalicButton.__super__._init.call(this);
  3309. };
  3310. ItalicButton.prototype._activeStatus = function() {
  3311. var active;
  3312. active = document.queryCommandState('italic') === true;
  3313. this.setActive(active);
  3314. return this.active;
  3315. };
  3316. ItalicButton.prototype.command = function() {
  3317. document.execCommand('italic');
  3318. if (!this.editor.util.support.oninput) {
  3319. this.editor.trigger('valuechanged');
  3320. }
  3321. return $(document).trigger('selectionchange');
  3322. };
  3323. return ItalicButton;
  3324. })(Button);
  3325. Simditor.Toolbar.addButton(ItalicButton);
  3326. UnderlineButton = (function(superClass) {
  3327. extend(UnderlineButton, superClass);
  3328. function UnderlineButton() {
  3329. return UnderlineButton.__super__.constructor.apply(this, arguments);
  3330. }
  3331. UnderlineButton.prototype.name = 'underline';
  3332. UnderlineButton.prototype.icon = 'underline';
  3333. UnderlineButton.prototype.htmlTag = 'u';
  3334. UnderlineButton.prototype.disableTag = 'pre';
  3335. UnderlineButton.prototype.shortcut = 'cmd+u';
  3336. UnderlineButton.prototype.render = function() {
  3337. if (this.editor.util.os.mac) {
  3338. this.title = this.title + ' ( Cmd + u )';
  3339. } else {
  3340. this.title = this.title + ' ( Ctrl + u )';
  3341. this.shortcut = 'ctrl+u';
  3342. }
  3343. return UnderlineButton.__super__.render.call(this);
  3344. };
  3345. UnderlineButton.prototype._activeStatus = function() {
  3346. var active;
  3347. active = document.queryCommandState('underline') === true;
  3348. this.setActive(active);
  3349. return this.active;
  3350. };
  3351. UnderlineButton.prototype.command = function() {
  3352. document.execCommand('underline');
  3353. if (!this.editor.util.support.oninput) {
  3354. this.editor.trigger('valuechanged');
  3355. }
  3356. return $(document).trigger('selectionchange');
  3357. };
  3358. return UnderlineButton;
  3359. })(Button);
  3360. Simditor.Toolbar.addButton(UnderlineButton);
  3361. ColorButton = (function(superClass) {
  3362. extend(ColorButton, superClass);
  3363. function ColorButton() {
  3364. return ColorButton.__super__.constructor.apply(this, arguments);
  3365. }
  3366. ColorButton.prototype.name = 'color';
  3367. ColorButton.prototype.icon = 'tint';
  3368. ColorButton.prototype.disableTag = 'pre';
  3369. ColorButton.prototype.menu = true;
  3370. ColorButton.prototype.render = function() {
  3371. var args;
  3372. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3373. return ColorButton.__super__.render.apply(this, args);
  3374. };
  3375. ColorButton.prototype.renderMenu = function() {
  3376. $('<ul class="color-list">\n <li><a href="javascript:;" class="font-color font-color-1"></a></li>\n <li><a href="javascript:;" class="font-color font-color-2"></a></li>\n <li><a href="javascript:;" class="font-color font-color-3"></a></li>\n <li><a href="javascript:;" class="font-color font-color-4"></a></li>\n <li><a href="javascript:;" class="font-color font-color-5"></a></li>\n <li><a href="javascript:;" class="font-color font-color-6"></a></li>\n <li><a href="javascript:;" class="font-color font-color-7"></a></li>\n <li><a href="javascript:;" class="font-color font-color-default"></a></li>\n</ul>').appendTo(this.menuWrapper);
  3377. this.menuWrapper.on('mousedown', '.color-list', function(e) {
  3378. return false;
  3379. });
  3380. return this.menuWrapper.on('click', '.font-color', (function(_this) {
  3381. return function(e) {
  3382. var $link, $p, hex, range, rgb, textNode;
  3383. _this.wrapper.removeClass('menu-on');
  3384. $link = $(e.currentTarget);
  3385. if ($link.hasClass('font-color-default')) {
  3386. $p = _this.editor.body.find('p, li');
  3387. if (!($p.length > 0)) {
  3388. return;
  3389. }
  3390. rgb = window.getComputedStyle($p[0], null).getPropertyValue('color');
  3391. hex = _this._convertRgbToHex(rgb);
  3392. } else {
  3393. rgb = window.getComputedStyle($link[0], null).getPropertyValue('background-color');
  3394. hex = _this._convertRgbToHex(rgb);
  3395. }
  3396. if (!hex) {
  3397. return;
  3398. }
  3399. range = _this.editor.selection.range();
  3400. if (!$link.hasClass('font-color-default') && range.collapsed) {
  3401. textNode = document.createTextNode(_this._t('coloredText'));
  3402. range.insertNode(textNode);
  3403. range.selectNodeContents(textNode);
  3404. }
  3405. _this.editor.selection.range(range);
  3406. document.execCommand('styleWithCSS', false, true);
  3407. document.execCommand('foreColor', false, hex);
  3408. document.execCommand('styleWithCSS', false, false);
  3409. if (!_this.editor.util.support.oninput) {
  3410. return _this.editor.trigger('valuechanged');
  3411. }
  3412. };
  3413. })(this));
  3414. };
  3415. ColorButton.prototype._convertRgbToHex = function(rgb) {
  3416. var match, re, rgbToHex;
  3417. re = /rgb\((\d+),\s?(\d+),\s?(\d+)\)/g;
  3418. match = re.exec(rgb);
  3419. if (!match) {
  3420. return '';
  3421. }
  3422. rgbToHex = function(r, g, b) {
  3423. var componentToHex;
  3424. componentToHex = function(c) {
  3425. var hex;
  3426. hex = c.toString(16);
  3427. if (hex.length === 1) {
  3428. return '0' + hex;
  3429. } else {
  3430. return hex;
  3431. }
  3432. };
  3433. return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
  3434. };
  3435. return rgbToHex(match[1] * 1, match[2] * 1, match[3] * 1);
  3436. };
  3437. return ColorButton;
  3438. })(Button);
  3439. Simditor.Toolbar.addButton(ColorButton);
  3440. ListButton = (function(superClass) {
  3441. extend(ListButton, superClass);
  3442. function ListButton() {
  3443. return ListButton.__super__.constructor.apply(this, arguments);
  3444. }
  3445. ListButton.prototype.type = '';
  3446. ListButton.prototype.disableTag = 'pre, table';
  3447. ListButton.prototype.command = function(param) {
  3448. var $list, $rootNodes, anotherType;
  3449. $rootNodes = this.editor.selection.blockNodes();
  3450. anotherType = this.type === 'ul' ? 'ol' : 'ul';
  3451. this.editor.selection.save();
  3452. $list = null;
  3453. $rootNodes.each((function(_this) {
  3454. return function(i, node) {
  3455. var $node;
  3456. $node = $(node);
  3457. if ($node.is('blockquote, li') || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || !$.contains(document, node)) {
  3458. return;
  3459. }
  3460. if ($node.is(_this.type)) {
  3461. $node.children('li').each(function(i, li) {
  3462. var $childList, $li;
  3463. $li = $(li);
  3464. $childList = $li.children('ul, ol').insertAfter($node);
  3465. return $('<p/>').append($(li).html() || _this.editor.util.phBr).insertBefore($node);
  3466. });
  3467. return $node.remove();
  3468. } else if ($node.is(anotherType)) {
  3469. return $('<' + _this.type + '/>').append($node.contents()).replaceAll($node);
  3470. } else if ($list && $node.prev().is($list)) {
  3471. $('<li/>').append($node.html() || _this.editor.util.phBr).appendTo($list);
  3472. return $node.remove();
  3473. } else {
  3474. $list = $("<" + _this.type + "><li></li></" + _this.type + ">");
  3475. $list.find('li').append($node.html() || _this.editor.util.phBr);
  3476. return $list.replaceAll($node);
  3477. }
  3478. };
  3479. })(this));
  3480. this.editor.selection.restore();
  3481. return this.editor.trigger('valuechanged');
  3482. };
  3483. return ListButton;
  3484. })(Button);
  3485. OrderListButton = (function(superClass) {
  3486. extend(OrderListButton, superClass);
  3487. function OrderListButton() {
  3488. return OrderListButton.__super__.constructor.apply(this, arguments);
  3489. }
  3490. OrderListButton.prototype.type = 'ol';
  3491. OrderListButton.prototype.name = 'ol';
  3492. OrderListButton.prototype.icon = 'list-ol';
  3493. OrderListButton.prototype.htmlTag = 'ol';
  3494. OrderListButton.prototype.shortcut = 'cmd+/';
  3495. OrderListButton.prototype._init = function() {
  3496. if (this.editor.util.os.mac) {
  3497. this.title = this.title + ' ( Cmd + / )';
  3498. } else {
  3499. this.title = this.title + ' ( ctrl + / )';
  3500. this.shortcut = 'ctrl+/';
  3501. }
  3502. return OrderListButton.__super__._init.call(this);
  3503. };
  3504. return OrderListButton;
  3505. })(ListButton);
  3506. UnorderListButton = (function(superClass) {
  3507. extend(UnorderListButton, superClass);
  3508. function UnorderListButton() {
  3509. return UnorderListButton.__super__.constructor.apply(this, arguments);
  3510. }
  3511. UnorderListButton.prototype.type = 'ul';
  3512. UnorderListButton.prototype.name = 'ul';
  3513. UnorderListButton.prototype.icon = 'list-ul';
  3514. UnorderListButton.prototype.htmlTag = 'ul';
  3515. UnorderListButton.prototype.shortcut = 'cmd+.';
  3516. UnorderListButton.prototype._init = function() {
  3517. if (this.editor.util.os.mac) {
  3518. this.title = this.title + ' ( Cmd + . )';
  3519. } else {
  3520. this.title = this.title + ' ( Ctrl + . )';
  3521. this.shortcut = 'ctrl+.';
  3522. }
  3523. return UnorderListButton.__super__._init.call(this);
  3524. };
  3525. return UnorderListButton;
  3526. })(ListButton);
  3527. Simditor.Toolbar.addButton(OrderListButton);
  3528. Simditor.Toolbar.addButton(UnorderListButton);
  3529. BlockquoteButton = (function(superClass) {
  3530. extend(BlockquoteButton, superClass);
  3531. function BlockquoteButton() {
  3532. return BlockquoteButton.__super__.constructor.apply(this, arguments);
  3533. }
  3534. BlockquoteButton.prototype.name = 'blockquote';
  3535. BlockquoteButton.prototype.icon = 'quote-left';
  3536. BlockquoteButton.prototype.htmlTag = 'blockquote';
  3537. BlockquoteButton.prototype.disableTag = 'pre, table';
  3538. BlockquoteButton.prototype.command = function() {
  3539. var $rootNodes, clearCache, nodeCache;
  3540. $rootNodes = this.editor.selection.rootNodes();
  3541. $rootNodes = $rootNodes.filter(function(i, node) {
  3542. return !$(node).parent().is('blockquote');
  3543. });
  3544. this.editor.selection.save();
  3545. nodeCache = [];
  3546. clearCache = (function(_this) {
  3547. return function() {
  3548. if (nodeCache.length > 0) {
  3549. $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).append(nodeCache);
  3550. return nodeCache.length = 0;
  3551. }
  3552. };
  3553. })(this);
  3554. $rootNodes.each((function(_this) {
  3555. return function(i, node) {
  3556. var $node;
  3557. $node = $(node);
  3558. if (!$node.parent().is(_this.editor.body)) {
  3559. return;
  3560. }
  3561. if ($node.is(_this.htmlTag)) {
  3562. clearCache();
  3563. return $node.children().unwrap();
  3564. } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) {
  3565. return clearCache();
  3566. } else {
  3567. return nodeCache.push(node);
  3568. }
  3569. };
  3570. })(this));
  3571. clearCache();
  3572. this.editor.selection.restore();
  3573. return this.editor.trigger('valuechanged');
  3574. };
  3575. return BlockquoteButton;
  3576. })(Button);
  3577. Simditor.Toolbar.addButton(BlockquoteButton);
  3578. CodeButton = (function(superClass) {
  3579. extend(CodeButton, superClass);
  3580. function CodeButton() {
  3581. return CodeButton.__super__.constructor.apply(this, arguments);
  3582. }
  3583. CodeButton.prototype.name = 'code';
  3584. CodeButton.prototype.icon = 'code';
  3585. CodeButton.prototype.htmlTag = 'pre';
  3586. CodeButton.prototype.disableTag = 'ul, ol, table';
  3587. CodeButton.prototype._init = function() {
  3588. CodeButton.__super__._init.call(this);
  3589. this.editor.on('decorate', (function(_this) {
  3590. return function(e, $el) {
  3591. return $el.find('pre').each(function(i, pre) {
  3592. return _this.decorate($(pre));
  3593. });
  3594. };
  3595. })(this));
  3596. return this.editor.on('undecorate', (function(_this) {
  3597. return function(e, $el) {
  3598. return $el.find('pre').each(function(i, pre) {
  3599. return _this.undecorate($(pre));
  3600. });
  3601. };
  3602. })(this));
  3603. };
  3604. CodeButton.prototype.render = function() {
  3605. var args;
  3606. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3607. CodeButton.__super__.render.apply(this, args);
  3608. return this.popover = new CodePopover({
  3609. button: this
  3610. });
  3611. };
  3612. CodeButton.prototype._checkMode = function() {
  3613. var $blockNodes, range;
  3614. range = this.editor.selection.range();
  3615. if (($blockNodes = $(range.cloneContents()).find(this.editor.util.blockNodes.join(','))) > 0 || (range.collapsed && this.editor.selection.startNodes().filter('code').length === 0)) {
  3616. this.inlineMode = false;
  3617. return this.htmlTag = 'pre';
  3618. } else {
  3619. this.inlineMode = true;
  3620. return this.htmlTag = 'code';
  3621. }
  3622. };
  3623. CodeButton.prototype._status = function() {
  3624. this._checkMode();
  3625. CodeButton.__super__._status.call(this);
  3626. if (this.inlineMode) {
  3627. return;
  3628. }
  3629. if (this.active) {
  3630. return this.popover.show(this.node);
  3631. } else {
  3632. return this.popover.hide();
  3633. }
  3634. };
  3635. CodeButton.prototype.decorate = function($pre) {
  3636. var $code, lang, ref, ref1;
  3637. $code = $pre.find('> code');
  3638. if ($code.length > 0) {
  3639. lang = (ref = $code.attr('class')) != null ? (ref1 = ref.match(/lang-(\S+)/)) != null ? ref1[1] : void 0 : void 0;
  3640. $code.contents().unwrap();
  3641. if (lang) {
  3642. return $pre.attr('data-lang', lang);
  3643. }
  3644. }
  3645. };
  3646. CodeButton.prototype.undecorate = function($pre) {
  3647. var $code, lang;
  3648. lang = $pre.attr('data-lang');
  3649. $code = $('<code/>');
  3650. if (lang && lang !== -1) {
  3651. $code.addClass('lang-' + lang);
  3652. }
  3653. return $pre.wrapInner($code).removeAttr('data-lang');
  3654. };
  3655. CodeButton.prototype.command = function() {
  3656. if (this.inlineMode) {
  3657. return this._inlineCommand();
  3658. } else {
  3659. return this._blockCommand();
  3660. }
  3661. };
  3662. CodeButton.prototype._blockCommand = function() {
  3663. var $rootNodes, clearCache, nodeCache, resultNodes;
  3664. $rootNodes = this.editor.selection.rootNodes();
  3665. nodeCache = [];
  3666. resultNodes = [];
  3667. clearCache = (function(_this) {
  3668. return function() {
  3669. var $pre;
  3670. if (!(nodeCache.length > 0)) {
  3671. return;
  3672. }
  3673. $pre = $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).text(_this.editor.formatter.clearHtml(nodeCache));
  3674. resultNodes.push($pre[0]);
  3675. return nodeCache.length = 0;
  3676. };
  3677. })(this);
  3678. $rootNodes.each((function(_this) {
  3679. return function(i, node) {
  3680. var $node, $p;
  3681. $node = $(node);
  3682. if ($node.is(_this.htmlTag)) {
  3683. clearCache();
  3684. $p = $('<p/>').append($node.html().replace('\n', '<br/>')).replaceAll($node);
  3685. return resultNodes.push($p[0]);
  3686. } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || $node.is('blockquote')) {
  3687. return clearCache();
  3688. } else {
  3689. return nodeCache.push(node);
  3690. }
  3691. };
  3692. })(this));
  3693. clearCache();
  3694. this.editor.selection.setRangeAtEndOf($(resultNodes).last());
  3695. return this.editor.trigger('valuechanged');
  3696. };
  3697. CodeButton.prototype._inlineCommand = function() {
  3698. var $code, $contents, range;
  3699. range = this.editor.selection.range();
  3700. if (this.active) {
  3701. range.selectNodeContents(this.node[0]);
  3702. this.editor.selection.save(range);
  3703. this.node.contents().unwrap();
  3704. this.editor.selection.restore();
  3705. } else {
  3706. $contents = $(range.extractContents());
  3707. $code = $("<" + this.htmlTag + "/>").append($contents.contents());
  3708. range.insertNode($code[0]);
  3709. range.selectNodeContents($code[0]);
  3710. this.editor.selection.range(range);
  3711. }
  3712. return this.editor.trigger('valuechanged');
  3713. };
  3714. return CodeButton;
  3715. })(Button);
  3716. CodePopover = (function(superClass) {
  3717. extend(CodePopover, superClass);
  3718. function CodePopover() {
  3719. return CodePopover.__super__.constructor.apply(this, arguments);
  3720. }
  3721. CodePopover.prototype.render = function() {
  3722. var $option, k, lang, len, ref;
  3723. this._tpl = "<div class=\"code-settings\">\n <div class=\"settings-field\">\n <select class=\"select-lang\">\n <option value=\"-1\">" + (this._t('selectLanguage')) + "</option>\n </select>\n </div>\n</div>";
  3724. this.langs = this.editor.opts.codeLanguages || [
  3725. {
  3726. name: 'Bash',
  3727. value: 'bash'
  3728. }, {
  3729. name: 'C++',
  3730. value: 'c++'
  3731. }, {
  3732. name: 'C#',
  3733. value: 'cs'
  3734. }, {
  3735. name: 'CSS',
  3736. value: 'css'
  3737. }, {
  3738. name: 'Erlang',
  3739. value: 'erlang'
  3740. }, {
  3741. name: 'Less',
  3742. value: 'less'
  3743. }, {
  3744. name: 'Sass',
  3745. value: 'sass'
  3746. }, {
  3747. name: 'Diff',
  3748. value: 'diff'
  3749. }, {
  3750. name: 'CoffeeScript',
  3751. value: 'coffeescript'
  3752. }, {
  3753. name: 'HTML,XML',
  3754. value: 'html'
  3755. }, {
  3756. name: 'JSON',
  3757. value: 'json'
  3758. }, {
  3759. name: 'Java',
  3760. value: 'java'
  3761. }, {
  3762. name: 'JavaScript',
  3763. value: 'js'
  3764. }, {
  3765. name: 'Markdown',
  3766. value: 'markdown'
  3767. }, {
  3768. name: 'Objective C',
  3769. value: 'oc'
  3770. }, {
  3771. name: 'PHP',
  3772. value: 'php'
  3773. }, {
  3774. name: 'Perl',
  3775. value: 'parl'
  3776. }, {
  3777. name: 'Python',
  3778. value: 'python'
  3779. }, {
  3780. name: 'Ruby',
  3781. value: 'ruby'
  3782. }, {
  3783. name: 'SQL',
  3784. value: 'sql'
  3785. }
  3786. ];
  3787. this.el.addClass('code-popover').append(this._tpl);
  3788. this.selectEl = this.el.find('.select-lang');
  3789. ref = this.langs;
  3790. for (k = 0, len = ref.length; k < len; k++) {
  3791. lang = ref[k];
  3792. $option = $('<option/>', {
  3793. text: lang.name,
  3794. value: lang.value
  3795. }).appendTo(this.selectEl);
  3796. }
  3797. this.selectEl.on('change', (function(_this) {
  3798. return function(e) {
  3799. var selected;
  3800. _this.lang = _this.selectEl.val();
  3801. selected = _this.target.hasClass('selected');
  3802. _this.target.removeClass().removeAttr('data-lang');
  3803. if (_this.lang !== -1) {
  3804. _this.target.attr('data-lang', _this.lang);
  3805. }
  3806. if (selected) {
  3807. _this.target.addClass('selected');
  3808. }
  3809. return _this.editor.trigger('valuechanged');
  3810. };
  3811. })(this));
  3812. return this.editor.on('valuechanged', (function(_this) {
  3813. return function(e) {
  3814. if (_this.active) {
  3815. return _this.refresh();
  3816. }
  3817. };
  3818. })(this));
  3819. };
  3820. CodePopover.prototype.show = function() {
  3821. var args;
  3822. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3823. CodePopover.__super__.show.apply(this, args);
  3824. this.lang = this.target.attr('data-lang');
  3825. if (this.lang != null) {
  3826. return this.selectEl.val(this.lang);
  3827. } else {
  3828. return this.selectEl.val(-1);
  3829. }
  3830. };
  3831. return CodePopover;
  3832. })(Popover);
  3833. Simditor.Toolbar.addButton(CodeButton);
  3834. LinkButton = (function(superClass) {
  3835. extend(LinkButton, superClass);
  3836. function LinkButton() {
  3837. return LinkButton.__super__.constructor.apply(this, arguments);
  3838. }
  3839. LinkButton.prototype.name = 'link';
  3840. LinkButton.prototype.icon = 'link';
  3841. LinkButton.prototype.htmlTag = 'a';
  3842. LinkButton.prototype.disableTag = 'pre';
  3843. LinkButton.prototype.render = function() {
  3844. var args;
  3845. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3846. LinkButton.__super__.render.apply(this, args);
  3847. return this.popover = new LinkPopover({
  3848. button: this
  3849. });
  3850. };
  3851. LinkButton.prototype._status = function() {
  3852. LinkButton.__super__._status.call(this);
  3853. if (this.active && !this.editor.selection.rangeAtEndOf(this.node)) {
  3854. return this.popover.show(this.node);
  3855. } else {
  3856. return this.popover.hide();
  3857. }
  3858. };
  3859. LinkButton.prototype.command = function() {
  3860. var $contents, $link, $newBlock, linkText, range, txtNode;
  3861. range = this.editor.selection.range();
  3862. if (this.active) {
  3863. txtNode = document.createTextNode(this.node.text());
  3864. this.node.replaceWith(txtNode);
  3865. range.selectNode(txtNode);
  3866. } else {
  3867. $contents = $(range.extractContents());
  3868. linkText = this.editor.formatter.clearHtml($contents.contents(), false);
  3869. $link = $('<a/>', {
  3870. href: '',
  3871. target: '_blank',
  3872. text: linkText || this._t('linkText')
  3873. });
  3874. if (this.editor.selection.blockNodes().length > 0) {
  3875. range.insertNode($link[0]);
  3876. } else {
  3877. $newBlock = $('<p/>').append($link);
  3878. range.insertNode($newBlock[0]);
  3879. }
  3880. range.selectNodeContents($link[0]);
  3881. this.popover.one('popovershow', (function(_this) {
  3882. return function() {
  3883. if (linkText) {
  3884. _this.popover.urlEl.focus();
  3885. return _this.popover.urlEl[0].select();
  3886. } else {
  3887. _this.popover.textEl.focus();
  3888. return _this.popover.textEl[0].select();
  3889. }
  3890. };
  3891. })(this));
  3892. }
  3893. this.editor.selection.range(range);
  3894. return this.editor.trigger('valuechanged');
  3895. };
  3896. return LinkButton;
  3897. })(Button);
  3898. LinkPopover = (function(superClass) {
  3899. extend(LinkPopover, superClass);
  3900. function LinkPopover() {
  3901. return LinkPopover.__super__.constructor.apply(this, arguments);
  3902. }
  3903. LinkPopover.prototype.render = function() {
  3904. var tpl;
  3905. tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('linkText')) + "</label>\n <input class=\"link-text\" type=\"text\"/>\n <a class=\"btn-unlink\" href=\"javascript:;\" title=\"" + (this._t('removeLink')) + "\"\n tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-unlink\"></span>\n </a>\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('linkUrl')) + "</label>\n <input class=\"link-url\" type=\"text\"/>\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('linkTarget')) + "</label>\n <select class=\"link-target\">\n <option value=\"_blank\">" + (this._t('openLinkInNewWindow')) + " (_blank)</option>\n <option value=\"_self\">" + (this._t('openLinkInCurrentWindow')) + " (_self)</option>\n </select>\n </div>\n</div>";
  3906. this.el.addClass('link-popover').append(tpl);
  3907. this.textEl = this.el.find('.link-text');
  3908. this.urlEl = this.el.find('.link-url');
  3909. this.unlinkEl = this.el.find('.btn-unlink');
  3910. this.selectTarget = this.el.find('.link-target');
  3911. this.textEl.on('keyup', (function(_this) {
  3912. return function(e) {
  3913. if (e.which === 13) {
  3914. return;
  3915. }
  3916. _this.target.text(_this.textEl.val());
  3917. return _this.editor.inputManager.throttledValueChanged();
  3918. };
  3919. })(this));
  3920. this.urlEl.on('keyup', (function(_this) {
  3921. return function(e) {
  3922. var val;
  3923. if (e.which === 13) {
  3924. return;
  3925. }
  3926. val = _this.urlEl.val();
  3927. if (!(/^(http|https|ftp|ftps|file)?:\/\/|^(mailto|tel)?:|^\//ig.test(val) || !val)) {
  3928. val = 'http://' + val;
  3929. }
  3930. _this.target.attr('href', val);
  3931. return _this.editor.inputManager.throttledValueChanged();
  3932. };
  3933. })(this));
  3934. $([this.urlEl[0], this.textEl[0]]).on('keydown', (function(_this) {
  3935. return function(e) {
  3936. var range;
  3937. if (e.which === 13 || e.which === 27 || (!e.shiftKey && e.which === 9 && $(e.target).hasClass('link-url'))) {
  3938. e.preventDefault();
  3939. range = document.createRange();
  3940. _this.editor.selection.setRangeAfter(_this.target, range);
  3941. _this.hide();
  3942. return _this.editor.inputManager.throttledValueChanged();
  3943. }
  3944. };
  3945. })(this));
  3946. this.unlinkEl.on('click', (function(_this) {
  3947. return function(e) {
  3948. var range, txtNode;
  3949. txtNode = document.createTextNode(_this.target.text());
  3950. _this.target.replaceWith(txtNode);
  3951. _this.hide();
  3952. range = document.createRange();
  3953. _this.editor.selection.setRangeAfter(txtNode, range);
  3954. return _this.editor.inputManager.throttledValueChanged();
  3955. };
  3956. })(this));
  3957. return this.selectTarget.on('change', (function(_this) {
  3958. return function(e) {
  3959. _this.target.attr('target', _this.selectTarget.val());
  3960. return _this.editor.inputManager.throttledValueChanged();
  3961. };
  3962. })(this));
  3963. };
  3964. LinkPopover.prototype.show = function() {
  3965. var args;
  3966. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  3967. LinkPopover.__super__.show.apply(this, args);
  3968. this.textEl.val(this.target.text());
  3969. return this.urlEl.val(this.target.attr('href'));
  3970. };
  3971. return LinkPopover;
  3972. })(Popover);
  3973. Simditor.Toolbar.addButton(LinkButton);
  3974. ImageButton = (function(superClass) {
  3975. extend(ImageButton, superClass);
  3976. function ImageButton() {
  3977. return ImageButton.__super__.constructor.apply(this, arguments);
  3978. }
  3979. ImageButton.prototype.name = 'image';
  3980. ImageButton.prototype.icon = 'picture-o';
  3981. ImageButton.prototype.htmlTag = 'img';
  3982. ImageButton.prototype.disableTag = 'pre, table';
  3983. ImageButton.prototype.defaultImage = '';
  3984. ImageButton.prototype.needFocus = false;
  3985. ImageButton.prototype._init = function() {
  3986. var item, k, len, ref;
  3987. if (this.editor.opts.imageButton) {
  3988. if (Array.isArray(this.editor.opts.imageButton)) {
  3989. this.menu = [];
  3990. ref = this.editor.opts.imageButton;
  3991. for (k = 0, len = ref.length; k < len; k++) {
  3992. item = ref[k];
  3993. this.menu.push({
  3994. name: item + '-image',
  3995. text: this._t(item + 'Image')
  3996. });
  3997. }
  3998. } else {
  3999. this.menu = false;
  4000. }
  4001. } else {
  4002. if (this.editor.uploader != null) {
  4003. this.menu = [
  4004. {
  4005. name: 'upload-image',
  4006. text: this._t('uploadImage')
  4007. }, {
  4008. name: 'external-image',
  4009. text: this._t('externalImage')
  4010. }, {
  4011. name: 'select-image',
  4012. text: this._t('selectImage'),
  4013. param: 'select-image'
  4014. }
  4015. ];
  4016. } else {
  4017. this.menu = false;
  4018. }
  4019. }
  4020. this.defaultImage = this.editor.opts.defaultImage;
  4021. this.editor.body.on('click', 'img:not([data-non-image])', (function(_this) {
  4022. return function(e) {
  4023. var $img, range;
  4024. $img = $(e.currentTarget);
  4025. range = document.createRange();
  4026. range.selectNode($img[0]);
  4027. _this.editor.selection.range(range);
  4028. if (!_this.editor.util.support.onselectionchange) {
  4029. _this.editor.trigger('selectionchanged');
  4030. }
  4031. return false;
  4032. };
  4033. })(this));
  4034. this.editor.body.on('mouseup', 'img:not([data-non-image])', function(e) {
  4035. return false;
  4036. });
  4037. this.editor.on('selectionchanged.image', (function(_this) {
  4038. return function() {
  4039. var $contents, $img, range;
  4040. range = _this.editor.selection.range();
  4041. if (range == null) {
  4042. return;
  4043. }
  4044. $contents = $(range.cloneContents()).contents();
  4045. if ($contents.length === 1 && $contents.is('img:not([data-non-image])')) {
  4046. $img = $(range.startContainer).contents().eq(range.startOffset);
  4047. return _this.popover.show($img);
  4048. } else {
  4049. return _this.popover.hide();
  4050. }
  4051. };
  4052. })(this));
  4053. this.editor.on('valuechanged.image', (function(_this) {
  4054. return function() {
  4055. var $masks;
  4056. $masks = _this.editor.wrapper.find('.simditor-image-loading');
  4057. if (!($masks.length > 0)) {
  4058. return;
  4059. }
  4060. return $masks.each(function(i, mask) {
  4061. var $img, $mask, file;
  4062. $mask = $(mask);
  4063. $img = $mask.data('img');
  4064. if (!($img && $img.parent().length > 0)) {
  4065. $mask.remove();
  4066. if ($img) {
  4067. file = $img.data('file');
  4068. if (file) {
  4069. _this.editor.uploader.cancel(file);
  4070. if (_this.editor.body.find('img.uploading').length < 1) {
  4071. return _this.editor.uploader.trigger('uploadready', [file]);
  4072. }
  4073. }
  4074. }
  4075. }
  4076. });
  4077. };
  4078. })(this));
  4079. return ImageButton.__super__._init.call(this);
  4080. };
  4081. ImageButton.prototype.render = function() {
  4082. var args;
  4083. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  4084. ImageButton.__super__.render.apply(this, args);
  4085. this.popover = new ImagePopover({
  4086. button: this
  4087. });
  4088. if (this.editor.opts.imageButton === 'upload') {
  4089. return this._initUploader(this.el);
  4090. }
  4091. };
  4092. ImageButton.prototype.renderMenu = function() {
  4093. ImageButton.__super__.renderMenu.call(this);
  4094. return this._initUploader();
  4095. };
  4096. ImageButton.prototype._initUploader = function($uploadItem) {
  4097. var $input, createInput, uploadProgress;
  4098. if ($uploadItem == null) {
  4099. $uploadItem = this.menuEl.find('.menu-item-upload-image');
  4100. }
  4101. if (this.editor.uploader == null) {
  4102. this.el.find('.btn-upload').remove();
  4103. return;
  4104. }
  4105. $input = null;
  4106. createInput = (function(_this) {
  4107. return function() {
  4108. if ($input) {
  4109. $input.remove();
  4110. }
  4111. return $input = $('<input/>', {
  4112. type: 'file',
  4113. title: _this._t('uploadImage'),
  4114. multiple: true,
  4115. accept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg'
  4116. }).appendTo($uploadItem);
  4117. };
  4118. })(this);
  4119. createInput();
  4120. $uploadItem.on('click mousedown', 'input[type=file]', function(e) {
  4121. return e.stopPropagation();
  4122. });
  4123. $uploadItem.on('change', 'input[type=file]', (function(_this) {
  4124. return function(e) {
  4125. if (_this.editor.inputManager.focused) {
  4126. _this.editor.uploader.upload($input, {
  4127. inline: true
  4128. });
  4129. createInput();
  4130. } else {
  4131. _this.editor.one('focus', function(e) {
  4132. _this.editor.uploader.upload($input, {
  4133. inline: true
  4134. });
  4135. return createInput();
  4136. });
  4137. _this.editor.focus();
  4138. }
  4139. return _this.wrapper.removeClass('menu-on');
  4140. };
  4141. })(this));
  4142. this.editor.uploader.on('beforeupload', (function(_this) {
  4143. return function(e, file) {
  4144. var $img;
  4145. if (!file.inline) {
  4146. return;
  4147. }
  4148. if (file.img) {
  4149. $img = $(file.img);
  4150. } else {
  4151. $img = _this.createImage(file.name);
  4152. file.img = $img;
  4153. }
  4154. $img.addClass('uploading');
  4155. $img.data('file', file);
  4156. return _this.editor.uploader.readImageFile(file.obj, function(img) {
  4157. var src;
  4158. if (!$img.hasClass('uploading')) {
  4159. return;
  4160. }
  4161. src = img ? img.src : _this.defaultImage;
  4162. return _this.loadImage($img, src, function() {
  4163. if (_this.popover.active) {
  4164. _this.popover.refresh();
  4165. return _this.popover.srcEl.val(_this._t('uploading')).prop('disabled', true);
  4166. }
  4167. });
  4168. });
  4169. };
  4170. })(this));
  4171. uploadProgress = $.proxy(this.editor.util.throttle(function(e, file, loaded, total) {
  4172. var $img, $mask, percent;
  4173. if (!file.inline) {
  4174. return;
  4175. }
  4176. $mask = file.img.data('mask');
  4177. if (!$mask) {
  4178. return;
  4179. }
  4180. $img = $mask.data('img');
  4181. if (!($img && $img.hasClass('uploading') && $img.parent().length > 0)) {
  4182. $mask.remove();
  4183. return;
  4184. }
  4185. percent = loaded / total;
  4186. percent = (percent * 100).toFixed(0);
  4187. if (percent > 99) {
  4188. percent = 99;
  4189. }
  4190. return $mask.find('.progress').height((100 - percent) + "%");
  4191. }, 500), this);
  4192. this.editor.uploader.on('uploadprogress', uploadProgress);
  4193. this.editor.uploader.on('uploadsuccess', (function(_this) {
  4194. return function(e, file, result) {
  4195. var $img, img_path, msg;
  4196. if (!file.inline) {
  4197. return;
  4198. }
  4199. $img = file.img;
  4200. if (!($img.hasClass('uploading') && $img.parent().length > 0)) {
  4201. return;
  4202. }
  4203. if (typeof result !== 'object') {
  4204. try {
  4205. result = $.parseJSON(result);
  4206. } catch (_error) {
  4207. e = _error;
  4208. result = {
  4209. success: false
  4210. };
  4211. }
  4212. }
  4213. if (result.success === false) {
  4214. msg = result.msg || _this._t('uploadFailed');
  4215. alert(msg);
  4216. img_path = _this.defaultImage;
  4217. } else {
  4218. img_path = result.file_path;
  4219. }
  4220. _this.loadImage($img, img_path, function() {
  4221. var $mask;
  4222. $img.removeData('file');
  4223. $img.removeClass('uploading').removeClass('loading');
  4224. $mask = $img.data('mask');
  4225. if ($mask) {
  4226. $mask.remove();
  4227. }
  4228. $img.removeData('mask');
  4229. _this.editor.trigger('valuechanged');
  4230. if (_this.editor.body.find('img.uploading').length < 1) {
  4231. return _this.editor.uploader.trigger('uploadready', [file, result]);
  4232. }
  4233. });
  4234. if (_this.popover.active) {
  4235. _this.popover.srcEl.prop('disabled', false);
  4236. return _this.popover.srcEl.val(result.file_path);
  4237. }
  4238. };
  4239. })(this));
  4240. return this.editor.uploader.on('uploaderror', (function(_this) {
  4241. return function(e, file, xhr) {
  4242. var $img, msg, result;
  4243. if (!file.inline) {
  4244. return;
  4245. }
  4246. if (xhr.statusText === 'abort') {
  4247. return;
  4248. }
  4249. if (xhr.responseText) {
  4250. try {
  4251. result = $.parseJSON(xhr.responseText);
  4252. msg = result.msg;
  4253. } catch (_error) {
  4254. e = _error;
  4255. msg = _this._t('uploadError');
  4256. }
  4257. }
  4258. $img = file.img;
  4259. if (!($img.hasClass('uploading') && $img.parent().length > 0)) {
  4260. return;
  4261. }
  4262. _this.loadImage($img, _this.defaultImage, function() {
  4263. var $mask;
  4264. $img.removeData('file');
  4265. $img.removeClass('uploading').removeClass('loading');
  4266. $mask = $img.data('mask');
  4267. if ($mask) {
  4268. $mask.remove();
  4269. }
  4270. return $img.removeData('mask');
  4271. });
  4272. if (_this.popover.active) {
  4273. _this.popover.srcEl.prop('disabled', false);
  4274. _this.popover.srcEl.val(_this.defaultImage);
  4275. }
  4276. _this.editor.trigger('valuechanged');
  4277. if (_this.editor.body.find('img.uploading').length < 1) {
  4278. return _this.editor.uploader.trigger('uploadready', [file, result]);
  4279. }
  4280. };
  4281. })(this));
  4282. };
  4283. ImageButton.prototype._status = function() {
  4284. return this._disableStatus();
  4285. };
  4286. ImageButton.prototype.loadImage = function($img, src, callback) {
  4287. var $mask, img, positionMask;
  4288. positionMask = (function(_this) {
  4289. return function() {
  4290. var imgOffset, wrapperOffset;
  4291. imgOffset = $img.offset();
  4292. wrapperOffset = _this.editor.wrapper.offset();
  4293. return $mask.css({
  4294. top: imgOffset.top - wrapperOffset.top,
  4295. left: imgOffset.left - wrapperOffset.left,
  4296. width: $img.width(),
  4297. height: $img.height()
  4298. }).show();
  4299. };
  4300. })(this);
  4301. $img.addClass('loading');
  4302. $mask = $img.data('mask');
  4303. if (!$mask) {
  4304. $mask = $('<div class="simditor-image-loading">\n <div class="progress"></div>\n</div>').hide().appendTo(this.editor.wrapper);
  4305. positionMask();
  4306. $img.data('mask', $mask);
  4307. $mask.data('img', $img);
  4308. }
  4309. img = new Image();
  4310. img.onload = (function(_this) {
  4311. return function() {
  4312. var height, width;
  4313. if (!$img.hasClass('loading') && !$img.hasClass('uploading')) {
  4314. return;
  4315. }
  4316. width = img.width;
  4317. height = img.height;
  4318. $img.attr({
  4319. src: src,
  4320. // width: width,
  4321. // height: height,
  4322. // 'data-image-size': width + ',' + height
  4323. }).removeClass('loading');
  4324. if ($img.hasClass('uploading')) {
  4325. _this.editor.util.reflow(_this.editor.body);
  4326. positionMask();
  4327. } else {
  4328. $mask.remove();
  4329. $img.removeData('mask');
  4330. }
  4331. if ($.isFunction(callback)) {
  4332. return callback(img);
  4333. }
  4334. };
  4335. })(this);
  4336. img.onerror = function() {
  4337. if ($.isFunction(callback)) {
  4338. callback(false);
  4339. }
  4340. $mask.remove();
  4341. return $img.removeData('mask').removeClass('loading');
  4342. };
  4343. return img.setAttribute('src', src);
  4344. };
  4345. ImageButton.prototype.createImage = function(name) {
  4346. var $img, range;
  4347. if (name == null) {
  4348. name = 'Image';
  4349. }
  4350. if (!this.editor.inputManager.focused) {
  4351. this.editor.focus();
  4352. }
  4353. range = this.editor.selection.range();
  4354. range.deleteContents();
  4355. this.editor.selection.range(range);
  4356. $img = $('<img/>').attr('alt', name);
  4357. range.insertNode($img[0]);
  4358. this.editor.selection.setRangeAfter($img, range);
  4359. this.editor.trigger('valuechanged');
  4360. return $img;
  4361. };
  4362. ImageButton.prototype.command = function(src) {
  4363. if(src && src==='select-image'){
  4364. return;
  4365. }
  4366. var $img;
  4367. $img = this.createImage();
  4368. return this.loadImage($img, src || this.defaultImage, (function(_this) {
  4369. return function() {
  4370. _this.editor.trigger('valuechanged');
  4371. _this.editor.util.reflow($img);
  4372. $img.click();
  4373. return _this.popover.one('popovershow', function() {
  4374. _this.popover.srcEl.focus();
  4375. return _this.popover.srcEl[0].select();
  4376. });
  4377. };
  4378. })(this));
  4379. };
  4380. return ImageButton;
  4381. })(Button);
  4382. ImagePopover = (function(superClass) {
  4383. extend(ImagePopover, superClass);
  4384. function ImagePopover() {
  4385. return ImagePopover.__super__.constructor.apply(this, arguments);
  4386. }
  4387. ImagePopover.prototype.offset = {
  4388. top: 6,
  4389. left: -4
  4390. };
  4391. ImagePopover.prototype.render = function() {
  4392. var tpl;
  4393. tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('imageUrl')) + "</label>\n <input class=\"image-src\" type=\"text\" tabindex=\"1\" />\n <a class=\"btn-upload\" href=\"javascript:;\"\n title=\"" + (this._t('uploadImage')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-upload\"></span>\n </a>\n </div>\n <div class='settings-field'>\n <label>" + (this._t('imageAlt')) + "</label>\n <input class=\"image-alt\" id=\"image-alt\" type=\"text\" tabindex=\"1\" />\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('imageSize')) + "</label>\n <input class=\"image-size\" id=\"image-width\" type=\"text\" tabindex=\"2\" />\n <span class=\"times\">×</span>\n <input class=\"image-size\" id=\"image-height\" type=\"text\" tabindex=\"3\" />\n <a class=\"btn-restore\" href=\"javascript:;\"\n title=\"" + (this._t('restoreImageSize')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-undo\"></span>\n </a>\n </div>\n</div>";
  4394. this.el.addClass('image-popover').append(tpl);
  4395. this.srcEl = this.el.find('.image-src');
  4396. this.widthEl = this.el.find('#image-width');
  4397. this.heightEl = this.el.find('#image-height');
  4398. this.altEl = this.el.find('#image-alt');
  4399. this.srcEl.on('keydown', (function(_this) {
  4400. return function(e) {
  4401. var range;
  4402. if (!(e.which === 13 && !_this.target.hasClass('uploading'))) {
  4403. return;
  4404. }
  4405. e.preventDefault();
  4406. range = document.createRange();
  4407. _this.button.editor.selection.setRangeAfter(_this.target, range);
  4408. return _this.hide();
  4409. };
  4410. })(this));
  4411. this.srcEl.on('blur', (function(_this) {
  4412. return function(e) {
  4413. return _this._loadImage(_this.srcEl.val());
  4414. };
  4415. })(this));
  4416. this.el.find('.image-size').on('blur', (function(_this) {
  4417. return function(e) {
  4418. _this._resizeImg($(e.currentTarget));
  4419. return _this.el.data('popover').refresh();
  4420. };
  4421. })(this));
  4422. this.el.find('.image-size').on('keyup', (function(_this) {
  4423. return function(e) {
  4424. var inputEl;
  4425. inputEl = $(e.currentTarget);
  4426. if (!(e.which === 13 || e.which === 27 || e.which === 9)) {
  4427. // 禁用
  4428. //return _this._resizeImg(inputEl, true);
  4429. }
  4430. };
  4431. })(this));
  4432. this.el.find('.image-size').on('keydown', (function(_this) {
  4433. return function(e) {
  4434. var $img, inputEl, range;
  4435. inputEl = $(e.currentTarget);
  4436. if (e.which === 13 || e.which === 27) {
  4437. e.preventDefault();
  4438. if (e.which === 13) {
  4439. _this._resizeImg(inputEl);
  4440. } else {
  4441. _this._restoreImg();
  4442. }
  4443. $img = _this.target;
  4444. _this.hide();
  4445. range = document.createRange();
  4446. return _this.button.editor.selection.setRangeAfter($img, range);
  4447. } else if (e.which === 9) {
  4448. return _this.el.data('popover').refresh();
  4449. }
  4450. };
  4451. })(this));
  4452. this.altEl.on('keydown', (function(_this) {
  4453. return function(e) {
  4454. var range;
  4455. if (e.which === 13) {
  4456. e.preventDefault();
  4457. range = document.createRange();
  4458. _this.button.editor.selection.setRangeAfter(_this.target, range);
  4459. return _this.hide();
  4460. }
  4461. };
  4462. })(this));
  4463. this.altEl.on('keyup', (function(_this) {
  4464. return function(e) {
  4465. if (e.which === 13 || e.which === 27 || e.which === 9) {
  4466. return;
  4467. }
  4468. _this.alt = _this.altEl.val();
  4469. return _this.target.attr('alt', _this.alt);
  4470. };
  4471. })(this));
  4472. this.el.find('.btn-restore').on('click', (function(_this) {
  4473. return function(e) {
  4474. _this._restoreImg();
  4475. return _this.el.data('popover').refresh();
  4476. };
  4477. })(this));
  4478. this.editor.on('valuechanged', (function(_this) {
  4479. return function(e) {
  4480. if (_this.active) {
  4481. return _this.refresh();
  4482. }
  4483. };
  4484. })(this));
  4485. return this._initUploader();
  4486. };
  4487. ImagePopover.prototype._initUploader = function() {
  4488. var $uploadBtn, createInput;
  4489. $uploadBtn = this.el.find('.btn-upload');
  4490. if (this.editor.uploader == null) {
  4491. $uploadBtn.remove();
  4492. return;
  4493. }
  4494. createInput = (function(_this) {
  4495. return function() {
  4496. if (_this.input) {
  4497. _this.input.remove();
  4498. }
  4499. return _this.input = $('<input/>', {
  4500. type: 'file',
  4501. title: _this._t('uploadImage'),
  4502. multiple: true,
  4503. accept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg'
  4504. }).appendTo($uploadBtn);
  4505. };
  4506. })(this);
  4507. createInput();
  4508. this.el.on('click mousedown', 'input[type=file]', function(e) {
  4509. return e.stopPropagation();
  4510. });
  4511. return this.el.on('change', 'input[type=file]', (function(_this) {
  4512. return function(e) {
  4513. _this.editor.uploader.upload(_this.input, {
  4514. inline: true,
  4515. img: _this.target
  4516. });
  4517. return createInput();
  4518. };
  4519. })(this));
  4520. };
  4521. ImagePopover.prototype._resizeImg = function(inputEl, onlySetVal) {
  4522. var height, value, width;
  4523. if (onlySetVal == null) {
  4524. onlySetVal = false;
  4525. }
  4526. if(!this.target){
  4527. return;
  4528. }
  4529. var inputValue = inputEl.val();
  4530. var isPercentage = inputValue && inputValue.toString().indexOf('%') !== -1;
  4531. if(!isPercentage && inputValue!==''){
  4532. value = inputValue * 1;
  4533. if (!(this.target && ($.isNumeric(value) || value < 0))) {
  4534. return;
  4535. }
  4536. }else{
  4537. value = inputValue;
  4538. }
  4539. if (inputEl.is(this.widthEl)) {
  4540. if(value === ''){
  4541. this.target.removeAttr('width');
  4542. return;
  4543. }
  4544. width = value;
  4545. if(width.toString().indexOf('%')===-1){
  4546. height = this.height * value / this.width;
  4547. this.heightEl.val(height);
  4548. }else{
  4549. }
  4550. this.target.attr('width', width);
  4551. } else {
  4552. if(value === ''){
  4553. this.target.removeAttr('height');
  4554. return;
  4555. }
  4556. height = value;
  4557. if(height.toString().indexOf('%')===-1 && height!=='') {
  4558. width = this.width * value / this.height;
  4559. this.widthEl.val(width);
  4560. }
  4561. this.target.attr('height', height);
  4562. }
  4563. if (!onlySetVal) {
  4564. return this.editor.trigger('valuechanged');
  4565. }
  4566. };
  4567. ImagePopover.prototype._restoreImg = function() {
  4568. var ref, size, originalWidth, originalHeight;
  4569. // 重新读取原图宽高
  4570. if (this.target[0].naturalWidth && this.target[0].naturalHeight) {
  4571. originalWidth = this.target[0].naturalWidth;
  4572. originalHeight = this.target[0].naturalHeight;
  4573. } else if (this.target[0].width && this.target[0].height) {
  4574. originalWidth = this.target[0].width;
  4575. originalHeight = this.target[0].height;
  4576. } else {
  4577. // 如果无法直接获取,创建临时图片对象获取
  4578. var tempImg = new Image();
  4579. tempImg.src = this.target.attr('src');
  4580. originalWidth = tempImg.naturalWidth || tempImg.width || this.width;
  4581. originalHeight = tempImg.naturalHeight || tempImg.height || this.height;
  4582. }
  4583. size = ((ref = this.target.data('image-size')) != null ? ref.split(",") : void 0) || [originalWidth, originalHeight];
  4584. this.target.attr({
  4585. width: size[0] * 1,
  4586. height: size[1] * 1
  4587. });
  4588. this.widthEl.val(size[0]);
  4589. this.heightEl.val(size[1]);
  4590. return this.editor.trigger('valuechanged');
  4591. };
  4592. ImagePopover.prototype._loadImage = function(src, callback) {
  4593. if (/^data:image/.test(src) && !this.editor.uploader) {
  4594. if (callback) {
  4595. callback(false);
  4596. }
  4597. return;
  4598. }
  4599. if (this.target.attr('src') === src) {
  4600. return;
  4601. }
  4602. return this.button.loadImage(this.target, src, (function(_this) {
  4603. return function(img) {
  4604. var blob;
  4605. if (!img) {
  4606. return;
  4607. }
  4608. if (_this.active) {
  4609. _this.width = img.width;
  4610. _this.height = img.height;
  4611. _this.widthEl.val(_this.width);
  4612. _this.heightEl.val(_this.height);
  4613. }
  4614. if (/^data:image/.test(src)) {
  4615. blob = _this.editor.util.dataURLtoBlob(src);
  4616. blob.name = "Base64 Image.png";
  4617. _this.editor.uploader.upload(blob, {
  4618. inline: true,
  4619. img: _this.target
  4620. });
  4621. } else {
  4622. _this.editor.trigger('valuechanged');
  4623. }
  4624. if (callback) {
  4625. return callback(img);
  4626. }
  4627. };
  4628. })(this));
  4629. };
  4630. ImagePopover.prototype.show = function() {
  4631. var $img, args;
  4632. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  4633. ImagePopover.__super__.show.apply(this, args);
  4634. $img = this.target;
  4635. this.width = $img.width();
  4636. this.height = $img.height();
  4637. this.alt = $img.attr('alt');
  4638. if ($img.hasClass('uploading')) {
  4639. return this.srcEl.val(this._t('uploading')).prop('disabled', true);
  4640. } else {
  4641. this.srcEl.val($img.attr('src')).prop('disabled', false);
  4642. if ($img.attr('width')) {
  4643. this.widthEl.val($img.attr('width'));
  4644. } else {
  4645. // this.widthEl.val(this.width);
  4646. }
  4647. if ($img.attr('height')) {
  4648. this.heightEl.val($img.attr('height'));
  4649. } else {
  4650. // this.heightEl.val(this.height);
  4651. }
  4652. return this.altEl.val(this.alt);
  4653. }
  4654. };
  4655. return ImagePopover;
  4656. })(Popover);
  4657. Simditor.Toolbar.addButton(ImageButton);
  4658. IndentButton = (function(superClass) {
  4659. extend(IndentButton, superClass);
  4660. function IndentButton() {
  4661. return IndentButton.__super__.constructor.apply(this, arguments);
  4662. }
  4663. IndentButton.prototype.name = 'indent';
  4664. IndentButton.prototype.icon = 'indent';
  4665. IndentButton.prototype._init = function() {
  4666. var hotkey;
  4667. hotkey = this.editor.opts.tabIndent === false ? '' : ' (Tab)';
  4668. this.title = this._t(this.name) + hotkey;
  4669. return IndentButton.__super__._init.call(this);
  4670. };
  4671. IndentButton.prototype._status = function() {};
  4672. IndentButton.prototype.command = function() {
  4673. return this.editor.indentation.indent();
  4674. };
  4675. return IndentButton;
  4676. })(Button);
  4677. Simditor.Toolbar.addButton(IndentButton);
  4678. OutdentButton = (function(superClass) {
  4679. extend(OutdentButton, superClass);
  4680. function OutdentButton() {
  4681. return OutdentButton.__super__.constructor.apply(this, arguments);
  4682. }
  4683. OutdentButton.prototype.name = 'outdent';
  4684. OutdentButton.prototype.icon = 'outdent';
  4685. OutdentButton.prototype._init = function() {
  4686. var hotkey;
  4687. hotkey = this.editor.opts.tabIndent === false ? '' : ' (Shift + Tab)';
  4688. this.title = this._t(this.name) + hotkey;
  4689. return OutdentButton.__super__._init.call(this);
  4690. };
  4691. OutdentButton.prototype._status = function() {};
  4692. OutdentButton.prototype.command = function() {
  4693. return this.editor.indentation.indent(true);
  4694. };
  4695. return OutdentButton;
  4696. })(Button);
  4697. Simditor.Toolbar.addButton(OutdentButton);
  4698. HrButton = (function(superClass) {
  4699. extend(HrButton, superClass);
  4700. function HrButton() {
  4701. return HrButton.__super__.constructor.apply(this, arguments);
  4702. }
  4703. HrButton.prototype.name = 'hr';
  4704. HrButton.prototype.icon = 'minus';
  4705. HrButton.prototype.htmlTag = 'hr';
  4706. HrButton.prototype._status = function() {};
  4707. HrButton.prototype.command = function() {
  4708. var $hr, $newBlock, $nextBlock, $rootBlock;
  4709. $rootBlock = this.editor.selection.rootNodes().first();
  4710. $nextBlock = $rootBlock.next();
  4711. if ($nextBlock.length > 0) {
  4712. this.editor.selection.save();
  4713. } else {
  4714. $newBlock = $('<p/>').append(this.editor.util.phBr);
  4715. }
  4716. $hr = $('<hr/>').insertAfter($rootBlock);
  4717. if ($newBlock) {
  4718. $newBlock.insertAfter($hr);
  4719. this.editor.selection.setRangeAtStartOf($newBlock);
  4720. } else {
  4721. this.editor.selection.restore();
  4722. }
  4723. return this.editor.trigger('valuechanged');
  4724. };
  4725. return HrButton;
  4726. })(Button);
  4727. Simditor.Toolbar.addButton(HrButton);
  4728. TableButton = (function(superClass) {
  4729. extend(TableButton, superClass);
  4730. function TableButton() {
  4731. return TableButton.__super__.constructor.apply(this, arguments);
  4732. }
  4733. TableButton.prototype.name = 'table';
  4734. TableButton.prototype.icon = 'table';
  4735. TableButton.prototype.htmlTag = 'table';
  4736. TableButton.prototype.disableTag = 'pre, li, blockquote';
  4737. TableButton.prototype.menu = true;
  4738. TableButton.prototype._init = function() {
  4739. TableButton.__super__._init.call(this);
  4740. $.merge(this.editor.formatter._allowedTags, ['thead', 'th', 'tbody', 'tr', 'td', 'colgroup', 'col']);
  4741. $.extend(this.editor.formatter._allowedAttributes, {
  4742. td: ['rowspan', 'colspan'],
  4743. col: ['width']
  4744. });
  4745. $.extend(this.editor.formatter._allowedStyles, {
  4746. td: ['text-align'],
  4747. th: ['text-align']
  4748. });
  4749. this._initShortcuts();
  4750. this._initResize();
  4751. this.editor.on('decorate', (function(_this) {
  4752. return function(e, $el) {
  4753. return $el.find('table').each(function(i, table) {
  4754. return _this.decorate($(table));
  4755. });
  4756. };
  4757. })(this));
  4758. this.editor.on('undecorate', (function(_this) {
  4759. return function(e, $el) {
  4760. return $el.find('table').each(function(i, table) {
  4761. return _this.undecorate($(table));
  4762. });
  4763. };
  4764. })(this));
  4765. this.editor.on('selectionchanged.table', (function(_this) {
  4766. return function(e) {
  4767. var $container, range;
  4768. _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active');
  4769. range = _this.editor.selection.range();
  4770. if (!range) {
  4771. return;
  4772. }
  4773. $container = _this.editor.selection.containerNode();
  4774. if (range.collapsed && $container.is('.simditor-table')) {
  4775. _this.editor.selection.setRangeAtEndOf($container);
  4776. }
  4777. return $container.closest('td, th', _this.editor.body).addClass('active');
  4778. };
  4779. })(this));
  4780. this.editor.on('blur.table', (function(_this) {
  4781. return function(e) {
  4782. return _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active');
  4783. };
  4784. })(this));
  4785. this.editor.keystroke.add('up', 'td', (function(_this) {
  4786. return function(e, $node) {
  4787. _this._tdNav($node, 'up');
  4788. return true;
  4789. };
  4790. })(this));
  4791. this.editor.keystroke.add('up', 'th', (function(_this) {
  4792. return function(e, $node) {
  4793. _this._tdNav($node, 'up');
  4794. return true;
  4795. };
  4796. })(this));
  4797. this.editor.keystroke.add('down', 'td', (function(_this) {
  4798. return function(e, $node) {
  4799. _this._tdNav($node, 'down');
  4800. return true;
  4801. };
  4802. })(this));
  4803. return this.editor.keystroke.add('down', 'th', (function(_this) {
  4804. return function(e, $node) {
  4805. _this._tdNav($node, 'down');
  4806. return true;
  4807. };
  4808. })(this));
  4809. };
  4810. TableButton.prototype._tdNav = function($td, direction) {
  4811. var $anotherTr, $tr, action, anotherTag, index, parentTag, ref;
  4812. if (direction == null) {
  4813. direction = 'up';
  4814. }
  4815. action = direction === 'up' ? 'prev' : 'next';
  4816. ref = direction === 'up' ? ['tbody', 'thead'] : ['thead', 'tbody'], parentTag = ref[0], anotherTag = ref[1];
  4817. $tr = $td.parent('tr');
  4818. $anotherTr = this["_" + action + "Row"]($tr);
  4819. if (!($anotherTr.length > 0)) {
  4820. return true;
  4821. }
  4822. index = $tr.find('td, th').index($td);
  4823. return this.editor.selection.setRangeAtEndOf($anotherTr.find('td, th').eq(index));
  4824. };
  4825. TableButton.prototype._nextRow = function($tr) {
  4826. var $nextTr;
  4827. $nextTr = $tr.next('tr');
  4828. if ($nextTr.length < 1 && $tr.parent('thead').length > 0) {
  4829. $nextTr = $tr.parent('thead').next('tbody').find('tr:first');
  4830. }
  4831. return $nextTr;
  4832. };
  4833. TableButton.prototype._prevRow = function($tr) {
  4834. var $prevTr;
  4835. $prevTr = $tr.prev('tr');
  4836. if ($prevTr.length < 1 && $tr.parent('tbody').length > 0) {
  4837. $prevTr = $tr.parent('tbody').prev('thead').find('tr');
  4838. }
  4839. return $prevTr;
  4840. };
  4841. TableButton.prototype._initResize = function() {
  4842. var $editor;
  4843. $editor = this.editor;
  4844. $(document).on('mousemove.simditor-table', '.simditor-table td, .simditor-table th', function(e) {
  4845. var $col, $colgroup, $resizeHandle, $td, $wrapper, index, ref, ref1, x;
  4846. $wrapper = $(this).parents('.simditor-table');
  4847. $resizeHandle = $wrapper.find('.simditor-resize-handle');
  4848. $colgroup = $wrapper.find('colgroup');
  4849. if ($wrapper.hasClass('resizing')) {
  4850. return;
  4851. }
  4852. $td = $(e.currentTarget);
  4853. x = e.pageX - $(e.currentTarget).offset().left;
  4854. if (x < 5 && $td.prev().length > 0) {
  4855. $td = $td.prev();
  4856. }
  4857. if ($td.next('td, th').length < 1) {
  4858. $resizeHandle.hide();
  4859. return;
  4860. }
  4861. if ((ref = $resizeHandle.data('td')) != null ? ref.is($td) : void 0) {
  4862. $resizeHandle.show();
  4863. return;
  4864. }
  4865. index = $td.parent().find('td, th').index($td);
  4866. $col = $colgroup.find('col').eq(index);
  4867. if ((ref1 = $resizeHandle.data('col')) != null ? ref1.is($col) : void 0) {
  4868. $resizeHandle.show();
  4869. return;
  4870. }
  4871. return $resizeHandle.css('left', $td.position().left + $td.outerWidth() - 5).data('td', $td).data('col', $col).show();
  4872. });
  4873. $(document).on('mouseleave.simditor-table', '.simditor-table', function(e) {
  4874. return $(this).find('.simditor-resize-handle').hide();
  4875. });
  4876. return $(document).on('mousedown.simditor-resize-handle', '.simditor-resize-handle', function(e) {
  4877. var $handle, $leftCol, $leftTd, $rightCol, $rightTd, $wrapper, minWidth, startHandleLeft, startLeftWidth, startRightWidth, startX, tableWidth;
  4878. $wrapper = $(this).parent('.simditor-table');
  4879. $handle = $(e.currentTarget);
  4880. $leftTd = $handle.data('td');
  4881. $leftCol = $handle.data('col');
  4882. $rightTd = $leftTd.next('td, th');
  4883. $rightCol = $leftCol.next('col');
  4884. startX = e.pageX;
  4885. startLeftWidth = $leftTd.outerWidth() * 1;
  4886. startRightWidth = $rightTd.outerWidth() * 1;
  4887. startHandleLeft = parseFloat($handle.css('left'));
  4888. tableWidth = $leftTd.closest('table').width();
  4889. minWidth = 50;
  4890. $(document).on('mousemove.simditor-resize-table', function(e) {
  4891. var deltaX, leftWidth, rightWidth;
  4892. deltaX = e.pageX - startX;
  4893. leftWidth = startLeftWidth + deltaX;
  4894. rightWidth = startRightWidth - deltaX;
  4895. if (leftWidth < minWidth) {
  4896. leftWidth = minWidth;
  4897. deltaX = minWidth - startLeftWidth;
  4898. rightWidth = startRightWidth - deltaX;
  4899. } else if (rightWidth < minWidth) {
  4900. rightWidth = minWidth;
  4901. deltaX = startRightWidth - minWidth;
  4902. leftWidth = startLeftWidth + deltaX;
  4903. }
  4904. $leftCol.attr('width', (leftWidth / tableWidth * 100) + '%');
  4905. $rightCol.attr('width', (rightWidth / tableWidth * 100) + '%');
  4906. return $handle.css('left', startHandleLeft + deltaX);
  4907. });
  4908. $(document).one('mouseup.simditor-resize-table', function(e) {
  4909. $editor.sync();
  4910. $(document).off('.simditor-resize-table');
  4911. return $wrapper.removeClass('resizing');
  4912. });
  4913. $wrapper.addClass('resizing');
  4914. return false;
  4915. });
  4916. };
  4917. TableButton.prototype._initShortcuts = function() {
  4918. this.editor.hotkeys.add('ctrl+alt+up', (function(_this) {
  4919. return function(e) {
  4920. _this.editMenu.find('.menu-item[data-param=insertRowAbove]').click();
  4921. return false;
  4922. };
  4923. })(this));
  4924. this.editor.hotkeys.add('ctrl+alt+down', (function(_this) {
  4925. return function(e) {
  4926. _this.editMenu.find('.menu-item[data-param=insertRowBelow]').click();
  4927. return false;
  4928. };
  4929. })(this));
  4930. this.editor.hotkeys.add('ctrl+alt+left', (function(_this) {
  4931. return function(e) {
  4932. _this.editMenu.find('.menu-item[data-param=insertColLeft]').click();
  4933. return false;
  4934. };
  4935. })(this));
  4936. return this.editor.hotkeys.add('ctrl+alt+right', (function(_this) {
  4937. return function(e) {
  4938. _this.editMenu.find('.menu-item[data-param=insertColRight]').click();
  4939. return false;
  4940. };
  4941. })(this));
  4942. };
  4943. TableButton.prototype.decorate = function($table) {
  4944. var $colgroup, $headRow, $resizeHandle, $tbody, $thead, $wrapper;
  4945. if ($table.parent('.simditor-table').length > 0) {
  4946. this.undecorate($table);
  4947. }
  4948. $table.wrap('<div class="simditor-table"></div>');
  4949. $wrapper = $table.parent('.simditor-table');
  4950. $colgroup = $table.find('colgroup');
  4951. if ($table.find('thead').length < 1) {
  4952. $thead = $('<thead />');
  4953. $headRow = $table.find('tr').first();
  4954. $thead.append($headRow);
  4955. this._changeCellTag($headRow, 'th');
  4956. $tbody = $table.find('tbody');
  4957. if ($tbody.length > 0) {
  4958. $tbody.before($thead);
  4959. } else {
  4960. $table.prepend($thead);
  4961. }
  4962. }
  4963. if ($colgroup.length < 1) {
  4964. $colgroup = $('<colgroup/>').prependTo($table);
  4965. $table.find('thead tr th').each(function(i, td) {
  4966. var $col;
  4967. return $col = $('<col/>').appendTo($colgroup);
  4968. });
  4969. this.refreshTableWidth($table);
  4970. }
  4971. $resizeHandle = $('<div />', {
  4972. "class": 'simditor-resize-handle',
  4973. contenteditable: 'false'
  4974. }).appendTo($wrapper);
  4975. return $table.parent();
  4976. };
  4977. TableButton.prototype.undecorate = function($table) {
  4978. if (!($table.parent('.simditor-table').length > 0)) {
  4979. return;
  4980. }
  4981. return $table.parent().replaceWith($table);
  4982. };
  4983. TableButton.prototype.renderMenu = function() {
  4984. var $table;
  4985. $("<div class=\"menu-create-table\">\n</div>\n<div class=\"menu-edit-table\">\n <ul>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteRow\">\n <span>" + (this._t('deleteRow')) + "</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertRowAbove\">\n <span>" + (this._t('insertRowAbove')) + " ( Ctrl + Alt + ↑ )</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertRowBelow\">\n <span>" + (this._t('insertRowBelow')) + " ( Ctrl + Alt + ↓ )</span>\n </a>\n </li>\n <li><span class=\"separator\"></span></li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteCol\">\n <span>" + (this._t('deleteColumn')) + "</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertColLeft\">\n <span>" + (this._t('insertColumnLeft')) + " ( Ctrl + Alt + ← )</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertColRight\">\n <span>" + (this._t('insertColumnRight')) + " ( Ctrl + Alt + → )</span>\n </a>\n </li>\n <li><span class=\"separator\"></span></li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteTable\">\n <span>" + (this._t('deleteTable')) + "</span>\n </a>\n </li>\n </ul>\n</div>").appendTo(this.menuWrapper);
  4986. this.createMenu = this.menuWrapper.find('.menu-create-table');
  4987. this.editMenu = this.menuWrapper.find('.menu-edit-table');
  4988. $table = this.createTable(6, 6).appendTo(this.createMenu);
  4989. this.createMenu.on('mouseenter', 'td, th', (function(_this) {
  4990. return function(e) {
  4991. var $td, $tr, $trs, num;
  4992. _this.createMenu.find('td, th').removeClass('selected');
  4993. $td = $(e.currentTarget);
  4994. $tr = $td.parent();
  4995. num = $tr.find('td, th').index($td) + 1;
  4996. $trs = $tr.prevAll('tr').addBack();
  4997. if ($tr.parent().is('tbody')) {
  4998. $trs = $trs.add($table.find('thead tr'));
  4999. }
  5000. return $trs.find("td:lt(" + num + "), th:lt(" + num + ")").addClass('selected');
  5001. };
  5002. })(this));
  5003. this.createMenu.on('mouseleave', function(e) {
  5004. return $(e.currentTarget).find('td, th').removeClass('selected');
  5005. });
  5006. return this.createMenu.on('mousedown', 'td, th', (function(_this) {
  5007. return function(e) {
  5008. var $closestBlock, $td, $tr, colNum, rowNum;
  5009. _this.wrapper.removeClass('menu-on');
  5010. if (!_this.editor.inputManager.focused) {
  5011. return;
  5012. }
  5013. $td = $(e.currentTarget);
  5014. $tr = $td.parent();
  5015. colNum = $tr.find('td').index($td) + 1;
  5016. rowNum = $tr.prevAll('tr').length + 1;
  5017. if ($tr.parent().is('tbody')) {
  5018. rowNum += 1;
  5019. }
  5020. $table = _this.createTable(rowNum, colNum, true);
  5021. $closestBlock = _this.editor.selection.blockNodes().last();
  5022. if (_this.editor.util.isEmptyNode($closestBlock)) {
  5023. $closestBlock.replaceWith($table);
  5024. } else {
  5025. $closestBlock.after($table);
  5026. }
  5027. _this.decorate($table);
  5028. _this.editor.selection.setRangeAtStartOf($table.find('th:first'));
  5029. _this.editor.trigger('valuechanged');
  5030. return false;
  5031. };
  5032. })(this));
  5033. };
  5034. TableButton.prototype.createTable = function(row, col, phBr) {
  5035. var $table, $tbody, $td, $thead, $tr, c, k, l, r, ref, ref1;
  5036. $table = $('<table/>');
  5037. $thead = $('<thead/>').appendTo($table);
  5038. $tbody = $('<tbody/>').appendTo($table);
  5039. for (r = k = 0, ref = row; 0 <= ref ? k < ref : k > ref; r = 0 <= ref ? ++k : --k) {
  5040. $tr = $('<tr/>');
  5041. $tr.appendTo(r === 0 ? $thead : $tbody);
  5042. for (c = l = 0, ref1 = col; 0 <= ref1 ? l < ref1 : l > ref1; c = 0 <= ref1 ? ++l : --l) {
  5043. $td = $(r === 0 ? '<th/>' : '<td/>').appendTo($tr);
  5044. if (phBr) {
  5045. $td.append(this.editor.util.phBr);
  5046. }
  5047. }
  5048. }
  5049. return $table;
  5050. };
  5051. TableButton.prototype.refreshTableWidth = function($table) {
  5052. return setTimeout((function(_this) {
  5053. return function() {
  5054. var cols, tableWidth;
  5055. tableWidth = $table.width();
  5056. cols = $table.find('col');
  5057. return $table.find('thead tr th').each(function(i, td) {
  5058. var $col;
  5059. $col = cols.eq(i);
  5060. return $col.attr('width', ($(td).outerWidth() / tableWidth * 100) + '%');
  5061. });
  5062. };
  5063. })(this), 0);
  5064. };
  5065. TableButton.prototype.setActive = function(active) {
  5066. TableButton.__super__.setActive.call(this, active);
  5067. if (active) {
  5068. this.createMenu.hide();
  5069. return this.editMenu.show();
  5070. } else {
  5071. this.createMenu.show();
  5072. return this.editMenu.hide();
  5073. }
  5074. };
  5075. TableButton.prototype._changeCellTag = function($tr, tagName) {
  5076. return $tr.find('td, th').each(function(i, cell) {
  5077. var $cell;
  5078. $cell = $(cell);
  5079. return $cell.replaceWith("<" + tagName + ">" + ($cell.html()) + "</" + tagName + ">");
  5080. });
  5081. };
  5082. TableButton.prototype.deleteRow = function($td) {
  5083. var $newTr, $tr, index;
  5084. $tr = $td.parent('tr');
  5085. if ($tr.closest('table').find('tr').length < 1) {
  5086. return this.deleteTable($td);
  5087. } else {
  5088. $newTr = this._nextRow($tr);
  5089. if (!($newTr.length > 0)) {
  5090. $newTr = this._prevRow($tr);
  5091. }
  5092. index = $tr.find('td, th').index($td);
  5093. if ($tr.parent().is('thead')) {
  5094. $newTr.appendTo($tr.parent());
  5095. this._changeCellTag($newTr, 'th');
  5096. }
  5097. $tr.remove();
  5098. return this.editor.selection.setRangeAtEndOf($newTr.find('td, th').eq(index));
  5099. }
  5100. };
  5101. TableButton.prototype.insertRow = function($td, direction) {
  5102. var $newTr, $table, $tr, cellTag, colNum, i, index, k, ref;
  5103. if (direction == null) {
  5104. direction = 'after';
  5105. }
  5106. $tr = $td.parent('tr');
  5107. $table = $tr.closest('table');
  5108. colNum = 0;
  5109. $table.find('tr').each(function(i, tr) {
  5110. return colNum = Math.max(colNum, $(tr).find('td').length);
  5111. });
  5112. index = $tr.find('td, th').index($td);
  5113. $newTr = $('<tr/>');
  5114. cellTag = 'td';
  5115. if (direction === 'after' && $tr.parent().is('thead')) {
  5116. $tr.parent().next('tbody').prepend($newTr);
  5117. } else if (direction === 'before' && $tr.parent().is('thead')) {
  5118. $tr.before($newTr);
  5119. $tr.parent().next('tbody').prepend($tr);
  5120. this._changeCellTag($tr, 'td');
  5121. cellTag = 'th';
  5122. } else {
  5123. $tr[direction]($newTr);
  5124. }
  5125. for (i = k = 1, ref = colNum; 1 <= ref ? k <= ref : k >= ref; i = 1 <= ref ? ++k : --k) {
  5126. $("<" + cellTag + "/>").append(this.editor.util.phBr).appendTo($newTr);
  5127. }
  5128. return this.editor.selection.setRangeAtStartOf($newTr.find('td, th').eq(index));
  5129. };
  5130. TableButton.prototype.deleteCol = function($td) {
  5131. var $newTd, $table, $tr, index, noOtherCol, noOtherRow;
  5132. $tr = $td.parent('tr');
  5133. noOtherRow = $tr.closest('table').find('tr').length < 2;
  5134. noOtherCol = $td.siblings('td, th').length < 1;
  5135. if (noOtherRow && noOtherCol) {
  5136. return this.deleteTable($td);
  5137. } else {
  5138. index = $tr.find('td, th').index($td);
  5139. $newTd = $td.next('td, th');
  5140. if (!($newTd.length > 0)) {
  5141. $newTd = $tr.prev('td, th');
  5142. }
  5143. $table = $tr.closest('table');
  5144. $table.find('col').eq(index).remove();
  5145. $table.find('tr').each(function(i, tr) {
  5146. return $(tr).find('td, th').eq(index).remove();
  5147. });
  5148. this.refreshTableWidth($table);
  5149. return this.editor.selection.setRangeAtEndOf($newTd);
  5150. }
  5151. };
  5152. TableButton.prototype.insertCol = function($td, direction) {
  5153. var $col, $newCol, $newTd, $table, $tr, index, tableWidth, width;
  5154. if (direction == null) {
  5155. direction = 'after';
  5156. }
  5157. $tr = $td.parent('tr');
  5158. index = $tr.find('td, th').index($td);
  5159. $table = $td.closest('table');
  5160. $col = $table.find('col').eq(index);
  5161. $table.find('tr').each((function(_this) {
  5162. return function(i, tr) {
  5163. var $newTd, cellTag;
  5164. cellTag = $(tr).parent().is('thead') ? 'th' : 'td';
  5165. $newTd = $("<" + cellTag + "/>").append(_this.editor.util.phBr);
  5166. return $(tr).find('td, th').eq(index)[direction]($newTd);
  5167. };
  5168. })(this));
  5169. $newCol = $('<col/>');
  5170. $col[direction]($newCol);
  5171. tableWidth = $table.width();
  5172. width = Math.max(parseFloat($col.attr('width')) / 2, 50 / tableWidth * 100);
  5173. $col.attr('width', width + '%');
  5174. $newCol.attr('width', width + '%');
  5175. this.refreshTableWidth($table);
  5176. $newTd = direction === 'after' ? $td.next('td, th') : $td.prev('td, th');
  5177. return this.editor.selection.setRangeAtStartOf($newTd);
  5178. };
  5179. TableButton.prototype.deleteTable = function($td) {
  5180. var $block, $table;
  5181. $table = $td.closest('.simditor-table');
  5182. $block = $table.next('p');
  5183. $table.remove();
  5184. if ($block.length > 0) {
  5185. return this.editor.selection.setRangeAtStartOf($block);
  5186. }
  5187. };
  5188. TableButton.prototype.command = function(param) {
  5189. var $td;
  5190. $td = this.editor.selection.containerNode().closest('td, th');
  5191. if (!($td.length > 0)) {
  5192. return;
  5193. }
  5194. if (param === 'deleteRow') {
  5195. this.deleteRow($td);
  5196. } else if (param === 'insertRowAbove') {
  5197. this.insertRow($td, 'before');
  5198. } else if (param === 'insertRowBelow') {
  5199. this.insertRow($td);
  5200. } else if (param === 'deleteCol') {
  5201. this.deleteCol($td);
  5202. } else if (param === 'insertColLeft') {
  5203. this.insertCol($td, 'before');
  5204. } else if (param === 'insertColRight') {
  5205. this.insertCol($td);
  5206. } else if (param === 'deleteTable') {
  5207. this.deleteTable($td);
  5208. } else {
  5209. return;
  5210. }
  5211. return this.editor.trigger('valuechanged');
  5212. };
  5213. return TableButton;
  5214. })(Button);
  5215. Simditor.Toolbar.addButton(TableButton);
  5216. StrikethroughButton = (function(superClass) {
  5217. extend(StrikethroughButton, superClass);
  5218. function StrikethroughButton() {
  5219. return StrikethroughButton.__super__.constructor.apply(this, arguments);
  5220. }
  5221. StrikethroughButton.prototype.name = 'strikethrough';
  5222. StrikethroughButton.prototype.icon = 'strikethrough';
  5223. StrikethroughButton.prototype.htmlTag = 'strike';
  5224. StrikethroughButton.prototype.disableTag = 'pre';
  5225. StrikethroughButton.prototype._activeStatus = function() {
  5226. var active;
  5227. active = document.queryCommandState('strikethrough') === true;
  5228. this.setActive(active);
  5229. return this.active;
  5230. };
  5231. StrikethroughButton.prototype.command = function() {
  5232. document.execCommand('strikethrough');
  5233. if (!this.editor.util.support.oninput) {
  5234. this.editor.trigger('valuechanged');
  5235. }
  5236. return $(document).trigger('selectionchange');
  5237. };
  5238. return StrikethroughButton;
  5239. })(Button);
  5240. Simditor.Toolbar.addButton(StrikethroughButton);
  5241. AlignmentButton = (function(superClass) {
  5242. extend(AlignmentButton, superClass);
  5243. function AlignmentButton() {
  5244. return AlignmentButton.__super__.constructor.apply(this, arguments);
  5245. }
  5246. AlignmentButton.prototype.name = "alignment";
  5247. AlignmentButton.prototype.icon = 'align-left';
  5248. AlignmentButton.prototype.htmlTag = 'p, h1, h2, h3, h4, td, th';
  5249. AlignmentButton.prototype._init = function() {
  5250. this.menu = [
  5251. {
  5252. name: 'left',
  5253. text: this._t('alignLeft'),
  5254. icon: 'align-left',
  5255. param: 'left'
  5256. }, {
  5257. name: 'center',
  5258. text: this._t('alignCenter'),
  5259. icon: 'align-center',
  5260. param: 'center'
  5261. }, {
  5262. name: 'right',
  5263. text: this._t('alignRight'),
  5264. icon: 'align-right',
  5265. param: 'right'
  5266. }
  5267. ];
  5268. return AlignmentButton.__super__._init.call(this);
  5269. };
  5270. AlignmentButton.prototype.setActive = function(active, align) {
  5271. if (align == null) {
  5272. align = 'left';
  5273. }
  5274. if (align !== 'left' && align !== 'center' && align !== 'right') {
  5275. align = 'left';
  5276. }
  5277. if (align === 'left') {
  5278. AlignmentButton.__super__.setActive.call(this, false);
  5279. } else {
  5280. AlignmentButton.__super__.setActive.call(this, active);
  5281. }
  5282. this.el.removeClass('align-left align-center align-right');
  5283. if (active) {
  5284. this.el.addClass('align-' + align);
  5285. }
  5286. this.setIcon('align-' + align);
  5287. return this.menuEl.find('.menu-item').show().end().find('.menu-item-' + align).hide();
  5288. };
  5289. AlignmentButton.prototype._status = function() {
  5290. this.nodes = this.editor.selection.nodes().filter(this.htmlTag);
  5291. if (this.nodes.length < 1) {
  5292. this.setDisabled(true);
  5293. return this.setActive(false);
  5294. } else {
  5295. this.setDisabled(false);
  5296. return this.setActive(true, this.nodes.first().css('text-align'));
  5297. }
  5298. };
  5299. AlignmentButton.prototype.command = function(align) {
  5300. if (align !== 'left' && align !== 'center' && align !== 'right') {
  5301. throw new Error("simditor alignment button: invalid align " + align);
  5302. }
  5303. this.nodes.css({
  5304. 'text-align': align === 'left' ? '' : align
  5305. });
  5306. this.editor.trigger('valuechanged');
  5307. return this.editor.inputManager.throttledSelectionChanged();
  5308. };
  5309. return AlignmentButton;
  5310. })(Button);
  5311. Simditor.Toolbar.addButton(AlignmentButton);
  5312. return Simditor;
  5313. }));