editormd.js 155 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585
  1. ;(function(factory) {
  2. "use strict";
  3. // CommonJS/Node.js
  4. if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
  5. {
  6. module.exports = factory;
  7. }
  8. else if (typeof define === "function") // AMD/CMD/Sea.js
  9. {
  10. if (define.amd) // for Require.js
  11. {
  12. /* Require.js define replace */
  13. }
  14. else
  15. {
  16. define(["jquery"], factory); // for Sea.js
  17. }
  18. }
  19. else
  20. {
  21. window.editormd = factory();
  22. }
  23. }(function() {
  24. /* Require.js assignment replace */
  25. "use strict";
  26. var $ = (typeof (jQuery) !== "undefined") ? jQuery : Zepto;
  27. if (typeof ($) === "undefined") {
  28. return ;
  29. }
  30. /**
  31. * editormd
  32. *
  33. * @param {String} id 编辑器的ID
  34. * @param {Object} options 配置选项 Key/Value
  35. * @returns {Object} editormd 返回editormd对象
  36. */
  37. var editormd = function (id, options) {
  38. return new editormd.fn.init(id, options);
  39. };
  40. editormd.title = editormd.$name = "Editor.md";
  41. editormd.version = "1.5.0";
  42. editormd.homePage = "https://pandao.github.io/editor.md/";
  43. editormd.classPrefix = "editormd-";
  44. editormd.toolbarModes = {
  45. full : [
  46. "undo", "redo", "|",
  47. "bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|",
  48. "h1", "h2", "h3", "h4", "h5", "h6", "|",
  49. "list-ul", "list-ol", "hr", "|",
  50. "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|",
  51. "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|",
  52. "help", "info"
  53. ],
  54. simple : [
  55. "undo", "redo", "|",
  56. "bold", "del", "italic", "quote", "uppercase", "lowercase", "|",
  57. "h1", "h2", "h3", "h4", "h5", "h6", "|",
  58. "list-ul", "list-ol", "hr", "|",
  59. "watch", "preview", "fullscreen", "|",
  60. "help", "info"
  61. ],
  62. mini : [
  63. "undo", "redo", "|",
  64. "watch", "preview", "|",
  65. "help", "info"
  66. ]
  67. };
  68. editormd.defaults = {
  69. mode : "gfm", //gfm or markdown
  70. name : "", // Form element name
  71. value : "", // value for CodeMirror, if mode not gfm/markdown
  72. theme : "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty
  73. editorTheme : "default", // Editor area, this is CodeMirror theme at v1.5.0
  74. previewTheme : "", // Preview area theme, default empty
  75. markdown : "", // Markdown source code
  76. appendMarkdown : "", // if in init textarea value not empty, append markdown to textarea
  77. width : "100%",
  78. height : "100%",
  79. path : "./lib/", // Dependents module file directory
  80. pluginPath : "", // If this empty, default use settings.path + "../plugins/"
  81. delay : 300, // Delay parse markdown to html, Uint : ms
  82. autoLoadModules : true, // Automatic load dependent module files
  83. watch : true,
  84. placeholder : "Enjoy Markdown! coding now...",
  85. gotoLine : true,
  86. codeFold : false,
  87. autoHeight : false,
  88. autoFocus : true,
  89. autoCloseTags : true,
  90. searchReplace : true,
  91. syncScrolling : true, // true | false | "single", default true
  92. readOnly : false,
  93. tabSize : 4,
  94. indentUnit : 4,
  95. lineNumbers : true,
  96. lineWrapping : true,
  97. autoCloseBrackets : true,
  98. showTrailingSpace : true,
  99. matchBrackets : true,
  100. indentWithTabs : true,
  101. styleSelectedText : true,
  102. matchWordHighlight : true, // options: true, false, "onselected"
  103. styleActiveLine : true, // Highlight the current line
  104. dialogLockScreen : true,
  105. dialogShowMask : true,
  106. dialogDraggable : true,
  107. dialogMaskBgColor : "#fff",
  108. dialogMaskOpacity : 0.1,
  109. fontSize : "13px",
  110. saveHTMLToTextarea : false,
  111. disabledKeyMaps : [],
  112. onload : function() {},
  113. onresize : function() {},
  114. onchange : function() {},
  115. onwatch : null,
  116. onunwatch : null,
  117. onpreviewing : function() {},
  118. onpreviewed : function() {},
  119. onfullscreen : function() {},
  120. onfullscreenExit : function() {},
  121. onscroll : function() {},
  122. onpreviewscroll : function() {},
  123. imageUpload : false,
  124. imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
  125. imageUploadURL : "",
  126. crossDomainUpload : false,
  127. uploadCallbackURL : "",
  128. toc : true, // Table of contents
  129. tocm : false, // Using [TOCM], auto create ToC dropdown menu
  130. tocTitle : "", // for ToC dropdown menu btn
  131. tocDropdown : false,
  132. tocContainer : "",
  133. tocStartLevel : 1, // Said from H1 to create ToC
  134. htmlDecode : false, // Open the HTML tag identification
  135. pageBreak : true, // Enable parse page break [========]
  136. atLink : true, // for @link
  137. emailLink : true, // for email address auto link
  138. taskList : false, // Enable Github Flavored Markdown task lists
  139. emoji : false, // :emoji: , Support Github emoji, Twitter Emoji (Twemoji);
  140. // Support FontAwesome icon emoji :fa-xxx: > Using fontAwesome icon web fonts;
  141. // Support Editor.md logo icon emoji :editormd-logo: :editormd-logo-1x: > 1~8x;
  142. tex : false, // TeX(LaTeX), based on KaTeX
  143. flowChart : false, // flowChart.js only support IE9+
  144. sequenceDiagram : false, // sequenceDiagram.js only support IE9+
  145. previewCodeHighlight : true,
  146. toolbar : true, // show/hide toolbar
  147. toolbarAutoFixed : true, // on window scroll auto fixed position
  148. toolbarIcons : "full",
  149. toolbarTitles : {},
  150. toolbarHandlers : {
  151. ucwords : function() {
  152. return editormd.toolbarHandlers.ucwords;
  153. },
  154. lowercase : function() {
  155. return editormd.toolbarHandlers.lowercase;
  156. }
  157. },
  158. toolbarCustomIcons : { // using html tag create toolbar icon, unused default <a> tag.
  159. lowercase : "<a href=\"javascript:;\" title=\"Lowercase\" unselectable=\"on\"><i class=\"fa\" name=\"lowercase\" style=\"font-size:24px;margin-top: -10px;\">a</i></a>",
  160. "ucwords" : "<a href=\"javascript:;\" title=\"ucwords\" unselectable=\"on\"><i class=\"fa\" name=\"ucwords\" style=\"font-size:20px;margin-top: -3px;\">Aa</i></a>"
  161. },
  162. toolbarIconsClass : {
  163. undo : "fa-undo",
  164. redo : "fa-repeat",
  165. bold : "fa-bold",
  166. del : "fa-strikethrough",
  167. italic : "fa-italic",
  168. quote : "fa-quote-left",
  169. uppercase : "fa-font",
  170. h1 : editormd.classPrefix + "bold",
  171. h2 : editormd.classPrefix + "bold",
  172. h3 : editormd.classPrefix + "bold",
  173. h4 : editormd.classPrefix + "bold",
  174. h5 : editormd.classPrefix + "bold",
  175. h6 : editormd.classPrefix + "bold",
  176. "list-ul" : "fa-list-ul",
  177. "list-ol" : "fa-list-ol",
  178. hr : "fa-minus",
  179. link : "fa-link",
  180. "reference-link" : "fa-anchor",
  181. image : "fa-picture-o",
  182. code : "fa-code",
  183. "preformatted-text" : "fa-file-code-o",
  184. "code-block" : "fa-file-code-o",
  185. table : "fa-table",
  186. datetime : "fa-clock-o",
  187. emoji : "fa-smile-o",
  188. "html-entities" : "fa-copyright",
  189. pagebreak : "fa-newspaper-o",
  190. "goto-line" : "fa-terminal", // fa-crosshairs
  191. watch : "fa-eye-slash",
  192. unwatch : "fa-eye",
  193. preview : "fa-desktop",
  194. search : "fa-search",
  195. fullscreen : "fa-arrows-alt",
  196. clear : "fa-eraser",
  197. help : "fa-question-circle",
  198. info : "fa-info-circle"
  199. },
  200. toolbarIconTexts : {},
  201. lang : {
  202. name : "zh-cn",
  203. description : "开源在线Markdown编辑器<br/>Open source online Markdown editor.",
  204. tocTitle : "目录",
  205. toolbar : {
  206. undo : "撤销(Ctrl+Z)",
  207. redo : "重做(Ctrl+Y)",
  208. bold : "粗体",
  209. del : "删除线",
  210. italic : "斜体",
  211. quote : "引用",
  212. ucwords : "将每个单词首字母转成大写",
  213. uppercase : "将所选转换成大写",
  214. lowercase : "将所选转换成小写",
  215. h1 : "标题1",
  216. h2 : "标题2",
  217. h3 : "标题3",
  218. h4 : "标题4",
  219. h5 : "标题5",
  220. h6 : "标题6",
  221. "list-ul" : "无序列表",
  222. "list-ol" : "有序列表",
  223. hr : "横线",
  224. link : "链接",
  225. "reference-link" : "引用链接",
  226. image : "添加图片",
  227. code : "行内代码",
  228. "preformatted-text" : "预格式文本 / 代码块(缩进风格)",
  229. "code-block" : "代码块(多语言风格)",
  230. table : "添加表格",
  231. datetime : "日期时间",
  232. emoji : "Emoji表情",
  233. "html-entities" : "HTML实体字符",
  234. pagebreak : "插入分页符",
  235. "goto-line" : "跳转到行",
  236. watch : "关闭实时预览",
  237. unwatch : "开启实时预览",
  238. preview : "全窗口预览HTML(按 Shift + ESC还原)",
  239. fullscreen : "全屏(按ESC还原)",
  240. clear : "清空",
  241. search : "搜索",
  242. help : "使用帮助",
  243. info : "关于" + editormd.title
  244. },
  245. buttons : {
  246. enter : "确定",
  247. cancel : "取消",
  248. close : "关闭"
  249. },
  250. dialog : {
  251. link : {
  252. title : "添加链接",
  253. url : "链接地址",
  254. urlTitle : "链接标题",
  255. urlEmpty : "错误:请填写链接地址。"
  256. },
  257. referenceLink : {
  258. title : "添加引用链接",
  259. name : "引用名称",
  260. url : "链接地址",
  261. urlId : "链接ID",
  262. urlTitle : "链接标题",
  263. nameEmpty: "错误:引用链接的名称不能为空。",
  264. idEmpty : "错误:请填写引用链接的ID。",
  265. urlEmpty : "错误:请填写引用链接的URL地址。"
  266. },
  267. image : {
  268. title : "添加图片",
  269. url : "图片地址",
  270. link : "图片链接",
  271. alt : "图片描述",
  272. uploadButton : "本地上传",
  273. imageURLEmpty : "错误:图片地址不能为空。",
  274. uploadFileEmpty : "错误:上传的图片不能为空。",
  275. formatNotAllowed : "错误:只允许上传图片文件,允许上传的图片文件格式有:"
  276. },
  277. preformattedText : {
  278. title : "添加预格式文本或代码块",
  279. emptyAlert : "错误:请填写预格式文本或代码的内容。"
  280. },
  281. codeBlock : {
  282. title : "添加代码块",
  283. selectLabel : "代码语言:",
  284. selectDefaultText : "请选择代码语言",
  285. otherLanguage : "其他语言",
  286. unselectedLanguageAlert : "错误:请选择代码所属的语言类型。",
  287. codeEmptyAlert : "错误:请填写代码内容。"
  288. },
  289. htmlEntities : {
  290. title : "HTML 实体字符"
  291. },
  292. help : {
  293. title : "使用帮助"
  294. }
  295. }
  296. }
  297. };
  298. editormd.classNames = {
  299. tex : editormd.classPrefix + "tex"
  300. };
  301. editormd.dialogZindex = 99999;
  302. editormd.$katex = null;
  303. editormd.$marked = null;
  304. editormd.$CodeMirror = null;
  305. editormd.$prettyPrint = null;
  306. var timer, flowchartTimer;
  307. editormd.prototype = editormd.fn = {
  308. state : {
  309. watching : false,
  310. loaded : false,
  311. preview : false,
  312. fullscreen : false
  313. },
  314. /**
  315. * 构造函数/实例初始化
  316. * Constructor / instance initialization
  317. *
  318. * @param {String} id 编辑器的ID
  319. * @param {Object} [options={}] 配置选项 Key/Value
  320. * @returns {editormd} 返回editormd的实例对象
  321. */
  322. init : function (id, options) {
  323. options = options || {};
  324. if (typeof id === "object")
  325. {
  326. options = id;
  327. }
  328. var _this = this;
  329. var classPrefix = this.classPrefix = editormd.classPrefix;
  330. var settings = this.settings = $.extend(true, editormd.defaults, options);
  331. id = (typeof id === "object") ? settings.id : id;
  332. var editor = this.editor = $("#" + id);
  333. this.id = id;
  334. this.lang = settings.lang;
  335. var classNames = this.classNames = {
  336. textarea : {
  337. html : classPrefix + "html-textarea",
  338. markdown : classPrefix + "markdown-textarea"
  339. }
  340. };
  341. settings.pluginPath = (settings.pluginPath === "") ? settings.path + "../plugins/" : settings.pluginPath;
  342. this.state.watching = (settings.watch) ? true : false;
  343. if ( !editor.hasClass("editormd") ) {
  344. editor.addClass("editormd");
  345. }
  346. editor.css({
  347. width : (typeof settings.width === "number") ? settings.width + "px" : settings.width,
  348. height : (typeof settings.height === "number") ? settings.height + "px" : settings.height
  349. });
  350. if (settings.autoHeight)
  351. {
  352. editor.css("height", "auto");
  353. }
  354. var markdownTextarea = this.markdownTextarea = editor.children("textarea");
  355. if (markdownTextarea.length < 1)
  356. {
  357. editor.append("<textarea></textarea>");
  358. markdownTextarea = this.markdownTextarea = editor.children("textarea");
  359. }
  360. markdownTextarea.addClass(classNames.textarea.markdown).attr("placeholder", settings.placeholder);
  361. if (typeof markdownTextarea.attr("name") === "undefined" || markdownTextarea.attr("name") === "")
  362. {
  363. markdownTextarea.attr("name", (settings.name !== "") ? settings.name : id + "-markdown-doc");
  364. }
  365. var appendElements = [
  366. (!settings.readOnly) ? "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "preview-close-btn\"></a>" : "",
  367. ( (settings.saveHTMLToTextarea) ? "<textarea class=\"" + classNames.textarea.html + "\" name=\"" + id + "-html-code\"></textarea>" : "" ),
  368. "<div class=\"" + classPrefix + "preview\"><div class=\"markdown-body " + classPrefix + "preview-container\"></div></div>",
  369. "<div class=\"" + classPrefix + "container-mask\" style=\"display:block;\"></div>",
  370. "<div class=\"" + classPrefix + "mask\"></div>"
  371. ].join("\n");
  372. editor.append(appendElements).addClass(classPrefix + "vertical");
  373. if (settings.theme !== "")
  374. {
  375. editor.addClass(classPrefix + "theme-" + settings.theme);
  376. }
  377. this.mask = editor.children("." + classPrefix + "mask");
  378. this.containerMask = editor.children("." + classPrefix + "container-mask");
  379. if (settings.markdown !== "")
  380. {
  381. markdownTextarea.val(settings.markdown);
  382. }
  383. if (settings.appendMarkdown !== "")
  384. {
  385. markdownTextarea.val(markdownTextarea.val() + settings.appendMarkdown);
  386. }
  387. this.htmlTextarea = editor.children("." + classNames.textarea.html);
  388. this.preview = editor.children("." + classPrefix + "preview");
  389. this.previewContainer = this.preview.children("." + classPrefix + "preview-container");
  390. if (settings.previewTheme !== "")
  391. {
  392. this.preview.addClass(classPrefix + "preview-theme-" + settings.previewTheme);
  393. }
  394. if (typeof define === "function" && define.amd)
  395. {
  396. if (typeof katex !== "undefined")
  397. {
  398. editormd.$katex = katex;
  399. }
  400. if (settings.searchReplace && !settings.readOnly)
  401. {
  402. editormd.loadCSS(settings.path + "codemirror/addon/dialog/dialog");
  403. editormd.loadCSS(settings.path + "codemirror/addon/search/matchesonscrollbar");
  404. }
  405. }
  406. if ((typeof define === "function" && define.amd) || !settings.autoLoadModules)
  407. {
  408. if (typeof CodeMirror !== "undefined") {
  409. editormd.$CodeMirror = CodeMirror;
  410. }
  411. if (typeof marked !== "undefined") {
  412. editormd.$marked = marked;
  413. }
  414. this.setCodeMirror().setToolbar().loadedDisplay();
  415. }
  416. else
  417. {
  418. this.loadQueues();
  419. }
  420. return this;
  421. },
  422. /**
  423. * 所需组件加载队列
  424. * Required components loading queue
  425. *
  426. * @returns {editormd} 返回editormd的实例对象
  427. */
  428. loadQueues : function() {
  429. var _this = this;
  430. var settings = this.settings;
  431. var loadPath = settings.path;
  432. var loadFlowChartOrSequenceDiagram = function() {
  433. if (editormd.isIE8)
  434. {
  435. _this.loadedDisplay();
  436. return ;
  437. }
  438. if (settings.flowChart || settings.sequenceDiagram)
  439. {
  440. editormd.loadScript(loadPath + "raphael.min", function() {
  441. editormd.loadScript(loadPath + "underscore.min", function() {
  442. if (!settings.flowChart && settings.sequenceDiagram)
  443. {
  444. editormd.loadScript(loadPath + "sequence-diagram.min", function() {
  445. _this.loadedDisplay();
  446. });
  447. }
  448. else if (settings.flowChart && !settings.sequenceDiagram)
  449. {
  450. editormd.loadScript(loadPath + "flowchart.min", function() {
  451. editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
  452. _this.loadedDisplay();
  453. });
  454. });
  455. }
  456. else if (settings.flowChart && settings.sequenceDiagram)
  457. {
  458. editormd.loadScript(loadPath + "flowchart.min", function() {
  459. editormd.loadScript(loadPath + "jquery.flowchart.min", function() {
  460. editormd.loadScript(loadPath + "sequence-diagram.min", function() {
  461. _this.loadedDisplay();
  462. });
  463. });
  464. });
  465. }
  466. });
  467. });
  468. }
  469. else
  470. {
  471. _this.loadedDisplay();
  472. }
  473. };
  474. editormd.loadCSS(loadPath + "codemirror/codemirror.min");
  475. if (settings.searchReplace && !settings.readOnly)
  476. {
  477. editormd.loadCSS(loadPath + "codemirror/addon/dialog/dialog");
  478. editormd.loadCSS(loadPath + "codemirror/addon/search/matchesonscrollbar");
  479. }
  480. if (settings.codeFold)
  481. {
  482. editormd.loadCSS(loadPath + "codemirror/addon/fold/foldgutter");
  483. }
  484. editormd.loadScript(loadPath + "codemirror/codemirror.min", function() {
  485. editormd.$CodeMirror = CodeMirror;
  486. editormd.loadScript(loadPath + "codemirror/modes.min", function() {
  487. editormd.loadScript(loadPath + "codemirror/addons.min", function() {
  488. _this.setCodeMirror();
  489. if (settings.mode !== "gfm" && settings.mode !== "markdown")
  490. {
  491. _this.loadedDisplay();
  492. return false;
  493. }
  494. _this.setToolbar();
  495. editormd.loadScript(loadPath + "marked.min", function() {
  496. editormd.$marked = marked;
  497. if (settings.previewCodeHighlight)
  498. {
  499. editormd.loadScript(loadPath + "prettify.min", function() {
  500. loadFlowChartOrSequenceDiagram();
  501. });
  502. }
  503. else
  504. {
  505. loadFlowChartOrSequenceDiagram();
  506. }
  507. });
  508. });
  509. });
  510. });
  511. return this;
  512. },
  513. /**
  514. * 设置 Editor.md 的整体主题,主要是工具栏
  515. * Setting Editor.md theme
  516. *
  517. * @returns {editormd} 返回editormd的实例对象
  518. */
  519. setTheme : function(theme) {
  520. var editor = this.editor;
  521. var oldTheme = this.settings.theme;
  522. var themePrefix = this.classPrefix + "theme-";
  523. editor.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
  524. this.settings.theme = theme;
  525. return this;
  526. },
  527. /**
  528. * 设置 CodeMirror(编辑区)的主题
  529. * Setting CodeMirror (Editor area) theme
  530. *
  531. * @returns {editormd} 返回editormd的实例对象
  532. */
  533. setEditorTheme : function(theme) {
  534. var settings = this.settings;
  535. settings.editorTheme = theme;
  536. if (theme !== "default")
  537. {
  538. editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
  539. }
  540. this.cm.setOption("theme", theme);
  541. return this;
  542. },
  543. /**
  544. * setEditorTheme() 的别名
  545. * setEditorTheme() alias
  546. *
  547. * @returns {editormd} 返回editormd的实例对象
  548. */
  549. setCodeMirrorTheme : function (theme) {
  550. this.setEditorTheme(theme);
  551. return this;
  552. },
  553. /**
  554. * 设置 Editor.md 的主题
  555. * Setting Editor.md theme
  556. *
  557. * @returns {editormd} 返回editormd的实例对象
  558. */
  559. setPreviewTheme : function(theme) {
  560. var preview = this.preview;
  561. var oldTheme = this.settings.previewTheme;
  562. var themePrefix = this.classPrefix + "preview-theme-";
  563. preview.removeClass(themePrefix + oldTheme).addClass(themePrefix + theme);
  564. this.settings.previewTheme = theme;
  565. return this;
  566. },
  567. /**
  568. * 配置和初始化CodeMirror组件
  569. * CodeMirror initialization
  570. *
  571. * @returns {editormd} 返回editormd的实例对象
  572. */
  573. setCodeMirror : function() {
  574. var settings = this.settings;
  575. var editor = this.editor;
  576. if (settings.editorTheme !== "default")
  577. {
  578. editormd.loadCSS(settings.path + "codemirror/theme/" + settings.editorTheme);
  579. }
  580. var codeMirrorConfig = {
  581. mode : settings.mode,
  582. theme : settings.editorTheme,
  583. tabSize : settings.tabSize,
  584. dragDrop : false,
  585. autofocus : settings.autoFocus,
  586. autoCloseTags : settings.autoCloseTags,
  587. readOnly : (settings.readOnly) ? "nocursor" : false,
  588. indentUnit : settings.indentUnit,
  589. lineNumbers : settings.lineNumbers,
  590. lineWrapping : settings.lineWrapping,
  591. extraKeys : {
  592. "Ctrl-Q": function(cm) {
  593. cm.foldCode(cm.getCursor());
  594. }
  595. },
  596. foldGutter : settings.codeFold,
  597. gutters : ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
  598. matchBrackets : settings.matchBrackets,
  599. indentWithTabs : settings.indentWithTabs,
  600. styleActiveLine : settings.styleActiveLine,
  601. styleSelectedText : settings.styleSelectedText,
  602. autoCloseBrackets : settings.autoCloseBrackets,
  603. showTrailingSpace : settings.showTrailingSpace,
  604. highlightSelectionMatches : ( (!settings.matchWordHighlight) ? false : { showToken: (settings.matchWordHighlight === "onselected") ? false : /\w/ } )
  605. };
  606. this.codeEditor = this.cm = editormd.$CodeMirror.fromTextArea(this.markdownTextarea[0], codeMirrorConfig);
  607. this.codeMirror = this.cmElement = editor.children(".CodeMirror");
  608. if (settings.value !== "")
  609. {
  610. this.cm.setValue(settings.value);
  611. }
  612. this.codeMirror.css({
  613. fontSize : settings.fontSize,
  614. width : (!settings.watch) ? "100%" : "50%"
  615. });
  616. if (settings.autoHeight)
  617. {
  618. this.codeMirror.css("height", "auto");
  619. this.cm.setOption("viewportMargin", Infinity);
  620. }
  621. if (!settings.lineNumbers)
  622. {
  623. this.codeMirror.find(".CodeMirror-gutters").css("border-right", "none");
  624. }
  625. return this;
  626. },
  627. /**
  628. * 获取CodeMirror的配置选项
  629. * Get CodeMirror setting options
  630. *
  631. * @returns {Mixed} return CodeMirror setting option value
  632. */
  633. getCodeMirrorOption : function(key) {
  634. return this.cm.getOption(key);
  635. },
  636. /**
  637. * 配置和重配置CodeMirror的选项
  638. * CodeMirror setting options / resettings
  639. *
  640. * @returns {editormd} 返回editormd的实例对象
  641. */
  642. setCodeMirrorOption : function(key, value) {
  643. this.cm.setOption(key, value);
  644. return this;
  645. },
  646. /**
  647. * 添加 CodeMirror 键盘快捷键
  648. * Add CodeMirror keyboard shortcuts key map
  649. *
  650. * @returns {editormd} 返回editormd的实例对象
  651. */
  652. addKeyMap : function(map, bottom) {
  653. this.cm.addKeyMap(map, bottom);
  654. return this;
  655. },
  656. /**
  657. * 移除 CodeMirror 键盘快捷键
  658. * Remove CodeMirror keyboard shortcuts key map
  659. *
  660. * @returns {editormd} 返回editormd的实例对象
  661. */
  662. removeKeyMap : function(map) {
  663. this.cm.removeKeyMap(map);
  664. return this;
  665. },
  666. /**
  667. * 跳转到指定的行
  668. * Goto CodeMirror line
  669. *
  670. * @param {String|Intiger} line line number or "first"|"last"
  671. * @returns {editormd} 返回editormd的实例对象
  672. */
  673. gotoLine : function (line) {
  674. var settings = this.settings;
  675. if (!settings.gotoLine)
  676. {
  677. return this;
  678. }
  679. var cm = this.cm;
  680. var editor = this.editor;
  681. var count = cm.lineCount();
  682. var preview = this.preview;
  683. if (typeof line === "string")
  684. {
  685. if(line === "last")
  686. {
  687. line = count;
  688. }
  689. if (line === "first")
  690. {
  691. line = 1;
  692. }
  693. }
  694. if (typeof line !== "number")
  695. {
  696. alert("Error: The line number must be an integer.");
  697. return this;
  698. }
  699. line = parseInt(line) - 1;
  700. if (line > count)
  701. {
  702. alert("Error: The line number range 1-" + count);
  703. return this;
  704. }
  705. cm.setCursor( {line : line, ch : 0} );
  706. var scrollInfo = cm.getScrollInfo();
  707. var clientHeight = scrollInfo.clientHeight;
  708. var coords = cm.charCoords({line : line, ch : 0}, "local");
  709. cm.scrollTo(null, (coords.top + coords.bottom - clientHeight) / 2);
  710. if (settings.watch)
  711. {
  712. var cmScroll = this.codeMirror.find(".CodeMirror-scroll")[0];
  713. var height = $(cmScroll).height();
  714. var scrollTop = cmScroll.scrollTop;
  715. var percent = (scrollTop / cmScroll.scrollHeight);
  716. if (scrollTop === 0)
  717. {
  718. preview.scrollTop(0);
  719. }
  720. else if (scrollTop + height >= cmScroll.scrollHeight - 16)
  721. {
  722. preview.scrollTop(preview[0].scrollHeight);
  723. }
  724. else
  725. {
  726. preview.scrollTop(preview[0].scrollHeight * percent);
  727. }
  728. }
  729. cm.focus();
  730. return this;
  731. },
  732. /**
  733. * 扩展当前实例对象,可同时设置多个或者只设置一个
  734. * Extend editormd instance object, can mutil setting.
  735. *
  736. * @returns {editormd} this(editormd instance object.)
  737. */
  738. extend : function() {
  739. if (typeof arguments[1] !== "undefined")
  740. {
  741. if (typeof arguments[1] === "function")
  742. {
  743. arguments[1] = $.proxy(arguments[1], this);
  744. }
  745. this[arguments[0]] = arguments[1];
  746. }
  747. if (typeof arguments[0] === "object" && typeof arguments[0].length === "undefined")
  748. {
  749. $.extend(true, this, arguments[0]);
  750. }
  751. return this;
  752. },
  753. /**
  754. * 设置或扩展当前实例对象,单个设置
  755. * Extend editormd instance object, one by one
  756. *
  757. * @param {String|Object} key option key
  758. * @param {String|Object} value option value
  759. * @returns {editormd} this(editormd instance object.)
  760. */
  761. set : function (key, value) {
  762. if (typeof value !== "undefined" && typeof value === "function")
  763. {
  764. value = $.proxy(value, this);
  765. }
  766. this[key] = value;
  767. return this;
  768. },
  769. /**
  770. * 重新配置
  771. * Resetting editor options
  772. *
  773. * @param {String|Object} key option key
  774. * @param {String|Object} value option value
  775. * @returns {editormd} this(editormd instance object.)
  776. */
  777. config : function(key, value) {
  778. var settings = this.settings;
  779. if (typeof key === "object")
  780. {
  781. settings = $.extend(true, settings, key);
  782. }
  783. if (typeof key === "string")
  784. {
  785. settings[key] = value;
  786. }
  787. this.settings = settings;
  788. this.recreate();
  789. return this;
  790. },
  791. /**
  792. * 注册事件处理方法
  793. * Bind editor event handle
  794. *
  795. * @param {String} eventType event type
  796. * @param {Function} callback 回调函数
  797. * @returns {editormd} this(editormd instance object.)
  798. */
  799. on : function(eventType, callback) {
  800. var settings = this.settings;
  801. if (typeof settings["on" + eventType] !== "undefined")
  802. {
  803. settings["on" + eventType] = $.proxy(callback, this);
  804. }
  805. return this;
  806. },
  807. /**
  808. * 解除事件处理方法
  809. * Unbind editor event handle
  810. *
  811. * @param {String} eventType event type
  812. * @returns {editormd} this(editormd instance object.)
  813. */
  814. off : function(eventType) {
  815. var settings = this.settings;
  816. if (typeof settings["on" + eventType] !== "undefined")
  817. {
  818. settings["on" + eventType] = function(){};
  819. }
  820. return this;
  821. },
  822. /**
  823. * 显示工具栏
  824. * Display toolbar
  825. *
  826. * @param {Function} [callback=function(){}] 回调函数
  827. * @returns {editormd} 返回editormd的实例对象
  828. */
  829. showToolbar : function(callback) {
  830. var settings = this.settings;
  831. if(settings.readOnly) {
  832. return this;
  833. }
  834. if (settings.toolbar && (this.toolbar.length < 1 || this.toolbar.find("." + this.classPrefix + "menu").html() === "") )
  835. {
  836. this.setToolbar();
  837. }
  838. settings.toolbar = true;
  839. this.toolbar.show();
  840. this.resize();
  841. $.proxy(callback || function(){}, this)();
  842. return this;
  843. },
  844. /**
  845. * 隐藏工具栏
  846. * Hide toolbar
  847. *
  848. * @param {Function} [callback=function(){}] 回调函数
  849. * @returns {editormd} this(editormd instance object.)
  850. */
  851. hideToolbar : function(callback) {
  852. var settings = this.settings;
  853. settings.toolbar = false;
  854. this.toolbar.hide();
  855. this.resize();
  856. $.proxy(callback || function(){}, this)();
  857. return this;
  858. },
  859. /**
  860. * 页面滚动时工具栏的固定定位
  861. * Set toolbar in window scroll auto fixed position
  862. *
  863. * @returns {editormd} 返回editormd的实例对象
  864. */
  865. setToolbarAutoFixed : function(fixed) {
  866. var state = this.state;
  867. var editor = this.editor;
  868. var toolbar = this.toolbar;
  869. var settings = this.settings;
  870. if (typeof fixed !== "undefined")
  871. {
  872. settings.toolbarAutoFixed = fixed;
  873. }
  874. var autoFixedHandle = function(){
  875. var $window = $(window);
  876. var top = $window.scrollTop();
  877. if (!settings.toolbarAutoFixed)
  878. {
  879. return false;
  880. }
  881. if (top - editor.offset().top > 10 && top < editor.height())
  882. {
  883. toolbar.css({
  884. position : "fixed",
  885. width : editor.width() + "px",
  886. left : ($window.width() - editor.width()) / 2 + "px"
  887. });
  888. }
  889. else
  890. {
  891. toolbar.css({
  892. position : "absolute",
  893. width : "100%",
  894. left : 0
  895. });
  896. }
  897. };
  898. if (!state.fullscreen && !state.preview && settings.toolbar && settings.toolbarAutoFixed)
  899. {
  900. $(window).bind("scroll", autoFixedHandle);
  901. }
  902. return this;
  903. },
  904. /**
  905. * 配置和初始化工具栏
  906. * Set toolbar and Initialization
  907. *
  908. * @returns {editormd} 返回editormd的实例对象
  909. */
  910. setToolbar : function() {
  911. var settings = this.settings;
  912. if(settings.readOnly) {
  913. return this;
  914. }
  915. var editor = this.editor;
  916. var preview = this.preview;
  917. var classPrefix = this.classPrefix;
  918. var toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar");
  919. if (settings.toolbar && toolbar.length < 1)
  920. {
  921. var toolbarHTML = "<div class=\"" + classPrefix + "toolbar\"><div class=\"" + classPrefix + "toolbar-container\"><ul class=\"" + classPrefix + "menu\"></ul></div></div>";
  922. editor.append(toolbarHTML);
  923. toolbar = this.toolbar = editor.children("." + classPrefix + "toolbar");
  924. }
  925. if (!settings.toolbar)
  926. {
  927. toolbar.hide();
  928. return this;
  929. }
  930. toolbar.show();
  931. var icons = (typeof settings.toolbarIcons === "function") ? settings.toolbarIcons()
  932. : ((typeof settings.toolbarIcons === "string") ? editormd.toolbarModes[settings.toolbarIcons] : settings.toolbarIcons);
  933. var toolbarMenu = toolbar.find("." + this.classPrefix + "menu"), menu = "";
  934. var pullRight = false;
  935. for (var i = 0, len = icons.length; i < len; i++)
  936. {
  937. var name = icons[i];
  938. if (name === "||")
  939. {
  940. pullRight = true;
  941. }
  942. else if (name === "|")
  943. {
  944. menu += "<li class=\"divider\" unselectable=\"on\">|</li>";
  945. }
  946. else
  947. {
  948. var isHeader = (/h(\d)/.test(name));
  949. var index = name;
  950. if (name === "watch" && !settings.watch) {
  951. index = "unwatch";
  952. }
  953. var title = settings.lang.toolbar[index];
  954. var iconTexts = settings.toolbarIconTexts[index];
  955. var iconClass = settings.toolbarIconsClass[index];
  956. title = (typeof title === "undefined") ? "" : title;
  957. iconTexts = (typeof iconTexts === "undefined") ? "" : iconTexts;
  958. iconClass = (typeof iconClass === "undefined") ? "" : iconClass;
  959. var menuItem = pullRight ? "<li class=\"pull-right\">" : "<li>";
  960. if (typeof settings.toolbarCustomIcons[name] !== "undefined" && typeof settings.toolbarCustomIcons[name] !== "function")
  961. {
  962. menuItem += settings.toolbarCustomIcons[name];
  963. }
  964. else
  965. {
  966. menuItem += "<a href=\"javascript:;\" title=\"" + title + "\" unselectable=\"on\">";
  967. menuItem += "<i class=\"fa " + iconClass + "\" name=\""+name+"\" unselectable=\"on\">"+((isHeader) ? name.toUpperCase() : ( (iconClass === "") ? iconTexts : "") ) + "</i>";
  968. menuItem += "</a>";
  969. }
  970. menuItem += "</li>";
  971. menu = pullRight ? menuItem + menu : menu + menuItem;
  972. }
  973. }
  974. toolbarMenu.html(menu);
  975. toolbarMenu.find("[title=\"Lowercase\"]").attr("title", settings.lang.toolbar.lowercase);
  976. toolbarMenu.find("[title=\"ucwords\"]").attr("title", settings.lang.toolbar.ucwords);
  977. this.setToolbarHandler();
  978. this.setToolbarAutoFixed();
  979. return this;
  980. },
  981. /**
  982. * 工具栏图标事件处理对象序列
  983. * Get toolbar icons event handlers
  984. *
  985. * @param {Object} cm CodeMirror的实例对象
  986. * @param {String} name 要获取的事件处理器名称
  987. * @returns {Object} 返回处理对象序列
  988. */
  989. dialogLockScreen : function() {
  990. $.proxy(editormd.dialogLockScreen, this)();
  991. return this;
  992. },
  993. dialogShowMask : function(dialog) {
  994. $.proxy(editormd.dialogShowMask, this)(dialog);
  995. return this;
  996. },
  997. getToolbarHandles : function(name) {
  998. var toolbarHandlers = this.toolbarHandlers = editormd.toolbarHandlers;
  999. return (name && typeof toolbarIconHandlers[name] !== "undefined") ? toolbarHandlers[name] : toolbarHandlers;
  1000. },
  1001. /**
  1002. * 工具栏图标事件处理器
  1003. * Bind toolbar icons event handle
  1004. *
  1005. * @returns {editormd} 返回editormd的实例对象
  1006. */
  1007. setToolbarHandler : function() {
  1008. var _this = this;
  1009. var settings = this.settings;
  1010. if (!settings.toolbar || settings.readOnly) {
  1011. return this;
  1012. }
  1013. var toolbar = this.toolbar;
  1014. var cm = this.cm;
  1015. var classPrefix = this.classPrefix;
  1016. var toolbarIcons = this.toolbarIcons = toolbar.find("." + classPrefix + "menu > li > a");
  1017. var toolbarIconHandlers = this.getToolbarHandles();
  1018. toolbarIcons.bind(editormd.mouseOrTouch("click", "touchend"), function(event) {
  1019. var icon = $(this).children(".fa");
  1020. var name = icon.attr("name");
  1021. var cursor = cm.getCursor();
  1022. var selection = cm.getSelection();
  1023. if (name === "") {
  1024. return ;
  1025. }
  1026. _this.activeIcon = icon;
  1027. if (typeof toolbarIconHandlers[name] !== "undefined")
  1028. {
  1029. $.proxy(toolbarIconHandlers[name], _this)(cm);
  1030. }
  1031. else
  1032. {
  1033. if (typeof settings.toolbarHandlers[name] !== "undefined")
  1034. {
  1035. $.proxy(settings.toolbarHandlers[name], _this)(cm, icon, cursor, selection);
  1036. }
  1037. }
  1038. if (name !== "link" && name !== "reference-link" && name !== "image" && name !== "code-block" &&
  1039. name !== "preformatted-text" && name !== "watch" && name !== "preview" && name !== "search" && name !== "fullscreen" && name !== "info")
  1040. {
  1041. cm.focus();
  1042. }
  1043. return false;
  1044. });
  1045. return this;
  1046. },
  1047. /**
  1048. * 动态创建对话框
  1049. * Creating custom dialogs
  1050. *
  1051. * @param {Object} options 配置项键值对 Key/Value
  1052. * @returns {dialog} 返回创建的dialog的jQuery实例对象
  1053. */
  1054. createDialog : function(options) {
  1055. return $.proxy(editormd.createDialog, this)(options);
  1056. },
  1057. /**
  1058. * 创建关于Editor.md的对话框
  1059. * Create about Editor.md dialog
  1060. *
  1061. * @returns {editormd} 返回editormd的实例对象
  1062. */
  1063. createInfoDialog : function() {
  1064. var _this = this;
  1065. var editor = this.editor;
  1066. var classPrefix = this.classPrefix;
  1067. var infoDialogHTML = [
  1068. "<div class=\"" + classPrefix + "dialog " + classPrefix + "dialog-info\" style=\"\">",
  1069. "<div class=\"" + classPrefix + "dialog-container\">",
  1070. "<h1><i class=\"editormd-logo editormd-logo-lg editormd-logo-color\"></i> " + editormd.title + "<small>v" + editormd.version + "</small></h1>",
  1071. "<p>" + this.lang.description + "</p>",
  1072. "<p style=\"margin: 10px 0 20px 0;\"><a href=\"" + editormd.homePage + "\" target=\"_blank\">" + editormd.homePage + " <i class=\"fa fa-external-link\"></i></a></p>",
  1073. "<p style=\"font-size: 0.85em;\">Copyright &copy; 2015 <a href=\"https://github.com/pandao\" target=\"_blank\" class=\"hover-link\">Pandao</a>, The <a href=\"https://github.com/pandao/editor.md/blob/master/LICENSE\" target=\"_blank\" class=\"hover-link\">MIT</a> License.</p>",
  1074. "</div>",
  1075. "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>",
  1076. "</div>"
  1077. ].join("\n");
  1078. editor.append(infoDialogHTML);
  1079. var infoDialog = this.infoDialog = editor.children("." + classPrefix + "dialog-info");
  1080. infoDialog.find("." + classPrefix + "dialog-close").bind(editormd.mouseOrTouch("click", "touchend"), function() {
  1081. _this.hideInfoDialog();
  1082. });
  1083. infoDialog.css("border", (editormd.isIE8) ? "1px solid #ddd" : "").css("z-index", editormd.dialogZindex).show();
  1084. this.infoDialogPosition();
  1085. return this;
  1086. },
  1087. /**
  1088. * 关于Editor.md对话居中定位
  1089. * Editor.md dialog position handle
  1090. *
  1091. * @returns {editormd} 返回editormd的实例对象
  1092. */
  1093. infoDialogPosition : function() {
  1094. var infoDialog = this.infoDialog;
  1095. var _infoDialogPosition = function() {
  1096. infoDialog.css({
  1097. top : ($(window).height() - infoDialog.height()) / 2 + "px",
  1098. left : ($(window).width() - infoDialog.width()) / 2 + "px"
  1099. });
  1100. };
  1101. _infoDialogPosition();
  1102. $(window).resize(_infoDialogPosition);
  1103. return this;
  1104. },
  1105. /**
  1106. * 显示关于Editor.md
  1107. * Display about Editor.md dialog
  1108. *
  1109. * @returns {editormd} 返回editormd的实例对象
  1110. */
  1111. showInfoDialog : function() {
  1112. $("html,body").css("overflow-x", "hidden");
  1113. var _this = this;
  1114. var editor = this.editor;
  1115. var settings = this.settings;
  1116. var infoDialog = this.infoDialog = editor.children("." + this.classPrefix + "dialog-info");
  1117. if (infoDialog.length < 1)
  1118. {
  1119. this.createInfoDialog();
  1120. }
  1121. this.lockScreen(true);
  1122. this.mask.css({
  1123. opacity : settings.dialogMaskOpacity,
  1124. backgroundColor : settings.dialogMaskBgColor
  1125. }).show();
  1126. infoDialog.css("z-index", editormd.dialogZindex).show();
  1127. this.infoDialogPosition();
  1128. return this;
  1129. },
  1130. /**
  1131. * 隐藏关于Editor.md
  1132. * Hide about Editor.md dialog
  1133. *
  1134. * @returns {editormd} 返回editormd的实例对象
  1135. */
  1136. hideInfoDialog : function() {
  1137. $("html,body").css("overflow-x", "");
  1138. this.infoDialog.hide();
  1139. this.mask.hide();
  1140. this.lockScreen(false);
  1141. return this;
  1142. },
  1143. /**
  1144. * 锁屏
  1145. * lock screen
  1146. *
  1147. * @param {Boolean} lock Boolean 布尔值,是否锁屏
  1148. * @returns {editormd} 返回editormd的实例对象
  1149. */
  1150. lockScreen : function(lock) {
  1151. editormd.lockScreen(lock);
  1152. this.resize();
  1153. return this;
  1154. },
  1155. /**
  1156. * 编辑器界面重建,用于动态语言包或模块加载等
  1157. * Recreate editor
  1158. *
  1159. * @returns {editormd} 返回editormd的实例对象
  1160. */
  1161. recreate : function() {
  1162. var _this = this;
  1163. var editor = this.editor;
  1164. var settings = this.settings;
  1165. this.codeMirror.remove();
  1166. this.setCodeMirror();
  1167. if (!settings.readOnly)
  1168. {
  1169. if (editor.find(".editormd-dialog").length > 0) {
  1170. editor.find(".editormd-dialog").remove();
  1171. }
  1172. if (settings.toolbar)
  1173. {
  1174. this.getToolbarHandles();
  1175. this.setToolbar();
  1176. }
  1177. }
  1178. this.loadedDisplay(true);
  1179. return this;
  1180. },
  1181. /**
  1182. * 高亮预览HTML的pre代码部分
  1183. * highlight of preview codes
  1184. *
  1185. * @returns {editormd} 返回editormd的实例对象
  1186. */
  1187. previewCodeHighlight : function() {
  1188. var settings = this.settings;
  1189. var previewContainer = this.previewContainer;
  1190. if (settings.previewCodeHighlight)
  1191. {
  1192. previewContainer.find("pre").addClass("prettyprint linenums");
  1193. if (typeof prettyPrint !== "undefined")
  1194. {
  1195. prettyPrint();
  1196. }
  1197. }
  1198. return this;
  1199. },
  1200. /**
  1201. * 解析TeX(KaTeX)科学公式
  1202. * TeX(KaTeX) Renderer
  1203. *
  1204. * @returns {editormd} 返回editormd的实例对象
  1205. */
  1206. katexRender : function() {
  1207. if (timer === null)
  1208. {
  1209. return this;
  1210. }
  1211. this.previewContainer.find("." + editormd.classNames.tex).each(function(){
  1212. var tex = $(this);
  1213. editormd.$katex.render(tex.text(), tex[0]);
  1214. tex.find(".katex").css("font-size", "1.6em");
  1215. });
  1216. return this;
  1217. },
  1218. /**
  1219. * 解析和渲染流程图及时序图
  1220. * FlowChart and SequenceDiagram Renderer
  1221. *
  1222. * @returns {editormd} 返回editormd的实例对象
  1223. */
  1224. flowChartAndSequenceDiagramRender : function() {
  1225. var $this = this;
  1226. var settings = this.settings;
  1227. var previewContainer = this.previewContainer;
  1228. if (editormd.isIE8) {
  1229. return this;
  1230. }
  1231. if (settings.flowChart) {
  1232. if (flowchartTimer === null) {
  1233. return this;
  1234. }
  1235. previewContainer.find(".flowchart").flowChart();
  1236. }
  1237. if (settings.sequenceDiagram) {
  1238. previewContainer.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
  1239. }
  1240. var preview = $this.preview;
  1241. var codeMirror = $this.codeMirror;
  1242. var codeView = codeMirror.find(".CodeMirror-scroll");
  1243. var height = codeView.height();
  1244. var scrollTop = codeView.scrollTop();
  1245. var percent = (scrollTop / codeView[0].scrollHeight);
  1246. var tocHeight = 0;
  1247. preview.find(".markdown-toc-list").each(function(){
  1248. tocHeight += $(this).height();
  1249. });
  1250. var tocMenuHeight = preview.find(".editormd-toc-menu").height();
  1251. tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
  1252. if (scrollTop === 0)
  1253. {
  1254. preview.scrollTop(0);
  1255. }
  1256. else if (scrollTop + height >= codeView[0].scrollHeight - 16)
  1257. {
  1258. preview.scrollTop(preview[0].scrollHeight);
  1259. }
  1260. else
  1261. {
  1262. preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent);
  1263. }
  1264. return this;
  1265. },
  1266. /**
  1267. * 注册键盘快捷键处理
  1268. * Register CodeMirror keyMaps (keyboard shortcuts).
  1269. *
  1270. * @param {Object} keyMap KeyMap key/value {"(Ctrl/Shift/Alt)-Key" : function(){}}
  1271. * @returns {editormd} return this
  1272. */
  1273. registerKeyMaps : function(keyMap) {
  1274. var _this = this;
  1275. var cm = this.cm;
  1276. var settings = this.settings;
  1277. var toolbarHandlers = editormd.toolbarHandlers;
  1278. var disabledKeyMaps = settings.disabledKeyMaps;
  1279. keyMap = keyMap || null;
  1280. if (keyMap)
  1281. {
  1282. for (var i in keyMap)
  1283. {
  1284. if ($.inArray(i, disabledKeyMaps) < 0)
  1285. {
  1286. var map = {};
  1287. map[i] = keyMap[i];
  1288. cm.addKeyMap(keyMap);
  1289. }
  1290. }
  1291. }
  1292. else
  1293. {
  1294. for (var k in editormd.keyMaps)
  1295. {
  1296. var _keyMap = editormd.keyMaps[k];
  1297. var handle = (typeof _keyMap === "string") ? $.proxy(toolbarHandlers[_keyMap], _this) : $.proxy(_keyMap, _this);
  1298. if ($.inArray(k, ["F9", "F10", "F11"]) < 0 && $.inArray(k, disabledKeyMaps) < 0)
  1299. {
  1300. var _map = {};
  1301. _map[k] = handle;
  1302. cm.addKeyMap(_map);
  1303. }
  1304. }
  1305. $(window).keydown(function(event) {
  1306. var keymaps = {
  1307. "120" : "F9",
  1308. "121" : "F10",
  1309. "122" : "F11"
  1310. };
  1311. if ( $.inArray(keymaps[event.keyCode], disabledKeyMaps) < 0 )
  1312. {
  1313. switch (event.keyCode)
  1314. {
  1315. case 120:
  1316. $.proxy(toolbarHandlers["watch"], _this)();
  1317. return false;
  1318. break;
  1319. case 121:
  1320. $.proxy(toolbarHandlers["preview"], _this)();
  1321. return false;
  1322. break;
  1323. case 122:
  1324. $.proxy(toolbarHandlers["fullscreen"], _this)();
  1325. return false;
  1326. break;
  1327. default:
  1328. break;
  1329. }
  1330. }
  1331. });
  1332. }
  1333. return this;
  1334. },
  1335. /**
  1336. * 绑定同步滚动
  1337. *
  1338. * @returns {editormd} return this
  1339. */
  1340. bindScrollEvent : function() {
  1341. var _this = this;
  1342. var preview = this.preview;
  1343. var settings = this.settings;
  1344. var codeMirror = this.codeMirror;
  1345. var mouseOrTouch = editormd.mouseOrTouch;
  1346. if (!settings.syncScrolling) {
  1347. return this;
  1348. }
  1349. var cmBindScroll = function() {
  1350. codeMirror.find(".CodeMirror-scroll").bind(mouseOrTouch("scroll", "touchmove"), function(event) {
  1351. var height = $(this).height();
  1352. var scrollTop = $(this).scrollTop();
  1353. var percent = (scrollTop / $(this)[0].scrollHeight);
  1354. var tocHeight = 0;
  1355. preview.find(".markdown-toc-list").each(function(){
  1356. tocHeight += $(this).height();
  1357. });
  1358. var tocMenuHeight = preview.find(".editormd-toc-menu").height();
  1359. tocMenuHeight = (!tocMenuHeight) ? 0 : tocMenuHeight;
  1360. if (scrollTop === 0)
  1361. {
  1362. preview.scrollTop(0);
  1363. }
  1364. else if (scrollTop + height >= $(this)[0].scrollHeight - 16)
  1365. {
  1366. preview.scrollTop(preview[0].scrollHeight);
  1367. }
  1368. else
  1369. {
  1370. preview.scrollTop((preview[0].scrollHeight + tocHeight + tocMenuHeight) * percent);
  1371. }
  1372. $.proxy(settings.onscroll, _this)(event);
  1373. });
  1374. };
  1375. var cmUnbindScroll = function() {
  1376. codeMirror.find(".CodeMirror-scroll").unbind(mouseOrTouch("scroll", "touchmove"));
  1377. };
  1378. var previewBindScroll = function() {
  1379. preview.bind(mouseOrTouch("scroll", "touchmove"), function(event) {
  1380. var height = $(this).height();
  1381. var scrollTop = $(this).scrollTop();
  1382. var percent = (scrollTop / $(this)[0].scrollHeight);
  1383. var codeView = codeMirror.find(".CodeMirror-scroll");
  1384. if(scrollTop === 0)
  1385. {
  1386. codeView.scrollTop(0);
  1387. }
  1388. else if (scrollTop + height >= $(this)[0].scrollHeight)
  1389. {
  1390. codeView.scrollTop(codeView[0].scrollHeight);
  1391. }
  1392. else
  1393. {
  1394. codeView.scrollTop(codeView[0].scrollHeight * percent);
  1395. }
  1396. $.proxy(settings.onpreviewscroll, _this)(event);
  1397. });
  1398. };
  1399. var previewUnbindScroll = function() {
  1400. preview.unbind(mouseOrTouch("scroll", "touchmove"));
  1401. };
  1402. codeMirror.bind({
  1403. mouseover : cmBindScroll,
  1404. mouseout : cmUnbindScroll,
  1405. touchstart : cmBindScroll,
  1406. touchend : cmUnbindScroll
  1407. });
  1408. if (settings.syncScrolling === "single") {
  1409. return this;
  1410. }
  1411. preview.bind({
  1412. mouseover : previewBindScroll,
  1413. mouseout : previewUnbindScroll,
  1414. touchstart : previewBindScroll,
  1415. touchend : previewUnbindScroll
  1416. });
  1417. return this;
  1418. },
  1419. bindChangeEvent : function() {
  1420. var _this = this;
  1421. var cm = this.cm;
  1422. var settings = this.settings;
  1423. if (!settings.syncScrolling) {
  1424. return this;
  1425. }
  1426. cm.on("change", function(_cm, changeObj) {
  1427. if (settings.watch)
  1428. {
  1429. _this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
  1430. }
  1431. timer = setTimeout(function() {
  1432. clearTimeout(timer);
  1433. _this.save();
  1434. timer = null;
  1435. }, settings.delay);
  1436. });
  1437. return this;
  1438. },
  1439. /**
  1440. * 加载队列完成之后的显示处理
  1441. * Display handle of the module queues loaded after.
  1442. *
  1443. * @param {Boolean} recreate 是否为重建编辑器
  1444. * @returns {editormd} 返回editormd的实例对象
  1445. */
  1446. loadedDisplay : function(recreate) {
  1447. recreate = recreate || false;
  1448. var _this = this;
  1449. var editor = this.editor;
  1450. var preview = this.preview;
  1451. var settings = this.settings;
  1452. this.containerMask.hide();
  1453. this.save();
  1454. if (settings.watch) {
  1455. preview.show();
  1456. }
  1457. editor.data("oldWidth", editor.width()).data("oldHeight", editor.height()); // 为了兼容Zepto
  1458. this.resize();
  1459. this.registerKeyMaps();
  1460. $(window).resize(function(){
  1461. _this.resize();
  1462. });
  1463. this.bindScrollEvent().bindChangeEvent();
  1464. if (!recreate)
  1465. {
  1466. $.proxy(settings.onload, this)();
  1467. }
  1468. this.state.loaded = true;
  1469. return this;
  1470. },
  1471. /**
  1472. * 设置编辑器的宽度
  1473. * Set editor width
  1474. *
  1475. * @param {Number|String} width 编辑器宽度值
  1476. * @returns {editormd} 返回editormd的实例对象
  1477. */
  1478. width : function(width) {
  1479. this.editor.css("width", (typeof width === "number") ? width + "px" : width);
  1480. this.resize();
  1481. return this;
  1482. },
  1483. /**
  1484. * 设置编辑器的高度
  1485. * Set editor height
  1486. *
  1487. * @param {Number|String} height 编辑器高度值
  1488. * @returns {editormd} 返回editormd的实例对象
  1489. */
  1490. height : function(height) {
  1491. this.editor.css("height", (typeof height === "number") ? height + "px" : height);
  1492. this.resize();
  1493. return this;
  1494. },
  1495. /**
  1496. * 调整编辑器的尺寸和布局
  1497. * Resize editor layout
  1498. *
  1499. * @param {Number|String} [width=null] 编辑器宽度值
  1500. * @param {Number|String} [height=null] 编辑器高度值
  1501. * @returns {editormd} 返回editormd的实例对象
  1502. */
  1503. resize : function(width, height) {
  1504. width = width || null;
  1505. height = height || null;
  1506. var state = this.state;
  1507. var editor = this.editor;
  1508. var preview = this.preview;
  1509. var toolbar = this.toolbar;
  1510. var settings = this.settings;
  1511. var codeMirror = this.codeMirror;
  1512. if (width)
  1513. {
  1514. editor.css("width", (typeof width === "number") ? width + "px" : width);
  1515. }
  1516. if (settings.autoHeight && !state.fullscreen && !state.preview)
  1517. {
  1518. editor.css("height", "auto");
  1519. codeMirror.css("height", "auto");
  1520. }
  1521. else
  1522. {
  1523. if (height)
  1524. {
  1525. editor.css("height", (typeof height === "number") ? height + "px" : height);
  1526. }
  1527. if (state.fullscreen)
  1528. {
  1529. editor.height($(window).height());
  1530. }
  1531. if (settings.toolbar && !settings.readOnly)
  1532. {
  1533. codeMirror.css("margin-top", toolbar.height() + 1).height(editor.height() - toolbar.height());
  1534. }
  1535. else
  1536. {
  1537. codeMirror.css("margin-top", 0).height(editor.height());
  1538. }
  1539. }
  1540. if(settings.watch)
  1541. {
  1542. codeMirror.width(editor.width() / 2);
  1543. preview.width((!state.preview) ? editor.width() / 2 : editor.width());
  1544. this.previewContainer.css("padding", settings.autoHeight ? "20px 20px 50px 40px" : "20px");
  1545. if (settings.toolbar && !settings.readOnly)
  1546. {
  1547. preview.css("top", toolbar.height() + 1);
  1548. }
  1549. else
  1550. {
  1551. preview.css("top", 0);
  1552. }
  1553. if (settings.autoHeight && !state.fullscreen && !state.preview)
  1554. {
  1555. preview.height("");
  1556. }
  1557. else
  1558. {
  1559. var previewHeight = (settings.toolbar && !settings.readOnly) ? editor.height() - toolbar.height() : editor.height();
  1560. preview.height(previewHeight);
  1561. }
  1562. }
  1563. else
  1564. {
  1565. codeMirror.width(editor.width());
  1566. preview.hide();
  1567. }
  1568. if (state.loaded)
  1569. {
  1570. $.proxy(settings.onresize, this)();
  1571. }
  1572. return this;
  1573. },
  1574. /**
  1575. * 解析和保存Markdown代码
  1576. * Parse & Saving Markdown source code
  1577. *
  1578. * @returns {editormd} 返回editormd的实例对象
  1579. */
  1580. save : function() {
  1581. if (timer === null)
  1582. {
  1583. return this;
  1584. }
  1585. var _this = this;
  1586. var state = this.state;
  1587. var settings = this.settings;
  1588. var cm = this.cm;
  1589. var cmValue = cm.getValue();
  1590. var previewContainer = this.previewContainer;
  1591. if (settings.mode !== "gfm" && settings.mode !== "markdown")
  1592. {
  1593. this.markdownTextarea.val(cmValue);
  1594. return this;
  1595. }
  1596. var marked = editormd.$marked;
  1597. var markdownToC = this.markdownToC = [];
  1598. var rendererOptions = this.markedRendererOptions = {
  1599. toc : settings.toc,
  1600. tocm : settings.tocm,
  1601. tocStartLevel : settings.tocStartLevel,
  1602. pageBreak : settings.pageBreak,
  1603. taskList : settings.taskList,
  1604. emoji : settings.emoji,
  1605. tex : settings.tex,
  1606. atLink : settings.atLink, // for @link
  1607. emailLink : settings.emailLink, // for mail address auto link
  1608. flowChart : settings.flowChart,
  1609. sequenceDiagram : settings.sequenceDiagram,
  1610. previewCodeHighlight : settings.previewCodeHighlight,
  1611. };
  1612. var markedOptions = this.markedOptions = {
  1613. renderer : editormd.markedRenderer(markdownToC, rendererOptions),
  1614. gfm : true,
  1615. tables : true,
  1616. breaks : true,
  1617. pedantic : false,
  1618. sanitize : (settings.htmlDecode) ? false : true, // 关闭忽略HTML标签,即开启识别HTML标签,默认为false
  1619. smartLists : true,
  1620. smartypants : true
  1621. };
  1622. marked.setOptions(markedOptions);
  1623. var newMarkdownDoc = editormd.$marked(cmValue, markedOptions);
  1624. //console.info("cmValue", cmValue, newMarkdownDoc);
  1625. newMarkdownDoc = editormd.filterHTMLTags(newMarkdownDoc, settings.htmlDecode);
  1626. //console.error("cmValue", cmValue, newMarkdownDoc);
  1627. this.markdownTextarea.text(cmValue);
  1628. cm.save();
  1629. if (settings.saveHTMLToTextarea)
  1630. {
  1631. this.htmlTextarea.text(newMarkdownDoc);
  1632. }
  1633. if(settings.watch || (!settings.watch && state.preview))
  1634. {
  1635. previewContainer.html(newMarkdownDoc);
  1636. this.previewCodeHighlight();
  1637. if (settings.toc)
  1638. {
  1639. var tocContainer = (settings.tocContainer === "") ? previewContainer : $(settings.tocContainer);
  1640. var tocMenu = tocContainer.find("." + this.classPrefix + "toc-menu");
  1641. tocContainer.attr("previewContainer", (settings.tocContainer === "") ? "true" : "false");
  1642. if (settings.tocContainer !== "" && tocMenu.length > 0)
  1643. {
  1644. tocMenu.remove();
  1645. }
  1646. editormd.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
  1647. if (settings.tocDropdown || tocContainer.find("." + this.classPrefix + "toc-menu").length > 0)
  1648. {
  1649. editormd.tocDropdownMenu(tocContainer, (settings.tocTitle !== "") ? settings.tocTitle : this.lang.tocTitle);
  1650. }
  1651. if (settings.tocContainer !== "")
  1652. {
  1653. previewContainer.find(".markdown-toc").css("border", "none");
  1654. }
  1655. }
  1656. if (settings.tex)
  1657. {
  1658. if (!editormd.kaTeXLoaded && settings.autoLoadModules)
  1659. {
  1660. editormd.loadKaTeX(function() {
  1661. editormd.$katex = katex;
  1662. editormd.kaTeXLoaded = true;
  1663. _this.katexRender();
  1664. });
  1665. }
  1666. else
  1667. {
  1668. editormd.$katex = katex;
  1669. this.katexRender();
  1670. }
  1671. }
  1672. if (settings.flowChart || settings.sequenceDiagram)
  1673. {
  1674. flowchartTimer = setTimeout(function(){
  1675. clearTimeout(flowchartTimer);
  1676. _this.flowChartAndSequenceDiagramRender();
  1677. flowchartTimer = null;
  1678. }, 10);
  1679. }
  1680. if (state.loaded)
  1681. {
  1682. $.proxy(settings.onchange, this)();
  1683. }
  1684. }
  1685. return this;
  1686. },
  1687. /**
  1688. * 聚焦光标位置
  1689. * Focusing the cursor position
  1690. *
  1691. * @returns {editormd} 返回editormd的实例对象
  1692. */
  1693. focus : function() {
  1694. this.cm.focus();
  1695. return this;
  1696. },
  1697. /**
  1698. * 设置光标的位置
  1699. * Set cursor position
  1700. *
  1701. * @param {Object} cursor 要设置的光标位置键值对象,例:{line:1, ch:0}
  1702. * @returns {editormd} 返回editormd的实例对象
  1703. */
  1704. setCursor : function(cursor) {
  1705. this.cm.setCursor(cursor);
  1706. return this;
  1707. },
  1708. /**
  1709. * 获取当前光标的位置
  1710. * Get the current position of the cursor
  1711. *
  1712. * @returns {Cursor} 返回一个光标Cursor对象
  1713. */
  1714. getCursor : function() {
  1715. return this.cm.getCursor();
  1716. },
  1717. /**
  1718. * 设置光标选中的范围
  1719. * Set cursor selected ranges
  1720. *
  1721. * @param {Object} from 开始位置的光标键值对象,例:{line:1, ch:0}
  1722. * @param {Object} to 结束位置的光标键值对象,例:{line:1, ch:0}
  1723. * @returns {editormd} 返回editormd的实例对象
  1724. */
  1725. setSelection : function(from, to) {
  1726. this.cm.setSelection(from, to);
  1727. return this;
  1728. },
  1729. /**
  1730. * 获取光标选中的文本
  1731. * Get the texts from cursor selected
  1732. *
  1733. * @returns {String} 返回选中文本的字符串形式
  1734. */
  1735. getSelection : function() {
  1736. return this.cm.getSelection();
  1737. },
  1738. /**
  1739. * 设置光标选中的文本范围
  1740. * Set the cursor selection ranges
  1741. *
  1742. * @param {Array} ranges cursor selection ranges array
  1743. * @returns {Array} return this
  1744. */
  1745. setSelections : function(ranges) {
  1746. this.cm.setSelections(ranges);
  1747. return this;
  1748. },
  1749. /**
  1750. * 获取光标选中的文本范围
  1751. * Get the cursor selection ranges
  1752. *
  1753. * @returns {Array} return selection ranges array
  1754. */
  1755. getSelections : function() {
  1756. return this.cm.getSelections();
  1757. },
  1758. /**
  1759. * 替换当前光标选中的文本或在当前光标处插入新字符
  1760. * Replace the text at the current cursor selected or insert a new character at the current cursor position
  1761. *
  1762. * @param {String} value 要插入的字符值
  1763. * @returns {editormd} 返回editormd的实例对象
  1764. */
  1765. replaceSelection : function(value) {
  1766. this.cm.replaceSelection(value);
  1767. return this;
  1768. },
  1769. /**
  1770. * 在当前光标处插入新字符
  1771. * Insert a new character at the current cursor position
  1772. *
  1773. * 同replaceSelection()方法
  1774. * With the replaceSelection() method
  1775. *
  1776. * @param {String} value 要插入的字符值
  1777. * @returns {editormd} 返回editormd的实例对象
  1778. */
  1779. insertValue : function(value) {
  1780. this.replaceSelection(value);
  1781. return this;
  1782. },
  1783. /**
  1784. * 追加markdown
  1785. * append Markdown to editor
  1786. *
  1787. * @param {String} md 要追加的markdown源文档
  1788. * @returns {editormd} 返回editormd的实例对象
  1789. */
  1790. appendMarkdown : function(md) {
  1791. var settings = this.settings;
  1792. var cm = this.cm;
  1793. cm.setValue(cm.getValue() + md);
  1794. return this;
  1795. },
  1796. /**
  1797. * 设置和传入编辑器的markdown源文档
  1798. * Set Markdown source document
  1799. *
  1800. * @param {String} md 要传入的markdown源文档
  1801. * @returns {editormd} 返回editormd的实例对象
  1802. */
  1803. setMarkdown : function(md) {
  1804. this.cm.setValue(md || this.settings.markdown);
  1805. return this;
  1806. },
  1807. /**
  1808. * 获取编辑器的markdown源文档
  1809. * Set Editor.md markdown/CodeMirror value
  1810. *
  1811. * @returns {editormd} 返回editormd的实例对象
  1812. */
  1813. getMarkdown : function() {
  1814. return this.cm.getValue();
  1815. },
  1816. /**
  1817. * 获取编辑器的源文档
  1818. * Get CodeMirror value
  1819. *
  1820. * @returns {editormd} 返回editormd的实例对象
  1821. */
  1822. getValue : function() {
  1823. return this.cm.getValue();
  1824. },
  1825. /**
  1826. * 设置编辑器的源文档
  1827. * Set CodeMirror value
  1828. *
  1829. * @param {String} value set code/value/string/text
  1830. * @returns {editormd} 返回editormd的实例对象
  1831. */
  1832. setValue : function(value) {
  1833. this.cm.setValue(value);
  1834. return this;
  1835. },
  1836. /**
  1837. * 清空编辑器
  1838. * Empty CodeMirror editor container
  1839. *
  1840. * @returns {editormd} 返回editormd的实例对象
  1841. */
  1842. clear : function() {
  1843. this.cm.setValue("");
  1844. return this;
  1845. },
  1846. /**
  1847. * 获取解析后存放在Textarea的HTML源码
  1848. * Get parsed html code from Textarea
  1849. *
  1850. * @returns {String} 返回HTML源码
  1851. */
  1852. getHTML : function() {
  1853. if (!this.settings.saveHTMLToTextarea)
  1854. {
  1855. alert("Error: settings.saveHTMLToTextarea == false");
  1856. return false;
  1857. }
  1858. return this.htmlTextarea.val();
  1859. },
  1860. /**
  1861. * getHTML()的别名
  1862. * getHTML (alias)
  1863. *
  1864. * @returns {String} Return html code 返回HTML源码
  1865. */
  1866. getTextareaSavedHTML : function() {
  1867. return this.getHTML();
  1868. },
  1869. /**
  1870. * 获取预览窗口的HTML源码
  1871. * Get html from preview container
  1872. *
  1873. * @returns {editormd} 返回editormd的实例对象
  1874. */
  1875. getPreviewedHTML : function() {
  1876. if (!this.settings.watch)
  1877. {
  1878. alert("Error: settings.watch == false");
  1879. return false;
  1880. }
  1881. return this.previewContainer.html();
  1882. },
  1883. /**
  1884. * 开启实时预览
  1885. * Enable real-time watching
  1886. *
  1887. * @returns {editormd} 返回editormd的实例对象
  1888. */
  1889. watch : function(callback) {
  1890. var settings = this.settings;
  1891. if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0)
  1892. {
  1893. return this;
  1894. }
  1895. this.state.watching = settings.watch = true;
  1896. this.preview.show();
  1897. if (this.toolbar)
  1898. {
  1899. var watchIcon = settings.toolbarIconsClass.watch;
  1900. var unWatchIcon = settings.toolbarIconsClass.unwatch;
  1901. var icon = this.toolbar.find(".fa[name=watch]");
  1902. icon.parent().attr("title", settings.lang.toolbar.watch);
  1903. icon.removeClass(unWatchIcon).addClass(watchIcon);
  1904. }
  1905. this.codeMirror.css("border-right", "1px solid #ddd").width(this.editor.width() / 2);
  1906. timer = 0;
  1907. this.save().resize();
  1908. if (!settings.onwatch)
  1909. {
  1910. settings.onwatch = callback || function() {};
  1911. }
  1912. $.proxy(settings.onwatch, this)();
  1913. return this;
  1914. },
  1915. /**
  1916. * 关闭实时预览
  1917. * Disable real-time watching
  1918. *
  1919. * @returns {editormd} 返回editormd的实例对象
  1920. */
  1921. unwatch : function(callback) {
  1922. var settings = this.settings;
  1923. this.state.watching = settings.watch = false;
  1924. this.preview.hide();
  1925. if (this.toolbar)
  1926. {
  1927. var watchIcon = settings.toolbarIconsClass.watch;
  1928. var unWatchIcon = settings.toolbarIconsClass.unwatch;
  1929. var icon = this.toolbar.find(".fa[name=watch]");
  1930. icon.parent().attr("title", settings.lang.toolbar.unwatch);
  1931. icon.removeClass(watchIcon).addClass(unWatchIcon);
  1932. }
  1933. this.codeMirror.css("border-right", "none").width(this.editor.width());
  1934. this.resize();
  1935. if (!settings.onunwatch)
  1936. {
  1937. settings.onunwatch = callback || function() {};
  1938. }
  1939. $.proxy(settings.onunwatch, this)();
  1940. return this;
  1941. },
  1942. /**
  1943. * 显示编辑器
  1944. * Show editor
  1945. *
  1946. * @param {Function} [callback=function()] 回调函数
  1947. * @returns {editormd} 返回editormd的实例对象
  1948. */
  1949. show : function(callback) {
  1950. callback = callback || function() {};
  1951. var _this = this;
  1952. this.editor.show(0, function() {
  1953. $.proxy(callback, _this)();
  1954. });
  1955. return this;
  1956. },
  1957. /**
  1958. * 隐藏编辑器
  1959. * Hide editor
  1960. *
  1961. * @param {Function} [callback=function()] 回调函数
  1962. * @returns {editormd} 返回editormd的实例对象
  1963. */
  1964. hide : function(callback) {
  1965. callback = callback || function() {};
  1966. var _this = this;
  1967. this.editor.hide(0, function() {
  1968. $.proxy(callback, _this)();
  1969. });
  1970. return this;
  1971. },
  1972. /**
  1973. * 隐藏编辑器部分,只预览HTML
  1974. * Enter preview html state
  1975. *
  1976. * @returns {editormd} 返回editormd的实例对象
  1977. */
  1978. previewing : function() {
  1979. var _this = this;
  1980. var editor = this.editor;
  1981. var preview = this.preview;
  1982. var toolbar = this.toolbar;
  1983. var settings = this.settings;
  1984. var codeMirror = this.codeMirror;
  1985. var previewContainer = this.previewContainer;
  1986. if ($.inArray(settings.mode, ["gfm", "markdown"]) < 0) {
  1987. return this;
  1988. }
  1989. if (settings.toolbar && toolbar) {
  1990. toolbar.toggle();
  1991. toolbar.find(".fa[name=preview]").toggleClass("active");
  1992. }
  1993. codeMirror.toggle();
  1994. var escHandle = function(event) {
  1995. if (event.shiftKey && event.keyCode === 27) {
  1996. _this.previewed();
  1997. }
  1998. };
  1999. if (codeMirror.css("display") === "none") // 为了兼容Zepto,而不使用codeMirror.is(":hidden")
  2000. {
  2001. this.state.preview = true;
  2002. if (this.state.fullscreen) {
  2003. preview.css("background", "#fff");
  2004. }
  2005. editor.find("." + this.classPrefix + "preview-close-btn").show().bind(editormd.mouseOrTouch("click", "touchend"), function(){
  2006. _this.previewed();
  2007. });
  2008. if (!settings.watch)
  2009. {
  2010. this.save();
  2011. }
  2012. else
  2013. {
  2014. previewContainer.css("padding", "");
  2015. }
  2016. previewContainer.addClass(this.classPrefix + "preview-active");
  2017. preview.show().css({
  2018. position : "",
  2019. top : 0,
  2020. width : editor.width(),
  2021. height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height()
  2022. });
  2023. if (this.state.loaded)
  2024. {
  2025. $.proxy(settings.onpreviewing, this)();
  2026. }
  2027. $(window).bind("keyup", escHandle);
  2028. }
  2029. else
  2030. {
  2031. $(window).unbind("keyup", escHandle);
  2032. this.previewed();
  2033. }
  2034. },
  2035. /**
  2036. * 显示编辑器部分,退出只预览HTML
  2037. * Exit preview html state
  2038. *
  2039. * @returns {editormd} 返回editormd的实例对象
  2040. */
  2041. previewed : function() {
  2042. var editor = this.editor;
  2043. var preview = this.preview;
  2044. var toolbar = this.toolbar;
  2045. var settings = this.settings;
  2046. var previewContainer = this.previewContainer;
  2047. var previewCloseBtn = editor.find("." + this.classPrefix + "preview-close-btn");
  2048. this.state.preview = false;
  2049. this.codeMirror.show();
  2050. if (settings.toolbar) {
  2051. toolbar.show();
  2052. }
  2053. preview[(settings.watch) ? "show" : "hide"]();
  2054. previewCloseBtn.hide().unbind(editormd.mouseOrTouch("click", "touchend"));
  2055. previewContainer.removeClass(this.classPrefix + "preview-active");
  2056. if (settings.watch)
  2057. {
  2058. previewContainer.css("padding", "20px");
  2059. }
  2060. preview.css({
  2061. background : null,
  2062. position : "absolute",
  2063. width : editor.width() / 2,
  2064. height : (settings.autoHeight && !this.state.fullscreen) ? "auto" : editor.height() - toolbar.height(),
  2065. top : (settings.toolbar) ? toolbar.height() : 0
  2066. });
  2067. if (this.state.loaded)
  2068. {
  2069. $.proxy(settings.onpreviewed, this)();
  2070. }
  2071. return this;
  2072. },
  2073. /**
  2074. * 编辑器全屏显示
  2075. * Fullscreen show
  2076. *
  2077. * @returns {editormd} 返回editormd的实例对象
  2078. */
  2079. fullscreen : function() {
  2080. var _this = this;
  2081. var state = this.state;
  2082. var editor = this.editor;
  2083. var preview = this.preview;
  2084. var toolbar = this.toolbar;
  2085. var settings = this.settings;
  2086. var fullscreenClass = this.classPrefix + "fullscreen";
  2087. if (toolbar) {
  2088. toolbar.find(".fa[name=fullscreen]").parent().toggleClass("active");
  2089. }
  2090. var escHandle = function(event) {
  2091. if (!event.shiftKey && event.keyCode === 27)
  2092. {
  2093. if (state.fullscreen)
  2094. {
  2095. _this.fullscreenExit();
  2096. }
  2097. }
  2098. };
  2099. if (!editor.hasClass(fullscreenClass))
  2100. {
  2101. state.fullscreen = true;
  2102. $("html,body").css("overflow", "hidden");
  2103. editor.css({
  2104. width : $(window).width(),
  2105. height : $(window).height()
  2106. }).addClass(fullscreenClass);
  2107. this.resize();
  2108. $.proxy(settings.onfullscreen, this)();
  2109. $(window).bind("keyup", escHandle);
  2110. }
  2111. else
  2112. {
  2113. $(window).unbind("keyup", escHandle);
  2114. this.fullscreenExit();
  2115. }
  2116. return this;
  2117. },
  2118. /**
  2119. * 编辑器退出全屏显示
  2120. * Exit fullscreen state
  2121. *
  2122. * @returns {editormd} 返回editormd的实例对象
  2123. */
  2124. fullscreenExit : function() {
  2125. var editor = this.editor;
  2126. var settings = this.settings;
  2127. var toolbar = this.toolbar;
  2128. var fullscreenClass = this.classPrefix + "fullscreen";
  2129. this.state.fullscreen = false;
  2130. if (toolbar) {
  2131. toolbar.find(".fa[name=fullscreen]").parent().removeClass("active");
  2132. }
  2133. $("html,body").css("overflow", "");
  2134. editor.css({
  2135. width : editor.data("oldWidth"),
  2136. height : editor.data("oldHeight")
  2137. }).removeClass(fullscreenClass);
  2138. this.resize();
  2139. $.proxy(settings.onfullscreenExit, this)();
  2140. return this;
  2141. },
  2142. /**
  2143. * 加载并执行插件
  2144. * Load and execute the plugin
  2145. *
  2146. * @param {String} name plugin name / function name
  2147. * @param {String} path plugin load path
  2148. * @returns {editormd} 返回editormd的实例对象
  2149. */
  2150. executePlugin : function(name, path) {
  2151. var _this = this;
  2152. var cm = this.cm;
  2153. var settings = this.settings;
  2154. path = settings.pluginPath + path;
  2155. if (typeof define === "function")
  2156. {
  2157. if (typeof this[name] === "undefined")
  2158. {
  2159. alert("Error: " + name + " plugin is not found, you are not load this plugin.");
  2160. return this;
  2161. }
  2162. this[name](cm);
  2163. return this;
  2164. }
  2165. if ($.inArray(path, editormd.loadFiles.plugin) < 0)
  2166. {
  2167. editormd.loadPlugin(path, function() {
  2168. editormd.loadPlugins[name] = _this[name];
  2169. _this[name](cm);
  2170. });
  2171. }
  2172. else
  2173. {
  2174. $.proxy(editormd.loadPlugins[name], this)(cm);
  2175. }
  2176. return this;
  2177. },
  2178. /**
  2179. * 搜索替换
  2180. * Search & replace
  2181. *
  2182. * @param {String} command CodeMirror serach commands, "find, fintNext, fintPrev, clearSearch, replace, replaceAll"
  2183. * @returns {editormd} return this
  2184. */
  2185. search : function(command) {
  2186. var settings = this.settings;
  2187. if (!settings.searchReplace)
  2188. {
  2189. alert("Error: settings.searchReplace == false");
  2190. return this;
  2191. }
  2192. if (!settings.readOnly)
  2193. {
  2194. this.cm.execCommand(command || "find");
  2195. }
  2196. return this;
  2197. },
  2198. searchReplace : function() {
  2199. this.search("replace");
  2200. return this;
  2201. },
  2202. searchReplaceAll : function() {
  2203. this.search("replaceAll");
  2204. return this;
  2205. }
  2206. };
  2207. editormd.fn.init.prototype = editormd.fn;
  2208. /**
  2209. * 锁屏
  2210. * lock screen when dialog opening
  2211. *
  2212. * @returns {void}
  2213. */
  2214. editormd.dialogLockScreen = function() {
  2215. var settings = this.settings || {dialogLockScreen : true};
  2216. if (settings.dialogLockScreen)
  2217. {
  2218. $("html,body").css("overflow", "hidden");
  2219. this.resize();
  2220. }
  2221. };
  2222. /**
  2223. * 显示透明背景层
  2224. * Display mask layer when dialog opening
  2225. *
  2226. * @param {Object} dialog dialog jQuery object
  2227. * @returns {void}
  2228. */
  2229. editormd.dialogShowMask = function(dialog) {
  2230. var editor = this.editor;
  2231. var settings = this.settings || {dialogShowMask : true};
  2232. dialog.css({
  2233. top : ($(window).height() - dialog.height()) / 2 + "px",
  2234. left : ($(window).width() - dialog.width()) / 2 + "px"
  2235. });
  2236. if (settings.dialogShowMask) {
  2237. editor.children("." + this.classPrefix + "mask").css("z-index", parseInt(dialog.css("z-index")) - 1).show();
  2238. }
  2239. };
  2240. editormd.toolbarHandlers = {
  2241. undo : function() {
  2242. this.cm.undo();
  2243. },
  2244. redo : function() {
  2245. this.cm.redo();
  2246. },
  2247. bold : function() {
  2248. var cm = this.cm;
  2249. var cursor = cm.getCursor();
  2250. var selection = cm.getSelection();
  2251. cm.replaceSelection("**" + selection + "**");
  2252. if(selection === "") {
  2253. cm.setCursor(cursor.line, cursor.ch + 2);
  2254. }
  2255. },
  2256. del : function() {
  2257. var cm = this.cm;
  2258. var cursor = cm.getCursor();
  2259. var selection = cm.getSelection();
  2260. cm.replaceSelection("~~" + selection + "~~");
  2261. if(selection === "") {
  2262. cm.setCursor(cursor.line, cursor.ch + 2);
  2263. }
  2264. },
  2265. italic : function() {
  2266. var cm = this.cm;
  2267. var cursor = cm.getCursor();
  2268. var selection = cm.getSelection();
  2269. cm.replaceSelection("*" + selection + "*");
  2270. if(selection === "") {
  2271. cm.setCursor(cursor.line, cursor.ch + 1);
  2272. }
  2273. },
  2274. quote : function() {
  2275. var cm = this.cm;
  2276. var cursor = cm.getCursor();
  2277. var selection = cm.getSelection();
  2278. if (cursor.ch !== 0)
  2279. {
  2280. cm.setCursor(cursor.line, 0);
  2281. cm.replaceSelection("> " + selection);
  2282. cm.setCursor(cursor.line, cursor.ch + 2);
  2283. }
  2284. else
  2285. {
  2286. cm.replaceSelection("> " + selection);
  2287. }
  2288. //cm.replaceSelection("> " + selection);
  2289. //cm.setCursor(cursor.line, (selection === "") ? cursor.ch + 2 : cursor.ch + selection.length + 2);
  2290. },
  2291. ucfirst : function() {
  2292. var cm = this.cm;
  2293. var selection = cm.getSelection();
  2294. var selections = cm.listSelections();
  2295. cm.replaceSelection(editormd.firstUpperCase(selection));
  2296. cm.setSelections(selections);
  2297. },
  2298. ucwords : function() {
  2299. var cm = this.cm;
  2300. var selection = cm.getSelection();
  2301. var selections = cm.listSelections();
  2302. cm.replaceSelection(editormd.wordsFirstUpperCase(selection));
  2303. cm.setSelections(selections);
  2304. },
  2305. uppercase : function() {
  2306. var cm = this.cm;
  2307. var selection = cm.getSelection();
  2308. var selections = cm.listSelections();
  2309. cm.replaceSelection(selection.toUpperCase());
  2310. cm.setSelections(selections);
  2311. },
  2312. lowercase : function() {
  2313. var cm = this.cm;
  2314. var cursor = cm.getCursor();
  2315. var selection = cm.getSelection();
  2316. var selections = cm.listSelections();
  2317. cm.replaceSelection(selection.toLowerCase());
  2318. cm.setSelections(selections);
  2319. },
  2320. h1 : function() {
  2321. var cm = this.cm;
  2322. var cursor = cm.getCursor();
  2323. var selection = cm.getSelection();
  2324. if (cursor.ch !== 0)
  2325. {
  2326. cm.setCursor(cursor.line, 0);
  2327. cm.replaceSelection("# " + selection);
  2328. cm.setCursor(cursor.line, cursor.ch + 2);
  2329. }
  2330. else
  2331. {
  2332. cm.replaceSelection("# " + selection);
  2333. }
  2334. },
  2335. h2 : function() {
  2336. var cm = this.cm;
  2337. var cursor = cm.getCursor();
  2338. var selection = cm.getSelection();
  2339. if (cursor.ch !== 0)
  2340. {
  2341. cm.setCursor(cursor.line, 0);
  2342. cm.replaceSelection("## " + selection);
  2343. cm.setCursor(cursor.line, cursor.ch + 3);
  2344. }
  2345. else
  2346. {
  2347. cm.replaceSelection("## " + selection);
  2348. }
  2349. },
  2350. h3 : function() {
  2351. var cm = this.cm;
  2352. var cursor = cm.getCursor();
  2353. var selection = cm.getSelection();
  2354. if (cursor.ch !== 0)
  2355. {
  2356. cm.setCursor(cursor.line, 0);
  2357. cm.replaceSelection("### " + selection);
  2358. cm.setCursor(cursor.line, cursor.ch + 4);
  2359. }
  2360. else
  2361. {
  2362. cm.replaceSelection("### " + selection);
  2363. }
  2364. },
  2365. h4 : function() {
  2366. var cm = this.cm;
  2367. var cursor = cm.getCursor();
  2368. var selection = cm.getSelection();
  2369. if (cursor.ch !== 0)
  2370. {
  2371. cm.setCursor(cursor.line, 0);
  2372. cm.replaceSelection("#### " + selection);
  2373. cm.setCursor(cursor.line, cursor.ch + 5);
  2374. }
  2375. else
  2376. {
  2377. cm.replaceSelection("#### " + selection);
  2378. }
  2379. },
  2380. h5 : function() {
  2381. var cm = this.cm;
  2382. var cursor = cm.getCursor();
  2383. var selection = cm.getSelection();
  2384. if (cursor.ch !== 0)
  2385. {
  2386. cm.setCursor(cursor.line, 0);
  2387. cm.replaceSelection("##### " + selection);
  2388. cm.setCursor(cursor.line, cursor.ch + 6);
  2389. }
  2390. else
  2391. {
  2392. cm.replaceSelection("##### " + selection);
  2393. }
  2394. },
  2395. h6 : function() {
  2396. var cm = this.cm;
  2397. var cursor = cm.getCursor();
  2398. var selection = cm.getSelection();
  2399. if (cursor.ch !== 0)
  2400. {
  2401. cm.setCursor(cursor.line, 0);
  2402. cm.replaceSelection("###### " + selection);
  2403. cm.setCursor(cursor.line, cursor.ch + 7);
  2404. }
  2405. else
  2406. {
  2407. cm.replaceSelection("###### " + selection);
  2408. }
  2409. },
  2410. "list-ul" : function() {
  2411. var cm = this.cm;
  2412. var cursor = cm.getCursor();
  2413. var selection = cm.getSelection();
  2414. if (selection === "")
  2415. {
  2416. cm.replaceSelection("- " + selection);
  2417. }
  2418. else
  2419. {
  2420. var selectionText = selection.split("\n");
  2421. for (var i = 0, len = selectionText.length; i < len; i++)
  2422. {
  2423. selectionText[i] = (selectionText[i] === "") ? "" : "- " + selectionText[i];
  2424. }
  2425. cm.replaceSelection(selectionText.join("\n"));
  2426. }
  2427. },
  2428. "list-ol" : function() {
  2429. var cm = this.cm;
  2430. var cursor = cm.getCursor();
  2431. var selection = cm.getSelection();
  2432. if(selection === "")
  2433. {
  2434. cm.replaceSelection("1. " + selection);
  2435. }
  2436. else
  2437. {
  2438. var selectionText = selection.split("\n");
  2439. for (var i = 0, len = selectionText.length; i < len; i++)
  2440. {
  2441. selectionText[i] = (selectionText[i] === "") ? "" : (i+1) + ". " + selectionText[i];
  2442. }
  2443. cm.replaceSelection(selectionText.join("\n"));
  2444. }
  2445. },
  2446. hr : function() {
  2447. var cm = this.cm;
  2448. var cursor = cm.getCursor();
  2449. var selection = cm.getSelection();
  2450. cm.replaceSelection(((cursor.ch !== 0) ? "\n\n" : "\n") + "------------\n\n");
  2451. },
  2452. tex : function() {
  2453. if (!this.settings.tex)
  2454. {
  2455. alert("settings.tex === false");
  2456. return this;
  2457. }
  2458. var cm = this.cm;
  2459. var cursor = cm.getCursor();
  2460. var selection = cm.getSelection();
  2461. cm.replaceSelection("$$" + selection + "$$");
  2462. if(selection === "") {
  2463. cm.setCursor(cursor.line, cursor.ch + 2);
  2464. }
  2465. },
  2466. link : function() {
  2467. this.executePlugin("linkDialog", "link-dialog/link-dialog");
  2468. },
  2469. "reference-link" : function() {
  2470. this.executePlugin("referenceLinkDialog", "reference-link-dialog/reference-link-dialog");
  2471. },
  2472. pagebreak : function() {
  2473. if (!this.settings.pageBreak)
  2474. {
  2475. alert("settings.pageBreak === false");
  2476. return this;
  2477. }
  2478. var cm = this.cm;
  2479. var selection = cm.getSelection();
  2480. cm.replaceSelection("\r\n[========]\r\n");
  2481. },
  2482. image : function() {
  2483. this.executePlugin("imageDialog", "image-dialog/image-dialog");
  2484. },
  2485. code : function() {
  2486. var cm = this.cm;
  2487. var cursor = cm.getCursor();
  2488. var selection = cm.getSelection();
  2489. cm.replaceSelection("`" + selection + "`");
  2490. if (selection === "") {
  2491. cm.setCursor(cursor.line, cursor.ch + 1);
  2492. }
  2493. },
  2494. "code-block" : function() {
  2495. this.executePlugin("codeBlockDialog", "code-block-dialog/code-block-dialog");
  2496. },
  2497. "preformatted-text" : function() {
  2498. this.executePlugin("preformattedTextDialog", "preformatted-text-dialog/preformatted-text-dialog");
  2499. },
  2500. table : function() {
  2501. this.executePlugin("tableDialog", "table-dialog/table-dialog");
  2502. },
  2503. datetime : function() {
  2504. var cm = this.cm;
  2505. var selection = cm.getSelection();
  2506. var date = new Date();
  2507. var langName = this.settings.lang.name;
  2508. var datefmt = editormd.dateFormat() + " " + editormd.dateFormat((langName === "zh-cn" || langName === "zh-tw") ? "cn-week-day" : "week-day");
  2509. cm.replaceSelection(datefmt);
  2510. },
  2511. emoji : function() {
  2512. this.executePlugin("emojiDialog", "emoji-dialog/emoji-dialog");
  2513. },
  2514. "html-entities" : function() {
  2515. this.executePlugin("htmlEntitiesDialog", "html-entities-dialog/html-entities-dialog");
  2516. },
  2517. "goto-line" : function() {
  2518. this.executePlugin("gotoLineDialog", "goto-line-dialog/goto-line-dialog");
  2519. },
  2520. watch : function() {
  2521. this[this.settings.watch ? "unwatch" : "watch"]();
  2522. },
  2523. preview : function() {
  2524. this.previewing();
  2525. },
  2526. fullscreen : function() {
  2527. this.fullscreen();
  2528. },
  2529. clear : function() {
  2530. this.clear();
  2531. },
  2532. search : function() {
  2533. this.search();
  2534. },
  2535. help : function() {
  2536. this.executePlugin("helpDialog", "help-dialog/help-dialog");
  2537. },
  2538. info : function() {
  2539. this.showInfoDialog();
  2540. }
  2541. };
  2542. editormd.keyMaps = {
  2543. "Ctrl-1" : "h1",
  2544. "Ctrl-2" : "h2",
  2545. "Ctrl-3" : "h3",
  2546. "Ctrl-4" : "h4",
  2547. "Ctrl-5" : "h5",
  2548. "Ctrl-6" : "h6",
  2549. "Ctrl-B" : "bold", // if this is string == editormd.toolbarHandlers.xxxx
  2550. "Ctrl-D" : "datetime",
  2551. "Ctrl-E" : function() { // emoji
  2552. var cm = this.cm;
  2553. var cursor = cm.getCursor();
  2554. var selection = cm.getSelection();
  2555. if (!this.settings.emoji)
  2556. {
  2557. alert("Error: settings.emoji == false");
  2558. return ;
  2559. }
  2560. cm.replaceSelection(":" + selection + ":");
  2561. if (selection === "") {
  2562. cm.setCursor(cursor.line, cursor.ch + 1);
  2563. }
  2564. },
  2565. "Ctrl-Alt-G" : "goto-line",
  2566. "Ctrl-H" : "hr",
  2567. "Ctrl-I" : "italic",
  2568. "Ctrl-K" : "code",
  2569. "Ctrl-L" : function() {
  2570. var cm = this.cm;
  2571. var cursor = cm.getCursor();
  2572. var selection = cm.getSelection();
  2573. var title = (selection === "") ? "" : " \""+selection+"\"";
  2574. cm.replaceSelection("[" + selection + "]("+title+")");
  2575. if (selection === "") {
  2576. cm.setCursor(cursor.line, cursor.ch + 1);
  2577. }
  2578. },
  2579. "Ctrl-U" : "list-ul",
  2580. "Shift-Ctrl-A" : function() {
  2581. var cm = this.cm;
  2582. var cursor = cm.getCursor();
  2583. var selection = cm.getSelection();
  2584. if (!this.settings.atLink)
  2585. {
  2586. alert("Error: settings.atLink == false");
  2587. return ;
  2588. }
  2589. cm.replaceSelection("@" + selection);
  2590. if (selection === "") {
  2591. cm.setCursor(cursor.line, cursor.ch + 1);
  2592. }
  2593. },
  2594. "Shift-Ctrl-C" : "code",
  2595. "Shift-Ctrl-Q" : "quote",
  2596. "Shift-Ctrl-S" : "del",
  2597. "Shift-Ctrl-K" : "tex", // KaTeX
  2598. "Shift-Alt-C" : function() {
  2599. var cm = this.cm;
  2600. var cursor = cm.getCursor();
  2601. var selection = cm.getSelection();
  2602. cm.replaceSelection(["```", selection, "```"].join("\n"));
  2603. if (selection === "") {
  2604. cm.setCursor(cursor.line, cursor.ch + 3);
  2605. }
  2606. },
  2607. "Shift-Ctrl-Alt-C" : "code-block",
  2608. "Shift-Ctrl-H" : "html-entities",
  2609. "Shift-Alt-H" : "help",
  2610. "Shift-Ctrl-E" : "emoji",
  2611. "Shift-Ctrl-U" : "uppercase",
  2612. "Shift-Alt-U" : "ucwords",
  2613. "Shift-Ctrl-Alt-U" : "ucfirst",
  2614. "Shift-Alt-L" : "lowercase",
  2615. "Shift-Ctrl-I" : function() {
  2616. var cm = this.cm;
  2617. var cursor = cm.getCursor();
  2618. var selection = cm.getSelection();
  2619. var title = (selection === "") ? "" : " \""+selection+"\"";
  2620. cm.replaceSelection("![" + selection + "]("+title+")");
  2621. if (selection === "") {
  2622. cm.setCursor(cursor.line, cursor.ch + 4);
  2623. }
  2624. },
  2625. "Shift-Ctrl-Alt-I" : "image",
  2626. "Shift-Ctrl-L" : "link",
  2627. "Shift-Ctrl-O" : "list-ol",
  2628. "Shift-Ctrl-P" : "preformatted-text",
  2629. "Shift-Ctrl-T" : "table",
  2630. "Shift-Alt-P" : "pagebreak",
  2631. "F9" : "watch",
  2632. "F10" : "preview",
  2633. "F11" : "fullscreen",
  2634. };
  2635. /**
  2636. * 清除字符串两边的空格
  2637. * Clear the space of strings both sides.
  2638. *
  2639. * @param {String} str string
  2640. * @returns {String} trimed string
  2641. */
  2642. var trim = function(str) {
  2643. return (!String.prototype.trim) ? str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") : str.trim();
  2644. };
  2645. editormd.trim = trim;
  2646. /**
  2647. * 所有单词首字母大写
  2648. * Words first to uppercase
  2649. *
  2650. * @param {String} str string
  2651. * @returns {String} string
  2652. */
  2653. var ucwords = function (str) {
  2654. return str.toLowerCase().replace(/\b(\w)|\s(\w)/g, function($1) {
  2655. return $1.toUpperCase();
  2656. });
  2657. };
  2658. editormd.ucwords = editormd.wordsFirstUpperCase = ucwords;
  2659. /**
  2660. * 字符串首字母大写
  2661. * Only string first char to uppercase
  2662. *
  2663. * @param {String} str string
  2664. * @returns {String} string
  2665. */
  2666. var firstUpperCase = function(str) {
  2667. return str.toLowerCase().replace(/\b(\w)/, function($1){
  2668. return $1.toUpperCase();
  2669. });
  2670. };
  2671. var ucfirst = firstUpperCase;
  2672. editormd.firstUpperCase = editormd.ucfirst = firstUpperCase;
  2673. editormd.urls = {
  2674. atLinkBase : "https://github.com/"
  2675. };
  2676. editormd.regexs = {
  2677. atLink : /@(\w+)/g,
  2678. email : /(\w+)@(\w+)\.(\w+)\.?(\w+)?/g,
  2679. emailLink : /(mailto:)?([\w\.\_]+)@(\w+)\.(\w+)\.?(\w+)?/g,
  2680. emoji : /:([\w\+-]+):/g,
  2681. emojiDatetime : /(\d{2}:\d{2}:\d{2})/g,
  2682. twemoji : /:(tw-([\w]+)-?(\w+)?):/g,
  2683. fontAwesome : /:(fa-([\w]+)(-(\w+)){0,}):/g,
  2684. editormdLogo : /:(editormd-logo-?(\w+)?):/g,
  2685. pageBreak : /^\[[=]{8,}\]$/
  2686. };
  2687. // Emoji graphics files url path
  2688. editormd.emoji = {
  2689. path : "http://www.emoji-cheat-sheet.com/graphics/emojis/",
  2690. ext : ".png"
  2691. };
  2692. // Twitter Emoji (Twemoji) graphics files url path
  2693. editormd.twemoji = {
  2694. path : "http://twemoji.maxcdn.com/36x36/",
  2695. ext : ".png"
  2696. };
  2697. /**
  2698. * 自定义marked的解析器
  2699. * Custom Marked renderer rules
  2700. *
  2701. * @param {Array} markdownToC 传入用于接收TOC的数组
  2702. * @returns {Renderer} markedRenderer 返回marked的Renderer自定义对象
  2703. */
  2704. editormd.markedRenderer = function(markdownToC, options) {
  2705. var defaults = {
  2706. toc : true, // Table of contents
  2707. tocm : false,
  2708. tocStartLevel : 1, // Said from H1 to create ToC
  2709. pageBreak : true,
  2710. atLink : true, // for @link
  2711. emailLink : true, // for mail address auto link
  2712. taskList : false, // Enable Github Flavored Markdown task lists
  2713. emoji : false, // :emoji: , Support Twemoji, fontAwesome, Editor.md logo emojis.
  2714. tex : false, // TeX(LaTeX), based on KaTeX
  2715. flowChart : false, // flowChart.js only support IE9+
  2716. sequenceDiagram : false, // sequenceDiagram.js only support IE9+
  2717. };
  2718. var settings = $.extend(defaults, options || {});
  2719. var marked = editormd.$marked;
  2720. var markedRenderer = new marked.Renderer();
  2721. markdownToC = markdownToC || [];
  2722. var regexs = editormd.regexs;
  2723. var atLinkReg = regexs.atLink;
  2724. var emojiReg = regexs.emoji;
  2725. var emailReg = regexs.email;
  2726. var emailLinkReg = regexs.emailLink;
  2727. var twemojiReg = regexs.twemoji;
  2728. var faIconReg = regexs.fontAwesome;
  2729. var editormdLogoReg = regexs.editormdLogo;
  2730. var pageBreakReg = regexs.pageBreak;
  2731. markedRenderer.emoji = function(text) {
  2732. text = text.replace(editormd.regexs.emojiDatetime, function($1) {
  2733. return $1.replace(/:/g, "&#58;");
  2734. });
  2735. var matchs = text.match(emojiReg);
  2736. if (!matchs || !settings.emoji) {
  2737. return text;
  2738. }
  2739. for (var i = 0, len = matchs.length; i < len; i++)
  2740. {
  2741. if (matchs[i] === ":+1:") {
  2742. matchs[i] = ":\\+1:";
  2743. }
  2744. text = text.replace(new RegExp(matchs[i]), function($1, $2){
  2745. var faMatchs = $1.match(faIconReg);
  2746. var name = $1.replace(/:/g, "");
  2747. if (faMatchs)
  2748. {
  2749. for (var fa = 0, len1 = faMatchs.length; fa < len1; fa++)
  2750. {
  2751. var faName = faMatchs[fa].replace(/:/g, "");
  2752. return "<i class=\"fa " + faName + " fa-emoji\" title=\"" + faName.replace("fa-", "") + "\"></i>";
  2753. }
  2754. }
  2755. else
  2756. {
  2757. var emdlogoMathcs = $1.match(editormdLogoReg);
  2758. var twemojiMatchs = $1.match(twemojiReg);
  2759. if (emdlogoMathcs)
  2760. {
  2761. for (var x = 0, len2 = emdlogoMathcs.length; x < len2; x++)
  2762. {
  2763. var logoName = emdlogoMathcs[x].replace(/:/g, "");
  2764. return "<i class=\"" + logoName + "\" title=\"Editor.md logo (" + logoName + ")\"></i>";
  2765. }
  2766. }
  2767. else if (twemojiMatchs)
  2768. {
  2769. for (var t = 0, len3 = twemojiMatchs.length; t < len3; t++)
  2770. {
  2771. var twe = twemojiMatchs[t].replace(/:/g, "").replace("tw-", "");
  2772. return "<img src=\"" + editormd.twemoji.path + twe + editormd.twemoji.ext + "\" title=\"twemoji-" + twe + "\" alt=\"twemoji-" + twe + "\" class=\"emoji twemoji\" />";
  2773. }
  2774. }
  2775. else
  2776. {
  2777. var src = (name === "+1") ? "plus1" : name;
  2778. src = (src === "black_large_square") ? "black_square" : src;
  2779. src = (src === "moon") ? "waxing_gibbous_moon" : src;
  2780. return "<img src=\"" + editormd.emoji.path + src + editormd.emoji.ext + "\" class=\"emoji\" title=\"&#58;" + name + "&#58;\" alt=\"&#58;" + name + "&#58;\" />";
  2781. }
  2782. }
  2783. });
  2784. }
  2785. return text;
  2786. };
  2787. markedRenderer.atLink = function(text) {
  2788. if (atLinkReg.test(text))
  2789. {
  2790. if (settings.atLink)
  2791. {
  2792. text = text.replace(emailReg, function($1, $2, $3, $4) {
  2793. return $1.replace(/@/g, "_#_&#64;_#_");
  2794. });
  2795. text = text.replace(atLinkReg, function($1, $2) {
  2796. return "<a href=\"" + editormd.urls.atLinkBase + "" + $2 + "\" title=\"&#64;" + $2 + "\" class=\"at-link\">" + $1 + "</a>";
  2797. }).replace(/_#_&#64;_#_/g, "@");
  2798. }
  2799. if (settings.emailLink)
  2800. {
  2801. text = text.replace(emailLinkReg, function($1, $2, $3, $4, $5) {
  2802. return (!$2 && $.inArray($5, "jpg|jpeg|png|gif|webp|ico|icon|pdf".split("|")) < 0) ? "<a href=\"mailto:" + $1 + "\">"+$1+"</a>" : $1;
  2803. });
  2804. }
  2805. return text;
  2806. }
  2807. return text;
  2808. };
  2809. markedRenderer.link = function (href, title, text) {
  2810. if (this.options.sanitize) {
  2811. try {
  2812. var prot = decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase();
  2813. } catch(e) {
  2814. return "";
  2815. }
  2816. if (prot.indexOf("javascript:") === 0) {
  2817. return "";
  2818. }
  2819. }
  2820. var out = "<a href=\"" + href + "\"";
  2821. if (atLinkReg.test(title) || atLinkReg.test(text))
  2822. {
  2823. if (title)
  2824. {
  2825. out += " title=\"" + title.replace(/@/g, "&#64;");
  2826. }
  2827. return out + "\">" + text.replace(/@/g, "&#64;") + "</a>";
  2828. }
  2829. if (title) {
  2830. out += " title=\"" + title + "\"";
  2831. }
  2832. out += ">" + text + "</a>";
  2833. return out;
  2834. };
  2835. markedRenderer.heading = function(text, level, raw) {
  2836. var linkText = text;
  2837. var hasLinkReg = /\s*\<a\s*href\=\"(.*)\"\s*([^\>]*)\>(.*)\<\/a\>\s*/;
  2838. var getLinkTextReg = /\s*\<a\s*([^\>]+)\>([^\>]*)\<\/a\>\s*/g;
  2839. if (hasLinkReg.test(text))
  2840. {
  2841. var tempText = [];
  2842. text = text.split(/\<a\s*([^\>]+)\>([^\>]*)\<\/a\>/);
  2843. for (var i = 0, len = text.length; i < len; i++)
  2844. {
  2845. tempText.push(text[i].replace(/\s*href\=\"(.*)\"\s*/g, ""));
  2846. }
  2847. text = tempText.join(" ");
  2848. }
  2849. text = trim(text);
  2850. var escapedText = text.toLowerCase().replace(/[^\w]+/g, "-");
  2851. var toc = {
  2852. text : text,
  2853. level : level,
  2854. slug : escapedText
  2855. };
  2856. var isChinese = /^[\u4e00-\u9fa5]+$/.test(text);
  2857. var id = (isChinese) ? escape(text).replace(/\%/g, "") : text.toLowerCase().replace(/[^\w]+/g, "-");
  2858. markdownToC.push(toc);
  2859. var headingHTML = "<h" + level + " id=\"h"+ level + "-" + this.options.headerPrefix + id +"\">";
  2860. headingHTML += "<a name=\"" + text + "\" class=\"reference-link\"></a>";
  2861. headingHTML += "<span class=\"header-link octicon octicon-link\"></span>";
  2862. headingHTML += (hasLinkReg) ? this.atLink(this.emoji(linkText)) : this.atLink(this.emoji(text));
  2863. headingHTML += "</h" + level + ">";
  2864. return headingHTML;
  2865. };
  2866. markedRenderer.pageBreak = function(text) {
  2867. if (pageBreakReg.test(text) && settings.pageBreak)
  2868. {
  2869. text = "<hr style=\"page-break-after:always;\" class=\"page-break editormd-page-break\" />";
  2870. }
  2871. return text;
  2872. };
  2873. markedRenderer.paragraph = function(text) {
  2874. var isTeXInline = /\$\$(.*)\$\$/g.test(text);
  2875. var isTeXLine = /^\$\$(.*)\$\$$/.test(text);
  2876. var isTeXAddClass = (isTeXLine) ? " class=\"" + editormd.classNames.tex + "\"" : "";
  2877. var isToC = (settings.tocm) ? /^(\[TOC\]|\[TOCM\])$/.test(text) : /^\[TOC\]$/.test(text);
  2878. var isToCMenu = /^\[TOCM\]$/.test(text);
  2879. if (!isTeXLine && isTeXInline)
  2880. {
  2881. text = text.replace(/(\$\$([^\$]*)\$\$)+/g, function($1, $2) {
  2882. return "<span class=\"" + editormd.classNames.tex + "\">" + $2.replace(/\$/g, "") + "</span>";
  2883. });
  2884. }
  2885. else
  2886. {
  2887. text = (isTeXLine) ? text.replace(/\$/g, "") : text;
  2888. }
  2889. var tocHTML = "<div class=\"markdown-toc editormd-markdown-toc\">" + text + "</div>";
  2890. return (isToC) ? ( (isToCMenu) ? "<div class=\"editormd-toc-menu\">" + tocHTML + "</div><br/>" : tocHTML )
  2891. : ( (pageBreakReg.test(text)) ? this.pageBreak(text) : "<p" + isTeXAddClass + ">" + this.atLink(this.emoji(text)) + "</p>\n" );
  2892. };
  2893. markedRenderer.code = function (code, lang, escaped) {
  2894. if (lang === "seq" || lang === "sequence")
  2895. {
  2896. return "<div class=\"sequence-diagram\">" + code + "</div>";
  2897. }
  2898. else if ( lang === "flow")
  2899. {
  2900. return "<div class=\"flowchart\">" + code + "</div>";
  2901. }
  2902. else if ( lang === "math" || lang === "latex" || lang === "katex")
  2903. {
  2904. return "<p class=\"" + editormd.classNames.tex + "\">" + code + "</p>";
  2905. }
  2906. else
  2907. {
  2908. return marked.Renderer.prototype.code.apply(this, arguments);
  2909. }
  2910. };
  2911. markedRenderer.tablecell = function(content, flags) {
  2912. var type = (flags.header) ? "th" : "td";
  2913. var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">";
  2914. return tag + this.atLink(this.emoji(content)) + "</" + type + ">\n";
  2915. };
  2916. markedRenderer.listitem = function(text) {
  2917. if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text))
  2918. {
  2919. text = text.replace(/^\s*\[\s\]\s*/, "<input type=\"checkbox\" class=\"task-list-item-checkbox\" /> ")
  2920. .replace(/^\s*\[x\]\s*/, "<input type=\"checkbox\" class=\"task-list-item-checkbox\" checked disabled /> ");
  2921. return "<li style=\"list-style: none;\">" + this.atLink(this.emoji(text)) + "</li>";
  2922. }
  2923. else
  2924. {
  2925. return "<li>" + this.atLink(this.emoji(text)) + "</li>";
  2926. }
  2927. };
  2928. return markedRenderer;
  2929. };
  2930. /**
  2931. *
  2932. * 生成TOC(Table of Contents)
  2933. * Creating ToC (Table of Contents)
  2934. *
  2935. * @param {Array} toc 从marked获取的TOC数组列表
  2936. * @param {Element} container 插入TOC的容器元素
  2937. * @param {Integer} startLevel Hx 起始层级
  2938. * @returns {Object} tocContainer 返回ToC列表容器层的jQuery对象元素
  2939. */
  2940. editormd.markdownToCRenderer = function(toc, container, tocDropdown, startLevel) {
  2941. var html = "";
  2942. var lastLevel = 0;
  2943. var classPrefix = this.classPrefix;
  2944. startLevel = startLevel || 1;
  2945. for (var i = 0, len = toc.length; i < len; i++)
  2946. {
  2947. var text = toc[i].text;
  2948. var level = toc[i].level;
  2949. if (level < startLevel) {
  2950. continue;
  2951. }
  2952. if (level > lastLevel)
  2953. {
  2954. html += "";
  2955. }
  2956. else if (level < lastLevel)
  2957. {
  2958. html += (new Array(lastLevel - level + 2)).join("</ul></li>");
  2959. }
  2960. else
  2961. {
  2962. html += "</ul></li>";
  2963. }
  2964. html += "<li><a class=\"toc-level-" + level + "\" href=\"#" + text + "\" level=\"" + level + "\">" + text + "</a><ul>";
  2965. lastLevel = level;
  2966. }
  2967. var tocContainer = container.find(".markdown-toc");
  2968. if ((tocContainer.length < 1 && container.attr("previewContainer") === "false"))
  2969. {
  2970. var tocHTML = "<div class=\"markdown-toc " + classPrefix + "markdown-toc\"></div>";
  2971. tocHTML = (tocDropdown) ? "<div class=\"" + classPrefix + "toc-menu\">" + tocHTML + "</div>" : tocHTML;
  2972. container.html(tocHTML);
  2973. tocContainer = container.find(".markdown-toc");
  2974. }
  2975. if (tocDropdown)
  2976. {
  2977. tocContainer.wrap("<div class=\"" + classPrefix + "toc-menu\"></div><br/>");
  2978. }
  2979. tocContainer.html("<ul class=\"markdown-toc-list\"></ul>").children(".markdown-toc-list").html(html.replace(/\r?\n?\<ul\>\<\/ul\>/g, ""));
  2980. return tocContainer;
  2981. };
  2982. /**
  2983. *
  2984. * 生成TOC下拉菜单
  2985. * Creating ToC dropdown menu
  2986. *
  2987. * @param {Object} container 插入TOC的容器jQuery对象元素
  2988. * @param {String} tocTitle ToC title
  2989. * @returns {Object} return toc-menu object
  2990. */
  2991. editormd.tocDropdownMenu = function(container, tocTitle) {
  2992. tocTitle = tocTitle || "Table of Contents";
  2993. var zindex = 400;
  2994. var tocMenus = container.find("." + this.classPrefix + "toc-menu");
  2995. tocMenus.each(function() {
  2996. var $this = $(this);
  2997. var toc = $this.children(".markdown-toc");
  2998. var icon = "<i class=\"fa fa-angle-down\"></i>";
  2999. var btn = "<a href=\"javascript:;\" class=\"toc-menu-btn\">" + icon + tocTitle + "</a>";
  3000. var menu = toc.children("ul");
  3001. var list = menu.find("li");
  3002. toc.append(btn);
  3003. list.first().before("<li><h1>" + tocTitle + " " + icon + "</h1></li>");
  3004. $this.mouseover(function(){
  3005. menu.show();
  3006. list.each(function(){
  3007. var li = $(this);
  3008. var ul = li.children("ul");
  3009. if (ul.html() === "")
  3010. {
  3011. ul.remove();
  3012. }
  3013. if (ul.length > 0 && ul.html() !== "")
  3014. {
  3015. var firstA = li.children("a").first();
  3016. if (firstA.children(".fa").length < 1)
  3017. {
  3018. firstA.append( $(icon).css({ float:"right", paddingTop:"4px" }) );
  3019. }
  3020. }
  3021. li.mouseover(function(){
  3022. ul.css("z-index", zindex).show();
  3023. zindex += 1;
  3024. }).mouseleave(function(){
  3025. ul.hide();
  3026. });
  3027. });
  3028. }).mouseleave(function(){
  3029. menu.hide();
  3030. });
  3031. });
  3032. return tocMenus;
  3033. };
  3034. /**
  3035. * 简单地过滤指定的HTML标签
  3036. * Filter custom html tags
  3037. *
  3038. * @param {String} html 要过滤HTML
  3039. * @param {String} filters 要过滤的标签
  3040. * @returns {String} html 返回过滤的HTML
  3041. */
  3042. editormd.filterHTMLTags = function(html, filters) {
  3043. if (typeof html !== "string") {
  3044. html = new String(html);
  3045. }
  3046. if (typeof filters !== "string") {
  3047. return html;
  3048. }
  3049. var expression = filters.split("|");
  3050. var filterTags = expression[0].split(",");
  3051. var attrs = expression[1];
  3052. for (var i = 0, len = filterTags.length; i < len; i++)
  3053. {
  3054. var tag = filterTags[i];
  3055. html = html.replace(new RegExp("\<\s*" + tag + "\s*([^\>]*)\>([^\>]*)\<\s*\/" + tag + "\s*\>", "igm"), "");
  3056. }
  3057. //return html;
  3058. if (typeof attrs !== "undefined")
  3059. {
  3060. var htmlTagRegex = /\<(\w+)\s*([^\>]*)\>([^\>]*)\<\/(\w+)\>/ig;
  3061. if (attrs === "*")
  3062. {
  3063. html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
  3064. return "<" + $2 + ">" + $4 + "</" + $5 + ">";
  3065. });
  3066. }
  3067. else if (attrs === "on*")
  3068. {
  3069. html = html.replace(htmlTagRegex, function($1, $2, $3, $4, $5) {
  3070. var el = $("<" + $2 + ">" + $4 + "</" + $5 + ">");
  3071. var _attrs = $($1)[0].attributes;
  3072. var $attrs = {};
  3073. $.each(_attrs, function(i, e) {
  3074. if (e.nodeName !== '"') $attrs[e.nodeName] = e.nodeValue;
  3075. });
  3076. $.each($attrs, function(i) {
  3077. if (i.indexOf("on") === 0) {
  3078. delete $attrs[i];
  3079. }
  3080. });
  3081. el.attr($attrs);
  3082. var text = (typeof el[1] !== "undefined") ? $(el[1]).text() : "";
  3083. return el[0].outerHTML + text;
  3084. });
  3085. }
  3086. else
  3087. {
  3088. html = html.replace(htmlTagRegex, function($1, $2, $3, $4) {
  3089. var filterAttrs = attrs.split(",");
  3090. var el = $($1);
  3091. el.html($4);
  3092. $.each(filterAttrs, function(i) {
  3093. el.attr(filterAttrs[i], null);
  3094. });
  3095. return el[0].outerHTML;
  3096. });
  3097. }
  3098. }
  3099. return html;
  3100. };
  3101. /**
  3102. * 将Markdown文档解析为HTML用于前台显示
  3103. * Parse Markdown to HTML for Font-end preview.
  3104. *
  3105. * @param {String} id 用于显示HTML的对象ID
  3106. * @param {Object} [options={}] 配置选项,可选
  3107. * @returns {Object} div 返回jQuery对象元素
  3108. */
  3109. editormd.markdownToHTML = function(id, options) {
  3110. var defaults = {
  3111. gfm : true,
  3112. toc : true,
  3113. tocm : false,
  3114. tocStartLevel : 1,
  3115. tocTitle : "目录",
  3116. tocDropdown : false,
  3117. tocContainer : "",
  3118. markdown : "",
  3119. markdownSourceCode : false,
  3120. htmlDecode : false,
  3121. autoLoadKaTeX : true,
  3122. pageBreak : true,
  3123. atLink : true, // for @link
  3124. emailLink : true, // for mail address auto link
  3125. tex : false,
  3126. taskList : false, // Github Flavored Markdown task lists
  3127. emoji : false,
  3128. flowChart : false,
  3129. sequenceDiagram : false,
  3130. previewCodeHighlight : true
  3131. };
  3132. editormd.$marked = marked;
  3133. var div = $("#" + id);
  3134. var settings = div.settings = $.extend(true, defaults, options || {});
  3135. var saveTo = div.find("textarea");
  3136. if (saveTo.length < 1)
  3137. {
  3138. div.append("<textarea></textarea>");
  3139. saveTo = div.find("textarea");
  3140. }
  3141. var markdownDoc = (settings.markdown === "") ? saveTo.val() : settings.markdown;
  3142. var markdownToC = [];
  3143. var rendererOptions = {
  3144. toc : settings.toc,
  3145. tocm : settings.tocm,
  3146. tocStartLevel : settings.tocStartLevel,
  3147. taskList : settings.taskList,
  3148. emoji : settings.emoji,
  3149. tex : settings.tex,
  3150. pageBreak : settings.pageBreak,
  3151. atLink : settings.atLink, // for @link
  3152. emailLink : settings.emailLink, // for mail address auto link
  3153. flowChart : settings.flowChart,
  3154. sequenceDiagram : settings.sequenceDiagram,
  3155. previewCodeHighlight : settings.previewCodeHighlight,
  3156. };
  3157. var markedOptions = {
  3158. renderer : editormd.markedRenderer(markdownToC, rendererOptions),
  3159. gfm : settings.gfm,
  3160. tables : true,
  3161. breaks : true,
  3162. pedantic : false,
  3163. sanitize : (settings.htmlDecode) ? false : true, // 是否忽略HTML标签,即是否开启HTML标签解析,为了安全性,默认不开启
  3164. smartLists : true,
  3165. smartypants : true
  3166. };
  3167. markdownDoc = new String(markdownDoc);
  3168. var markdownParsed = marked(markdownDoc, markedOptions);
  3169. markdownParsed = editormd.filterHTMLTags(markdownParsed, settings.htmlDecode);
  3170. if (settings.markdownSourceCode) {
  3171. saveTo.text(markdownDoc);
  3172. } else {
  3173. saveTo.remove();
  3174. }
  3175. div.addClass("markdown-body " + this.classPrefix + "html-preview").append(markdownParsed);
  3176. var tocContainer = (settings.tocContainer !== "") ? $(settings.tocContainer) : div;
  3177. if (settings.tocContainer !== "")
  3178. {
  3179. tocContainer.attr("previewContainer", false);
  3180. }
  3181. if (settings.toc)
  3182. {
  3183. div.tocContainer = this.markdownToCRenderer(markdownToC, tocContainer, settings.tocDropdown, settings.tocStartLevel);
  3184. if (settings.tocDropdown || div.find("." + this.classPrefix + "toc-menu").length > 0)
  3185. {
  3186. this.tocDropdownMenu(div, settings.tocTitle);
  3187. }
  3188. if (settings.tocContainer !== "")
  3189. {
  3190. div.find(".editormd-toc-menu, .editormd-markdown-toc").remove();
  3191. }
  3192. }
  3193. if (settings.previewCodeHighlight)
  3194. {
  3195. div.find("pre").addClass("prettyprint linenums");
  3196. prettyPrint();
  3197. }
  3198. if (!editormd.isIE8)
  3199. {
  3200. if (settings.flowChart) {
  3201. div.find(".flowchart").flowChart();
  3202. }
  3203. if (settings.sequenceDiagram) {
  3204. div.find(".sequence-diagram").sequenceDiagram({theme: "simple"});
  3205. }
  3206. }
  3207. if (settings.tex)
  3208. {
  3209. var katexHandle = function() {
  3210. div.find("." + editormd.classNames.tex).each(function(){
  3211. var tex = $(this);
  3212. katex.render(tex.html().replace(/&lt;/g, "<").replace(/&gt;/g, ">"), tex[0]);
  3213. tex.find(".katex").css("font-size", "1.6em");
  3214. });
  3215. };
  3216. if (settings.autoLoadKaTeX && !editormd.$katex && !editormd.kaTeXLoaded)
  3217. {
  3218. this.loadKaTeX(function() {
  3219. editormd.$katex = katex;
  3220. editormd.kaTeXLoaded = true;
  3221. katexHandle();
  3222. });
  3223. }
  3224. else
  3225. {
  3226. katexHandle();
  3227. }
  3228. }
  3229. div.getMarkdown = function() {
  3230. return saveTo.val();
  3231. };
  3232. return div;
  3233. };
  3234. // Editor.md themes, change toolbar themes etc.
  3235. // added @1.5.0
  3236. editormd.themes = ["default", "dark"];
  3237. // Preview area themes
  3238. // added @1.5.0
  3239. editormd.previewThemes = ["default", "dark"];
  3240. // CodeMirror / editor area themes
  3241. // @1.5.0 rename -> editorThemes, old version -> themes
  3242. editormd.editorThemes = [
  3243. "default", "3024-day", "3024-night",
  3244. "ambiance", "ambiance-mobile",
  3245. "base16-dark", "base16-light", "blackboard",
  3246. "cobalt",
  3247. "eclipse", "elegant", "erlang-dark",
  3248. "lesser-dark",
  3249. "mbo", "mdn-like", "midnight", "monokai",
  3250. "neat", "neo", "night",
  3251. "paraiso-dark", "paraiso-light", "pastel-on-dark",
  3252. "rubyblue",
  3253. "solarized",
  3254. "the-matrix", "tomorrow-night-eighties", "twilight",
  3255. "vibrant-ink",
  3256. "xq-dark", "xq-light"
  3257. ];
  3258. editormd.loadPlugins = {};
  3259. editormd.loadFiles = {
  3260. js : [],
  3261. css : [],
  3262. plugin : []
  3263. };
  3264. /**
  3265. * 动态加载Editor.md插件,但不立即执行
  3266. * Load editor.md plugins
  3267. *
  3268. * @param {String} fileName 插件文件路径
  3269. * @param {Function} [callback=function()] 加载成功后执行的回调函数
  3270. * @param {String} [into="head"] 嵌入页面的位置
  3271. */
  3272. editormd.loadPlugin = function(fileName, callback, into) {
  3273. callback = callback || function() {};
  3274. this.loadScript(fileName, function() {
  3275. editormd.loadFiles.plugin.push(fileName);
  3276. callback();
  3277. }, into);
  3278. };
  3279. /**
  3280. * 动态加载CSS文件的方法
  3281. * Load css file method
  3282. *
  3283. * @param {String} fileName CSS文件名
  3284. * @param {Function} [callback=function()] 加载成功后执行的回调函数
  3285. * @param {String} [into="head"] 嵌入页面的位置
  3286. */
  3287. editormd.loadCSS = function(fileName, callback, into) {
  3288. into = into || "head";
  3289. callback = callback || function() {};
  3290. var css = document.createElement("link");
  3291. css.type = "text/css";
  3292. css.rel = "stylesheet";
  3293. css.onload = css.onreadystatechange = function() {
  3294. editormd.loadFiles.css.push(fileName);
  3295. callback();
  3296. };
  3297. css.href = fileName + ".css";
  3298. if(into === "head") {
  3299. document.getElementsByTagName("head")[0].appendChild(css);
  3300. } else {
  3301. document.body.appendChild(css);
  3302. }
  3303. };
  3304. editormd.isIE = (navigator.appName == "Microsoft Internet Explorer");
  3305. editormd.isIE8 = (editormd.isIE && navigator.appVersion.match(/8./i) == "8.");
  3306. /**
  3307. * 动态加载JS文件的方法
  3308. * Load javascript file method
  3309. *
  3310. * @param {String} fileName JS文件名
  3311. * @param {Function} [callback=function()] 加载成功后执行的回调函数
  3312. * @param {String} [into="head"] 嵌入页面的位置
  3313. */
  3314. editormd.loadScript = function(fileName, callback, into) {
  3315. into = into || "head";
  3316. callback = callback || function() {};
  3317. var script = null;
  3318. script = document.createElement("script");
  3319. script.id = fileName.replace(/[\./]+/g, "-");
  3320. script.type = "text/javascript";
  3321. script.src = fileName + ".js";
  3322. if (editormd.isIE8)
  3323. {
  3324. script.onreadystatechange = function() {
  3325. if(script.readyState)
  3326. {
  3327. if (script.readyState === "loaded" || script.readyState === "complete")
  3328. {
  3329. script.onreadystatechange = null;
  3330. editormd.loadFiles.js.push(fileName);
  3331. callback();
  3332. }
  3333. }
  3334. };
  3335. }
  3336. else
  3337. {
  3338. script.onload = function() {
  3339. editormd.loadFiles.js.push(fileName);
  3340. callback();
  3341. };
  3342. }
  3343. if (into === "head") {
  3344. document.getElementsByTagName("head")[0].appendChild(script);
  3345. } else {
  3346. document.body.appendChild(script);
  3347. }
  3348. };
  3349. // 使用国外的CDN,加载速度有时会很慢,或者自定义URL
  3350. // You can custom KaTeX load url.
  3351. editormd.katexURL = {
  3352. css : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min",
  3353. js : "//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"
  3354. };
  3355. editormd.kaTeXLoaded = false;
  3356. /**
  3357. * 加载KaTeX文件
  3358. * load KaTeX files
  3359. *
  3360. * @param {Function} [callback=function()] 加载成功后执行的回调函数
  3361. */
  3362. editormd.loadKaTeX = function (callback) {
  3363. editormd.loadCSS(editormd.katexURL.css, function(){
  3364. editormd.loadScript(editormd.katexURL.js, callback || function(){});
  3365. });
  3366. };
  3367. /**
  3368. * 锁屏
  3369. * lock screen
  3370. *
  3371. * @param {Boolean} lock Boolean 布尔值,是否锁屏
  3372. * @returns {void}
  3373. */
  3374. editormd.lockScreen = function(lock) {
  3375. $("html,body").css("overflow", (lock) ? "hidden" : "");
  3376. };
  3377. /**
  3378. * 动态创建对话框
  3379. * Creating custom dialogs
  3380. *
  3381. * @param {Object} options 配置项键值对 Key/Value
  3382. * @returns {dialog} 返回创建的dialog的jQuery实例对象
  3383. */
  3384. editormd.createDialog = function(options) {
  3385. var defaults = {
  3386. name : "",
  3387. width : 420,
  3388. height: 240,
  3389. title : "",
  3390. drag : true,
  3391. closed : true,
  3392. content : "",
  3393. mask : true,
  3394. maskStyle : {
  3395. backgroundColor : "#fff",
  3396. opacity : 0.1
  3397. },
  3398. lockScreen : true,
  3399. footer : true,
  3400. buttons : false
  3401. };
  3402. options = $.extend(true, defaults, options);
  3403. var $this = this;
  3404. var editor = this.editor;
  3405. var classPrefix = editormd.classPrefix;
  3406. var guid = (new Date()).getTime();
  3407. var dialogName = ( (options.name === "") ? classPrefix + "dialog-" + guid : options.name);
  3408. var mouseOrTouch = editormd.mouseOrTouch;
  3409. var html = "<div class=\"" + classPrefix + "dialog " + dialogName + "\">";
  3410. if (options.title !== "")
  3411. {
  3412. html += "<div class=\"" + classPrefix + "dialog-header\"" + ( (options.drag) ? " style=\"cursor: move;\"" : "" ) + ">";
  3413. html += "<strong class=\"" + classPrefix + "dialog-title\">" + options.title + "</strong>";
  3414. html += "</div>";
  3415. }
  3416. if (options.closed)
  3417. {
  3418. html += "<a href=\"javascript:;\" class=\"fa fa-close " + classPrefix + "dialog-close\"></a>";
  3419. }
  3420. html += "<div class=\"" + classPrefix + "dialog-container\">" + options.content;
  3421. if (options.footer || typeof options.footer === "string")
  3422. {
  3423. html += "<div class=\"" + classPrefix + "dialog-footer\">" + ( (typeof options.footer === "boolean") ? "" : options.footer) + "</div>";
  3424. }
  3425. html += "</div>";
  3426. html += "<div class=\"" + classPrefix + "dialog-mask " + classPrefix + "dialog-mask-bg\"></div>";
  3427. html += "<div class=\"" + classPrefix + "dialog-mask " + classPrefix + "dialog-mask-con\"></div>";
  3428. html += "</div>";
  3429. editor.append(html);
  3430. var dialog = editor.find("." + dialogName);
  3431. dialog.lockScreen = function(lock) {
  3432. if (options.lockScreen)
  3433. {
  3434. $("html,body").css("overflow", (lock) ? "hidden" : "");
  3435. $this.resize();
  3436. }
  3437. return dialog;
  3438. };
  3439. dialog.showMask = function() {
  3440. if (options.mask)
  3441. {
  3442. editor.find("." + classPrefix + "mask").css(options.maskStyle).css("z-index", editormd.dialogZindex - 1).show();
  3443. }
  3444. return dialog;
  3445. };
  3446. dialog.hideMask = function() {
  3447. if (options.mask)
  3448. {
  3449. editor.find("." + classPrefix + "mask").hide();
  3450. }
  3451. return dialog;
  3452. };
  3453. dialog.loading = function(show) {
  3454. var loading = dialog.find("." + classPrefix + "dialog-mask");
  3455. loading[(show) ? "show" : "hide"]();
  3456. return dialog;
  3457. };
  3458. dialog.lockScreen(true).showMask();
  3459. dialog.show().css({
  3460. zIndex : editormd.dialogZindex,
  3461. border : (editormd.isIE8) ? "1px solid #ddd" : "",
  3462. width : (typeof options.width === "number") ? options.width + "px" : options.width,
  3463. height : (typeof options.height === "number") ? options.height + "px" : options.height
  3464. });
  3465. var dialogPosition = function(){
  3466. dialog.css({
  3467. top : ($(window).height() - dialog.height()) / 2 + "px",
  3468. left : ($(window).width() - dialog.width()) / 2 + "px"
  3469. });
  3470. };
  3471. dialogPosition();
  3472. $(window).resize(dialogPosition);
  3473. dialog.children("." + classPrefix + "dialog-close").bind(mouseOrTouch("click", "touchend"), function() {
  3474. dialog.hide().lockScreen(false).hideMask();
  3475. });
  3476. if (typeof options.buttons === "object")
  3477. {
  3478. var footer = dialog.footer = dialog.find("." + classPrefix + "dialog-footer");
  3479. for (var key in options.buttons)
  3480. {
  3481. var btn = options.buttons[key];
  3482. var btnClassName = classPrefix + key + "-btn";
  3483. footer.append("<button class=\"" + classPrefix + "btn " + btnClassName + "\">" + btn[0] + "</button>");
  3484. btn[1] = $.proxy(btn[1], dialog);
  3485. footer.children("." + btnClassName).bind(mouseOrTouch("click", "touchend"), btn[1]);
  3486. }
  3487. }
  3488. if (options.title !== "" && options.drag)
  3489. {
  3490. var posX, posY;
  3491. var dialogHeader = dialog.children("." + classPrefix + "dialog-header");
  3492. if (!options.mask) {
  3493. dialogHeader.bind(mouseOrTouch("click", "touchend"), function(){
  3494. editormd.dialogZindex += 2;
  3495. dialog.css("z-index", editormd.dialogZindex);
  3496. });
  3497. }
  3498. dialogHeader.mousedown(function(e) {
  3499. e = e || window.event; //IE
  3500. posX = e.clientX - parseInt(dialog[0].style.left);
  3501. posY = e.clientY - parseInt(dialog[0].style.top);
  3502. document.onmousemove = moveAction;
  3503. });
  3504. var userCanSelect = function (obj) {
  3505. obj.removeClass(classPrefix + "user-unselect").off("selectstart");
  3506. };
  3507. var userUnselect = function (obj) {
  3508. obj.addClass(classPrefix + "user-unselect").on("selectstart", function(event) { // selectstart for IE
  3509. return false;
  3510. });
  3511. };
  3512. var moveAction = function (e) {
  3513. e = e || window.event; //IE
  3514. var left, top, nowLeft = parseInt(dialog[0].style.left), nowTop = parseInt(dialog[0].style.top);
  3515. if( nowLeft >= 0 ) {
  3516. if( nowLeft + dialog.width() <= $(window).width()) {
  3517. left = e.clientX - posX;
  3518. } else {
  3519. left = $(window).width() - dialog.width();
  3520. document.onmousemove = null;
  3521. }
  3522. } else {
  3523. left = 0;
  3524. document.onmousemove = null;
  3525. }
  3526. if( nowTop >= 0 ) {
  3527. top = e.clientY - posY;
  3528. } else {
  3529. top = 0;
  3530. document.onmousemove = null;
  3531. }
  3532. document.onselectstart = function() {
  3533. return false;
  3534. };
  3535. userUnselect($("body"));
  3536. userUnselect(dialog);
  3537. dialog[0].style.left = left + "px";
  3538. dialog[0].style.top = top + "px";
  3539. };
  3540. document.onmouseup = function() {
  3541. userCanSelect($("body"));
  3542. userCanSelect(dialog);
  3543. document.onselectstart = null;
  3544. document.onmousemove = null;
  3545. };
  3546. dialogHeader.touchDraggable = function() {
  3547. var offset = null;
  3548. var start = function(e) {
  3549. var orig = e.originalEvent;
  3550. var pos = $(this).parent().position();
  3551. offset = {
  3552. x : orig.changedTouches[0].pageX - pos.left,
  3553. y : orig.changedTouches[0].pageY - pos.top
  3554. };
  3555. };
  3556. var move = function(e) {
  3557. e.preventDefault();
  3558. var orig = e.originalEvent;
  3559. $(this).parent().css({
  3560. top : orig.changedTouches[0].pageY - offset.y,
  3561. left : orig.changedTouches[0].pageX - offset.x
  3562. });
  3563. };
  3564. this.bind("touchstart", start).bind("touchmove", move);
  3565. };
  3566. dialogHeader.touchDraggable();
  3567. }
  3568. editormd.dialogZindex += 2;
  3569. return dialog;
  3570. };
  3571. /**
  3572. * 鼠标和触摸事件的判断/选择方法
  3573. * MouseEvent or TouchEvent type switch
  3574. *
  3575. * @param {String} [mouseEventType="click"] 供选择的鼠标事件
  3576. * @param {String} [touchEventType="touchend"] 供选择的触摸事件
  3577. * @returns {String} EventType 返回事件类型名称
  3578. */
  3579. editormd.mouseOrTouch = function(mouseEventType, touchEventType) {
  3580. mouseEventType = mouseEventType || "click";
  3581. touchEventType = touchEventType || "touchend";
  3582. var eventType = mouseEventType;
  3583. try {
  3584. document.createEvent("TouchEvent");
  3585. eventType = touchEventType;
  3586. } catch(e) {}
  3587. return eventType;
  3588. };
  3589. /**
  3590. * 日期时间的格式化方法
  3591. * Datetime format method
  3592. *
  3593. * @param {String} [format=""] 日期时间的格式,类似PHP的格式
  3594. * @returns {String} datefmt 返回格式化后的日期时间字符串
  3595. */
  3596. editormd.dateFormat = function(format) {
  3597. format = format || "";
  3598. var addZero = function(d) {
  3599. return (d < 10) ? "0" + d : d;
  3600. };
  3601. var date = new Date();
  3602. var year = date.getFullYear();
  3603. var year2 = year.toString().slice(2, 4);
  3604. var month = addZero(date.getMonth() + 1);
  3605. var day = addZero(date.getDate());
  3606. var weekDay = date.getDay();
  3607. var hour = addZero(date.getHours());
  3608. var min = addZero(date.getMinutes());
  3609. var second = addZero(date.getSeconds());
  3610. var ms = addZero(date.getMilliseconds());
  3611. var datefmt = "";
  3612. var ymd = year2 + "-" + month + "-" + day;
  3613. var fymd = year + "-" + month + "-" + day;
  3614. var hms = hour + ":" + min + ":" + second;
  3615. switch (format)
  3616. {
  3617. case "UNIX Time" :
  3618. datefmt = date.getTime();
  3619. break;
  3620. case "UTC" :
  3621. datefmt = date.toUTCString();
  3622. break;
  3623. case "yy" :
  3624. datefmt = year2;
  3625. break;
  3626. case "year" :
  3627. case "yyyy" :
  3628. datefmt = year;
  3629. break;
  3630. case "month" :
  3631. case "mm" :
  3632. datefmt = month;
  3633. break;
  3634. case "cn-week-day" :
  3635. case "cn-wd" :
  3636. var cnWeekDays = ["日", "一", "二", "三", "四", "五", "六"];
  3637. datefmt = "星期" + cnWeekDays[weekDay];
  3638. break;
  3639. case "week-day" :
  3640. case "wd" :
  3641. var weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  3642. datefmt = weekDays[weekDay];
  3643. break;
  3644. case "day" :
  3645. case "dd" :
  3646. datefmt = day;
  3647. break;
  3648. case "hour" :
  3649. case "hh" :
  3650. datefmt = hour;
  3651. break;
  3652. case "min" :
  3653. case "ii" :
  3654. datefmt = min;
  3655. break;
  3656. case "second" :
  3657. case "ss" :
  3658. datefmt = second;
  3659. break;
  3660. case "ms" :
  3661. datefmt = ms;
  3662. break;
  3663. case "yy-mm-dd" :
  3664. datefmt = ymd;
  3665. break;
  3666. case "yyyy-mm-dd" :
  3667. datefmt = fymd;
  3668. break;
  3669. case "yyyy-mm-dd h:i:s ms" :
  3670. case "full + ms" :
  3671. datefmt = fymd + " " + hms + " " + ms;
  3672. break;
  3673. case "full" :
  3674. case "yyyy-mm-dd h:i:s" :
  3675. default:
  3676. datefmt = fymd + " " + hms;
  3677. break;
  3678. }
  3679. return datefmt;
  3680. };
  3681. return editormd;
  3682. }));