diff --git a/www/code/inner.js b/www/code/inner.js index 8f9a4e6d1..e7f080c02 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -15,7 +15,7 @@ define([ 'css!cm/addon/dialog/dialog.css', 'css!cm/addon/fold/foldgutter.css', - 'cm/mode/markdown/markdown', + 'cm/mode/gfm/gfm', 'cm/addon/mode/loadmode', 'cm/mode/meta', 'cm/addon/mode/overlay', @@ -80,7 +80,7 @@ define([ } catch (e) { console.error(e); } }; var drawPreview = Util.throttle(function () { - if (CodeMirror.highlightMode !== 'markdown') { return; } + if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { return; } if (!$previewButton.is('.cp-toolbar-button-active')) { return; } forceDrawPreview(); }, 150); @@ -98,7 +98,7 @@ define([ previewTo = setTimeout(function () { $codeMirror.removeClass('transition'); }, 500); - if (CodeMirror.highlightMode !== 'markdown') { + if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { $previewContainer.show(); } $previewContainer.toggle(); @@ -132,7 +132,7 @@ define([ }); var modeChange = function (mode) { - if (mode === "markdown") { + if (['markdown', 'gfm'].indexOf(mode) !== -1) { $previewButton.show(); framework._.sfCommon.getPadAttribute('previewMode', function (e, data) { if (e) { return void console.error(e); } @@ -293,7 +293,7 @@ define([ framework.onReady(function (newPad) { if (newPad && !CodeMirror.highlightMode) { - CodeMirror.setMode('markdown', evModeChange.fire); + CodeMirror.setMode('gfm', evModeChange.fire); //console.log("%s => %s", CodeMirror.highlightMode, CodeMirror.$language.val()); } diff --git a/www/code/orgmode.js b/www/code/orgmode.js new file mode 100644 index 000000000..09e19966b --- /dev/null +++ b/www/code/orgmode.js @@ -0,0 +1,156 @@ +define([ + 'cm/lib/codemirror', + 'cm/addon/mode/simple' +], function (CodeMirror) { + CodeMirror.__mode = 'orgmode'; + + CodeMirror.defineSimpleMode("orgmode", { + start: [ + {regex: /^(^\*{1,6}\s)(TODO|DOING|WAITING|NEXT){0,1}(CANCELLED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED){0,1}(.*)$/, token: ["header org-level-star", "header org-todo", "header org-done", "header"]}, + {regex: /(^\+[^\/]*\+)/, token: ["strikethrough"]}, + {regex: /(^\*[^\/]*\*)/, token: ["strong"]}, + {regex: /(^\/[^\/]*\/)/, token: ["em"]}, + {regex: /(^\_[^\/]*\_)/, token: ["link"]}, + {regex: /(^\~[^\/]*\~)/, token: ["comment"]}, + {regex: /(^\=[^\/]*\=)/, token: ["comment"]}, + {regex: /\[\[[^\[\]]*\]\[[^\[\]]*\]\]/, token: "url"}, // links + {regex: /\[[xX\s]?\]/, token: 'qualifier'}, // checkbox + {regex: /\#\+BEGIN_[A-Z]*/, token: "comment", next: "env"}, // comments + {regex: /:?[A-Z_]+\:.*/, token: "comment"}, // property drawers + {regex: /(\#\+[A-Z_]*)(\:.*)/, token: ["keyword", 'qualifier']}, // environments + {regex: /(CLOCK\:|SHEDULED\:)(\s.+)/, token: ["comment", "keyword"]} + ], + env: [ + {regex: /.*?\#\+END_[A-Z]*/, token: "comment", next: "start"}, + {regex: /.*/, token: "comment"} + ] + }); + CodeMirror.registerHelper("fold", "orgmode", function(cm, start) { + function headerLevel (lineNo) { + var line = cm.getLine(lineNo); + var match = /^\*+/.exec(line); + if (match && match.length === 1 && /header/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)))) { + return match[0].length; + } + return null; + } + // init + var levelToMatch = headerLevel(start.line); + + // no folding needed + if(levelToMatch === null) { return; } + + // find folding limits + var lastLine = cm.lastLine(); + var end = start.line; + while (end < lastLine){ + end += 1; + var level = headerLevel(end); + if (level && level <= levelToMatch) { + end = end - 1; + break; + } + } + + return { + from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), + to: CodeMirror.Pos(end, cm.getLine(end).length) + }; + }); + CodeMirror.registerGlobalHelper("fold", "drawer", function(mode) { + return mode.name === 'orgmode' ? true : false; + }, function(cm, start) { + function isBeginningOfADrawer(lineNo) { + var line = cm.getLine(lineNo); + var match = /^\:.*\:$/.exec(line); + if(match && match.length === 1 && match[0] !== ':END:'){ + return true; + } + return false; + } + function isEndOfADrawer(lineNo){ + var line = cm.getLine(lineNo); + return line.trim() === ':END:' ? true : false; + } + + var drawer = isBeginningOfADrawer(start.line); + if (drawer === false) { return; } + + // find folding limits + var lastLine = cm.lastLine(); + var end = start.line; + while(end < lastLine){ + end += 1; + if (isEndOfADrawer(end)) { + break; + } + } + return { + from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), + to: CodeMirror.Pos(end, cm.getLine(end).length) + }; + }); + + CodeMirror.afterInit = function(editor){ + function fold(cm, start){ + cm.foldCode(start, null, "fold"); + } + function unfold(cm, start){ + cm.foldCode(start, null, "unfold"); + } + function isFold(cm, start){ + var line = start.line; + var marks = cm.findMarks(CodeMirror.Pos(line, 0), CodeMirror.Pos(line + 1, 0)); + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold && marks[i].find().from.line === line) { return marks[i]; } + } + return false; + } + + var state = { + stab: 'OVERVIEW' + }; + editor.setOption("extraKeys", { + "Tab": function(cm) { + var pos = cm.getCursor(); + return isFold(cm, pos) ? unfold(cm, pos) : fold(cm, pos); + }, + "Shift-Tab": function(cm){ + if(state.stab === "SHOW_ALL"){ + // fold everything that can be fold + state.stab = 'OVERVIEW'; + cm.operation(function() { + for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){ + fold(cm, CodeMirror.Pos(i, 0)); + } + }); + }else{ + // unfold all headers + state.stab = 'SHOW_ALL'; + cm.operation(function() { + for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){ + if(/header/.test(cm.getTokenTypeAt(CodeMirror.Pos(i, 0))) === true){ + unfold(cm, CodeMirror.Pos(i, 0)); + } + } + }); + } + } + }); + + editor.on('touchstart', function(cm){ + setTimeout(function () { + return isFold(cm, cm.getCursor()) ? unfold(cm, cm.getCursor()) : fold(cm, cm.getCursor()); + }, 150); + }); + // fold everything except headers by default + editor.operation(function() { + for (var i = 0; i < editor.lineCount() ; i++) { + if(/header/.test(editor.getTokenTypeAt(CodeMirror.Pos(i, 0))) === false){ + fold(editor, CodeMirror.Pos(i, 0)); + } + } + }); + }; + +}); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 5c7005f4f..8890fe1f9 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -243,13 +243,13 @@ define([ value: 1, checked: 'checked' }), - h('label', { 'for': 'cp-share-editable-true' }, Messages.share_linkEdit), + h('label', { 'for': 'cp-share-editable-true' }, Messages.share_linkEdit), h('input#cp-share-editable-false.cp-share-editable-value', { type: 'radio', name: 'cp-share-editable', value: 0 }), - h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView), + h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView), h('br'), h('br'), h('label', Messages.share_linkOptions), @@ -258,13 +258,13 @@ define([ type: 'checkbox', name: 'cp-share-embed' }), - h('label', { 'for': 'cp-share-embed' }, Messages.share_linkEmbed), + h('label', { 'for': 'cp-share-embed' }, Messages.share_linkEmbed), h('br'), h('input#cp-share-present', { type: 'checkbox', name: 'cp-share-present' }), - h('label', { 'for': 'cp-share-present' }, Messages.share_linkPresent), + h('label', { 'for': 'cp-share-present' }, Messages.share_linkPresent), h('br'), h('br'), UI.dialog.selectable('', { id: 'cp-share-link-preview' }) @@ -366,7 +366,7 @@ define([ }); } common.getAttribute(['general', 'share'], function (err, val) { - val = val || {}; + val = val || {}; if (val.edit === false) { $(link).find('#cp-share-editable-false').attr('checked', true); } @@ -530,7 +530,7 @@ define([ var meta; if (Array.isArray(parsed) && typeof(parsed[3]) === "object") { meta = parsed[3].metadata; // pad - } else if (parsed.info) { + } else if (parsed.info) { meta = parsed.info; // poll } else { meta = parsed.metadata; @@ -572,7 +572,7 @@ define([ UI.confirm(msg, function (yes) { if (!yes) { return; } sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) { - if (err) { return void callback(err); } + if (err) { return void callback(err); } var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted; UI.alert(cMsg, undefined, true); callback(); @@ -702,7 +702,7 @@ define([ }, 'heading': { apply: function (str) { - return '\n'+clean(str).split('\n').map(function (line) { + return '\n'+clean(str).split('\n').map(function (line) { return '# '+line; }).join('\n')+'\n'; }, @@ -714,7 +714,7 @@ define([ }, 'quote': { apply: function (str) { - return '\n\n'+str.split('\n').map(function (line) { + return '\n\n'+str.split('\n').map(function (line) { return '> '+line; }).join('\n')+'\n\n'; }, @@ -722,7 +722,7 @@ define([ }, 'nlist': { apply: function (str) { - return '\n'+clean(str).split('\n').map(function (line) { + return '\n'+clean(str).split('\n').map(function (line) { return '1. '+line; }).join('\n')+'\n'; }, @@ -730,7 +730,7 @@ define([ }, 'list': { apply: function (str) { - return '\n'+clean(str).split('\n').map(function (line) { + return '\n'+clean(str).split('\n').map(function (line) { return '* '+line; }).join('\n')+'\n'; }, @@ -738,8 +738,8 @@ define([ }, 'check': { apply: function (str) { - return '\n' + clean(str).split('\n').map(function (line) { - return '* [ ] ' + line; + return '\n' + clean(str).split('\n').map(function (line) { + return '* [ ] ' + line; }).join('\n') + '\n'; }, icon: 'fa-check-square-o' @@ -1673,13 +1673,13 @@ define([ value: 1, checked: 'checked' }), - h('label', { 'for': 'cp-creation-owned-true' }, Messages.creation_ownedTrue), + h('label', { 'for': 'cp-creation-owned-true' }, Messages.creation_ownedTrue), h('input#cp-creation-owned-false.cp-creation-owned-value', { type: 'radio', name: 'cp-creation-owned', value: 0 }), - h('label', { 'for': 'cp-creation-owned-false' }, Messages.creation_ownedFalse) + h('label', { 'for': 'cp-creation-owned-false' }, Messages.creation_ownedFalse) ]); $creation.append(owned); @@ -1701,13 +1701,13 @@ define([ value: 0, checked: 'checked' }), - h('label', { 'for': 'cp-creation-expire-false' }, Messages.creation_expireFalse), + h('label', { 'for': 'cp-creation-expire-false' }, Messages.creation_expireFalse), h('input#cp-creation-expire-true.cp-creation-expire-value', { type: 'radio', name: 'cp-creation-expire', value: 1 }), - h('label', { 'for': 'cp-creation-expire-true' }, [ + h('label', { 'for': 'cp-creation-expire-true' }, [ Messages.creation_expireTrue, h('span.cp-creation-expire-picker', [ h('input#cp-creation-expire-val', { diff --git a/www/common/modes.js b/www/common/modes.js index e5d9892fb..af2119f8f 100644 --- a/www/common/modes.js +++ b/www/common/modes.js @@ -1,126 +1,127 @@ -define(function () { +define([ + '/code/orgmode.js' +], function () { var Modes = {}; // mode language (extension) var list = Modes.list = [ - "apl apl .apl", - "asciiarmor asciiarmor", - "asn.1 asn.1", - "asterisk asterisk", - "brainfuck brainfuck .b", - "clike clike", - "clojure clojure", - "cmake cmake", - "cobol cobol", - "coffeescript coffeescript", - "commonlisp commonlisp", - "crystal crystal", - "css css .css", - "cypher cypher", - "d d", - "dart dart", - "diff diff", - "django django", - "dockerfile dockerfile", - "dtd dtd", - "dylan dylan", - "ebnf ebnf", - "ecl ecl", - "eiffel eiffel", - "elm elm .elm", - "erlang erlang", - "factor factor", - "fcl fcl", - "forth forth", - "fortran fortran", - "gas gas", - "gfm gfm .md", - "gherkin gherkin", - "go go", - "groovy groovy", - "haml haml", - "handlebars handlebars", - "haskell haskell .hs", - "haskell-literate haskell-literate", - "haxe haxe", - "htmlembedded htmlembedded", - "htmlmixed htmlmixed .html", - "http http", - "idl idl", - "index.html index.html", - "jade jade", - "javascript javascript .js", - "jinja2 jinja2", - "jsx jsx .jsx", - "julia julia", - "livescript livescript", - "lua lua", - "markdown markdown .md", - "mathematica mathematica", - "mirc mirc", - "mllike mllike", - "modelica modelica", - "mscgen mscgen", - "mumps mumps", - "nginx nginx", - "nsis nsis", - "ntriples ntriples", - "octave octave", - "oz oz", - "pascal pascal", - "pegjs pegjs", - "perl perl", - "php php", - "pig pig", - "properties properties", - "protobuf protobuf", - "puppet puppet", - "python python .py", - "q q", - "r r", - "rpm rpm", - "rst rst", - "ruby ruby", - "rust rust", - "sass sass", - "scheme scheme .scm", - "shell shell .sh", - "sieve sieve", - "slim slim", - "smalltalk smalltalk", - "smarty smarty", - "solr solr", - "soy soy", - "sparql sparql", - "spreadsheet spreadsheet", - "sql sql", - "stex stex", - "stylus stylus", - "swift swift", - "tcl tcl", - "text text .txt", - "textile textile", - "tiddlywiki tiddlywiki", - "tiki tiki", - "toml toml", - "tornado tornado", + "APL apl .apl", + "ASCII-Armor asciiarmor", + "ASN.1 asn.1", + "Asterisk asterisk", + "Brainfuck brainfuck .b", + "C-like clike", + "Clojure clojure", + "CMake cmake", + "COBOL cobol", + "CoffeeScript coffeescript", + "Common_Lisp commonlisp", + "Crystal crystal", + "CSS css .css", + "Cypher cypher", + "D d", + "Dart dart", + "Diff diff", + "Django django", + "Dockerfile dockerfile", + "DTD dtd", + "Dylan dylan", + "EBNF ebnf", + "ECL ecl", + "Eiffel eiffel", + "Elm elm .elm", + "Erlang erlang", + "Factor factor", + "FCL fcl", + "Forth forth", + "Fortran fortran", + "GAS gas", + "Gherkin gherkin", + "Go go", + "Groovy groovy", + "Haml haml", + "Handlebars handlebars", + "Haskell haskell .hs", + "Haskell-Literate haskell-literate", + "Haxe haxe", + "HTML htmlmixed .html", + "HTTP http", + "IDL idl", + "JADE jade", + "JavaScript javascript .js", + "Jinja2 jinja2", + "JSX jsx .jsx", + "Julia julia", + "LiveScript livescript", + "Lua lua", + "Markdown gfm .md", + //"markdown markdown .md", + "Mathematica mathematica", + "mIRC mirc", + "ML mllike", + "Modelica modelica", + "MscGen mscgen", + "MUMPS mumps", + "Nginx nginx", + "NSIS nsis", + "N-Triples ntriples", + "Octave octave", + "Org-mode orgmode .org", + "Oz oz", + "Pascal pascal", + "PEG.js pegjs", + "Perl perl", + "PHP php", + "Pig pig", + "Properties properties", + "Protocol_Buffers protobuf", + "Puppet puppet", + "Python python .py", + "Q q", + "R r", + "RPM rpm", + "RST rst", + "Ruby ruby", + "Rust rust", + "Sass sass", + "Scheme scheme .scm", + "Shell shell .sh", + "Sieve sieve", + "Slim slim", + "Smalltalk smalltalk", + "Smarty smarty", + "Solr solr", + "Soy soy", + "SPARQL sparql", + "Spreadsheet spreadsheet", + "SQL sql", + "sTeX stex", + "Stylus stylus", + "Swift swift", + "Tcl tcl", + "Text text .txt", + "Textile textile", + "TiddlyWiki tiddlywiki", + "Tiki tiki", + "TOML toml", + "Tornado tornado", "troff troff", - "ttcn ttcn", - "ttcn-cfg ttcn-cfg", - "turtle turtle", - "twig twig", - "vb vb", - "vbscript vbscript", - "velocity velocity", - "verilog verilog", - "vhdl vhdl", - "vue vue", - "xml xml", + "TTCN ttcn", + "TTCN-cfg ttcn-cfg", + "Turtle turtle", + "Twig twig", + "Visual_Basic vb", + "VBScript vbscript", + "Velocity velocity", + "Verilog verilog", + "VHDL vhdl", + "Vue vue", + "XML xml", //"xwiki xwiki21", - "xquery xquery", - "yaml yaml .yaml", - "yaml-frontmatter yaml-frontmatter", - "z80 z80" + "XQuery xquery", + "YAML yaml .yaml", + "YAML_Frontmatter yaml-frontmatter", + "Z80 z80" ].map(function (line) { var kv = line.split(/\s/); return { diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index ff6f817f9..cb507c2a8 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -146,6 +146,7 @@ define([ var setMode = exp.setMode = function (mode, cb) { exp.highlightMode = mode; + if (mode === 'markdown') { mode = 'gfm'; } if (mode !== "text") { CMeditor.autoLoadMode(editor, mode); }