diff --git a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js index 0ac86768..4ccbbe01 100755 --- a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js +++ b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js @@ -65,7 +65,16 @@ const className = el.className.split(' ') if (className.indexOf('cm-url') !== -1) { - const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(el.textContent) + // multiple cm-url because of search term + const cmUrlSpans = Array.from( + el.parentNode.getElementsByClassName('cm-url') + ) + const textContent = + cmUrlSpans.length > 1 + ? cmUrlSpans.map(span => span.textContent).join('') + : el.textContent + + const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(textContent) const url = match[1] || match[2] || match[3] // `:storage` is the value of the variable `STORAGE_FOLDER_PLACEHOLDER` defined in `browser/main/lib/dataApi/attachmentManagement` @@ -134,7 +143,16 @@ return } - const rawHref = e.target.innerText.trim().slice(1, -1) // get link text from markdown text + // Create URL spans array used for special case "search term is hitting a link". + const cmUrlSpans = Array.from( + e.target.parentNode.getElementsByClassName('cm-url') + ) + + const innerText = + cmUrlSpans.length > 1 + ? cmUrlSpans.map(span => span.textContent).join('') + : e.target.innerText + const rawHref = innerText.trim().slice(1, -1) // get link text from markdown text if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() diff --git a/extra_scripts/codemirror/mode/bfm/bfm.js b/extra_scripts/codemirror/mode/bfm/bfm.js index 80f797b9..d08183cd 100644 --- a/extra_scripts/codemirror/mode/bfm/bfm.js +++ b/extra_scripts/codemirror/mode/bfm/bfm.js @@ -1,10 +1,20 @@ -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../codemirror/lib/codemirror"), require("../codemirror/mode/gfm/gfm"), require("../codemirror/mode/yaml-frontmatter/yaml-frontmatter")) - else if (typeof define == "function" && define.amd) // AMD - define(["../codemirror/lib/codemirror", "../codemirror/mode/gfm/gfm", "../codemirror/mode/yaml-frontmatter/yaml-frontmatter"], mod) - else // Plain browser env - mod(CodeMirror) +;(function(mod) { + if (typeof exports == 'object' && typeof module == 'object') + // CommonJS + mod( + require('../codemirror/lib/codemirror'), + require('../codemirror/mode/gfm/gfm'), + require('../codemirror/mode/yaml-frontmatter/yaml-frontmatter') + ) + else if (typeof define == 'function' && define.amd) + // AMD + define([ + '../codemirror/lib/codemirror', + '../codemirror/mode/gfm/gfm', + '../codemirror/mode/yaml-frontmatter/yaml-frontmatter' + ], mod) + // Plain browser env + else mod(CodeMirror) })(function(CodeMirror) { 'use strict' @@ -45,189 +55,208 @@ } } - CodeMirror.defineMode('bfm', function (config, baseConfig) { - baseConfig.name = 'yaml-frontmatter' - const baseMode = CodeMirror.getMode(config, baseConfig) + CodeMirror.defineMode( + 'bfm', + function(config, baseConfig) { + baseConfig.name = 'yaml-frontmatter' + const baseMode = CodeMirror.getMode(config, baseConfig) - return { - startState: function() { - return { - baseState: CodeMirror.startState(baseMode), + return { + startState: function() { + return { + baseState: CodeMirror.startState(baseMode), - basePos: 0, - baseCur: null, - overlayPos: 0, - overlayCur: null, - streamSeen: null, + basePos: 0, + baseCur: null, + overlayPos: 0, + overlayCur: null, + streamSeen: null, - fencedEndRE: null, + fencedEndRE: null, - inTable: false, - rowIndex: 0 - } - }, - copyState: function(s) { - return { - baseState: CodeMirror.copyState(baseMode, s.baseState), + inTable: false, + rowIndex: 0 + } + }, + copyState: function(s) { + return { + baseState: CodeMirror.copyState(baseMode, s.baseState), - basePos: s.basePos, - baseCur: null, - overlayPos: s.overlayPos, - overlayCur: null, + basePos: s.basePos, + baseCur: null, + overlayPos: s.overlayPos, + overlayCur: null, - fencedMode: s.fencedMode, - fencedState: s.fencedMode ? CodeMirror.copyState(s.fencedMode, s.fencedState) : null, + fencedMode: s.fencedMode, + fencedState: s.fencedMode + ? CodeMirror.copyState(s.fencedMode, s.fencedState) + : null, - fencedEndRE: s.fencedEndRE, + fencedEndRE: s.fencedEndRE, - inTable: s.inTable, - rowIndex: s.rowIndex - } - }, - token: function(stream, state) { - const initialPos = stream.pos + inTable: s.inTable, + rowIndex: s.rowIndex + } + }, + token: function(stream, state) { + const initialPos = stream.pos - if (state.fencedEndRE && stream.match(state.fencedEndRE)) { - state.fencedEndRE = null - state.fencedMode = null - state.fencedState = null + if (state.fencedEndRE && stream.match(state.fencedEndRE)) { + state.fencedEndRE = null + state.fencedMode = null + state.fencedState = null - stream.pos = initialPos - } - else { - if (state.fencedMode) { - return state.fencedMode.token(stream, state.fencedState) + stream.pos = initialPos + } else { + if (state.fencedMode) { + return state.fencedMode.token(stream, state.fencedState) + } + + const match = stream.match(fencedCodeRE, true) + if (match) { + state.fencedEndRE = new RegExp(match[1] + '+ *$') + + state.fencedMode = getMode( + match[2], + match[3], + config, + stream.lineOracle.doc.cm + ) + if (state.fencedMode) { + state.fencedState = CodeMirror.startState(state.fencedMode) + } + + stream.pos = initialPos + } + } + + if ( + stream != state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start + ) { + state.streamSeen = stream + state.basePos = state.overlayPos = stream.start + } + + if (stream.start == state.basePos) { + state.baseCur = baseMode.token(stream, state.baseState) + state.basePos = stream.pos + } + if (stream.start == state.overlayPos) { + stream.pos = stream.start + state.overlayCur = this.overlayToken(stream, state) + state.overlayPos = stream.pos + } + stream.pos = Math.min(state.basePos, state.overlayPos) + + if (state.overlayCur == null) { + return state.baseCur + } else if (state.baseCur != null && state.combineTokens) { + return state.baseCur + ' ' + state.overlayCur + } else { + return state.overlayCur + } + }, + overlayToken: function(stream, state) { + state.combineTokens = false + + if (state.fencedEndRE && stream.match(state.fencedEndRE)) { + state.fencedEndRE = null + state.localMode = null + state.localState = null + + return null + } + + if (state.localMode) { + return state.localMode.token(stream, state.localState) || '' } const match = stream.match(fencedCodeRE, true) if (match) { state.fencedEndRE = new RegExp(match[1] + '+ *$') - state.fencedMode = getMode(match[2], match[3], config, stream.lineOracle.doc.cm) - if (state.fencedMode) { - state.fencedState = CodeMirror.startState(state.fencedMode) + state.localMode = getMode( + match[2], + match[3], + config, + stream.lineOracle.doc.cm + ) + if (state.localMode) { + state.localState = CodeMirror.startState(state.localMode) } - stream.pos = initialPos - } - } - - if (stream != state.streamSeen || Math.min(state.basePos, state.overlayPos) < stream.start) { - state.streamSeen = stream - state.basePos = state.overlayPos = stream.start - } - - if (stream.start == state.basePos) { - state.baseCur = baseMode.token(stream, state.baseState) - state.basePos = stream.pos - } - if (stream.start == state.overlayPos) { - stream.pos = stream.start - state.overlayCur = this.overlayToken(stream, state) - state.overlayPos = stream.pos - } - stream.pos = Math.min(state.basePos, state.overlayPos) - - if (state.overlayCur == null) { - return state.baseCur - } - else if (state.baseCur != null && state.combineTokens) { - return state.baseCur + ' ' + state.overlayCur - } - else { - return state.overlayCur - } - }, - overlayToken: function(stream, state) { - state.combineTokens = false - - if (state.fencedEndRE && stream.match(state.fencedEndRE)) { - state.fencedEndRE = null - state.localMode = null - state.localState = null - - return null - } - - if (state.localMode) { - return state.localMode.token(stream, state.localState) || '' - } - - const match = stream.match(fencedCodeRE, true) - if (match) { - state.fencedEndRE = new RegExp(match[1] + '+ *$') - - state.localMode = getMode(match[2], match[3], config, stream.lineOracle.doc.cm) - if (state.localMode) { - state.localState = CodeMirror.startState(state.localMode) - } - - return null - } - - state.combineTokens = true - - if (state.inTable) { - if (stream.match(/^\|/)) { - ++state.rowIndex - - stream.skipToEnd() - - if (state.rowIndex === 1) { - return 'table table-separator' - } else if (state.rowIndex % 2 === 0) { - return 'table table-row table-row-even' - } else { - return 'table table-row table-row-odd' - } - } else { - state.inTable = false - - stream.skipToEnd() return null } - } else if (stream.match(/^\|/)) { - state.inTable = true - state.rowIndex = 0 + + state.combineTokens = true + + if (state.inTable) { + if (stream.match(/^\|/)) { + ++state.rowIndex + + stream.skipToEnd() + + if (state.rowIndex === 1) { + return 'table table-separator' + } else if (state.rowIndex % 2 === 0) { + return 'table table-row table-row-even' + } else { + return 'table table-row table-row-odd' + } + } else { + state.inTable = false + + stream.skipToEnd() + return null + } + } else if (stream.match(/^\|/)) { + state.inTable = true + state.rowIndex = 0 + + stream.skipToEnd() + return 'table table-header' + } stream.skipToEnd() - return 'table table-header' - } - - stream.skipToEnd() - return null - }, - electricChars: baseMode.electricChars, - innerMode: function(state) { - if (state.fencedMode) { - return { - mode: state.fencedMode, - state: state.fencedState + return null + }, + electricChars: baseMode.electricChars, + innerMode: function(state) { + if (state.fencedMode) { + return { + mode: state.fencedMode, + state: state.fencedState + } + } else { + return { + mode: baseMode, + state: state.baseState + } } - } else { - return { - mode: baseMode, - state: state.baseState - } - } - }, - blankLine: function(state) { - state.inTable = false + }, + blankLine: function(state) { + state.inTable = false - if (state.fencedMode) { - return state.fencedMode.blankLine && state.fencedMode.blankLine(state.fencedState) - } else { - return baseMode.blankLine(state.baseState) + if (state.fencedMode) { + return ( + state.fencedMode.blankLine && + state.fencedMode.blankLine(state.fencedState) + ) + } else { + return baseMode.blankLine(state.baseState) + } } } - } - }, 'yaml-frontmatter') + }, + 'yaml-frontmatter' + ) CodeMirror.defineMIME('text/x-bfm', 'bfm') CodeMirror.modeInfo.push({ - name: "Boost Flavored Markdown", - mime: "text/x-bfm", - mode: "bfm" + name: 'Boost Flavored Markdown', + mime: 'text/x-bfm', + mode: 'bfm' }) -}) \ No newline at end of file +}) diff --git a/extra_scripts/codemirror/mode/gfm/gfm.js b/extra_scripts/codemirror/mode/gfm/gfm.js new file mode 100644 index 00000000..9fed7591 --- /dev/null +++ b/extra_scripts/codemirror/mode/gfm/gfm.js @@ -0,0 +1,157 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +;(function(mod) { + if (typeof exports == 'object' && typeof module == 'object') + // CommonJS + mod( + require('../codemirror/lib/codemirror'), + require('../codemirror/mode/markdown/markdown'), + require('../codemirror/addon/mode/overlay') + ) + else if (typeof define == 'function' && define.amd) + // AMD + define([ + '../codemirror/lib/codemirror', + '../codemirror/mode/markdown/markdown', + '../codemirror/addon/mode/overlay' + ], mod) + // Plain browser env + else mod(CodeMirror) +})(function(CodeMirror) { + 'use strict' + + var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i + + CodeMirror.defineMode( + 'gfm', + function(config, modeConfig) { + var codeDepth = 0 + function blankLine(state) { + state.code = false + return null + } + var gfmOverlay = { + startState: function() { + return { + code: false, + codeBlock: false, + ateSpace: false + } + }, + copyState: function(s) { + return { + code: s.code, + codeBlock: s.codeBlock, + ateSpace: s.ateSpace + } + }, + token: function(stream, state) { + state.combineTokens = null + + // Hack to prevent formatting override inside code blocks (block and inline) + if (state.codeBlock) { + if (stream.match(/^```+/)) { + state.codeBlock = false + return null + } + stream.skipToEnd() + return null + } + if (stream.sol()) { + state.code = false + } + if (stream.sol() && stream.match(/^```+/)) { + stream.skipToEnd() + state.codeBlock = true + return null + } + // If this block is changed, it may need to be updated in Markdown mode + if (stream.peek() === '`') { + stream.next() + var before = stream.pos + stream.eatWhile('`') + var difference = 1 + stream.pos - before + if (!state.code) { + codeDepth = difference + state.code = true + } else { + if (difference === codeDepth) { + // Must be exact + state.code = false + } + } + return null + } else if (state.code) { + stream.next() + return null + } + // Check if space. If so, links can be formatted later on + if (stream.eatSpace()) { + state.ateSpace = true + return null + } + if (stream.sol() || state.ateSpace) { + state.ateSpace = false + if (modeConfig.gitHubSpice !== false) { + if ( + stream.match( + /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/ + ) + ) { + // User/Project@SHA + // User@SHA + // SHA + state.combineTokens = true + return 'link' + } else if ( + stream.match( + /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/ + ) + ) { + // User/Project#Num + // User#Num + // #Num + state.combineTokens = true + return 'link' + } + } + } + if ( + stream.match(urlRE) && + stream.string.slice(stream.start - 2, stream.start) != '](' && + (stream.start == 0 || + /\W/.test(stream.string.charAt(stream.start - 1))) + ) { + // URLs + // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls + // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine + // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL + state.combineTokens = true + return 'link' + } + stream.next() + return null + }, + blankLine: blankLine + } + + var markdownConfig = { + taskLists: true, + strikethrough: true, + emoji: true + } + for (var attr in modeConfig) { + markdownConfig[attr] = modeConfig[attr] + } + markdownConfig.name = 'markdown' + return CodeMirror.overlayMode( + CodeMirror.getMode(config, markdownConfig), + gfmOverlay + ) + }, + 'markdown' + ) + + CodeMirror.defineMIME('text/x-gfm', 'gfm') +}) diff --git a/lib/main.development.html b/lib/main.development.html index 63e50af1..900c66c7 100644 --- a/lib/main.development.html +++ b/lib/main.development.html @@ -108,12 +108,12 @@ - + diff --git a/lib/main.production.html b/lib/main.production.html index aea19e3c..05d80345 100644 --- a/lib/main.production.html +++ b/lib/main.production.html @@ -104,12 +104,12 @@ - +