simditor.min.js 177 KB

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